Пример #1
0
        public override Stream GetOutputStream(bool disableCompression = false)
        {
            bool gzipEnable = false;

            if (!m_IsHeaderSent)
            {
                Headers.Remove("Content-Length");
                if (!disableCompression && AcceptedEncodings != null && AcceptedEncodings.Contains("gzip"))
                {
                    gzipEnable = true;
                    Headers["Content-Encoding"] = "gzip";
                }
                SendHeaders();
            }
            else
            {
                throw new InvalidOperationException();
            }

            Stream stream = m_Output;

            if (gzipEnable)
            {
                stream = new GZipStream(stream, CompressionMode.Compress);
            }
            m_Output = null;
            return(stream);
        }
Пример #2
0
        private static Http2Connection.Http2Stream TryReuseStream(string scheme, string host, int port)
        {
            string key = scheme + "://" + host + ":" + port.ToString();

            Http2Connection.Http2Stream h2stream = null;

            RwLockedList <H2StreamInfo> streaminfo;

            if (m_H2StreamList.TryGetValue(key, out streaminfo))
            {
                lock (streaminfo)
                {
                    for (int i = 0; i < streaminfo.Count; ++i)
                    {
                        H2StreamInfo h2info = streaminfo[i];
                        if ((h2info.ValidUntil - Environment.TickCount) > 0 &&
                            h2info.Connection.AvailableStreams > 0)
                        {
                            h2info.ValidUntil = Environment.TickCount + 5000;
                            h2stream          = h2info.Connection.OpenClientStream();
                        }
                    }
                }
            }
            return(h2stream);
        }
Пример #3
0
        public override HttpResponse BeginResponse(HttpStatusCode statuscode, string statusDescription)
        {
            FinishRequestBody();
            var res = new Http2Response(m_Stream, this, statuscode, statusDescription);

            m_Stream = null;
            return(res);
        }
Пример #4
0
 public Http2Response(Http2Connection.Http2Stream output, HttpRequest request, HttpStatusCode statusCode, string statusDescription)
     : base(request, statusCode, statusDescription)
 {
     Headers["Content-Type"] = "text/html";
     m_Output          = output;
     MajorVersion      = request.MajorVersion;
     MinorVersion      = request.MinorVersion;
     StatusCode        = statusCode;
     StatusDescription = statusDescription;
 }
Пример #5
0
        public override Stream GetOutputStream(long contentLength)
        {
            if (!m_IsHeaderSent)
            {
                Headers["Content-Length"] = contentLength.ToString();
                SendHeaders();
            }
            else
            {
                throw new InvalidOperationException();
            }

            Http2Connection.Http2Stream s = m_Output;
            m_Output = null;
            return(s);
        }
Пример #6
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);
        }
