コード例 #1
0
        private async Task HandleFrameProcessingError(
            Http2Error err)
        {
            if (err.StreamId == 0)
            {
                await InitiateGoAway(err.Code, true);
            }
            else
            {
                StreamImpl stream = null;
                lock (shared.Mutex)
                {
                    shared.streamMap.TryGetValue(err.StreamId, out stream);
                }

                if (stream != null)
                {
                    await stream.Reset(err.Code, false);
                }
                else
                {
                    var fh = new FrameHeader
                    {
                        StreamId = err.StreamId,
                        Type     = FrameType.ResetStream,
                        Flags    = 0,
                    };
                    var resetData = new ResetFrameData
                    {
                        ErrorCode = err.Code,
                    };
                    await writer.WriteResetStream(fh, resetData);
                }
            }
        }
コード例 #2
0
        private async ValueTask <Http2Error?> HandleResetFrame(FrameHeader fh)
        {
            if (fh.StreamId == 0 || fh.Length != ResetFrameData.Size)
            {
                var errc = ErrorCode.ProtocolError;
                if (fh.Length != ResetFrameData.Size)
                {
                    errc = ErrorCode.FrameSizeError;
                }
                return(new Http2Error
                {
                    StreamId = 0,
                    Code = errc,
                    Message = "接收到无效的RST_STREAM帧头",
                });
            }

            await inputStream.ReadAll(
                new ArraySegment <byte>(receiveBuffer, 0, ResetFrameData.Size));

            var resetData = ResetFrameData.DecodeFrom(
                new ArraySegment <byte>(receiveBuffer, 0, ResetFrameData.Size));

            StreamImpl stream             = null;
            uint       lastOutgoingStream = 0u;
            uint       lastIncomingStream = 0u;

            lock (shared.Mutex)
            {
                lastIncomingStream = shared.LastIncomingStreamId;
                lastOutgoingStream = shared.LastOutgoingStreamId;
                shared.streamMap.TryGetValue(fh.StreamId, out stream);
                if (stream != null)
                {
                    shared.streamMap.Remove(fh.StreamId);
                }
            }

            if (stream != null)
            {
                await stream.Reset(resetData.ErrorCode, true);
            }
            else
            {
                if (IsIdleStreamId(fh.StreamId, lastOutgoingStream, lastIncomingStream))
                {
                    return(new Http2Error
                    {
                        StreamId = 0u,
                        Code = ErrorCode.ProtocolError,
                        Message = "接收到空闲流的RST_STREAM",
                    });
                }
            }

            return(null);
        }
コード例 #3
0
 /// <summary>
 /// 要注销的流
 /// </summary>
 /// <param name="stream"></param>
 internal void UnregisterStream(StreamImpl stream)
 {
     lock (shared.Mutex)
     {
         if (shared.streamMap != null)
         {
             shared.streamMap.Remove(stream.Id);
         }
     }
 }
