Beispiel #1
0
        /// <summary>
        /// Reads a frame header from the given stream
        /// </summary>
        public static async ValueTask <FrameHeader> ReceiveAsync(
            IReadableByteStream stream, byte[] headerSpace)
        {
            await stream.ReadAll(new ArraySegment <byte>(headerSpace, 0, HeaderSize));

            return(DecodeFrom(new ArraySegment <byte>(headerSpace, 0, HeaderSize)));
        }
        public static async Task ReadAllWithTimeout(
            this IReadableByteStream stream, ArraySegment <byte> buf)
        {
            var readTask    = stream.ReadAll(buf).AsTask();
            var timeoutTask = Task.Delay(ReadTimeout);
            var combined    = Task.WhenAny(new Task[] { readTask, timeoutTask });
            var done        = await combined;

            if (done == readTask)
            {
                await readTask;
                return;
            }
            throw new TimeoutException();
        }
Beispiel #3
0
        /// <summary>
        /// Reads the preface from the given stream and compares it to
        /// the expected value.
        /// Will throw an error if the preface could not be read or if the stream
        /// has finished unexpectedly.
        /// </summary>
        public static async ValueTask <DoneHandle> ReadAsync(IReadableByteStream stream)
        {
            var buffer = new byte[Length];
            await stream.ReadAll(new ArraySegment <byte>(buffer));

            // Compare with the expected preface
            for (var i = 0; i < buffer.Length; i++)
            {
                if (buffer[i] != Bytes[i])
                {
                    throw new Exception("Invalid prefix received");
                }
            }

            return(DoneHandle.Instance);
        }
Beispiel #4
0
        /// <summary>
        /// 从给定流读取序言并将其与预期值进行比较。
        /// </summary>
        public static async ValueTask <DoneHandle> ReadAsync(IReadableByteStream stream)
        {
            var buffer = new byte[Length];

            await stream.ReadAll(new ArraySegment <byte>(buffer));

            for (var i = 0; i < buffer.Length; i++)
            {
                if (buffer[i] != Bytes[i])
                {
                    throw new Exception("接收到无效前缀");
                }
            }

            return(DoneHandle.Instance);
        }
Beispiel #5
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);
        }
