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; } }
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; }