/// <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(); }
/// <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); }
/// <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); }
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); }
/// <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, }, }); }
/// <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, }, }); }