Пример #7
0
        private static Stream DoStreamRequestHttp2Response(
            Http2Connection.Http2Stream s,
            Request request,
            Uri uri,
            bool doPost)
        {
            Dictionary <string, string> rxheaders;

            if (doPost)
            {
                if (request.Expect100Continue)
                {
                    rxheaders = s.ReceiveHeaders();
                    string status;
                    if (!rxheaders.TryGetValue(":status", out status))
                    {
                        s.SendRstStream(Http2Connection.Http2ErrorCode.ProtocolError);
                        throw new BadHttpResponseException("Not a HTTP response");
                    }

                    if (status != "100")
                    {
                        int statusCode;
                        if (!int.TryParse(status, out statusCode))
                        {
                            statusCode = 500;
                        }
                        request.StatusCode = (HttpStatusCode)statusCode;

                        if (statusCode == 401 && request.Authorization != null && request.Authorization.CanHandleUnauthorized(rxheaders))
                        {
                            using (GetResponseBodyStream(request, rxheaders, s))
                            {
                                /* just consume it */
                            }
                            throw new RedoAfter401Exception();
                        }

                        if (statusCode == 401)
                        {
                            string data;
                            if (rxheaders.TryGetValue("www-authenticate", out data))
                            {
                                string authtype;
                                Dictionary <string, string> authpara = ParseWWWAuthenticate(data, out authtype);
                                if (authpara == null)
                                {
                                    s.SendRstStream(Http2Connection.Http2ErrorCode.StreamClosed);
                                    throw new BadHttpResponseException("Invalid WWW-Authenticate");
                                }
                                else if (request.IsExceptionDisabled())
                                {
                                    return(GetResponseBodyStream(request, rxheaders, s));
                                }
                                else
                                {
                                    string realm;
                                    if (!authpara.TryGetValue("realm", out realm))
                                    {
                                        realm = string.Empty;
                                    }
                                    using (GetResponseBodyStream(request, rxheaders, s))
                                    {
                                        /* just consume it */
                                    }
                                    throw new HttpUnauthorizedException(authtype, realm, authpara);
                                }
                            }
                        }

                        if (request.IsExceptionDisabled())
                        {
                            return(GetResponseBodyStream(request, rxheaders, s));
                        }
                        else
                        {
                            using (GetResponseBodyStream(request, rxheaders, s))
                            {
                                /* just consume it */
                            }
                            throw new HttpException(statusCode, statusCode == 404 ? statusCode.ToString() + " (" + uri + ")" : statusCode.ToString());
                        }
                    }
                }

                /* append request POST data */
                request.RequestBodyDelegate(s);
                s.SendEndOfStream();
            }

            s.ReadTimeout = request.TimeoutMs;
            rxheaders     = s.ReceiveHeaders();

            IDictionary <string, string> headers = request.Headers;

            if (headers != null)
            {
                headers.Clear();
                foreach (KeyValuePair <string, string> kvp in rxheaders)
                {
                    headers.Add(kvp.Key, kvp.Value);
                }
            }
            string statusVal;

            if (!rxheaders.TryGetValue(":status", out statusVal))
            {
                s.SendRstStream(Http2Connection.Http2ErrorCode.ProtocolError);
                throw new BadHttpResponseException("Missing status");
            }

            if (!statusVal.StartsWith("2"))
            {
                int statusCode;
                if (!int.TryParse(statusVal, out statusCode))
                {
                    statusCode = 500;
                }

                request.StatusCode = (HttpStatusCode)statusCode;

                if (statusCode == 401 && request.Authorization != null && request.Authorization.CanHandleUnauthorized(rxheaders))
                {
                    using (GetResponseBodyStream(request, rxheaders, s))
                    {
                        /* just consume it */
                    }
                    throw new RedoAfter401Exception();
                }

                if (statusCode == 401)
                {
                    string data;
                    if (rxheaders.TryGetValue("www-authenticate", out data))
                    {
                        string authtype;
                        Dictionary <string, string> authpara = ParseWWWAuthenticate(data, out authtype);
                        if (authpara == null)
                        {
                            s.SendRstStream(Http2Connection.Http2ErrorCode.StreamClosed);
                            throw new BadHttpResponseException("Invalid WWW-Authenticate");
                        }
                        else if (request.IsExceptionDisabled())
                        {
                            return(GetResponseBodyStream(request, rxheaders, s));
                        }
                        else
                        {
                            string realm;
                            if (!authpara.TryGetValue("realm", out realm))
                            {
                                realm = string.Empty;
                            }

                            using (GetResponseBodyStream(request, rxheaders, s))
                            {
                                /* just consume it */
                            }
                            throw new HttpUnauthorizedException(authtype, realm, authpara);
                        }
                    }
                }
                else
                {
                    request.Authorization?.ProcessResponseHeaders(rxheaders);
                }

                if (request.IsExceptionDisabled())
                {
                    return(GetResponseBodyStream(request, rxheaders, s));
                }
                else
                {
                    using (GetResponseBodyStream(request, rxheaders, s))
                    {
                        /* just consume it */
                    }
                    throw new HttpException(statusCode, statusCode == 404 ? statusVal + " (" + uri + ")" : statusVal);
                }
            }
            else
            {
                request.StatusCode = (HttpStatusCode)int.Parse(statusVal);
            }

            request.Authorization?.ProcessResponseHeaders(rxheaders);

            return(GetResponseBodyStream(request, rxheaders, s));
        }
