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); } } }
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); }
/// <summary> /// 要注销的流 /// </summary> /// <param name="stream"></param> internal void UnregisterStream(StreamImpl stream) { lock (shared.Mutex) { if (shared.streamMap != null) { shared.streamMap.Remove(stream.Id); } } }
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); }
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); }
/// <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(); } }
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(); }
/// <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()); }