public ValueTask <ConnectionContext> StartBidirectionalStreamAsync() { var stream = new Http3RequestStream(this, _connection); // TODO put these somewhere to be read. return(new ValueTask <ConnectionContext>(stream.StreamContext)); }
internal ValueTask <Http3RequestStream> CreateRequestStream() { var stream = new Http3RequestStream(this, _connection); _acceptConnectionQueue.Writer.TryWrite(stream.ConnectionContext); return(new ValueTask <Http3RequestStream>(stream)); }
internal ValueTask <Http3RequestStream> CreateRequestStream() { var stream = new Http3RequestStream(this, _connection); _multiplexedContext.AcceptQueue.Writer.TryWrite(stream.StreamContext); return(new ValueTask <Http3RequestStream>(stream)); }
internal ValueTask <Http3RequestStream> CreateRequestStream() { var stream = new Http3RequestStream(this, Connection); _runningStreams[stream.StreamId] = stream; MultiplexedConnectionContext.ToServerAcceptQueue.Writer.TryWrite(stream.StreamContext); return(new ValueTask <Http3RequestStream>(stream)); }
internal ValueTask <Http3RequestStream> CreateRequestStream(Http3RequestHeaderHandler headerHandler = null) { if (!_streamContextPool.TryDequeue(out var testStreamContext)) { testStreamContext = new TestStreamContext(canRead: true, canWrite: true, this); } testStreamContext.Initialize(GetStreamId(0x00)); var stream = new Http3RequestStream(this, Connection, testStreamContext, headerHandler ?? new Http3RequestHeaderHandler()); _runningStreams[stream.StreamId] = stream; MultiplexedConnectionContext.ToServerAcceptQueue.Writer.TryWrite(stream.StreamContext); return(new ValueTask <Http3RequestStream>(stream)); }
private Task <HttpResponseMessage> SendWithoutWaitingAsync(HttpRequestMessage request, CancellationToken cancellationToken) { QuicStream quicStream = null; Http3RequestStream stream; try { // TODO: do less work in this lock, try to get rid of goto. lock (SyncObj) { if (_connection == null) { goto retryRequest; } quicStream = _connection.OpenBidirectionalStream(); if (_lastProcessedStreamId != -1 && quicStream.StreamId > _lastProcessedStreamId) { goto retryRequest; } stream = new Http3RequestStream(request, this, quicStream); _activeRequests.Add(quicStream.StreamId, stream); } return(stream.SendAsync(cancellationToken)); } catch (QuicConnectionAbortedException ex) { // This will happen if we aborted _connection somewhere. Abort(ex); } retryRequest: // We lost a race between GOAWAY/abort and our pool sending the request to this connection. quicStream?.Dispose(); return(Task.FromException <HttpResponseMessage>(new HttpRequestException(SR.net_http_request_aborted, null, RequestRetryType.RetryOnSameOrNextProxy))); }
public override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken) { // Wait for an available stream (based on QUIC MAX_STREAMS) if there isn't one available yet. TaskCompletionSourceWithCancellation <bool>?waitForAvailableStreamTcs = null; lock (SyncObj) { long remaining = _requestStreamsRemaining; if (remaining > 0) { _requestStreamsRemaining = remaining - 1; } else { waitForAvailableStreamTcs = new TaskCompletionSourceWithCancellation <bool>(); _waitingRequests.Enqueue(waitForAvailableStreamTcs); } } if (waitForAvailableStreamTcs != null) { await waitForAvailableStreamTcs.WaitWithCancellationAsync(cancellationToken).ConfigureAwait(false); } // Allocate an active request QuicStream? quicStream = null; Http3RequestStream?requestStream = null; try { lock (SyncObj) { if (_connection != null) { quicStream = _connection.OpenBidirectionalStream(); requestStream = new Http3RequestStream(request, this, quicStream); _activeRequests.Add(quicStream, requestStream); } } if (quicStream == null) { throw new HttpRequestException(SR.net_http_request_aborted, null, RequestRetryType.RetryOnSameOrNextProxy); } // 0-byte write to force QUIC to allocate a stream ID. await quicStream.WriteAsync(Array.Empty <byte>(), cancellationToken).ConfigureAwait(false); requestStream !.StreamId = quicStream.StreamId; bool goAway; lock (SyncObj) { goAway = _lastProcessedStreamId != -1 && requestStream.StreamId > _lastProcessedStreamId; } if (goAway) { throw new HttpRequestException(SR.net_http_request_aborted, null, RequestRetryType.RetryOnSameOrNextProxy); } Task <HttpResponseMessage> responseTask = requestStream.SendAsync(cancellationToken); // null out requestStream to avoid disposing in finally block. It is now in charge of disposing itself. requestStream = null; return(await responseTask.ConfigureAwait(false)); } catch (QuicConnectionAbortedException ex) { // This will happen if we aborted _connection somewhere. Abort(ex); throw new HttpRequestException(SR.Format(SR.net_http_http3_connection_error, ex.ErrorCode), ex, RequestRetryType.RetryOnSameOrNextProxy); } finally { requestStream?.Dispose(); } }
public async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, long queueStartingTimestamp, CancellationToken cancellationToken) { // Allocate an active request QuicStream? quicStream = null; Http3RequestStream?requestStream = null; ValueTask waitTask = default; try { try { while (true) { lock (SyncObj) { if (_connection == null) { break; } if (_connection.GetRemoteAvailableBidirectionalStreamCount() > 0) { quicStream = _connection.OpenBidirectionalStream(); requestStream = new Http3RequestStream(request, this, quicStream); _activeRequests.Add(quicStream, requestStream); break; } waitTask = _connection.WaitForAvailableBidirectionalStreamsAsync(cancellationToken); } if (HttpTelemetry.Log.IsEnabled() && !waitTask.IsCompleted && queueStartingTimestamp == 0) { // We avoid logging RequestLeftQueue if a stream was available immediately (synchronously) queueStartingTimestamp = Stopwatch.GetTimestamp(); } // Wait for an available stream (based on QUIC MAX_STREAMS) if there isn't one available yet. await waitTask.ConfigureAwait(false); } } finally { if (HttpTelemetry.Log.IsEnabled() && queueStartingTimestamp != 0) { HttpTelemetry.Log.Http30RequestLeftQueue(Stopwatch.GetElapsedTime(queueStartingTimestamp).TotalMilliseconds); } } if (quicStream == null) { throw new HttpRequestException(SR.net_http_request_aborted, null, RequestRetryType.RetryOnConnectionFailure); } requestStream !.StreamId = quicStream.StreamId; bool goAway; lock (SyncObj) { goAway = _lastProcessedStreamId != -1 && requestStream.StreamId > _lastProcessedStreamId; } if (goAway) { throw new HttpRequestException(SR.net_http_request_aborted, null, RequestRetryType.RetryOnConnectionFailure); } if (NetEventSource.Log.IsEnabled()) { Trace($"Sending request: {request}"); } Task <HttpResponseMessage> responseTask = requestStream.SendAsync(cancellationToken); // null out requestStream to avoid disposing in finally block. It is now in charge of disposing itself. requestStream = null; return(await responseTask.ConfigureAwait(false)); } catch (QuicConnectionAbortedException ex) { // This will happen if we aborted _connection somewhere. Abort(ex); throw new HttpRequestException(SR.Format(SR.net_http_http3_connection_error, ex.ErrorCode), ex, RequestRetryType.RetryOnConnectionFailure); } finally { requestStream?.Dispose(); } }
public async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, long queueStartingTimestamp, CancellationToken cancellationToken) { // Allocate an active request QuicStream? quicStream = null; Http3RequestStream?requestStream = null; try { try { QuicConnection?conn = _connection; if (conn != null) { if (HttpTelemetry.Log.IsEnabled() && queueStartingTimestamp == 0) { queueStartingTimestamp = Stopwatch.GetTimestamp(); } quicStream = await conn.OpenOutboundStreamAsync(QuicStreamType.Bidirectional, cancellationToken).ConfigureAwait(false); requestStream = new Http3RequestStream(request, this, quicStream); lock (SyncObj) { _activeRequests.Add(quicStream, requestStream); } } } // Swallow any exceptions caused by the connection being closed locally or even disposed due to a race. // Since quicStream will stay `null`, the code below will throw appropriate exception to retry the request. catch (ObjectDisposedException) { } catch (QuicException e) when(e.QuicError != QuicError.OperationAborted) { } finally { if (HttpTelemetry.Log.IsEnabled() && queueStartingTimestamp != 0) { HttpTelemetry.Log.Http30RequestLeftQueue(Stopwatch.GetElapsedTime(queueStartingTimestamp).TotalMilliseconds); } } if (quicStream == null) { throw new HttpRequestException(SR.net_http_request_aborted, null, RequestRetryType.RetryOnConnectionFailure); } requestStream !.StreamId = quicStream.Id; bool goAway; lock (SyncObj) { goAway = _firstRejectedStreamId != -1 && requestStream.StreamId >= _firstRejectedStreamId; } if (goAway) { throw new HttpRequestException(SR.net_http_request_aborted, null, RequestRetryType.RetryOnConnectionFailure); } if (NetEventSource.Log.IsEnabled()) { Trace($"Sending request: {request}"); } Task <HttpResponseMessage> responseTask = requestStream.SendAsync(cancellationToken); // null out requestStream to avoid disposing in finally block. It is now in charge of disposing itself. requestStream = null; return(await responseTask.ConfigureAwait(false)); } catch (QuicException ex) when(ex.QuicError == QuicError.OperationAborted) { // This will happen if we aborted _connection somewhere and we have pending OpenOutboundStreamAsync call. // note that _abortException may be null if we closed the connection in response to a GOAWAY frame throw new HttpRequestException(SR.net_http_client_execution_error, _abortException, RequestRetryType.RetryOnConnectionFailure); } finally { requestStream?.Dispose(); } }
public async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken) { Debug.Assert(async); // Allocate an active request QuicStream? quicStream = null; Http3RequestStream?requestStream = null; ValueTask waitTask = default; try { while (true) { lock (SyncObj) { if (_connection == null) { break; } if (_connection.GetRemoteAvailableBidirectionalStreamCount() > 0) { quicStream = _connection.OpenBidirectionalStream(); requestStream = new Http3RequestStream(request, this, quicStream); _activeRequests.Add(quicStream, requestStream); break; } waitTask = _connection.WaitForAvailableBidirectionalStreamsAsync(cancellationToken); } // Wait for an available stream (based on QUIC MAX_STREAMS) if there isn't one available yet. await waitTask.ConfigureAwait(false); } if (quicStream == null) { throw new HttpRequestException(SR.net_http_request_aborted, null, RequestRetryType.RetryOnConnectionFailure); } requestStream !.StreamId = quicStream.StreamId; bool goAway; lock (SyncObj) { goAway = _lastProcessedStreamId != -1 && requestStream.StreamId > _lastProcessedStreamId; } if (goAway) { throw new HttpRequestException(SR.net_http_request_aborted, null, RequestRetryType.RetryOnConnectionFailure); } Task <HttpResponseMessage> responseTask = requestStream.SendAsync(cancellationToken); // null out requestStream to avoid disposing in finally block. It is now in charge of disposing itself. requestStream = null; return(await responseTask.ConfigureAwait(false)); } catch (QuicConnectionAbortedException ex) { // This will happen if we aborted _connection somewhere. Abort(ex); throw new HttpRequestException(SR.Format(SR.net_http_http3_connection_error, ex.ErrorCode), ex, RequestRetryType.RetryOnConnectionFailure); } finally { requestStream?.Dispose(); } }