public override async ValueTask <int> ReadAsync(Memory <byte> buffer, CancellationToken cancellationToken) { CancellationHelper.ThrowIfCancellationRequested(cancellationToken); if (_connection == null || buffer.Length == 0) { // Response body fully consumed or the caller didn't ask for any data return(0); } ValueTask <int> readTask = _connection.ReadBufferedAsync(buffer); int bytesRead; if (readTask.IsCompletedSuccessfully) { bytesRead = readTask.Result; } else { CancellationTokenRegistration ctr = _connection.RegisterCancellation(cancellationToken); try { bytesRead = await readTask.ConfigureAwait(false); } #if MONOTOUCH_WATCH catch (Exception exc) { if (CancellationHelper.ShouldWrapInOperationCanceledException(exc, cancellationToken)) { throw CancellationHelper.CreateOperationCanceledException(exc, cancellationToken); } throw; } #else catch (Exception exc) when(CancellationHelper.ShouldWrapInOperationCanceledException(exc, cancellationToken)) { throw CancellationHelper.CreateOperationCanceledException(exc, cancellationToken); } #endif finally { ctr.Dispose(); } } if (bytesRead == 0) { // A cancellation request may have caused the EOF. CancellationHelper.ThrowIfCancellationRequested(cancellationToken); // We cannot reuse this connection, so close it. _connection.Dispose(); _connection = null; return(0); } return(bytesRead); }
public static async ValueTask <(Socket, Stream)> ConnectAsync(string host, int port, CancellationToken cancellationToken) { // Rather than creating a new Socket and calling ConnectAsync on it, we use the static // Socket.ConnectAsync with a SocketAsyncEventArgs, as we can then use Socket.CancelConnectAsync // to cancel it if needed. Rent or allocate one. ConnectEventArgs saea; if (!s_connectEventArgs.TryDequeue(out saea)) { saea = new ConnectEventArgs(); } try { saea.Initialize(cancellationToken); // Configure which server to which to connect. saea.RemoteEndPoint = new DnsEndPoint(host, port); // Initiate the connection. if (Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, saea)) { // Connect completing asynchronously. Enable it to be canceled and wait for it. using (cancellationToken.Register(s => Socket.CancelConnectAsync((SocketAsyncEventArgs)s), saea)) { await saea.Builder.Task.ConfigureAwait(false); } } else if (saea.SocketError != SocketError.Success) { // Connect completed synchronously but unsuccessfully. throw new SocketException((int)saea.SocketError); } Debug.Assert(saea.SocketError == SocketError.Success, $"Expected Success, got {saea.SocketError}."); Debug.Assert(saea.ConnectSocket != null, "Expected non-null socket"); // Configure the socket and return a stream for it. Socket socket = saea.ConnectSocket; socket.NoDelay = true; return(socket, new NetworkStream(socket, ownsSocket: true)); } catch (Exception error) { throw CancellationHelper.ShouldWrapInOperationCanceledException(error, cancellationToken) ? CancellationHelper.CreateOperationCanceledException(error, cancellationToken) : new HttpRequestException(error.Message, error); } finally { // Pool the event args, or if the pool is full, dispose of it. saea.Clear(); if (!s_connectEventArgs.TryEnqueue(saea)) { saea.Dispose(); } } }
public override async ValueTask <int> ReadAsync(Memory <byte> buffer, CancellationToken cancellationToken) { CancellationHelper.ThrowIfCancellationRequested(cancellationToken); HttpConnection?connection = _connection; if (connection == null || buffer.Length == 0) { // Response body fully consumed or the caller didn't ask for any data return(0); } ValueTask <int> readTask = connection.ReadAsync(buffer); int bytesRead; if (readTask.IsCompletedSuccessfully) { bytesRead = readTask.Result; } else { CancellationTokenRegistration ctr = connection.RegisterCancellation(cancellationToken); try { bytesRead = await readTask.ConfigureAwait(false); } catch (Exception exc) when(CancellationHelper.ShouldWrapInOperationCanceledException(exc, cancellationToken)) { throw CancellationHelper.CreateOperationCanceledException(exc, cancellationToken); } finally { ctr.Dispose(); } } if (bytesRead == 0) { // If cancellation is requested and tears down the connection, it could cause the read // to return 0, which would otherwise signal the end of the data, but that would lead // the caller to think that it actually received all of the data, rather than it ending // early due to cancellation. So we prioritize cancellation in this race condition, and // if we read 0 bytes and then find that cancellation has requested, we assume cancellation // was the cause and throw. CancellationHelper.ThrowIfCancellationRequested(cancellationToken); // We cannot reuse this connection, so close it. if (HttpTelemetry.IsEnabled) { LogRequestStop(); } _connection = null; connection.Dispose(); } return(bytesRead); }
public override async ValueTask <int> ReadAsync(Memory <byte> buffer, CancellationToken cancellationToken) { CancellationHelper.ThrowIfCancellationRequested(cancellationToken); HttpConnection?connection = _connection; if (connection == null) { // Response body fully consumed return(0); } ValueTask <int> readTask = connection.ReadBufferedAsync(buffer); int bytesRead; if (readTask.IsCompletedSuccessfully) { bytesRead = readTask.Result; } else { CancellationTokenRegistration ctr = connection.RegisterCancellation(cancellationToken); try { bytesRead = await readTask.ConfigureAwait(false); } catch (Exception exc) when(CancellationHelper.ShouldWrapInOperationCanceledException(exc, cancellationToken)) { throw CancellationHelper.CreateOperationCanceledException(exc, cancellationToken); } finally { ctr.Dispose(); } } if (bytesRead == 0 && buffer.Length != 0) { // A cancellation request may have caused the EOF. CancellationHelper.ThrowIfCancellationRequested(cancellationToken); // We cannot reuse this connection, so close it. _connection = null; connection.Dispose(); } return(bytesRead); }
private async Task WaitWithConnectionCancellationAsync(Task task, CancellationToken cancellationToken) { CancellationTokenRegistration ctr = _connection.RegisterCancellation(cancellationToken); try { await task.ConfigureAwait(false); } catch (Exception exc) when (CancellationHelper.ShouldWrapInOperationCanceledException(exc, cancellationToken)) { throw CancellationHelper.CreateOperationCanceledException(exc, cancellationToken); } finally { ctr.Dispose(); } }
private async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken) { CancellationTokenRegistration ctr = _connection.RegisterCancellation(cancellationToken); try { while (true) { while (true) { ReadOnlyMemory <byte> bytesRead = ReadChunkFromConnectionBuffer(int.MaxValue, ctr); if (bytesRead.Length == 0) { break; } await destination.WriteAsync(bytesRead, cancellationToken).ConfigureAwait(false); } if (_connection == null) { // Fully consumed the response. return; } await _connection.FillAsync().ConfigureAwait(false); } } #if MONOTOUCH_WATCH catch (Exception exc) { if (CancellationHelper.ShouldWrapInOperationCanceledException(exc, cancellationToken)) { throw CancellationHelper.CreateOperationCanceledException(exc, cancellationToken); } throw; } #else catch (Exception exc) when(CancellationHelper.ShouldWrapInOperationCanceledException(exc, cancellationToken)) { throw CancellationHelper.CreateOperationCanceledException(exc, cancellationToken); } #endif finally { ctr.Dispose(); } }
private static async ValueTask <SslStream> EstablishSslConnectionAsyncCore(bool async, Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken) { SslStream sslStream = new SslStream(stream); try { if (async) { await sslStream.AuthenticateAsClientAsync(sslOptions, cancellationToken).ConfigureAwait(false); } else { using (cancellationToken.UnsafeRegister(s => ((Stream)s !).Dispose(), stream)) { sslStream.AuthenticateAsClient(sslOptions); } } } catch (Exception e) { sslStream.Dispose(); if (e is OperationCanceledException) { throw; } if (CancellationHelper.ShouldWrapInOperationCanceledException(e, cancellationToken)) { throw CancellationHelper.CreateOperationCanceledException(e, cancellationToken); } throw new HttpRequestException(SR.net_http_ssl_connection_failed, e); } // Handle race condition if cancellation happens after SSL auth completes but before the registration is disposed if (cancellationToken.IsCancellationRequested) { sslStream.Dispose(); throw CancellationHelper.CreateOperationCanceledException(null, cancellationToken); } return(sslStream); }
private async Task CompleteCopyToAsync(Task copyTask, CancellationToken cancellationToken) { Debug.Assert(_connection != null); CancellationTokenRegistration ctr = _connection.RegisterCancellation(cancellationToken); try { await copyTask.ConfigureAwait(false); } catch (Exception exc) when(CancellationHelper.ShouldWrapInOperationCanceledException(exc, cancellationToken)) { throw CancellationHelper.CreateOperationCanceledException(exc, cancellationToken); } finally { ctr.Dispose(); } Finish(); }
private async Task CompleteCopyToAsync(Task copyTask, CancellationToken cancellationToken) { CancellationTokenRegistration ctr = _connection.RegisterCancellation(cancellationToken); try { await copyTask.ConfigureAwait(false); } catch (Exception exc) when (CancellationHelper.ShouldWrapInOperationCanceledException(exc, cancellationToken)) { throw CancellationHelper.CreateOperationCanceledException(exc, cancellationToken); } finally { ctr.Dispose(); } // If cancellation is requested and tears down the connection, it could cause the copy // to end early but think it ended successfully. So we prioritize cancellation in this // race condition, and if we find after the copy has completed that cancellation has // been requested, we assume the copy completed due to cancellation and throw. CancellationHelper.ThrowIfCancellationRequested(cancellationToken); Finish(); }
private static async ValueTask <SslStream> EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken) { SslStream sslStream = new SslStream(stream); // TODO #25206 and #24430: Register/IsCancellationRequested should be removable once SslStream auth and sockets respect cancellation. CancellationTokenRegistration ctr = cancellationToken.Register(s => ((Stream)s).Dispose(), stream); try { await sslStream.AuthenticateAsClientAsync(sslOptions, cancellationToken).ConfigureAwait(false); } catch (Exception e) { sslStream.Dispose(); if (CancellationHelper.ShouldWrapInOperationCanceledException(e, cancellationToken)) { throw CancellationHelper.CreateOperationCanceledException(e, cancellationToken); } throw new HttpRequestException(SR.net_http_ssl_connection_failed, e); } finally { ctr.Dispose(); } // Handle race condition if cancellation happens after SSL auth completes but before the registration is disposed if (cancellationToken.IsCancellationRequested) { sslStream.Dispose(); throw CancellationHelper.CreateOperationCanceledException(null, cancellationToken); } return(sslStream); }
private async ValueTask <int> ReadAsyncCore(Memory <byte> buffer, CancellationToken cancellationToken) { // Should only be called if ReadChunksFromConnectionBuffer returned 0. Debug.Assert(_connection != null); Debug.Assert(buffer.Length > 0); CancellationTokenRegistration ctr = _connection.RegisterCancellation(cancellationToken); try { while (true) { if (_connection == null) { // Fully consumed the response in ReadChunksFromConnectionBuffer. return(0); } if (_state == ParsingState.ExpectChunkData && buffer.Length >= _connection.ReadBufferSize && _chunkBytesRemaining >= (ulong)_connection.ReadBufferSize) { // As an optimization, we skip going through the connection's read buffer if both // the remaining chunk data and the buffer are both at least as large // as the connection buffer. That avoids an unnecessary copy while still reading // the maximum amount we'd otherwise read at a time. Debug.Assert(_connection.RemainingBuffer.Length == 0); int bytesRead = await _connection.ReadAsync(buffer.Slice(0, (int)Math.Min((ulong)buffer.Length, _chunkBytesRemaining))).ConfigureAwait(false); if (bytesRead == 0) { throw new IOException(SR.Format(SR.net_http_invalid_response_premature_eof_bytecount, _chunkBytesRemaining)); } _chunkBytesRemaining -= (ulong)bytesRead; if (_chunkBytesRemaining == 0) { _state = ParsingState.ExpectChunkTerminator; } return(bytesRead); } // We're only here if we need more data to make forward progress. await _connection.FillAsync().ConfigureAwait(false); // Now that we have more, see if we can get any response data, and if // we can we're done. int bytesCopied = ReadChunksFromConnectionBuffer(buffer.Span, ctr); if (bytesCopied > 0) { return(bytesCopied); } } } catch (Exception exc) when(CancellationHelper.ShouldWrapInOperationCanceledException(exc, cancellationToken)) { throw CancellationHelper.CreateOperationCanceledException(exc, cancellationToken); } finally { ctr.Dispose(); } }
private async ValueTask <int> ReadAsyncCore(Memory <byte> buffer, CancellationToken cancellationToken) { // Should only be called if ReadChunksFromConnectionBuffer returned 0. Debug.Assert(_connection != null); CancellationTokenRegistration ctr = _connection.RegisterCancellation(cancellationToken); try { while (true) { if (_connection == null) { // Fully consumed the response in ReadChunksFromConnectionBuffer. return(0); } if (_state == ParsingState.ExpectChunkData && buffer.Length >= _connection.ReadBufferSize && _chunkBytesRemaining >= (ulong)_connection.ReadBufferSize) { // As an optimization, we skip going through the connection's read buffer if both // the remaining chunk data and the buffer are both at least as large // as the connection buffer. That avoids an unnecessary copy while still reading // the maximum amount we'd otherwise read at a time. Debug.Assert(_connection.RemainingBuffer.Length == 0); Debug.Assert(buffer.Length != 0); int bytesRead = await _connection.ReadAsync(buffer.Slice(0, (int)Math.Min((ulong)buffer.Length, _chunkBytesRemaining))).ConfigureAwait(false); if (bytesRead == 0) { throw new IOException(SR.Format(SR.net_http_invalid_response_premature_eof_bytecount, _chunkBytesRemaining)); } _chunkBytesRemaining -= (ulong)bytesRead; if (_chunkBytesRemaining == 0) { _state = ParsingState.ExpectChunkTerminator; } return(bytesRead); } if (buffer.Length == 0) { // User requested a zero-byte read, and we have no data available in the buffer for processing. // This zero-byte read indicates their desire to trade off the extra cost of a zero-byte read // for reduced memory consumption when data is not immediately available. // So, we will issue our own zero-byte read against the underlying stream to allow it to make use of // optimizations, such as deferring buffer allocation until data is actually available. await _connection.ReadAsync(buffer).ConfigureAwait(false); } // We're only here if we need more data to make forward progress. await _connection.FillAsync(async : true).ConfigureAwait(false); // Now that we have more, see if we can get any response data, and if // we can we're done. if (buffer.Length == 0) { if (PeekChunkFromConnectionBuffer()) { return(0); } } else { int bytesCopied = ReadChunksFromConnectionBuffer(buffer.Span, ctr); if (bytesCopied > 0) { return(bytesCopied); } } } } catch (Exception exc) when(CancellationHelper.ShouldWrapInOperationCanceledException(exc, cancellationToken)) { throw CancellationHelper.CreateOperationCanceledException(exc, cancellationToken); } finally { ctr.Dispose(); } }
public override async ValueTask <int> ReadAsync(Memory <byte> buffer, CancellationToken cancellationToken) { CancellationHelper.ThrowIfCancellationRequested(cancellationToken); if (_connection == null || buffer.Length == 0) { // Response body fully consumed or the caller didn't ask for any data return(0); } Debug.Assert(_contentBytesRemaining > 0); if ((ulong)buffer.Length > _contentBytesRemaining) { buffer = buffer.Slice(0, (int)_contentBytesRemaining); } ValueTask <int> readTask = _connection.ReadAsync(buffer); int bytesRead; if (readTask.IsCompletedSuccessfully) { bytesRead = readTask.Result; } else { CancellationTokenRegistration ctr = _connection.RegisterCancellation(cancellationToken); try { bytesRead = await readTask.ConfigureAwait(false); } catch (Exception exc) when(CancellationHelper.ShouldWrapInOperationCanceledException(exc, cancellationToken)) { throw CancellationHelper.CreateOperationCanceledException(exc, cancellationToken); } finally { ctr.Dispose(); } } if (bytesRead <= 0) { // A cancellation request may have caused the EOF. CancellationHelper.ThrowIfCancellationRequested(cancellationToken); // Unexpected end of response stream. throw new IOException(SR.Format(SR.net_http_invalid_response_premature_eof_bytecount, _contentBytesRemaining)); } Debug.Assert((ulong)bytesRead <= _contentBytesRemaining); _contentBytesRemaining -= (ulong)bytesRead; if (_contentBytesRemaining == 0) { // End of response body _connection.CompleteResponse(); _connection = null; } return(bytesRead); }
private static Exception CreateWrappedException(Exception error, CancellationToken cancellationToken) { return(CancellationHelper.ShouldWrapInOperationCanceledException(error, cancellationToken) ? CancellationHelper.CreateOperationCanceledException(error, cancellationToken) : new HttpRequestException(error.Message, error, RequestRetryType.RetryOnNextProxy)); }
private static Exception CreateWrappedException(Exception error, string host, int port, CancellationToken cancellationToken) { return(CancellationHelper.ShouldWrapInOperationCanceledException(error, cancellationToken) ? CancellationHelper.CreateOperationCanceledException(error, cancellationToken) : new HttpRequestException($"{error.Message} ({host}:{port})", error, RequestRetryType.RetryOnNextProxy)); }
public static async ValueTask <(Socket, Stream)> ConnectAsync(string host, int port, CancellationToken cancellationToken) { try { // Rather than creating a new Socket and calling ConnectAsync on it, we use the static // Socket.ConnectAsync with a SocketAsyncEventArgs, as we can then use Socket.CancelConnectAsync // to cancel it if needed. using (var saea = new BuilderAndCancellationTokenSocketAsyncEventArgs(cancellationToken)) { // Configure which server to which to connect. saea.RemoteEndPoint = IPAddress.TryParse(host, out IPAddress address) ? (EndPoint) new IPEndPoint(address, port) : new DnsEndPoint(host, port); // Hook up a callback that'll complete the Task when the operation completes. saea.Completed += (s, e) => { var csaea = (BuilderAndCancellationTokenSocketAsyncEventArgs)e; switch (e.SocketError) { case SocketError.Success: csaea.Builder.SetResult(); break; case SocketError.OperationAborted: case SocketError.ConnectionAborted: if (csaea.CancellationToken.IsCancellationRequested) { csaea.Builder.SetException(CancellationHelper.CreateOperationCanceledException(null, csaea.CancellationToken)); break; } goto default; default: csaea.Builder.SetException(new SocketException((int)e.SocketError)); break; } }; // Initiate the connection. if (Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, saea)) { // Connect completing asynchronously. Enable it to be canceled and wait for it. using (cancellationToken.Register(s => Socket.CancelConnectAsync((SocketAsyncEventArgs)s), saea)) { await saea.Builder.Task.ConfigureAwait(false); } } else if (saea.SocketError != SocketError.Success) { // Connect completed synchronously but unsuccessfully. throw new SocketException((int)saea.SocketError); } Debug.Assert(saea.SocketError == SocketError.Success, $"Expected Success, got {saea.SocketError}."); Debug.Assert(saea.ConnectSocket != null, "Expected non-null socket"); // Configure the socket and return a stream for it. Socket socket = saea.ConnectSocket; socket.NoDelay = true; return(socket, new NetworkStream(socket, ownsSocket: true)); } } catch (Exception error) { throw CancellationHelper.ShouldWrapInOperationCanceledException(error, cancellationToken) ? CancellationHelper.CreateOperationCanceledException(error, cancellationToken) : new HttpRequestException(error.Message, error); } }