示例#1
0
 protected override void Close_internal(ref bool disposed)
 {
     WebConnection.Debug($"{ME} CLOSE: disposed={disposed} closed={closed} nextReadCalled={nextReadCalled}");
     if (!closed && !nextReadCalled)
     {
         nextReadCalled = true;
         WebConnection.Debug($"{ME} CLOSE #1: read_eof={read_eof} bufferedEntireContent={bufferedEntireContent}");
         if (read_eof || bufferedEntireContent)
         {
             disposed = true;
             innerStream?.Dispose();
             innerStream = null;
             Operation.Finish(true);
         }
         else
         {
             // If we have not read all the contents
             closed   = true;
             disposed = true;
             Operation.Finish(false);
         }
     }
 }
示例#2
0
        internal async Task ReadAllAsync(bool resending, CancellationToken cancellationToken)
        {
            WebConnection.Debug($"{ME} READ ALL ASYNC: resending={resending} eof={read_eof} " +
                                "nextReadCalled={nextReadCalled}");
            if (read_eof || bufferedEntireContent || nextReadCalled)
            {
                if (!nextReadCalled)
                {
                    nextReadCalled = true;
                    Operation.Finish(true);
                }
                return;
            }

            var completion = new WebCompletionSource();
            var timeoutCts = new CancellationTokenSource();

            try {
                var timeoutTask = Task.Delay(ReadTimeout, timeoutCts.Token);
                while (true)
                {
                    /*
                     * 'currentRead' is set by ReadAsync().
                     */
                    cancellationToken.ThrowIfCancellationRequested();
                    var oldCompletion = Interlocked.CompareExchange(ref pendingRead, completion, null);
                    if (oldCompletion == null)
                    {
                        break;
                    }

                    // ReadAsync() is in progress.
                    var oldReadTask = oldCompletion.WaitForCompletion();
                    var anyTask     = await Task.WhenAny(oldReadTask, timeoutTask).ConfigureAwait(false);

                    if (anyTask == timeoutTask)
                    {
                        throw new WebException("The operation has timed out.", WebExceptionStatus.Timeout);
                    }
                }
            } finally {
                timeoutCts.Cancel();
                timeoutCts.Dispose();
            }

            WebConnection.Debug($"{ME} READ ALL ASYNC #1");

            try {
                cancellationToken.ThrowIfCancellationRequested();

                /*
                 * We may have awaited on the 'readTcs', so check
                 * for eof again as ReadAsync() may have set it.
                 */
                if (read_eof || bufferedEntireContent)
                {
                    return;
                }

                /*
                 * Simplify: if we're resending on a new connection,
                 * then we can simply close the connection here.
                 */
                if (resending && !KeepAlive)
                {
                    Close();
                    return;
                }

                var buffer = await ReadAllAsyncInner(cancellationToken).ConfigureAwait(false);

                var bos = new BufferOffsetSize(buffer, 0, buffer.Length, false);
                innerStream = new BufferedReadStream(Operation, null, bos);

                nextReadCalled = true;
                completion.TrySetCompleted();
            } catch (Exception ex) {
                WebConnection.Debug($"{ME} READ ALL ASYNC EX: {ex.Message}");
                completion.TrySetException(ex);
                throw;
            } finally {
                WebConnection.Debug($"{ME} READ ALL ASYNC #2");
                pendingRead = null;
            }

            Operation.Finish(true);
        }