Пример #8
0
        private static Stream DoStreamRequestHttp2(
            Request request,
            Uri uri,
            Http2Connection.Http2Stream reuseStream = null)
        {
            bool   doPost = false;
            string encval;

redoafter401:
            var actheaders = new Dictionary <string, string>();

            actheaders.Add(":method", request.Method);
            actheaders.Add(":scheme", uri.Scheme);
            actheaders.Add(":path", uri.PathAndQuery);
            if (uri.IsDefaultPort)
            {
                actheaders.Add(":authority", uri.Host);
            }
            else
            {
                actheaders.Add(":authority", $"{uri.Host}:{uri.Port}");
            }

            IDictionary <string, string> headers = request.Headers;
            string content_type      = request.RequestContentType;
            bool   compressed        = request.IsCompressed;
            bool   expect100Continue = request.Expect100Continue;

            if (headers != null)
            {
                foreach (KeyValuePair <string, string> k in headers)
                {
                    string kn = k.Key.ToLower();
                    if (kn == "content-length" ||
                        kn == "content-type" ||
                        kn == "connection" ||
                        kn == "expect")
                    {
                        continue;
                    }
                    actheaders.Add(kn, k.Value);
                }
            }

            if (request.Authorization != null)
            {
                request.Authorization.GetRequestHeaders(actheaders, request.Method, uri.PathAndQuery);
            }

            if (actheaders.TryGetValue("transfer-encoding", out encval) && encval == "chunked")
            {
                actheaders.Remove("transfer-encoding");
            }

            if (request.RequestBodyDelegate != null)
            {
                doPost = true;
                if (content_type != null)
                {
                    actheaders.Add("content-type", content_type);
                }
                if (compressed && content_type != "application/x-gzip")
                {
                    actheaders.Add("x-content-encoding", "gzip");
                }

                if (expect100Continue)
                {
                    actheaders.Add("expect", "100-continue");
                }
            }

            if (request.Method != "HEAD")
            {
                actheaders.Add("Accept-Encoding", "gzip, deflate");
            }

            int retrycnt = 1;

retry:
            Http2Connection.Http2Stream s = reuseStream ?? OpenHttp2Stream(uri.Scheme, uri.Host, uri.Port, request.ClientCertificates, request.EnabledSslProtocols, request.CheckCertificateRevocation, request.RemoteCertificateValidationCallback, request.EnableIPv6 && OSSupportsIPv6);
            headers.Clear();
            try
            {
                s.SendHeaders(actheaders, 0, !doPost);
            }
            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;

            try
            {
                return(DoStreamRequestHttp2Response(
                           s,
                           request,
                           uri,
                           doPost));
            }
            catch (RedoAfter401Exception)
            {
                goto redoafter401;
            }
        }
Пример #9
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));
        }
Пример #10
0
        private static Stream GetResponseBodyStream(Request request, Dictionary <string, string> rxheaders, Http2Connection.Http2Stream s)
        {
            string value;
            string compressedresult = string.Empty;

            if (rxheaders.TryGetValue("content-encoding", out value) || rxheaders.TryGetValue("x-content-encoding", out value))
            {
                /* Content-Encoding */
                /* x-gzip is deprecated but better feel safe about having that */
                if (value == "gzip" || value == "x-gzip")
                {
                    compressedresult = "gzip";
                }
                else if (value == "deflate")
                {
                    compressedresult = "deflate";
                }
                else
                {
                    throw new NotSupportedException("Unsupport content-encoding");
                }
            }

            if (request.Method == "HEAD")
            {
                /* HEAD does not have any response data */
                return(s);
            }
            else
            {
                Stream bs = s;
                if (compressedresult.Length != 0)
                {
                    if (compressedresult == "gzip")
                    {
                        bs = new GZipStream(bs, CompressionMode.Decompress);
                    }
                    else if (compressedresult == "deflate")
                    {
                        bs = new DeflateStream(bs, CompressionMode.Decompress);
                    }
                }
                return(bs);
            }
        }
Пример #11
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);
            }
        }