コード例 #4
0
        private async ValueTask <Http2Error?> HandleDataFrame(FrameHeader fh)
        {
            if (fh.StreamId == 0)
            {
                return(new Http2Error
                {
                    StreamId = 0,
                    Code = ErrorCode.ProtocolError,
                    Message = "接收到无效的数据帧头",
                });
            }
            if ((fh.Flags & (byte)DataFrameFlags.Padded) != 0 &&
                fh.Length < 1)
            {
                return(new Http2Error
                {
                    StreamId = 0,
                    Code = ErrorCode.ProtocolError,
                    Message = "帧太小,无法包含填充",
                });
            }
            if (fh.Length > localSettings.MaxFrameSize)
            {
                return(new Http2Error
                {
                    StreamId = 0,
                    Code = ErrorCode.FrameSizeError,
                    Message = "超过最大帧大小",
                });
            }

            var dataBuffer = config.BufferPool.Rent(fh.Length);

            try
            {
                await inputStream.ReadAll(
                    new ArraySegment <byte>(dataBuffer, 0, fh.Length));
            }
            catch (Exception)
            {
                config.BufferPool.Return(dataBuffer);
                throw;
            }

            var isPadded = (fh.Flags & (byte)DataFrameFlags.Padded) != 0;
            var padLen   = 0;
            var offset   = isPadded ? 1 : 0;
            var dataSize = fh.Length;

            if (isPadded)
            {
                padLen   = dataBuffer[0];
                dataSize = fh.Length - 1 - padLen;
                if (dataSize < 0)
                {
                    config.BufferPool.Return(dataBuffer);
                    return(new Http2Error
                    {
                        StreamId = 0,
                        Code = ErrorCode.ProtocolError,
                        Message = "减法填充后帧太小",
                    });
                }
            }

            if (dataSize != 0)
            {
                if (dataSize > connReceiveFlowWindow)
                {
                    config.BufferPool.Return(dataBuffer);
                    return(new Http2Error
                    {
                        StreamId = 0,
                        Code = ErrorCode.FlowControlError,
                        Message = "超过接收窗口",
                    });
                }
                connReceiveFlowWindow -= dataSize;
            }

            StreamImpl stream = null;
            uint       lastIncomingStreamId;
            uint       lastOutgoingStreamId;

            lock (shared.Mutex)
            {
                lastIncomingStreamId = shared.LastIncomingStreamId;
                lastOutgoingStreamId = shared.LastOutgoingStreamId;
                shared.streamMap.TryGetValue(fh.StreamId, out stream);
            }

            Http2Error?processError = null;
            bool       streamTookBufferOwnership = false;

            if (stream != null)
            {
                processError = stream.PushBuffer(
                    new ArraySegment <byte>(dataBuffer, offset, dataSize),
                    (fh.Flags & (byte)DataFrameFlags.EndOfStream) != 0,
                    out streamTookBufferOwnership);
            }
            else
            {
                var isIdleStreamId = IsIdleStreamId(
                    fh.StreamId, lastOutgoingStreamId, lastIncomingStreamId);
                processError = new Http2Error
                {
                    StreamId = isIdleStreamId ? 0u : fh.StreamId,
                    Code     = ErrorCode.StreamClosed,
                    Message  = "接收到未知帧的数据",
                };
            }

            if (!streamTookBufferOwnership)
            {
                config.BufferPool.Return(dataBuffer);
            }
            dataBuffer = null;

            if (processError.HasValue && processError.Value.StreamId == 0)
            {
                return(processError);
            }

            var maxWindow            = Constants.InitialConnectionWindowSize;
            var possibleWindowUpdate = maxWindow - connReceiveFlowWindow;
            var windowUpdateAmount   = 0;

            if (possibleWindowUpdate >= (maxWindow / 2))
            {
                windowUpdateAmount     = possibleWindowUpdate;
                connReceiveFlowWindow += windowUpdateAmount;
            }

            if (windowUpdateAmount > 0)
            {
                var wfh = new FrameHeader
                {
                    StreamId = 0,
                    Type     = FrameType.WindowUpdate,
                    Flags    = 0,
                };

                var updateData = new WindowUpdateData
                {
                    WindowSizeIncrement = windowUpdateAmount,
                };

                try
                {
                    await writer.WriteWindowUpdate(wfh, updateData);
                }
                catch (Exception)
                {
                }
            }

            return(processError);
        }
