public H2StreamInfo(Http2Connection connection, string scheme, string host, int port) { Connection = connection; Scheme = scheme; Host = host; Port = port; ValidUntil = Environment.TickCount + 5000; }
internal void CreateHttp2Connection <TContext>(IHttpApplication <TContext> httpApplication, IPipeConnection transport, IPipeConnection application) { _http2Connection = new Http2Connection(new Http2ConnectionContext { ConnectionId = _context.ConnectionId, ServiceContext = _context.ServiceContext, ConnectionFeatures = _context.ConnectionFeatures, BufferPool = BufferPool, LocalEndPoint = LocalEndPoint, RemoteEndPoint = RemoteEndPoint, Application = application, Transport = transport }); }
private static void AddH2Connection(Http2Connection conn, string scheme, string host, int port) { string key = scheme + "://" + host + ":" + port.ToString(); var h2info = new H2StreamInfo(conn, scheme, host, port); m_H2StreamList[key].Add(h2info); ThreadManager.CreateThread((o) => { var info = (H2StreamInfo)o; System.Threading.Thread.CurrentThread.Name = "HTTP/2 client connection for " + key; try { info.Connection.Run(); } catch { m_H2StreamList[key].Remove(info); } }).Start(h2info); }
public static void Run( string knownHeadersPath, string httpProtocolFeatureCollectionPath, string httpUtilitiesPath, string http2ConnectionPath, string transportMultiplexedConnectionFeatureCollectionPath, string transportConnectionFeatureCollectionPath) { var knownHeadersContent = KnownHeaders.GeneratedFile(); var httpProtocolFeatureCollectionContent = HttpProtocolFeatureCollection.GenerateFile(); var httpUtilitiesContent = HttpUtilities.HttpUtilities.GeneratedFile(); var transportMultiplexedConnectionFeatureCollectionContent = TransportMultiplexedConnectionFeatureCollection.GenerateFile(); var transportConnectionFeatureCollectionContent = TransportConnectionFeatureCollection.GenerateFile(); var http2ConnectionContent = Http2Connection.GenerateFile(); UpdateFile(knownHeadersPath, knownHeadersContent); UpdateFile(httpProtocolFeatureCollectionPath, httpProtocolFeatureCollectionContent); UpdateFile(httpUtilitiesPath, httpUtilitiesContent); UpdateFile(http2ConnectionPath, http2ConnectionContent); UpdateFile(transportMultiplexedConnectionFeatureCollectionPath, transportMultiplexedConnectionFeatureCollectionContent); UpdateFile(transportConnectionFeatureCollectionPath, transportConnectionFeatureCollectionContent); }
private static Http2Connection.Http2Stream OpenHttp2Stream( string scheme, string host, int port, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation, RemoteCertificateValidationCallback remoteCertificateValidationCallback, bool enableIPv6) { Http2Connection.Http2Stream h2stream = TryReuseStream(scheme, host, port); if (h2stream != null) { return(h2stream); } Stream s; if (scheme == Uri.UriSchemeHttp) { s = new HttpStream(ConnectToTcp(host, port, enableIPv6)) { IsReusable = false }; } else if (scheme == Uri.UriSchemeHttps) { s = ConnectToSslServer(host, port, clientCertificates, enabledSslProtocols, checkCertificateRevocation, false, remoteCertificateValidationCallback, enableIPv6); } else { throw new NotSupportedException(); } var h2con = new Http2Connection(s, false); h2stream = h2con.OpenClientStream(); AddH2Connection(h2con, scheme, host, port); return(h2stream); }
public override void Initialize(MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper) { base.Initialize(methodInfo, testMethodArguments, testOutputHelper); var mockKestrelTrace = new Mock <IKestrelTrace>(); mockKestrelTrace .Setup(m => m.Http2ConnectionClosing(It.IsAny <string>())) .Callback(() => _closingStateReached.SetResult(null)); mockKestrelTrace .Setup(m => m.Http2ConnectionClosed(It.IsAny <string>(), It.IsAny <int>())) .Callback(() => _closedStateReached.SetResult(null)); _connectionContext = new Http2ConnectionContext { ConnectionContext = Mock.Of <ConnectionContext>(), ConnectionFeatures = new FeatureCollection(), ServiceContext = new TestServiceContext(LoggerFactory, mockKestrelTrace.Object), MemoryPool = _memoryPool, Transport = _pair.Transport }; _connection = new Http2Connection(_connectionContext); }
public Http2FrameWriter( PipeWriter outputPipeWriter, ConnectionContext connectionContext, Http2Connection http2Connection, OutputFlowControl connectionOutputFlowControl, ITimeoutControl timeoutControl, MinDataRate minResponseDataRate, string connectionId, MemoryPool <byte> memoryPool, IKestrelTrace log) { // Allow appending more data to the PipeWriter when a flush is pending. _outputWriter = new ConcurrentPipeWriter(outputPipeWriter, memoryPool, _writeLock); _connectionContext = connectionContext; _http2Connection = http2Connection; _connectionOutputFlowControl = connectionOutputFlowControl; _connectionId = connectionId; _log = log; _timeoutControl = timeoutControl; _minResponseDataRate = minResponseDataRate; _flusher = new TimingPipeFlusher(_outputWriter, timeoutControl, log); _outgoingFrame = new Http2Frame(); _headerEncodingBuffer = new byte[_maxFrameSize]; }
public async Task ProcessRequestsAsync <TContext>(IHttpApplication <TContext> httpApplication) { try { // Ensure TimeoutControl._lastTimestamp is initialized before anything that could set timeouts runs. _timeoutControl.Initialize(_systemClock.UtcNowTicks); IRequestProcessor requestProcessor = null; var httpConnectionContext = new HttpConnectionContext { ConnectionId = _context.ConnectionId, ConnectionFeatures = _context.ConnectionFeatures, MemoryPool = _context.MemoryPool, LocalEndPoint = _context.LocalEndPoint, RemoteEndPoint = _context.RemoteEndPoint, ServiceContext = _context.ServiceContext, ConnectionContext = _context.ConnectionContext, TimeoutControl = _timeoutControl, Transport = _context.Transport }; switch (SelectProtocol()) { case HttpProtocols.Http1: // _http1Connection must be initialized before adding the connection to the connection manager requestProcessor = _http1Connection = new Http1Connection(httpConnectionContext); _protocolSelectionState = ProtocolSelectionState.Selected; break; case HttpProtocols.Http2: // _http2Connection must be initialized before yielding control to the transport thread, // to prevent a race condition where _http2Connection.Abort() is called just as // _http2Connection is about to be initialized. requestProcessor = new Http2Connection(httpConnectionContext); _protocolSelectionState = ProtocolSelectionState.Selected; break; case HttpProtocols.None: // An error was already logged in SelectProtocol(), but we should close the connection. Abort(new ConnectionAbortedException(CoreStrings.ProtocolSelectionFailed)); break; default: // SelectProtocol() only returns Http1, Http2 or None. throw new NotSupportedException($"{nameof(SelectProtocol)} returned something other than Http1, Http2 or None."); } _requestProcessor = requestProcessor; if (requestProcessor != null) { var connectionHeartbeatFeature = _context.ConnectionFeatures.Get <IConnectionHeartbeatFeature>(); var connectionLifetimeNotificationFeature = _context.ConnectionFeatures.Get <IConnectionLifetimeNotificationFeature>(); // These features should never be null in Kestrel itself, if this middleware is ever refactored to run outside of kestrel, // we'll need to handle these missing. Debug.Assert(connectionHeartbeatFeature != null, nameof(IConnectionHeartbeatFeature) + " is missing!"); Debug.Assert(connectionLifetimeNotificationFeature != null, nameof(IConnectionLifetimeNotificationFeature) + " is missing!"); // Register the various callbacks once we're going to start processing requests // The heart beat for various timeouts connectionHeartbeatFeature?.OnHeartbeat(state => ((HttpConnection)state).Tick(), this); // Register for graceful shutdown of the server using var shutdownRegistration = connectionLifetimeNotificationFeature?.ConnectionClosedRequested.Register(state => ((HttpConnection)state).StopProcessingNextRequest(), this); // Register for connection close using var closedRegistration = _context.ConnectionContext.ConnectionClosed.Register(state => ((HttpConnection)state).OnConnectionClosed(), this); await requestProcessor.ProcessRequestsAsync(httpApplication); } } catch (Exception ex) { Log.LogCritical(0, ex, $"Unexpected exception in {nameof(HttpConnection)}.{nameof(ProcessRequestsAsync)}."); } finally { if (_http1Connection?.IsUpgraded == true) { _context.ServiceContext.ConnectionManager.UpgradedConnectionCount.ReleaseOne(); } } }
public async Task ProcessRequestsAsync <TContext>(IHttpApplication <TContext> httpApplication) { try { AdaptedPipeline adaptedPipeline = null; var adaptedPipelineTask = Task.CompletedTask; // _adaptedTransport must be set prior to wiring up callbacks // to allow the connection to be aborted prior to protocol selection. _adaptedTransport = _context.Transport; if (_context.ConnectionAdapters.Count > 0) { adaptedPipeline = new AdaptedPipeline(_adaptedTransport, new Pipe(AdaptedInputPipeOptions), new Pipe(AdaptedOutputPipeOptions), Log); _adaptedTransport = adaptedPipeline; } // This feature should never be null in Kestrel var connectionHeartbeatFeature = _context.ConnectionFeatures.Get <IConnectionHeartbeatFeature>(); Debug.Assert(connectionHeartbeatFeature != null, nameof(IConnectionHeartbeatFeature) + " is missing!"); connectionHeartbeatFeature?.OnHeartbeat(state => ((HttpConnection)state).Tick(), this); var connectionLifetimeNotificationFeature = _context.ConnectionFeatures.Get <IConnectionLifetimeNotificationFeature>(); Debug.Assert(connectionLifetimeNotificationFeature != null, nameof(IConnectionLifetimeNotificationFeature) + " is missing!"); using (connectionLifetimeNotificationFeature?.ConnectionClosedRequested.Register(state => ((HttpConnection)state).StopProcessingNextRequest(), this)) { // Ensure TimeoutControl._lastTimestamp is initialized before anything that could set timeouts runs. _timeoutControl.Initialize(_systemClock.UtcNowTicks); _context.ConnectionFeatures.Set <IConnectionTimeoutFeature>(_timeoutControl); if (adaptedPipeline != null) { // Stream can be null here and run async will close the connection in that case var stream = await ApplyConnectionAdaptersAsync(); adaptedPipelineTask = adaptedPipeline.RunAsync(stream); } IRequestProcessor requestProcessor = null; lock (_protocolSelectionLock) { // Ensure that the connection hasn't already been stopped. if (_protocolSelectionState == ProtocolSelectionState.Initializing) { var derivedContext = CreateDerivedContext(_adaptedTransport); switch (SelectProtocol()) { case HttpProtocols.Http1: // _http1Connection must be initialized before adding the connection to the connection manager requestProcessor = _http1Connection = new Http1Connection(derivedContext); _protocolSelectionState = ProtocolSelectionState.Selected; break; case HttpProtocols.Http2: // _http2Connection must be initialized before yielding control to the transport thread, // to prevent a race condition where _http2Connection.Abort() is called just as // _http2Connection is about to be initialized. requestProcessor = new Http2Connection(derivedContext); _protocolSelectionState = ProtocolSelectionState.Selected; break; case HttpProtocols.None: // An error was already logged in SelectProtocol(), but we should close the connection. Abort(ex: null); break; default: // SelectProtocol() only returns Http1, Http2 or None. throw new NotSupportedException($"{nameof(SelectProtocol)} returned something other than Http1, Http2 or None."); } _requestProcessor = requestProcessor; } } _context.Transport.Input.OnWriterCompleted( (_, state) => ((HttpConnection)state).OnInputOrOutputCompleted(), this); _context.Transport.Output.OnReaderCompleted( (_, state) => ((HttpConnection)state).OnInputOrOutputCompleted(), this); if (requestProcessor != null) { await requestProcessor.ProcessRequestsAsync(httpApplication); } await adaptedPipelineTask; } } catch (Exception ex) { Log.LogCritical(0, ex, $"Unexpected exception in {nameof(HttpConnection)}.{nameof(ProcessRequestsAsync)}."); } finally { DisposeAdaptedConnections(); if (_http1Connection?.IsUpgraded == true) { _context.ServiceContext.ConnectionManager.UpgradedConnectionCount.ReleaseOne(); } } }
public static Stream ExecuteStreamRequest( this Request request) { byte[] postdata = request.RequestBody; if (postdata != null) { request.RequestContentLength = 0; if (request.IsCompressed || request.RequestContentType == "application/x-gzip") { using (var ms = new MemoryStream()) { using (var comp = new GZipStream(ms, CompressionMode.Compress)) { comp.Write(postdata, 0, postdata.Length); /* The GZIP stream has a CRC-32 and a EOF marker, so we close it first to have it completed */ } postdata = ms.ToArray(); } } request.RequestBodyDelegate = (Stream poststream) => poststream.Write(postdata, 0, postdata.Length); /* append request POST data */ request.RequestContentLength = postdata.Length; } string url = request.Url; if (request.GetValues != null) { url += "?" + BuildQueryString(request.GetValues); } var uri = new Uri(url); if (request.ConnectionMode == ConnectionModeEnum.Http2PriorKnowledge) { return(DoStreamRequestHttp2(request, uri)); } if (request.ConnectionMode == ConnectionModeEnum.UpgradeHttp2) { Http2Connection.Http2Stream h2stream = TryReuseStream(uri.Scheme, uri.Host, uri.Port); if (h2stream != null) { return(DoStreamRequestHttp2(request, uri, h2stream)); } } byte[] outdata; string method = request.Method; redoafter401: var reqdata = new StringBuilder(uri.IsDefaultPort ? $"{method} {uri.PathAndQuery} HTTP/1.1\r\nHost: {uri.Host}\r\n" : $"{method} {uri.PathAndQuery} HTTP/1.1\r\nHost: {uri.Host}:{uri.Port}\r\n"); bool doPost = false; bool doChunked = false; bool compressed = request.IsCompressed; int content_length = request.RequestContentLength; IDictionary <string, string> headers = request.Headers; bool haveAccept = false; if (headers != null) { var removal = new List <string>(); foreach (string k in headers.Keys) { if (string.Compare(k, "content-length", true) == 0 || string.Compare(k, "content-type", true) == 0 || string.Compare(k, "connection", true) == 0 || string.Compare(k, "expect", true) == 0 || string.Compare(k, "transfer-encoding", true) == 0) { removal.Add(k); } if (string.Compare(k, "accept", true) == 0) { haveAccept = true; } } if (removal.Count != 0) { foreach (string k in removal) { headers.Remove(k); } } } if (request.Authorization != null) { if (headers == null) { headers = new Dictionary <string, string>(); } if (request.Authorization.IsSchemeAllowed(uri.Scheme)) { request.Authorization.GetRequestHeaders(headers, request.Method, uri.PathAndQuery); } } if (headers != null) { foreach (KeyValuePair <string, string> kvp in headers) { reqdata.Append($"{kvp.Key}: {kvp.Value}\r\n"); } } if (request.UseChunkedEncoding) { reqdata.Append("Transfer-Encoding: chunked\r\n"); } string content_type = request.RequestContentType; bool expect100Continue = request.Expect100Continue; if (request.RequestBodyDelegate != null) { if (content_type != null) { reqdata.Append($"Content-Type: {content_type}\r\n"); } if (request.UseChunkedEncoding) { doPost = true; doChunked = true; reqdata.Append("Transfer-Encoding: chunked\r\n"); if (compressed && content_type != "application/x-gzip") { reqdata.Append("X-Content-Encoding: gzip\r\n"); } if (expect100Continue) { reqdata.Append("Expect: 100-continue\r\n"); } } else { doPost = true; if (content_length > request.Expect100ContinueMinSize) { expect100Continue = true; request.Expect100Continue = true; } reqdata.Append($"Content-Length: {content_length}\r\n"); if (compressed && content_type != "application/x-gzip") { reqdata.Append("X-Content-Encoding: gzip\r\n"); } if (expect100Continue) { reqdata.Append("Expect: 100-continue\r\n"); } } } if (method != "HEAD") { reqdata.Append("Accept-Encoding: gzip, deflate\r\n"); } if (!haveAccept) { reqdata.Append("Accept: */*\r\n"); } int retrycnt = 1; retry: AbstractHttpStream s = OpenStream(uri.Scheme, uri.Host, uri.Port, request.ClientCertificates, request.EnabledSslProtocols, request.CheckCertificateRevocation, request.ConnectionMode, request.RemoteCertificateValidationCallback, request.EnableIPv6 && OSSupportsIPv6); string finalreqdata = reqdata.ToString(); if (!s.IsReusable) { finalreqdata += "Connection: close\r\n"; } bool h2cUpgrade = request.ConnectionMode == ConnectionModeEnum.UpgradeHttp2; if (h2cUpgrade) { finalreqdata += "Upgrade: h2c\r\nHTTP2-Settings:\r\n"; } finalreqdata += "\r\n"; outdata = Encoding.ASCII.GetBytes(finalreqdata); try { s.Write(outdata, 0, outdata.Length); s.Flush(); } catch (ObjectDisposedException) { if (retrycnt-- > 0) { goto retry; } throw; } catch (SocketException) { if (retrycnt-- > 0) { goto retry; } throw; } catch (IOException) { if (retrycnt-- > 0) { goto retry; } throw; } s.ReadTimeout = 10000; string resline; string[] splits; if (doPost) { if (expect100Continue) { try { resline = s.ReadHeaderLine(); splits = resline.Split(new char[] { ' ' }, 3); if (splits.Length < 3) { throw new BadHttpResponseException("Not a HTTP response"); } if (!splits[0].StartsWith("HTTP/")) { throw new BadHttpResponseException("Missing HTTP version info"); } if (splits[1] == "101") { ReadHeaderLines(s, headers); headers.Clear(); var conn = new Http2Connection(s, false); Http2Connection.Http2Stream h2stream = conn.UpgradeClientStream(); AddH2Connection(conn, uri.Scheme, uri.Host, uri.Port); return(DoStreamRequestHttp2Response(h2stream, request, uri, doPost)); } if (splits[1] != "100") { int statusCode; headers.Clear(); ReadHeaderLines(s, headers); if (!int.TryParse(splits[1], out statusCode)) { statusCode = 500; } request.StatusCode = (HttpStatusCode)statusCode; if (statusCode == 401 && request.Authorization != null && request.Authorization.CanHandleUnauthorized(headers)) { using (GetResponseBodyStream(headers, uri, splits, method, s)) { /* just consume it */ } goto redoafter401; } if (statusCode == 401) { string data; if (headers.TryGetValue("www-authenticate", out data)) { string authtype; Dictionary <string, string> authpara = ParseWWWAuthenticate(data, out authtype); if (authpara == null) { using (GetResponseBodyStream(headers, uri, splits, method, s)) { /* just consume it */ } throw new BadHttpResponseException("Invalid WWW-Authenticate"); } else if (request.IsExceptionDisabled()) { return(GetResponseBodyStream(headers, uri, splits, method, s)); } else { string realm; if (!authpara.TryGetValue("realm", out realm)) { realm = string.Empty; } using (GetResponseBodyStream(headers, uri, splits, method, s)) { /* just consume it */ } throw new HttpUnauthorizedException(authtype, realm, authpara); } } } if (request.IsExceptionDisabled()) { return(GetResponseBodyStream(headers, uri, splits, method, s)); } else { using (GetResponseBodyStream(headers, uri, splits, method, s)) { /* just consume it */ } throw new HttpException(statusCode, statusCode == 404 ? splits[2] + " (" + url + ")" : splits[2]); } } while (s.ReadHeaderLine().Length != 0) { /* ReadHeaderLine() is all we have to do */ } } catch (HttpStream.TimeoutException) { /* keep caller from being exceptioned */ } catch (IOException) { /* keep caller from being exceptioned */ } catch (SocketException) { /* keep caller from being exceptioned */ } } if (doChunked) { /* append request POST data */ using (var reqbody = new HttpWriteChunkedBodyStream(s)) { request.RequestBodyDelegate(reqbody); } } else { /* append request POST data */ using (var reqbody = new RequestBodyStream(s, content_length)) { request.RequestBodyDelegate(reqbody); } } s.Flush(); } s.ReadTimeout = request.TimeoutMs; resline = s.ReadHeaderLine(); splits = resline.Split(new char[] { ' ' }, 3); if (splits.Length < 3) { throw new BadHttpResponseException("Not a HTTP response"); } if (!splits[0].StartsWith("HTTP/")) { throw new BadHttpResponseException("Missing HTTP version info"); } if (headers == null) { headers = new Dictionary <string, string>(); } else { headers.Clear(); } if (splits[1] == "101") { ReadHeaderLines(s, headers); headers.Clear(); var conn = new Http2Connection(s, false); Http2Connection.Http2Stream h2stream = conn.UpgradeClientStream(); AddH2Connection(conn, uri.Scheme, uri.Host, uri.Port); return(DoStreamRequestHttp2Response(h2stream, request, uri, doPost)); } if (!splits[1].StartsWith("2")) { ReadHeaderLines(s, headers); int statusCode; if (!int.TryParse(splits[1], out statusCode)) { statusCode = 500; } request.StatusCode = (HttpStatusCode)statusCode; if (statusCode == 401 && request.Authorization != null && request.Authorization.CanHandleUnauthorized(headers)) { using (GetResponseBodyStream(headers, uri, splits, method, s)) { /* just consume it */ } goto redoafter401; } if (statusCode == 401) { string data; if (headers.TryGetValue("www-authenticate", out data)) { string authtype; Dictionary <string, string> authpara = ParseWWWAuthenticate(data, out authtype); if (authpara == null) { using (GetResponseBodyStream(headers, uri, splits, method, s)) { /* just consume it */ } throw new BadHttpResponseException("Invalid WWW-Authenticate"); } else if (request.IsExceptionDisabled()) { return(GetResponseBodyStream(headers, uri, splits, method, s)); } else { string realm; if (!authpara.TryGetValue("realm", out realm)) { realm = string.Empty; } using (GetResponseBodyStream(headers, uri, splits, method, s)) { /* just consume it */ } throw new HttpUnauthorizedException(authtype, realm, authpara); } } } else { request.Authorization?.ProcessResponseHeaders(headers); } if (request.IsExceptionDisabled()) { return(GetResponseBodyStream(headers, uri, splits, method, s)); } else { using (GetResponseBodyStream(headers, uri, splits, method, s)) { /* just consume it */ } throw new HttpException(statusCode, statusCode == 404 ? splits[2] + " (" + url + ")" : splits[2]); } } else { request.StatusCode = (HttpStatusCode)int.Parse(splits[1]); } /* needs a little passthrough for not changing the API, this actually comes from HTTP/2 */ headers.Add(":status", splits[1]); ReadHeaderLines(s, headers); request.Authorization?.ProcessResponseHeaders(headers); return(GetResponseBodyStream(headers, uri, splits, method, s)); }
public async Task ProcessRequestsAsync <TContext>(IHttpApplication <TContext> httpApplication) { try { // This feature should never be null in Kestrel var connectionHeartbeatFeature = _context.ConnectionFeatures.Get <IConnectionHeartbeatFeature>(); Debug.Assert(connectionHeartbeatFeature != null, nameof(IConnectionHeartbeatFeature) + " is missing!"); connectionHeartbeatFeature?.OnHeartbeat(state => ((HttpConnection)state).Tick(), this); var connectionLifetimeNotificationFeature = _context.ConnectionFeatures.Get <IConnectionLifetimeNotificationFeature>(); Debug.Assert(connectionLifetimeNotificationFeature != null, nameof(IConnectionLifetimeNotificationFeature) + " is missing!"); using (connectionLifetimeNotificationFeature?.ConnectionClosedRequested.Register(state => ((HttpConnection)state).StopProcessingNextRequest(), this)) { // Ensure TimeoutControl._lastTimestamp is initialized before anything that could set timeouts runs. _timeoutControl.Initialize(_systemClock.UtcNowTicks); _context.ConnectionFeatures.Set <IConnectionTimeoutFeature>(_timeoutControl); IRequestProcessor requestProcessor = null; lock (_protocolSelectionLock) { // Ensure that the connection hasn't already been stopped. if (_protocolSelectionState == ProtocolSelectionState.Initializing) { var derivedContext = CreateDerivedContext(_context.Transport); switch (SelectProtocol()) { case HttpProtocols.Http1: // _http1Connection must be initialized before adding the connection to the connection manager requestProcessor = _http1Connection = new Http1Connection(derivedContext); _protocolSelectionState = ProtocolSelectionState.Selected; break; case HttpProtocols.Http2: // _http2Connection must be initialized before yielding control to the transport thread, // to prevent a race condition where _http2Connection.Abort() is called just as // _http2Connection is about to be initialized. requestProcessor = new Http2Connection(derivedContext); _protocolSelectionState = ProtocolSelectionState.Selected; break; case HttpProtocols.None: // An error was already logged in SelectProtocol(), but we should close the connection. Abort(new ConnectionAbortedException(CoreStrings.ProtocolSelectionFailed)); break; default: // SelectProtocol() only returns Http1, Http2 or None. throw new NotSupportedException($"{nameof(SelectProtocol)} returned something other than Http1, Http2 or None."); } _requestProcessor = requestProcessor; } } var closedRegistration = _context.ConnectionContext.ConnectionClosed.Register(state => ((HttpConnection)state).OnInputOrOutputCompleted(), this); // We don't care about callbacks once all requests are processed using (closedRegistration) { if (requestProcessor != null) { await requestProcessor.ProcessRequestsAsync(httpApplication); } } } } catch (Exception ex) { Log.LogCritical(0, ex, $"Unexpected exception in {nameof(HttpConnection)}.{nameof(ProcessRequestsAsync)}."); } finally { if (_http1Connection?.IsUpgraded == true) { _context.ServiceContext.ConnectionManager.UpgradedConnectionCount.ReleaseOne(); } } }
private void AcceptedConnection_Internal(Stream httpstream, string remoteAddr, bool isSsl, X509Certificate remoteCertificate) { Interlocked.Increment(ref m_AcceptedConnectionsCount); try { AddThread(Thread.CurrentThread); while (true) { HttpRequest req; try { req = new Http1Request(httpstream, remoteAddr, m_IsBehindProxy, isSsl, remoteCertificate); } catch (HttpResponse.ConnectionCloseException) { return; } catch (HttpStream.TimeoutException) { return; } catch (TimeoutException) { return; } catch (IOException) { return; } catch (InvalidDataException) { return; } catch (ObjectDisposedException) { return; } catch (SocketException) { return; } catch (Exception e) { m_Log.WarnFormat("Unexpected exception: {0}: {1}\n{2}", e.GetType().FullName, e.Message, e.StackTrace); return; } /* Recognition for HTTP/2.0 connection */ if (req.Method == "PRI") { if (req.MajorVersion != 2 || req.MinorVersion != 0) { req.SetConnectionClose(); req.ErrorResponse(HttpStatusCode.BadRequest, "Bad Request"); } else { X509Certificate cert = req.RemoteCertificate; req = null; /* no need for the initial request data */ HandleHttp2(new Http2Connection(httpstream, true), remoteAddr, isSsl, cert); } return; } else if (req.IsH2CUpgradable || req.IsH2CUpgradableAfterReadingBody) { Stream upgradeStream = null; if (req.IsH2CUpgradableAfterReadingBody) { upgradeStream = new MemoryStream(); req.Body.CopyTo(upgradeStream); upgradeStream.Seek(0, SeekOrigin.Begin); } httpstream.Write(m_H2cUpgrade, 0, m_H2cUpgrade.Length); byte[] settingsdata = FromUriBase64(req["http2-settings"]); var preface_receive = new byte[24]; if (24 != httpstream.Read(preface_receive, 0, 24)) { httpstream.Write(m_H2cGoAwayProtocolError, 0, m_H2cGoAwayProtocolError.Length); httpstream.Close(); upgradeStream?.Dispose(); return; } if (!preface_receive.SequenceEqual(m_H2cClientPreface)) { httpstream.Write(m_H2cGoAwayProtocolError, 0, m_H2cGoAwayProtocolError.Length); httpstream.Close(); upgradeStream?.Dispose(); return; } var h2con = new Http2Connection(httpstream, true); Http2Connection.Http2Stream h2stream = h2con.UpgradeStream(settingsdata, !req.IsH2CUpgradableAfterReadingBody && req.Expect100Continue); req = new Http2Request(h2stream, req.CallerIP, m_IsBehindProxy, isSsl, req.RemoteCertificate, req, upgradeStream); ThreadPool.UnsafeQueueUserWorkItem(HandleHttp2WorkItem, req); HandleHttp2(h2con, remoteAddr, isSsl, req.RemoteCertificate); return; } ProcessHttpRequest(req); } } catch (HttpStream.TimeoutException) { /* ignore */ } catch (Http2Connection.ConnectionClosedException) { /* HTTP/2 connection closed */ } catch (Http2Connection.ProtocolErrorException #if DEBUG e #endif ) { /* HTTP/2 protocol errors */ #if DEBUG m_Log.Debug("HTTP/2 Protocol Exception: ", e); #endif } catch (HttpResponse.DisconnectFromThreadException) { /* we simply disconnected that HttpRequest from HttpServer */ throw; } catch (HttpResponse.ConnectionCloseException) { /* simply a closed connection */ } catch (IOException) { /* commonly a broken pipe */ } catch (SocketException) { /* commonly a broken pipe */ } catch (ObjectDisposedException) { /* commonly a broken pipe */ } catch (Exception e) { m_Log.DebugFormat("Exception: {0}: {1}\n{2}", e.GetType().Name, e.Message, e.StackTrace); } finally { RemoveThread(Thread.CurrentThread); } }