public void UpdateWindowSize(Int32 delta) { if (IsFlowControlEnabled) { //09 -> 6.9.1. The Flow Control Window //A sender MUST NOT allow a flow control window to exceed 2^31 - 1 //bytes. If a sender receives a WINDOW_UPDATE that causes a flow //control window to exceed this maximum it MUST terminate either the //stream or the connection, as appropriate. For streams, the sender //sends a RST_STREAM with the error code of FLOW_CONTROL_ERROR code; //for the connection, a GOAWAY frame with a FLOW_CONTROL_ERROR code. WindowSize += delta; if (WindowSize > Constants.MaxWindowSize) { Http2Logger.LogDebug("Incorrect window size : {0}", WindowSize); throw new ProtocolError(ResetStatusCode.FlowControlError, String.Format("Incorrect window size : {0}", WindowSize)); } } //Unblock stream if it was blocked by flowCtrlManager if (WindowSize > 0 && IsFlowControlBlocked) { IsFlowControlBlocked = false; } }
/// <summary> /// Starts session. /// </summary> /// <returns></returns> public Task Start() { Http2Logger.LogDebug("Session start"); //Write settings. Settings must be the first frame in session. if (_useFlowControl) { WriteSettings(new[] { new SettingsPair(SettingsFlags.None, SettingsIds.InitialWindowSize, 200000) }); } else { WriteSettings(new[] { new SettingsPair(SettingsFlags.None, SettingsIds.InitialWindowSize, 200000), new SettingsPair(SettingsFlags.None, SettingsIds.FlowControlOptions, (byte)FlowControlOptions.DontUseFlowControl) }); } // Listen for incoming Http/2.0 frames var incomingTask = new Task(PumpIncommingData); // Send outgoing Http/2.0 frames var outgoingTask = new Task(() => PumpOutgoingData()); incomingTask.Start(); outgoingTask.Start(); return(Task.WhenAll(incomingTask, outgoingTask)); }
private void RequestSentHandler(object sender, RequestSentEventArgs args) { var stream = args.Stream; var method = stream.Headers.GetValue(":method"); if (method == "put" || method == "post") { var localPath = stream.Headers.GetValue(":localPath".ToLower()); byte[] binary = null; bool gotException = false; try { binary = _fileHelper.GetFile(localPath); } catch (FileNotFoundException) { gotException = true; Http2Logger.LogError("Specified file not found: " + localPath); } if (!gotException) { SendDataTo(args.Stream, binary); } } }
public void SendRequestAsync(Uri request, string method, string localPath = null, string serverPostAct = null) { if (!_socket.IsClosed) { if (_host != request.Host || _port != request.Port || _scheme != request.Scheme) { throw new InvalidOperationException("Trying to send request to non connected address"); } if (_useHttp20 == false) { Http2Logger.LogConsole("Download with Http/1.1"); //Download with http11 in another thread. Http11Manager.Http11DownloadResource(_socket, request); return; } //Submit request if http2 was chosen Http2Logger.LogConsole("Submitting request"); //Submit request in the current thread, response will be handled in the session thread. SubmitRequest(request, method, localPath, serverPostAct); } }
public async void StartConnection() { Console.WriteLine("Start connection called"); if (_useHttp20 && !_sessionAdapter.IsDisposed && !_isDisposed) { Dictionary <string, string> initialRequest = null; if (!_isSecure) { initialRequest = new Dictionary <string, string> { { CommonHeaders.Path, _path }, }; } await _sessionAdapter.StartSessionAsync(initialRequest); //GC.Collect(); } if (!_sessionAdapter.IsDisposed) { return; } Http2Logger.LogError("Connection was aborted by the remote side. Check your session header."); Dispose(true); }
/// <summary> /// Writes the SETTINGS frame. /// </summary> /// <param name="settings">The settings pairs.</param> /// <param name="isAck">The ACK flag.</param> public void WriteSettings(SettingsPair[] settings, bool isAck) { if (settings == null) { throw new ArgumentNullException("settings"); } var frame = new SettingsFrame(new List <SettingsPair>(settings), isAck); Http2Logger.LogDebug("Sending SETTINGS frame: stream id={0}, payload len={1}, is ack={2}, count={3}", frame.StreamId, frame.PayloadLength, frame.IsAck, frame.EntryCount); foreach (var s in settings) { Http2Logger.LogDebug("{0}: {1}", s.Id.ToString(), s.Value); } _writeQueue.WriteFrame(frame); if (!isAck && !_settingsAckReceived.WaitOne(60000)) { WriteGoAway(ResetStatusCode.SettingsTimeout); Dispose(); } _settingsAckReceived.Reset(); if (OnSettingsSent != null) { OnSettingsSent(this, new SettingsSentEventArgs(frame)); } }
/// <summary> /// Writes the data frame. Method is used for pushing unshipped frames. /// If flow control manager has blocked stream, frames are adding to the unshippedFrames collection. /// After window update for that stream they will be delivered. /// </summary> /// <param name="dataFrame">The data frame.</param> private void WriteDataFrame(DataFrame dataFrame) { if (dataFrame == null) { throw new ArgumentNullException("dataFrame is null"); } if (Closed) { return; } if (!IsFlowControlBlocked) { _writeQueue.WriteFrame(dataFrame); SentDataAmount += dataFrame.Data.Count; _flowCrtlManager.DataFrameSentHandler(this, new DataFrameSentEventArgs(dataFrame)); if (dataFrame.IsEndStream) { Http2Logger.LogDebug("Bytes sent: {0}", SentDataAmount); HalfClosedLocal = true; } if (OnFrameSent != null) { OnFrameSent(this, new FrameSentEventArgs(dataFrame)); } } else { _unshippedFrames.Enqueue(dataFrame); } }
public void WriteWindowUpdate(Int32 windowSize) { if (windowSize <= 0) { throw new ArgumentOutOfRangeException("windowSize should be greater than 0"); } if (windowSize > Constants.MaxWindowSize) { throw new ProtocolError(ResetStatusCode.FlowControlError, "window size is too large"); } //09 -> 6.9.4. Ending Flow Control //After a receiver reads in a frame that marks the end of a stream (for //example, a data stream with a END_STREAM flag set), it MUST cease //transmission of WINDOW_UPDATE frames for that stream. if (Closed) { return; } //TODO handle idle state var frame = new WindowUpdateFrame(_id, windowSize); _writeQueue.WriteFrame(frame); Http2Logger.LogDebug("Sending WINDOW_UPDATE: stream id={0}, delta={1}", frame.StreamId, frame.Delta); if (OnFrameSent != null) { OnFrameSent(this, new FrameSentEventArgs(frame)); } }
//Method for future usage in server push private void SendDataTo(Http2Stream stream, byte[] binaryData) { int i = 0; Http2Logger.LogConsole("Transfer begin"); do { bool isLastData = binaryData.Length - i < Constants.MaxDataFrameContentSize; int chunkSize = stream.WindowSize > 0 ? MathEx.Min(binaryData.Length - i, Constants.MaxDataFrameContentSize, stream.WindowSize) : MathEx.Min(binaryData.Length - i, Constants.MaxDataFrameContentSize); var chunk = new byte[chunkSize]; Buffer.BlockCopy(binaryData, i, chunk, 0, chunk.Length); stream.WriteDataFrame(chunk, isLastData); i += chunkSize; } while (binaryData.Length > i); //It was not send exactly. Some of the data frames could be pushed to the unshipped frames collection Http2Logger.LogConsole("File sent: " + stream.Headers.GetValue(":path")); }
private void HandleRstFrame(RstStreamFrame resetFrame, out Http2Stream stream) { Http2Logger.LogDebug("RST_STREAM frame: stream id={0}, status code={1}", resetFrame.StreamId, resetFrame.StatusCode); /* 12 -> 6.4 * RST_STREAM frames MUST be associated with a stream. If a RST_STREAM * frame is received with a stream identifier of 0x0, the recipient MUST * treat this as a connection error of type PROTOCOL_ERROR. */ if (resetFrame.StreamId == 0) { throw new ProtocolError(ResetStatusCode.ProtocolError, "Rst frame with stream id=0"); } stream = GetStream(resetFrame.StreamId); if (stream.Closed) { /* 12 -> 5.4.2 * An endpoint MUST NOT send a RST_STREAM in response to an RST_STREAM * frame, to avoid looping. */ if (!stream.WasRstSent) { throw new Http2StreamNotFoundException(resetFrame.StreamId); } return; } if (!(stream.ReservedRemote || stream.Opened || stream.HalfClosedLocal)) { throw new ProtocolError(ResetStatusCode.ProtocolError, "Rst for non opened or reserved stream"); } stream.Close(ResetStatusCode.None); }
private void HandlePingFrame(PingFrame pingFrame) { Http2Logger.LogDebug("PING frame: stream id={0}, payload={1}", pingFrame.StreamId, pingFrame.Payload.Count); /* 12 -> 6.7 * PING frames are not associated with any individual stream. If a PING * frame is received with a stream identifier field value other than * 0x0, the recipient MUST respond with a connection error of type PROTOCOL_ERROR. */ if (pingFrame.StreamId != 0) { throw new ProtocolError(ResetStatusCode.ProtocolError, "Incoming ping frame with stream id != 0"); } if (pingFrame.PayloadLength != PingFrame.DefPayloadLength) { throw new ProtocolError(ResetStatusCode.FrameSizeError, "Ping payload size is not equal to 8"); } if (pingFrame.IsAck) { _pingReceived.Set(); } else { var pingAckFrame = new PingFrame(true, pingFrame.Payload.ToArray()); _writeQueue.WriteFrame(pingAckFrame); } }
private void FrameReceivedHandler(object sender, FrameReceivedEventArgs args) { var stream = args.Stream; var method = stream.Headers.GetValue(":method").ToLower(); try { switch (method) { case "dir": case "get": if (args.Frame is DataFrame) { SaveDataFrame(stream, (DataFrame)args.Frame); } else if (args.Frame is HeadersFrame) { Http2Logger.LogConsole("Headers received for stream: " + args.Frame.StreamId + " status:" + ((HeadersFrame)args.Frame).Headers.GetValue(":status")); } break; } } catch (Exception) { stream.WriteRst(ResetStatusCode.InternalError); stream.Dispose(); } }
public void Close(ResetStatusCode code) { if (Closed || Idle) { return; } OnFrameSent = null; Http2Logger.LogDebug("Total outgoing data frames volume " + SentDataAmount); Http2Logger.LogDebug("Total frames sent: {0}", FramesSent); Http2Logger.LogDebug("Total frames received: {0}", FramesReceived); if (code == ResetStatusCode.Cancel || code == ResetStatusCode.InternalError) { WriteRst(code); } _flowCrtlManager.StreamClosedHandler(this); Closed = true; if (OnClose != null) { OnClose(this, new StreamClosedEventArgs(_id)); } OnClose = null; Http2Logger.LogDebug("Stream closed " + _id); }
/// <summary> /// Writes the data frame. /// If flow control manager has blocked stream, frames are adding to the unshippedFrames collection. /// After window update for that stream they will be delivered. /// </summary> /// <param name="dataFrame">The data frame.</param> public void WriteDataFrame(DataFrame dataFrame) { if (IsFlowControlBlocked == false) { _writeQueue.WriteFrame(dataFrame); SentDataAmount += dataFrame.FrameLength; _flowCrtlManager.DataFrameSentHandler(this, new DataFrameSentEventArgs(dataFrame)); if (dataFrame.IsEndStream) { Http2Logger.LogDebug("Transfer end"); EndStreamSent = true; } if (OnFrameSent != null) { OnFrameSent(this, new FrameSentArgs(dataFrame)); } } else { _unshippedFrames.Enqueue(dataFrame); } }
private void SaveDataFrame(Http2Stream stream, DataFrame dataFrame) { string originalPath = stream.Headers.GetValue(CommonHeaders.Path.ToLower()); //If user sets the empty file in get command we return notFound webpage string fileName = string.IsNullOrEmpty(Path.GetFileName(originalPath)) ? Index : Path.GetFileName(originalPath); string path = Path.Combine(AssemblyPath, fileName); try { _fileHelper.SaveToFile(dataFrame.Data.Array, dataFrame.Data.Offset, dataFrame.Data.Count, path, stream.ReceivedDataAmount != 0); } catch (IOException) { Http2Logger.LogError("File is still downloading. Repeat request later"); stream.Close(ResetStatusCode.InternalError); return; } stream.ReceivedDataAmount += dataFrame.Data.Count; if (dataFrame.IsEndStream) { if (stream.HalfClosedRemote) { //send terminator stream.WriteDataFrame(new ArraySegment <byte>(new byte[0]), true); Http2Logger.LogConsole("Terminator was sent"); } _fileHelper.RemoveStream(path); Http2Logger.LogConsole("Bytes received: " + stream.ReceivedDataAmount); } }
private void StartResponse() { Debug.Assert(!_responseStarted, "Response started more than once"); _responseStarted = true; Http2Logger.LogDebug("Transfer begin"); SendHeaders(final: false); }
/// <summary> /// Writes the data frame. /// If flow control manager has blocked stream, frames are adding to the unshippedFrames collection. /// After window update for that stream they will be delivered. /// </summary> /// <param name="data">The data.</param> /// <param name="isEndStream">if set to <c>true</c> [is fin].</param> public void WriteDataFrame(ArraySegment <byte> data, bool isEndStream) { if (data.Array == null) { throw new ArgumentNullException("data is null"); } if (Closed) { return; } var dataFrame = new DataFrame(_id, data, isEndStream, true); Http2Logger.LogDebug( "Sending DATA frame: stream id={0}, payload len={1}, has pad={2}, pad high={3}, pad low={4}, " + "end stream={5}", dataFrame.StreamId, dataFrame.PayloadLength, dataFrame.HasPadding, dataFrame.PadHigh, dataFrame.PadLow, dataFrame.IsEndStream); //We cant let lesser frame that were passed through flow control window //be sent before greater frames that were not passed through flow control window //09 -> 6.9.1. The Flow Control Window //The sender MUST NOT //send a flow controlled frame with a length that exceeds the space //available in either of the flow control windows advertised by the receiver. if (_unshippedFrames.Count != 0 || WindowSize - dataFrame.Data.Count < 0) { _unshippedFrames.Enqueue(dataFrame); return; } if (!IsFlowControlBlocked) { _writeQueue.WriteFrame(dataFrame); SentDataAmount += dataFrame.Data.Count; _flowCrtlManager.DataFrameSentHandler(this, new DataFrameSentEventArgs(dataFrame)); if (dataFrame.IsEndStream) { Http2Logger.LogDebug("Transfer end"); Http2Logger.LogDebug("Sent bytes: {0}", SentDataAmount); HalfClosedLocal = true; } if (OnFrameSent != null) { OnFrameSent(this, new FrameSentEventArgs(dataFrame)); } } else { _unshippedFrames.Enqueue(dataFrame); } }
/// <summary> /// Accepts client and deals handshake with it. /// </summary> internal void Accept() { SecureSocket incomingClient; using (var monitor = new ALPNExtensionMonitor()) { incomingClient = _server.AcceptSocket(monitor); } Http2Logger.LogDebug("New connection accepted"); Task.Run(() => HandleAcceptedClient(incomingClient)); }
public async void StartConnection() { if (_useHttp20 && !_socket.IsClosed && !_isDisposed) { await _clientSession.Start(); } else if (_socket.IsClosed || _isDisposed) { Http2Logger.LogError("Connection was aborted by the remote side. Check your session header."); Dispose(true); } }
private void SaveDataFrame(Http2Stream stream, DataFrame dataFrame) { lock (_writeLock) { string originalPath = stream.Headers.GetValue(":path".ToLower()); //If user sets the empty file in get command we return notFound webpage string fileName = string.IsNullOrEmpty(Path.GetFileName(originalPath)) ? NotFound : Path.GetFileName(originalPath); string path = Path.Combine(AssemblyPath, fileName); try { _fileHelper.SaveToFile(dataFrame.Data.Array, dataFrame.Data.Offset, dataFrame.Data.Count, path, stream.ReceivedDataAmount != 0); } catch (IOException) { Http2Logger.LogError("File is still downloading. Repeat request later"); stream.WriteDataFrame(new byte[0], true); stream.Dispose(); } stream.ReceivedDataAmount += dataFrame.FrameLength; if (dataFrame.IsEndStream) { if (!stream.EndStreamSent) { //send terminator stream.WriteDataFrame(new byte[0], true); Http2Logger.LogConsole("Terminator was sent"); } _fileHelper.RemoveStream(path); Http2Logger.LogConsole("Bytes received " + stream.ReceivedDataAmount); #if DEBUG const string wayToServerRoot1 = @"..\..\..\..\..\Drop\Root"; const string wayToServerRoot2 = @".\Root"; var areFilesEqual = _fileHelper.CompareFiles(path, wayToServerRoot1 + originalPath) || _fileHelper.CompareFiles(path, wayToServerRoot2 + originalPath); if (!areFilesEqual) { Console.ForegroundColor = ConsoleColor.Red; Http2Logger.LogError("Files are NOT EQUAL!"); } else { Console.ForegroundColor = ConsoleColor.Green; Http2Logger.LogConsole("Files are EQUAL!"); } Console.ForegroundColor = ConsoleColor.Gray; #endif } } }
private void HandleWindowUpdateFrame(WindowUpdateFrame windowUpdateFrame, out Http2Stream stream) { Http2Logger.LogDebug("WINDOW_UPDATE frame: stream id={0}, delta={1}", windowUpdateFrame.StreamId, windowUpdateFrame.Delta); if (!_useFlowControl) { stream = null; return; } // TODO Remove this hack /* The WINDOW_UPDATE frame can be specific to a stream or to the entire * connection. In the former case, the frame's stream identifier * indicates the affected stream; in the latter, the value "0" indicates * that the _entire connection_ is the subject of the frame. */ if (windowUpdateFrame.StreamId == 0) { _flowControlManager.StreamsInitialWindowSize += windowUpdateFrame.Delta; stream = null; return; } stream = GetStream(windowUpdateFrame.StreamId); /* 12 -> 6.9 * WINDOW_UPDATE can be sent by a peer that has sent a frame bearing the * END_STREAM flag. This means that a receiver could receive a * WINDOW_UPDATE frame on a "half closed (remote)" or "closed" stream. * A receiver MUST NOT treat this as an error. */ if (!(stream.Opened || stream.HalfClosedRemote || stream.HalfClosedLocal || stream.Closed)) { throw new ProtocolError(ResetStatusCode.ProtocolError, "window update in incorrect state"); } //09 -> 6.9. WINDOW_UPDATE //The payload of a WINDOW_UPDATE frame is one reserved bit, plus an //unsigned 31-bit integer indicating the number of bytes that the //sender can transmit in addition to the existing flow control window. //The legal range for the increment to the flow control window is 1 to //2^31 - 1 (0x7fffffff) bytes. if (!(0 < windowUpdateFrame.Delta && windowUpdateFrame.Delta <= Constants.MaxPriority)) { Http2Logger.LogDebug("Incorrect window update delta : {0}", windowUpdateFrame.Delta); throw new ProtocolError(ResetStatusCode.FlowControlError, String.Format("Incorrect window update delta : {0}", windowUpdateFrame.Delta)); } stream.UpdateWindowSize(windowUpdateFrame.Delta); stream.PumpUnshippedFrames(); }
public void ReadHeadersAndInspectHandshake() { try { _response = Read11Headers(); _wasResponseReceived = true; } catch (Exception ex) { Http2Logger.LogError(ex.Message); throw; } }
public HttpSocketServer(Func <IDictionary <string, object>, Task> next, IDictionary <string, object> properties) { _next = next; var addresses = (IList <IDictionary <string, object> >)properties[OwinConstants.CommonKeys.Addresses]; var address = addresses.First(); _port = Int32.Parse(address.Get <string>("port")); _scheme = address.Get <string>("scheme"); _useHandshake = (bool)properties["use-handshake"]; _usePriorities = (bool)properties["use-priorities"]; _useFlowControl = (bool)properties["use-flowControl"]; int securePort; try { securePort = int.Parse(ConfigurationManager.AppSettings["securePort"]); } catch (Exception) { Http2Logger.LogError("Incorrect port in the config file!" + ConfigurationManager.AppSettings["securePort"]); return; } if (_port == securePort && _scheme == Uri.UriSchemeHttp || _port != securePort && _scheme == Uri.UriSchemeHttps) { Http2Logger.LogError("Invalid scheme or port! Use https for secure port."); return; } var extensions = new[] { ExtensionType.Renegotiation, ExtensionType.ALPN }; // protocols should be in order of their priority _options = _port == securePort ? new SecurityOptions(SecureProtocol.Tls1, extensions, new[] { Protocols.Http2, Protocols.Http1 }, ConnectionEnd.Server) : new SecurityOptions(SecureProtocol.None, extensions, new[] { Protocols.Http2, Protocols.Http1 }, ConnectionEnd.Server); _options.VerificationType = CredentialVerification.None; _options.Certificate = Certificate.CreateFromCerFile(AssemblyName + CertificateFilename); _options.Flags = SecurityFlags.Default; _options.AllowedAlgorithms = SslAlgorithms.RSA_AES_256_SHA | SslAlgorithms.NULL_COMPRESSION; _server = new SecureTcpListener(_port, _options); ThreadPool.SetMaxThreads(30, 10); Listen(); }
public static void Http11SendResponse(SecureSocket socket) { string[] headers = GetHttp11Headers(socket); string filename = GetFileName(headers); if (headers.Length == 0) { Http2Logger.LogError("Request headers empty!"); } string path = Path.GetFullPath(AssemblyPath + @"\root" + filename); string contentType = ContentTypes.GetTypeFromFileName(filename); if (!File.Exists(path)) { Http2Logger.LogError("File " + filename + " not found"); SendResponse(socket, new byte[0], StatusCode.Code404NotFound, contentType); socket.Close(); return; } try { using (var sr = new StreamReader(path)) { string file = sr.ReadToEnd(); var fileBytes = Encoding.UTF8.GetBytes(file); int sent = SendResponse(socket, fileBytes, StatusCode.Code200Ok, contentType); Http2Logger.LogDebug(string.Format("Sent: {0} bytes", sent)); Http2Logger.LogInfo("File sent: " + filename); socket.Close(); if (OnSocketClosed != null) { OnSocketClosed(null, new SocketCloseEventArgs()); } } } catch (Exception ex) { var msgBytes = Encoding.UTF8.GetBytes(ex.Message); SendResponse(socket, msgBytes, StatusCode.Code500InternalServerError, contentType); Http2Logger.LogError(ex.Message); } }
public void HandleHttp11Response(byte[] responseBinaryHeaders, int offset, int length) { var bytes = new byte[offset + length]; Buffer.BlockCopy(responseBinaryHeaders, 0, bytes, offset, length); var response = ParseHeadersAndReadResponseBody(bytes); //TODO Handle headers somehow if it's needed using (var stream = new FileStream(AssemblyName + _path, FileMode.Create)) { stream.Write(response.Value, 0, response.Value.Length); } Http2Logger.LogDebug("Response was saved as {0}", _path); }
/// <summary> /// Pumps the outgoing data to write queue /// </summary> /// <returns></returns> private Task PumpOutgoingData() { return(Task.Run(() => { try { _writeQueue.PumpToStream(); } catch (Exception) { Http2Logger.LogError("Sending frame was cancelled because connection was lost"); Dispose(); } })); }
/// <summary> /// Writes the GOAWAY frame. /// </summary> /// <param name="code">The Reset Status code.</param> public void WriteGoAway(ResetStatusCode code) { //if there were no streams opened if (_lastId == -1) { _lastId = 0; //then set lastId to 0 as spec tells. (See GoAway chapter) } var frame = new GoAwayFrame(_lastId, code); Http2Logger.LogDebug("Sending GOAWAY frame: stream id={0}, code={1}, last good id={2}", frame.StreamId, frame.StatusCode, frame.LastGoodStreamId); _writeQueue.WriteFrame(frame); }
private void HandleRequest(Stream incomingClient, string alpnSelectedProtocol, bool backToHttp11) { //Server checks selected protocol and calls http2 or http11 layer if (backToHttp11 || alpnSelectedProtocol == Protocols.Http1) { Http2Logger.LogDebug("Selected protocol: HTTP/1.1"); new Http11ProtocolOwinAdapter(incomingClient, SslProtocols.Tls, _next.Invoke).ProcessRequest(); return; } //ALPN selected http2. No need to perform upgrade handshake. Http2Logger.LogDebug("Selected protocol (ALPN): http/2"); OpenHttp2Session(incomingClient); }
private void HandleGoAwayFrame(GoAwayFrame goAwayFrame) { Http2Logger.LogDebug("GOAWAY frame: stream id={0}, status code={1}", goAwayFrame.StreamId, goAwayFrame.StatusCode); if (goAwayFrame.StreamId != 0) { throw new ProtocolError(ResetStatusCode.ProtocolError, "GoAway Stream id should always be null"); } _goAwayReceived = true; Http2Logger.LogDebug("last successful id = {0}", goAwayFrame.LastGoodStreamId); Dispose(); }
private async void OpenHttp2Session(SecureSocket incomingClient, IDictionary <string, object> handshakeResult) { Http2Logger.LogDebug("Handshake successful"); _session = new Http2Session(incomingClient, ConnectionEnd.Server, _usePriorities, _useFlowControl, handshakeResult); _session.OnFrameReceived += FrameReceivedHandler; try { await _session.Start(); } catch (Exception) { Http2Logger.LogError("Client was disconnected"); } }