コード例 #5
0
        private async ValueTask <Http2Error?> HandleHeaders(CompleteHeadersFrameData headers)
        {
            if (headers.StreamId == 0)
            {
                return(new Http2Error
                {
                    StreamId = headers.StreamId,
                    Code = ErrorCode.ProtocolError,
                    Message = "Received HEADERS frame with stream ID 0",
                });
            }

            StreamImpl stream             = null;
            uint       lastOutgoingStream = 0u;
            uint       lastIncomingStream = 0u;

            lock (shared.Mutex)
            {
                lastIncomingStream = shared.LastIncomingStreamId;
                lastOutgoingStream = shared.LastOutgoingStreamId;
                shared.streamMap.TryGetValue(headers.StreamId, out stream);
            }

            if (stream != null)
            {
                if (headers.Priority.HasValue)
                {
                    var handlePrioErr = HandlePriorityData(
                        headers.StreamId, headers.Priority.Value);
                    if (handlePrioErr != null)
                    {
                        return(handlePrioErr);
                    }
                }
                return(stream.ProcessHeaders(headers));
            }

            var isServerInitiated = headers.StreamId % 2 == 0;
            var isRemoteInitiated =
                (IsServer && !isServerInitiated) || (!IsServer && isServerInitiated);

            var isValidNewStream =
                IsServer &&
                isRemoteInitiated &&
                (headers.StreamId > lastIncomingStream);


            if (!isValidNewStream)
            {
                return(new Http2Error
                {
                    StreamId = headers.StreamId,
                    Code = ErrorCode.StreamClosed,
                    Message = "拒绝不打开新流的头",
                });
            }

            lock (shared.Mutex)
            {
                shared.LastIncomingStreamId = headers.StreamId;

                if (shared.GoAwaySent)
                {
                    return(new Http2Error
                    {
                        StreamId = headers.StreamId,
                        Code = ErrorCode.RefusedStream,
                        Message = "离开",
                    });
                }

                if ((uint)shared.streamMap.Count + 1 > localSettings.MaxConcurrentStreams)
                {
                    return(new Http2Error
                    {
                        StreamId = headers.StreamId,
                        Code = ErrorCode.RefusedStream,
                        Message = "由于最大并发流而拒绝流",
                    });
                }
            }

            var newStream = new StreamImpl(
                this, headers.StreamId, StreamState.Idle,
                (int)localSettings.InitialWindowSize);

            lock (shared.Mutex)
            {
                shared.streamMap[headers.StreamId] = newStream;
            }

            if (!writer.RegisterStream(headers.StreamId))
            {
                return(new Http2Error
                {
                    StreamId = 0,
                    Code = ErrorCode.InternalError,
                    Message = "Can't register stream at writer",
                });
            }

            var err = newStream.ProcessHeaders(headers);

            if (err != null)
            {
                return(err);
            }

            if (headers.Priority.HasValue)
            {
                err = HandlePriorityData(
                    headers.StreamId,
                    headers.Priority.Value);
                if (err != null)
                {
                    return(err);
                }
            }

            var handledByUser = config.StreamListener(newStream);

            if (!handledByUser)
            {
                await newStream.Reset(ErrorCode.RefusedStream, false);
            }

            return(null);
        }
コード例 #6
0
        /// <summary>
        /// 在连接顶部创建新流。
        /// </summary>
        /// <param name="headers"></param>
        /// <param name="endOfStream"></param>
        /// <returns></returns>
        public async Task <IStream> CreateStreamAsync(IEnumerable <HeaderField> headers, bool endOfStream = false)
        {
            if (config.IsServer)
            {
                throw new NotSupportedException("只能为客户端创建流");
            }

            var hvr = HeaderValidator.ValidateRequestHeaders(headers);

            if (hvr != HeaderValidationResult.Ok)
            {
                throw new Exception(hvr.ToString());
            }

            await clientState.CreateStreamMutex.WaitAsync();

            try
            {
                uint       streamId = 0u;
                StreamImpl stream   = null;
                lock (shared.Mutex)
                {
                    if (shared.Closed)
                    {
                        throw new ConnectionClosedException();
                    }

                    if (shared.LastOutgoingStreamId == 0)
                    {
                        shared.LastOutgoingStreamId = 1;
                    }
                    else if (shared.LastOutgoingStreamId <= int.MaxValue - 2)
                    {
                        shared.LastOutgoingStreamId += 2;
                    }
                    else
                    {
                        throw new ConnectionExhaustedException();
                    }

                    streamId = shared.LastOutgoingStreamId;

                    stream = new StreamImpl(
                        this, streamId,
                        StreamState.Idle,
                        (int)localSettings.InitialWindowSize);

                    shared.streamMap[streamId] = stream;
                }

                if (!writer.RegisterStream(streamId))
                {
                    throw new ConnectionClosedException();
                }

                try
                {
                    await stream.WriteValidatedHeadersAsync(headers, endOfStream);
                }
                catch (Exception)
                {
                    throw new ConnectionClosedException();
                }
                return(stream);
            }
            finally
            {
                clientState.CreateStreamMutex.Release();
            }
        }
