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 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(); } }
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); }
protected override void OnCompleted(SocketAsyncEventArgs _) { switch (SocketError) { case SocketError.Success: Builder.SetResult(); break; case SocketError.OperationAborted: case SocketError.ConnectionAborted: if (CancellationToken.IsCancellationRequested) { Builder.SetException(ExceptionDispatchInfo.SetCurrentStackTrace(CancellationHelper.CreateOperationCanceledException(null, CancellationToken))); break; } goto default; default: Builder.SetException(ExceptionDispatchInfo.SetCurrentStackTrace(new SocketException((int)SocketError))); break; } }
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)); }
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 static async Task <(Socket, Stream)> ConnectAsync(string host, int port, ConfigureSocket configureSocket, 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; configureSocket(socket); 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.Count <= Math.Max(2, Environment.ProcessorCount)) { s_connectEventArgs.Enqueue(saea); } else { saea.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); } 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); } #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) { // 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. _connection.Dispose(); _connection = null; return(0); } return(bytesRead); }
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); } }