Ejemplo n.º 1
0
 public H2StreamInfo(Http2Connection connection, string scheme, string host, int port)
 {
     Connection = connection;
     Scheme     = scheme;
     Host       = host;
     Port       = port;
     ValidUntil = Environment.TickCount + 5000;
 }
Ejemplo n.º 2
0
 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
     });
 }
Ejemplo n.º 3
0
        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);
        }
Ejemplo n.º 4
0
    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);
    }
Ejemplo n.º 5
0
        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);
        }
Ejemplo n.º 6
0
        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);
        }
Ejemplo n.º 7
0
 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];
 }
Ejemplo n.º 8
0
        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();
                }
            }
        }
Ejemplo n.º 9
0
        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();
                }
            }
        }
Ejemplo n.º 10
0
        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));
        }
Ejemplo n.º 11
0
        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();
                }
            }
        }
Ejemplo n.º 12
0
        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);
            }
        }