コード例 #7
0
        private async Task RunReaderAsync()
        {
            try
            {
                if (IsServer)
                {
                    EnsureBuffer(ClientPreface.Length);
                    await ClientPreface.ReadAsync(inputStream, config.ClientPrefaceTimeout);
                }

                var continueRead = true;

                if (serverUpgradeRequest != null)
                {
                    var upgrade = serverUpgradeRequest;
                    serverUpgradeRequest = null;

                    var headers = new CompleteHeadersFrameData
                    {
                        StreamId    = 1u,
                        Priority    = null,
                        Headers     = upgrade.Headers,
                        EndOfStream = upgrade.Payload == null,
                    };

                    var err = await HandleHeaders(headers);

                    if (err != null)
                    {
                        if (err.Value.StreamId == 0)
                        {
                            continueRead = false;
                        }
                        await HandleFrameProcessingError(err.Value);
                    }
                    else if (upgrade.Payload != null)
                    {
                        var buf = config.BufferPool.Rent(upgrade.Payload.Length);
                        Array.Copy(
                            upgrade.Payload, 0, buf, 0, upgrade.Payload.Length);

                        StreamImpl stream = null;
                        lock (shared.Mutex)
                        {
                            shared.streamMap.TryGetValue(1u, out stream);
                        }

                        bool tookBufferOwnership;
                        err = stream.PushBuffer(
                            new ArraySegment <byte>(buf, 0, upgrade.Payload.Length),
                            true,
                            out tookBufferOwnership);
                        if (!tookBufferOwnership)
                        {
                            config.BufferPool.Return(buf);
                        }
                        if (err != null)
                        {
                            if (err.Value.StreamId == 0)
                            {
                                continueRead = false;
                            }
                            await HandleFrameProcessingError(err.Value);
                        }
                    }
                }

                while (continueRead)
                {
                    EnsureBuffer(PersistentBufferSize);
                    var err = await ReadOneFrame();

                    ReleaseBuffer(PersistentBufferSize);
                    if (err != null)
                    {
                        if (err.Value.StreamId == 0)
                        {
                            continueRead = false;
                        }
                        await HandleFrameProcessingError(err.Value);
                    }
                }
            }
            catch (Exception e)
            {
            }

            await writer.CloseNow();

            await writer.Done;

            Dictionary <uint, StreamImpl> activeStreams = null;

            lock (shared.Mutex)
            {
                activeStreams    = shared.streamMap;
                shared.streamMap = null;


                shared.Closed = true;
            }
            foreach (var kvp in activeStreams)
            {
                await kvp.Value.Reset(ErrorCode.ConnectError, fromRemote : true);
            }

            PingState pingState = null;

            lock (shared.Mutex)
            {
                if (shared.PingState != null)
                {
                    pingState        = shared.PingState;
                    shared.PingState = null;
                }
            }
            if (pingState != null)
            {
                var ex = new ConnectionClosedException();
                foreach (var kvp in pingState.PingMap)
                {
                    kvp.Value.SetException(ex);
                }
            }
            if (!remoteGoAwayTcs.Task.IsCompleted)
            {
                remoteGoAwayTcs.TrySetException(new EndOfStreamException());
            }

            if (receiveBuffer != null)
            {
                config.BufferPool.Return(receiveBuffer);
                receiveBuffer = null;
            }

            headerReader.Dispose();
        }
