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); } } }
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); }
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); } }