Ejemplo n.º 1
0
        public ValueTask <ConnectionContext> StartBidirectionalStreamAsync()
        {
            var stream = new Http3RequestStream(this, _connection);

            // TODO put these somewhere to be read.
            return(new ValueTask <ConnectionContext>(stream.StreamContext));
        }
Ejemplo n.º 2
0
        internal ValueTask <Http3RequestStream> CreateRequestStream()
        {
            var stream = new Http3RequestStream(this, _connection);

            _acceptConnectionQueue.Writer.TryWrite(stream.ConnectionContext);
            return(new ValueTask <Http3RequestStream>(stream));
        }
Ejemplo n.º 3
0
        internal ValueTask <Http3RequestStream> CreateRequestStream()
        {
            var stream = new Http3RequestStream(this, _connection);

            _multiplexedContext.AcceptQueue.Writer.TryWrite(stream.StreamContext);
            return(new ValueTask <Http3RequestStream>(stream));
        }
Ejemplo n.º 4
0
        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));
        }
Ejemplo n.º 5
0
        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));
        }
Ejemplo n.º 6
0
        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)));
        }
Ejemplo n.º 7
0
        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();
            }
        }
Ejemplo n.º 8
0
        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();
            }
        }
Ejemplo n.º 9
0
        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();
            }
        }
Ejemplo n.º 10
0
        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();
            }
        }