コード例 #8
0
        /// <summary>
        /// 在双向流的顶部创建新的HTTP/2连接
        /// </summary>
        /// <param name="config"></param>
        /// <param name="inputStream"></param>
        /// <param name="outputStream"></param>
        /// <param name="options"></param>
        public Connection(ConnectionConfiguration config, IReadableByteStream inputStream, IWriteAndCloseableByteStream outputStream, Options?options = null)
        {
            if (config == null)
            {
                throw new ArgumentNullException(nameof(config));
            }
            this.config = config;

            if (!config.IsServer)
            {
                clientState = new ClientState();
            }

            localSettings = config.Settings;

            localSettings.EnablePush = false;


            if (IsServer && options?.ServerUpgradeRequest != null)
            {
                serverUpgradeRequest = options.Value.ServerUpgradeRequest;
                if (!serverUpgradeRequest.IsValid)
                {
                    throw new ArgumentException(
                              "ServerUpgradeRequest无效.\n" +
                              "无效的HTTP/1升级请求必须被拒绝");
                }
                else
                {
                    remoteSettings = serverUpgradeRequest.Settings;
                }
            }

            if (inputStream == null)
            {
                throw new ArgumentNullException(nameof(inputStream));
            }
            if (outputStream == null)
            {
                throw new ArgumentNullException(nameof(outputStream));
            }
            this.inputStream = inputStream;


            shared.Mutex                = new object();
            shared.streamMap            = new Dictionary <uint, StreamImpl>();
            shared.LastOutgoingStreamId = 0u;
            shared.LastIncomingStreamId = 0u;
            shared.GoAwaySent           = false;
            shared.Closed               = false;
            shared.PingState            = null;


            if (!IsServer && options?.ClientUpgradeRequest != null)
            {
                var upgrade = options.Value.ClientUpgradeRequest;
                if (!upgrade.IsValid)
                {
                    throw new ArgumentException(
                              "ClientUpgradeRequest无效。\n" +
                              "无效的升级请求必须被HTTP/1处理程序拒绝");
                }

                localSettings = upgrade.Settings;

                var newStream = new StreamImpl(
                    this,
                    1u,
                    StreamState.HalfClosedLocal,
                    (int)localSettings.InitialWindowSize);

                shared.streamMap[1u]        = newStream;
                shared.LastOutgoingStreamId = 1u;
                var setStream =
                    upgrade.UpgradeRequestStreamTcs.TrySetResult(newStream);
            }

            var dynTableSizeLimit = Math.Min(localSettings.HeaderTableSize, int.MaxValue);

            writer = new ConnectionWriter(
                this, outputStream,
                new ConnectionWriter.Options
            {
                MaxFrameSize          = (int)remoteSettings.MaxFrameSize,
                MaxHeaderListSize     = (int)remoteSettings.MaxHeaderListSize,
                InitialWindowSize     = (int)remoteSettings.InitialWindowSize,
                DynamicTableSizeLimit = (int)dynTableSizeLimit,
            },
                new HPack.Encoder.Options
            {
                DynamicTableSize = (int)remoteSettings.HeaderTableSize,
                HuffmanStrategy  = config.HuffmanStrategy,
            }
                );

            nrUnackedSettings++;

            headerReader = new HeaderReader(
                new HPack.Decoder(new HPack.Decoder.Options
            {
                DynamicTableSizeLimit = (int)dynTableSizeLimit,
                BufferPool            = config.BufferPool,
            }),
                localSettings.MaxFrameSize,
                localSettings.MaxHeaderListSize,
                inputStream
                );

            readerDone = Task.Run(() => this.RunReaderAsync());
        }