Beispiel #6
0
        /// <summary>
        /// Reads and decodes a header block which consists of a single HEADER
        /// frame and 0 or more CONTINUATION frames.
        /// </summary>
        /// <param name="firstHeader">
        /// The frame header of the HEADER frame which indicates that headers
        /// must be read.
        /// </param>
        public async ValueTask <Result> ReadHeaders(
            FrameHeader firstHeader,
            Func <int, byte[]> ensureBuffer)
        {
            // Check maximum frame size
            if (firstHeader.Length > maxFrameSize)
            {
                return(new Result
                {
                    Error = new Http2Error
                    {
                        StreamId = 0,
                        Code = ErrorCode.FrameSizeError,
                        Message = "Maximum frame size exceeded",
                    },
                });
            }

            PriorityData?prioData           = null;
            var          allowedHeadersSize = maxHeaderFieldsSize;
            var          headers            = new List <HeaderField>();
            var          initialFlags       = firstHeader.Flags;

            var f              = (HeadersFrameFlags)firstHeader.Flags;
            var isEndOfStream  = f.HasFlag(HeadersFrameFlags.EndOfStream);
            var isEndOfHeaders = f.HasFlag(HeadersFrameFlags.EndOfHeaders);
            var isPadded       = f.HasFlag(HeadersFrameFlags.Padded);
            var hasPriority    = f.HasFlag(HeadersFrameFlags.Priority);

            // Do a first check whether frame is big enough for the given flags
            var minLength = 0;

            if (isPadded)
            {
                minLength += 1;
            }
            if (hasPriority)
            {
                minLength += 5;
            }
            if (firstHeader.Length < minLength)
            {
                return(new Result
                {
                    Error = new Http2Error
                    {
                        StreamId = 0,
                        Code = ErrorCode.ProtocolError,
                        Message = "Invalid frame content size",
                    },
                });
            }

            // Get a buffer for the initial frame
            // TODO: We now always read the initial frame at once, but we could
            // split it up into multiple smaller reads if the receive buffer is
            // smaller. The code needs to be slightly adapted for this.
            byte[] buffer = ensureBuffer(firstHeader.Length);
            // Read the content of the initial frame
            await reader.ReadAll(new ArraySegment <byte>(buffer, 0, firstHeader.Length));

            var offset = 0;
            var padLen = 0;

            if (isPadded)
            {
                // Extract padding Length
                padLen = buffer[0];
                offset++;
            }

            if (hasPriority)
            {
                // Extract priority
                prioData = PriorityData.DecodeFrom(
                    new ArraySegment <byte>(buffer, offset, 5));
                offset += 5;
            }

            var contentLen = firstHeader.Length - offset - padLen;

            if (contentLen < 0)
            {
                return(new Result
                {
                    Error = new Http2Error
                    {
                        StreamId = 0,
                        Code = ErrorCode.ProtocolError,
                        Message = "Invalid frame content size",
                    },
                });
            }

            // Allow table updates at the start of header header block
            // This will be reset once the first header was decoded and will
            // persist also during the continuation frame
            hpackDecoder.AllowTableSizeUpdates = true;

            // Decode headers from the first header block
            var decodeResult = hpackDecoder.DecodeHeaderBlockFragment(
                new ArraySegment <byte>(buffer, offset, contentLen),
                allowedHeadersSize,
                headers);

            var err = DecodeResultToError(decodeResult);

            if (err != null)
            {
                return(new Result {
                    Error = err
                });
            }

            allowedHeadersSize -= decodeResult.HeaderFieldsSize;

            while (!isEndOfHeaders)
            {
                // Read the next frame header
                // This must be a continuation frame
                // Remark: No need for ensureBuffer, since the buffer is
                // guaranteed to be larger than the first frameheader buffer
                var contHeader = await FrameHeader.ReceiveAsync(reader, buffer);

                if (logger != null && logger.IsEnabled(LogLevel.Trace))
                {
                    logger.LogTrace("recv " + FramePrinter.PrintFrameHeader(contHeader));
                }
                if (contHeader.Type != FrameType.Continuation ||
                    contHeader.StreamId != firstHeader.StreamId ||
                    contHeader.Length > maxFrameSize ||
                    contHeader.Length == 0)
                {
                    return(new Result
                    {
                        Error = new Http2Error
                        {
                            StreamId = 0,
                            Code = ErrorCode.ProtocolError,
                            Message = "Invalid continuation frame",
                        },
                    });
                }

                var contFlags = ((ContinuationFrameFlags)contHeader.Flags);
                isEndOfHeaders = contFlags.HasFlag(ContinuationFrameFlags.EndOfHeaders);

                // Read the HeaderBlockFragment of the continuation frame
                // TODO: We now always read the frame at once, but we could
                // split it up into multiple smaller reads if the receive buffer
                // is smaller. The code needs to be slightly adapted for this.
                buffer = ensureBuffer(contHeader.Length);
                await reader.ReadAll(new ArraySegment <byte>(buffer, 0, contHeader.Length));

                offset     = 0;
                contentLen = contHeader.Length;

                // Decode headers from continuation fragment
                decodeResult = hpackDecoder.DecodeHeaderBlockFragment(
                    new ArraySegment <byte>(buffer, offset, contentLen),
                    allowedHeadersSize,
                    headers);

                var err2 = DecodeResultToError(decodeResult);
                if (err2 != null)
                {
                    return(new Result {
                        Error = err2
                    });
                }

                allowedHeadersSize -= decodeResult.HeaderFieldsSize;
            }

            // Check if decoder is initial state, which means a complete header
            // block was received
            if (!hpackDecoder.HasInitialState)
            {
                return(new Result
                {
                    Error = new Http2Error
                    {
                        Code = ErrorCode.CompressionError,
                        StreamId = 0u,
                        Message = "Received incomplete header block",
                    },
                });
            }

            return(new Result
            {
                Error = null,
                HeaderData = new CompleteHeadersFrameData
                {
                    StreamId = firstHeader.StreamId,
                    Headers = headers,
                    Priority = prioData,
                    EndOfStream = isEndOfStream,
                },
            });
        }
