Ejemplo n.º 1
0
        public async Task Read_StreamAborted_Throws()
        {
            const int ExpectedErrorCode = 0xfffffff;

            await Task.Run(async() =>
            {
                using QuicListener listener = CreateQuicListener();
                ValueTask <QuicConnection> serverConnectionTask = listener.AcceptConnectionAsync();

                using QuicConnection clientConnection = CreateQuicConnection(listener.ListenEndPoint);
                await clientConnection.ConnectAsync();

                using QuicConnection serverConnection = await serverConnectionTask;

                await using QuicStream clientStream = clientConnection.OpenBidirectionalStream();
                await clientStream.WriteAsync(new byte[1]);

                await using QuicStream serverStream = await serverConnection.AcceptStreamAsync();
                await serverStream.ReadAsync(new byte[1]);

                clientStream.AbortWrite(ExpectedErrorCode);

                byte[] buffer = new byte[100];
                QuicStreamAbortedException ex = await Assert.ThrowsAsync <QuicStreamAbortedException>(() => serverStream.ReadAsync(buffer).AsTask());
                Assert.Equal(ExpectedErrorCode, ex.ErrorCode);
            }).TimeoutAfter(millisecondsTimeout: 5_000);
        }
        public override void Abort(ConnectionAbortedException abortReason)
        {
            // Don't call _stream.Shutdown and _stream.Abort at the same time.
            lock (_shutdownLock)
            {
                _stream.AbortRead(_context.Options.AbortErrorCode);
                _stream.AbortWrite(_context.Options.AbortErrorCode);
            }

            // Cancel ProcessSends loop after calling shutdown to ensure the correct _shutdownReason gets set.
            Output.CancelPendingRead();
        }
Ejemplo n.º 3
0
        public override void Abort(ConnectionAbortedException abortReason)
        {
            // This abort is called twice, make sure that doesn't happen.
            // Don't call _stream.Shutdown and _stream.Abort at the same time.
            if (_aborted)
            {
                return;
            }

            _aborted = true;

            _log.StreamAbort(ConnectionId, abortReason);

            lock (_shutdownLock)
            {
                _stream.AbortRead(Error);
                _stream.AbortWrite(Error);
            }

            // Cancel ProcessSends loop after calling shutdown to ensure the correct _shutdownReason gets set.
            Output.CancelPendingRead();
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Routes a stream to an appropriate stream-type-specific processor
        /// </summary>
        private async Task ProcessServerStreamAsync(QuicStream stream)
        {
            try
            {
                await using (stream.ConfigureAwait(false))
                    using (var buffer = new ArrayBuffer(initialSize: 32, usePool: true))
                    {
                        if (stream.CanWrite)
                        {
                            // Server initiated bidirectional streams are either push streams or extensions, and we support neither.
                            throw new Http3ConnectionException(Http3ErrorCode.StreamCreationError);
                        }

                        int bytesRead;

                        try
                        {
                            bytesRead = await stream.ReadAsync(buffer.AvailableMemory, CancellationToken.None).ConfigureAwait(false);
                        }
                        catch (QuicStreamAbortedException)
                        {
                            // Treat identical to receiving 0. See below comment.
                            bytesRead = 0;
                        }

                        if (bytesRead == 0)
                        {
                            // https://quicwg.org/base-drafts/draft-ietf-quic-http.html#name-unidirectional-streams
                            // A sender can close or reset a unidirectional stream unless otherwise specified. A receiver MUST
                            // tolerate unidirectional streams being closed or reset prior to the reception of the unidirectional
                            // stream header.
                            return;
                        }

                        buffer.Commit(bytesRead);

                        // Stream type is a variable-length integer, but we only check the first byte. There is no known type requiring more than 1 byte.
                        switch (buffer.ActiveSpan[0])
                        {
                        case (byte)Http3StreamType.Control:
                            if (Interlocked.Exchange(ref _haveServerControlStream, 1) != 0)
                            {
                                // A second control stream has been received.
                                throw new Http3ConnectionException(Http3ErrorCode.StreamCreationError);
                            }

                            // Discard the stream type header.
                            buffer.Discard(1);

                            await ProcessServerControlStreamAsync(stream, buffer).ConfigureAwait(false);

                            return;

                        case (byte)Http3StreamType.QPackDecoder:
                            if (Interlocked.Exchange(ref _haveServerQpackDecodeStream, 1) != 0)
                            {
                                // A second QPack decode stream has been received.
                                throw new Http3ConnectionException(Http3ErrorCode.StreamCreationError);
                            }

                            // The stream must not be closed, but we aren't using QPACK right now -- ignore.
                            buffer.Dispose();
                            await stream.CopyToAsync(Stream.Null).ConfigureAwait(false);

                            return;

                        case (byte)Http3StreamType.QPackEncoder:
                            if (Interlocked.Exchange(ref _haveServerQpackEncodeStream, 1) != 0)
                            {
                                // A second QPack encode stream has been received.
                                throw new Http3ConnectionException(Http3ErrorCode.StreamCreationError);
                            }

                            // The stream must not be closed, but we aren't using QPACK right now -- ignore.
                            buffer.Dispose();
                            await stream.CopyToAsync(Stream.Null).ConfigureAwait(false);

                            return;

                        case (byte)Http3StreamType.Push:
                            // We don't support push streams.
                            // Because no maximum push stream ID was negotiated via a MAX_PUSH_ID frame, server should not have sent this. Abort the connection with H3_ID_ERROR.
                            throw new Http3ConnectionException(Http3ErrorCode.IdError);

                        default:
                            // Unknown stream type. Per spec, these must be ignored and aborted but not be considered a connection-level error.

                            if (NetEventSource.Log.IsEnabled())
                            {
                                // Read the rest of the integer, which might be more than 1 byte, so we can log it.

                                long unknownStreamType;
                                while (!VariableLengthIntegerHelper.TryRead(buffer.ActiveSpan, out unknownStreamType, out _))
                                {
                                    buffer.EnsureAvailableSpace(VariableLengthIntegerHelper.MaximumEncodedLength);
                                    bytesRead = await stream.ReadAsync(buffer.AvailableMemory, CancellationToken.None).ConfigureAwait(false);

                                    if (bytesRead == 0)
                                    {
                                        unknownStreamType = -1;
                                        break;
                                    }

                                    buffer.Commit(bytesRead);
                                }

                                NetEventSource.Info(this, $"Ignoring server-initiated stream of unknown type {unknownStreamType}.");
                            }

                            stream.AbortWrite((long)Http3ErrorCode.StreamCreationError);
                            return;
                        }
                    }
            }
            catch (Exception ex)
            {
                Abort(ex);
            }
        }