/// <summary> /// Parses a HTTP header from a HTTP/2 header collection /// </summary> /// <param name="headers">the header collection including pseudo headers</param> /// <param name="methodResolver">the method resolver</param> /// <returns>an optional used to determine if the invoker should listen for a payload</returns> public static HttpParserOptional ParseFrom(HttpHeaderCollection headers, MethodResolver <HttpMethod> methodResolver) { return(new HttpParserOptional { ExpectPayload = methodResolver.GetMethod(Encoding.UTF8.GetBytes(headers[":method"])).ExpectPayload || headers.ContainsKey("content-length") && headers["content-length"] != "0", Headers = headers, MethodResolver = methodResolver, Payload = null }); }
/// <summary> /// Creates and populates this collection from a <see cref="HttpHeaderCollection"/>, and optionally make it read-only /// </summary> /// <param name="headers"></param> /// <param name="readOnly"></param> public HttpCookieCollection(HttpHeaderCollection headers, bool readOnly = false) { _cookies = new List <HttpCookie>(); foreach (var headerField in headers) { if (headerField.Name == "cookie") { _cookies.AddRange(HttpCookie.Parse(headerField.Value)); } } _readOnly = readOnly; }
/// <summary> /// Instantiates with the given <see cref="HttpHeaderCollection" />, payload, and method resolver. /// </summary> /// <param name="headers"> /// a collection of HTTP/2 headers with pseudo headers included (:method, :scheme, :authority, :path, /// and (optionally), :version /// </param> /// <param name="payload">the request payload (can be null)</param> /// <param name="methodResolver"> /// a method resolver to use to map the method name to a server-implemented /// <see cref="HttpMethod" /> /// </param> public HttpRequest(HttpHeaderCollection headers, byte[] payload, MethodResolver <HttpMethod> methodResolver) { if (payload != null) { InputStream = new MemoryStream(payload); } Method = methodResolver.GetMethod(Encoding.UTF8.GetBytes(headers[":method"].ToUpper())); RawMethod = headers[":method"]; Url = new Uri(headers[":scheme"] + "://" + headers[":authority"] + headers[":path"]); RawUrl = headers[":path"]; Headers = new HttpHeaderCollection(headers.Where <HeaderField>(x => !x.Name.StartsWith(":"))); InputStream = payload == null ? new MemoryStream() : new MemoryStream(payload); #pragma warning disable 618 Protocol = headers.ContainsKey(":version") ? headers[":version"] : null; #pragma warning restore 618 Cookies = new HttpCookieCollection(headers, true); }
/// <summary> /// Parses a HTTP header from a string /// </summary> /// <param name="requestHeader">the header to parse</param> /// <param name="methodResolver">the method resolver</param> /// <param name="connection"> /// the connection that the requestHeader was sourced from. Used to determine the scheme /// (http/https) /// </param> /// <returns>an optional that will determine the next steps of context construction</returns> /// <exception cref="BadRequestException">the request was of an invalid structure</exception> public static HttpParserOptional ParseFrom(string requestHeader, MethodResolver <HttpMethod> methodResolver, IConnection connection) { var lines = requestHeader.Split(new[] { "\r\n" }, StringSplitOptions.None); if (lines.Length < 1) { throw new BadRequestException("Bad Request (HTTP/1 parser failure)", new ArgumentException("Request header contains less than one line", nameof(requestHeader))); } var rparts = lines[0].Split(' '); var method = rparts.First(); var path = rparts.Where(part => part != rparts[0]).Where(part => part != rparts[rparts.Length - 1]) .Aggregate("", (current, part) => current + part + " ").TrimEnd(); var proto = rparts.Last(); if (string.IsNullOrEmpty(method) || string.IsNullOrEmpty(path) || string.IsNullOrEmpty(proto)) { throw new BadRequestException("Bad Request (HTTP/1 parser failure)", new NullReferenceException( "The method, path, and/or protocol parameters aren't present in the request line")); } var headers = new HttpHeaderCollection(new Dictionary <string, string> { { ":method", method }, { ":path", path }, { ":version", proto }, { ":scheme", connection.GetType().FullName == "Ultz.SimpleServer.Common.SslListener.SecureConnection" ? "https" : "http" } }.Select(x => new HeaderField { Name = x.Key, Value = x.Value })); for (var i = 1; i < lines.Length; i++) { var line = lines[i]; var colonIdx = line.IndexOf(':'); if (colonIdx == -1) { throw new BadRequestException("Bad Request (HTTP/1 parser failure)"); } var name = line.Substring(0, colonIdx).Trim().ToLowerInvariant(); var value = line.Substring(colonIdx + 1).Trim(); if (name == "host") { name = ":authority"; } headers[name] = value; } return(new HttpParserOptional { ExpectPayload = methodResolver.GetMethod(Encoding.UTF8.GetBytes(headers[":method"])).ExpectPayload || headers.ContainsKey("content-length") && headers["content-length"] != "0", Headers = headers, MethodResolver = methodResolver, Payload = null }); }