Пример #12
0
        public Http2Request(Http2Connection.Http2Stream stream, string callerIP, bool isBehindProxy, bool isSsl, X509Certificate remoteCertificate, HttpRequest upgradeReq = null, Stream upgradeBody = null)
            : base(isSsl, remoteCertificate)
        {
            MajorVersion = 2;
            MinorVersion = 0;
            m_Stream     = stream;
            if (upgradeReq == null)
            {
                foreach (KeyValuePair <string, string> kvp in m_Stream.ReceiveHeaders())
                {
                    m_Headers[kvp.Key] = kvp.Value;
                }
            }
            else
            {
                foreach (KeyValuePair <string, string> kvp in upgradeReq.m_Headers)
                {
                    m_Headers[kvp.Key] = kvp.Value;
                }
                m_Headers[":method"] = upgradeReq.Method;
                m_Headers[":path"]   = upgradeReq.RawUrl;
            }

            string value;

            if (!m_Headers.TryGetValue(":method", out value))
            {
                m_Stream.SendRstStream(Http2Connection.Http2ErrorCode.ProtocolError);
                throw new InvalidDataException();
            }
            Method = value;
            if (!m_Headers.TryGetValue(":path", out value))
            {
                m_Stream.SendRstStream(Http2Connection.Http2ErrorCode.ProtocolError);
                throw new InvalidDataException();
            }
            RawUrl         = value;
            ConnectionMode = HttpConnectionMode.Close;

            Expect100Continue = false;
            if (m_Headers.ContainsKey("expect") &&
                m_Headers["expect"] == "100-continue")
            {
                Expect100Continue = true;
            }

            bool havePostData = false;

            if (upgradeReq != null && upgradeReq.IsH2CUpgradableAfterReadingBody)
            {
                /* skip following postdata handling here since it is already decoded */
                m_Body = upgradeBody;
            }
            else if (m_Headers.ContainsKey("content-length") || m_Headers.ContainsKey("content-type") ||
                     m_Headers.ContainsKey("transfer-encoding"))
            {
                /* there is a body */
                m_Body = new RequestBodyStream(this);

                if (m_Headers.ContainsKey("transfer-encoding"))
                {
                    foreach (string transferEncoding in m_Headers["transfer-encoding"].Split(new char[] { '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries))
                    {
                        if (transferEncoding == "gzip" || transferEncoding == "x-gzip")
                        {
                            m_Body = new GZipStream(m_Body, CompressionMode.Decompress);
                        }
                        else if (transferEncoding == "deflate")
                        {
                            m_Body = new DeflateStream(m_Body, CompressionMode.Decompress);
                        }
                        else
                        {
                            ConnectionMode = HttpConnectionMode.Close;
                            ErrorResponse(HttpStatusCode.NotImplemented, "Transfer-Encoding " + transferEncoding + " not implemented");
                            throw new InvalidDataException();
                        }
                    }
                }

                havePostData = true;
            }

            if (havePostData)
            {
                string contentEncoding = string.Empty;
                if (m_Headers.ContainsKey("content-encoding"))
                {
                    contentEncoding = m_Headers["content-encoding"];
                }
                else if (m_Headers.ContainsKey("x-content-encoding"))
                {
                    contentEncoding = m_Headers["x-content-encoding"];
                }
                else
                {
                    contentEncoding = "identity";
                }

                /* check for gzip encoding */
                if (contentEncoding == "gzip" || contentEncoding == "x-gzip") /* x-gzip is deprecated as per RFC7230 but better accept it if sent */
                {
                    m_Body = new GZipStream(m_Body, CompressionMode.Decompress);
                }
                else if (contentEncoding == "deflate")
                {
                    m_Body = new DeflateStream(m_Body, CompressionMode.Decompress);
                }
                else if (contentEncoding == "identity")
                {
                    /* word is a synomyn for no-encoding so we use it for code simplification */
                    /* no additional action required, identity is simply transfer as-is */
                }
                else
                {
                    ConnectionMode = HttpConnectionMode.Close;
                    ErrorResponse(HttpStatusCode.NotImplemented, "Content-Encoding not accepted");
                    throw new InvalidDataException();
                }
            }

            CallerIP = (m_Headers.ContainsKey("x-forwarded-for") && isBehindProxy) ?
                       m_Headers["x-forwarded-for"] :
                       callerIP;
        }