Beispiel #7
0
        /// <summary>
        /// 读取和解码包含单个头段帧和0个或多个连续帧的头段块。
        /// </summary>
        /// <param name="firstHeader"></param>
        /// <param name="ensureBuffer"></param>
        /// <returns></returns>
        public async ValueTask <Result> ReadHeaders(
            FrameHeader firstHeader,
            Func <int, byte[]> ensureBuffer)
        {
            if (firstHeader.Length > maxFrameSize)
            {
                return(new Result
                {
                    Error = new Http2Error
                    {
                        StreamId = 0,
                        Code = ErrorCode.FrameSizeError,
                        Message = "超过最大帧大小",
                    },
                });
            }

            PriorityData?prioData           = null;
            var          allowedHeadersSize = maxHeaderFieldsSize;
            var          headers            = new List <HeaderField>();
            var          initialFlags       = firstHeader.Flags;

            var f              = (HeadersFrameFlags)firstHeader.Flags;
            var isEndOfStream  = f.HasFlag(HeadersFrameFlags.EndOfStream);
            var isEndOfHeaders = f.HasFlag(HeadersFrameFlags.EndOfHeaders);
            var isPadded       = f.HasFlag(HeadersFrameFlags.Padded);
            var hasPriority    = f.HasFlag(HeadersFrameFlags.Priority);


            var minLength = 0;

            if (isPadded)
            {
                minLength += 1;
            }
            if (hasPriority)
            {
                minLength += 5;
            }
            if (firstHeader.Length < minLength)
            {
                return(new Result
                {
                    Error = new Http2Error
                    {
                        StreamId = 0,
                        Code = ErrorCode.ProtocolError,
                        Message = "帧内容大小无效",
                    },
                });
            }

            byte[] buffer = ensureBuffer(firstHeader.Length);

            await reader.ReadAll(new ArraySegment <byte>(buffer, 0, firstHeader.Length));

            var offset = 0;
            var padLen = 0;

            if (isPadded)
            {
                padLen = buffer[0];
                offset++;
            }

            if (hasPriority)
            {
                prioData = PriorityData.DecodeFrom(
                    new ArraySegment <byte>(buffer, offset, 5));
                offset += 5;
            }

            var contentLen = firstHeader.Length - offset - padLen;

            if (contentLen < 0)
            {
                return(new Result
                {
                    Error = new Http2Error
                    {
                        StreamId = 0,
                        Code = ErrorCode.ProtocolError,
                        Message = "帧内容大小无效",
                    },
                });
            }


            hpackDecoder.AllowTableSizeUpdates = true;

            var decodeResult = hpackDecoder.DecodeHeaderBlockFragment(
                new ArraySegment <byte>(buffer, offset, contentLen),
                allowedHeadersSize,
                headers);

            var err = DecodeResultToError(decodeResult);

            if (err != null)
            {
                return(new Result {
                    Error = err
                });
            }

            allowedHeadersSize -= decodeResult.HeaderFieldsSize;

            while (!isEndOfHeaders)
            {
                var contHeader = await FrameHeader.ReceiveAsync(reader, buffer);

                if (contHeader.Type != FrameType.Continuation ||
                    contHeader.StreamId != firstHeader.StreamId ||
                    contHeader.Length > maxFrameSize ||
                    contHeader.Length == 0)
                {
                    return(new Result
                    {
                        Error = new Http2Error
                        {
                            StreamId = 0,
                            Code = ErrorCode.ProtocolError,
                            Message = "延续帧无效",
                        },
                    });
                }

                var contFlags = ((ContinuationFrameFlags)contHeader.Flags);
                isEndOfHeaders = contFlags.HasFlag(ContinuationFrameFlags.EndOfHeaders);


                buffer = ensureBuffer(contHeader.Length);
                await reader.ReadAll(new ArraySegment <byte>(buffer, 0, contHeader.Length));

                offset     = 0;
                contentLen = contHeader.Length;


                decodeResult = hpackDecoder.DecodeHeaderBlockFragment(
                    new ArraySegment <byte>(buffer, offset, contentLen),
                    allowedHeadersSize,
                    headers);

                var err2 = DecodeResultToError(decodeResult);
                if (err2 != null)
                {
                    return(new Result {
                        Error = err2
                    });
                }

                allowedHeadersSize -= decodeResult.HeaderFieldsSize;
            }


            if (!hpackDecoder.HasInitialState)
            {
                return(new Result
                {
                    Error = new Http2Error
                    {
                        Code = ErrorCode.CompressionError,
                        StreamId = 0u,
                        Message = "接收到不完整的头块",
                    },
                });
            }

            return(new Result
            {
                Error = null,
                HeaderData = new CompleteHeadersFrameData
                {
                    StreamId = firstHeader.StreamId,
                    Headers = headers,
                    Priority = prioData,
                    EndOfStream = isEndOfStream,
                },
            });
        }