コード例 #1
0
 private void FinishRequestBody()
 {
     if (m_Body != null)
     {
         if (Expect100Continue)
         {
             m_Body.Dispose();
         }
         else
         {
             m_Body.Close();
         }
         m_Body = null;
     }
     if (RawBody != null)
     {
         if (Expect100Continue)
         {
             RawBody.Dispose();
         }
         else
         {
             RawBody.Close();
         }
         RawBody = null;
     }
 }
コード例 #2
0
        public Http1Request(Stream httpStream, string callerIP, bool isBehindProxy, bool isSsl, X509Certificate remoteCertificate)
            : base(isSsl, remoteCertificate)
        {
            m_HttpStream = httpStream;
            m_Body       = null;
            string headerLine;

            m_HttpStream.ReadTimeout = 10000;
            string requestInfo = ReadHeaderLine();

            /* Parse request line */
            string[] requestData = requestInfo.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
            if (requestData.Length != 3)
            {
                MajorVersion   = 1;
                MinorVersion   = 1;
                ConnectionMode = HttpConnectionMode.Close;
                ErrorResponse(HttpStatusCode.BadRequest, "Bad Request");
                throw new InvalidDataException("Invalid header line");
            }
            string[] version = requestData[2].Split('/');
            if (version.Length != 2)
            {
                ConnectionMode = HttpConnectionMode.Close;
                ErrorResponse(HttpStatusCode.BadRequest, "Bad Request");
                throw new InvalidDataException("Invalid version part");
            }

            /* Check for version */
            if (version[0] != "HTTP")
            {
                MajorVersion   = 1;
                MinorVersion   = 1;
                ConnectionMode = HttpConnectionMode.Close;
                ErrorResponse(HttpStatusCode.BadRequest, "Bad Request");
                throw new InvalidDataException("Missing HTTP specifier");
            }

            string[] versiondata = version[1].Split('.');
            if (versiondata.Length != 2)
            {
                MajorVersion   = 1;
                MinorVersion   = 1;
                ConnectionMode = HttpConnectionMode.Close;
                ErrorResponse(HttpStatusCode.BadRequest, "Bad Request");
                throw new InvalidDataException("Invalid version string");
            }

            /* Check whether we know that request version */
            try
            {
                MajorVersion = uint.Parse(versiondata[0]);
                MinorVersion = uint.Parse(versiondata[1]);
            }
            catch
            {
                MajorVersion   = 1;
                MinorVersion   = 1;
                ConnectionMode = HttpConnectionMode.Close;
                ErrorResponse(HttpStatusCode.BadRequest, "Bad Request");
                throw new InvalidDataException("Version string parts not a number");
            }

            if (MajorVersion == 2)
            {
                Method = requestData[0];
                RawUrl = requestData[1];

                /* this is HTTP/2 client preface */
                while (ReadHeaderLine().Length != 0)
                {
                    /* skip headers */
                }
                if (ReadHeaderLine() != "SM")
                {
                    ErrorResponse(HttpStatusCode.BadRequest, "Bad Request");
                    throw new InvalidDataException("HTTP/2 client preface error");
                }
                if (ReadHeaderLine().Length != 0)
                {
                    ErrorResponse(HttpStatusCode.BadRequest, "Bad Request");
                    throw new InvalidDataException("HTTP/2 client preface error");
                }
                CallerIP = (m_Headers.ContainsKey("x-forwarded-for") && isBehindProxy) ?
                           m_Headers["x-forwarded-for"] :
                           callerIP;
                return;
            }

            if (MajorVersion != 1)
            {
                MajorVersion   = 1;
                MinorVersion   = 1;
                ConnectionMode = HttpConnectionMode.Close;
                ErrorResponse(HttpStatusCode.HttpVersionNotSupported, "HTTP Version not supported");
                throw new InvalidDataException("HTTP version not supported");
            }

            /* Configure connection mode default according to version */
            ConnectionMode = MinorVersion > 0 ? HttpConnectionMode.KeepAlive : HttpConnectionMode.Close;

            Method = requestData[0];
            RawUrl = requestData[1];

            /* parse Headers */
            string lastHeader = string.Empty;

            while ((headerLine = ReadHeaderLine()).Length != 0)
            {
                if (m_Headers.Count == 0)
                {
                    /* we have to trim first header line as per RFC7230 when it starts with whitespace */
                    headerLine = headerLine.TrimStart(new char[] { ' ', '\t' });
                }
                /* a white space designates a continuation , RFC7230 deprecates is use for anything else than Content-Type but we stay more permissive here */
                else if (char.IsWhiteSpace(headerLine[0]))
                {
                    m_Headers[lastHeader] += headerLine.Trim();
                    continue;
                }

                string[] headerData = headerLine.Split(new char[] { ':' }, 2);
                if (headerData.Length != 2 || headerData[0].Trim() != headerData[0])
                {
                    ConnectionMode = HttpConnectionMode.Close;
                    ErrorResponse(HttpStatusCode.BadRequest, "Bad Request");
                    throw new InvalidDataException("Invalid header");
                }
                lastHeader            = headerData[0].ToLowerInvariant();
                m_Headers[lastHeader] = headerData[1].Trim();
            }

            string connectionfield;

            if (TryGetHeader("connection", out connectionfield))
            {
                if (connectionfield == "keep-alive")
                {
                    ConnectionMode = HttpConnectionMode.KeepAlive;
                }
                else if (connectionfield == "close")
                {
                    ConnectionMode = HttpConnectionMode.Close;
                }
            }

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

            bool   havePostData = false;
            string upgradeToken;
            bool   isH2CUpgrade = !isSsl && m_Headers.TryGetValue("upgrade", out upgradeToken) && upgradeToken == "h2c" &&
                                  m_Headers.ContainsKey("http2-settings");

            bool hasContentLength = m_Headers.ContainsKey("content-length");
            bool hasRequestBody   = (hasContentLength || m_Headers.ContainsKey("transfer-encoding"));

            IsH2CUpgradableAfterReadingBody = isH2CUpgrade && !Expect100Continue && hasContentLength;

            if (isH2CUpgrade && (!hasRequestBody || Expect100Continue))
            {
                IsH2CUpgradable = true;
                /* skip over post handling */
            }
            else if (hasContentLength)
            {
                /* there is a body */
                long contentLength;
                if (!long.TryParse(m_Headers["content-length"], out contentLength))
                {
                    ConnectionMode = HttpConnectionMode.Close;
                    ErrorResponse(HttpStatusCode.BadRequest, "Bad Request");
                    throw new InvalidDataException();
                }
                if (IsH2CUpgradableAfterReadingBody && contentLength > 65536)
                {
                    IsH2CUpgradableAfterReadingBody = false;
                }
                RawBody = new HttpRequestBodyStream(m_HttpStream, contentLength);
                m_Body  = RawBody;

                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("Transfer-Encoding " + transferEncoding + " not implemented");
                        }
                    }
                }

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

                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("Content-Encoding not accepted");
                }
            }

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