示例#3
0
        void Initialize(BufferOffsetSize buffer)
        {
            WebConnection.Debug($"{ME} INIT: status={(int)StatusCode} bos={buffer.Offset}/{buffer.Size}");

            long   contentLength;
            string contentType = Headers["Transfer-Encoding"];
            bool   chunkedRead = (contentType != null && contentType.IndexOf("chunked", StringComparison.OrdinalIgnoreCase) != -1);
            string clength     = Headers["Content-Length"];

            if (!chunkedRead && !string.IsNullOrEmpty(clength))
            {
                if (!long.TryParse(clength, out contentLength))
                {
                    contentLength = Int64.MaxValue;
                }
            }
            else
            {
                contentLength = Int64.MaxValue;
            }

            string tencoding = null;

            if (ExpectContent)
            {
                tencoding = Headers["Transfer-Encoding"];
            }

            ChunkedRead = (tencoding != null && tencoding.IndexOf("chunked", StringComparison.OrdinalIgnoreCase) != -1);

            if (Version == HttpVersion.Version11 && RequestStream.KeepAlive)
            {
                KeepAlive = true;
                var cncHeader = Headers[ServicePoint.UsesProxy ? "Proxy-Connection" : "Connection"];
                if (cncHeader != null)
                {
                    cncHeader = cncHeader.ToLower();
                    KeepAlive = cncHeader.IndexOf("keep-alive", StringComparison.Ordinal) != -1;
                    if (cncHeader.IndexOf("close", StringComparison.Ordinal) != -1)
                    {
                        KeepAlive = false;
                    }
                }

                if (!ChunkedRead && contentLength == Int64.MaxValue)
                {
                    /*
                     * This is a violation of the HTTP Spec - the server neither send a
                     * "Content-Length:" nor a "Transfer-Encoding: chunked" header.
                     * The only way to recover from this is to keep reading until the
                     * remote closes the connection, so we can't reuse it.
                     */
                    KeepAlive = false;
                }
            }

            /*
             * Inner layer:
             * We may have read a few extra bytes while parsing the headers, these will be
             * passed to us in the @buffer parameter and we need to read these before
             * reading from the `InnerStream`.
             */
            Stream networkStream;

            if (!ExpectContent || (!ChunkedRead && buffer.Size >= contentLength))
            {
                bufferedEntireContent = true;
                innerStream           = new BufferedReadStream(Operation, null, buffer);
                networkStream         = innerStream;
            }
            else if (buffer.Size > 0)
            {
                networkStream = new BufferedReadStream(Operation, RequestStream.InnerStream, buffer);
            }
            else
            {
                networkStream = RequestStream.InnerStream;
            }

            /*
             * Intermediate layer:
             * - Wrap with MonoChunkStream when using chunked encoding.
             * - Otherwise, we should have a Content-Length, wrap with
             *   FixedSizeReadStream to read exactly that many bytes.
             */
            if (ChunkedRead)
            {
                innerStream = new MonoChunkStream(Operation, networkStream, Headers);
            }
            else if (bufferedEntireContent)
            {
                // 'innerStream' has already been set above.
            }
            else if (contentLength != Int64.MaxValue)
            {
                innerStream = new FixedSizeReadStream(Operation, networkStream, contentLength);
            }
            else
            {
                // Violation of the HTTP Spec - neither chunked nor length.
                innerStream = new BufferedReadStream(Operation, networkStream, null);
            }

            /*
             * Outer layer:
             * - Decode gzip/deflate if requested.
             */
            string content_encoding = Headers["Content-Encoding"];

            if (content_encoding == "gzip" && (Request.AutomaticDecompression & DecompressionMethods.GZip) != 0)
            {
                innerStream = ContentDecodeStream.Create(Operation, innerStream, ContentDecodeStream.Mode.GZip);
                Headers.Remove(HttpRequestHeader.ContentEncoding);
            }
            else if (content_encoding == "deflate" && (Request.AutomaticDecompression & DecompressionMethods.Deflate) != 0)
            {
                innerStream = ContentDecodeStream.Create(Operation, innerStream, ContentDecodeStream.Mode.Deflate);
                Headers.Remove(HttpRequestHeader.ContentEncoding);
            }

            WebConnection.Debug($"{ME} INIT #1: - {ExpectContent} {closed} {nextReadCalled}");

            if (!ExpectContent)
            {
                nextReadCalled = true;
                Operation.Finish(true);
            }
        }