using System; using System.IO; using System.Collections.Generic; using System.Linq; using ln.http.exceptions; using ln.types.net; using System.Threading; using ln.http.session; namespace ln.http { public class HttpRequest : IDisposable { static ThreadLocal current = new ThreadLocal(); static public HttpRequest Current => current.Value; Dictionary requestHeaders; Dictionary requestCookies; Dictionary requestParameters; public HTTPServer HTTPServer { get; } public Endpoint RemoteEndpoint { get; private set; } public Endpoint LocalEndpoint { get; private set; } public Uri BaseURI { get; set; } public Uri URI { get; private set; } public String Method { get; private set; } public String RequestURL { get; private set; } public String Protocol { get; private set; } public String Hostname { get; private set; } public int Port { get; private set; } public QueryStringParameters Query { get; private set; } public Session Session { get; set; } public HttpUser CurrentUser => Session.CurrentUser; public MemoryStream ContentStream { get; } public TextReader ContentReader { get { if (contentReader == null) contentReader = new StreamReader(ContentStream); return contentReader; } } public Stream GetConnectionStream() { return connectionStream; } StreamReader contentReader; byte[] requestBody; Stream connectionStream; public HttpRequest(HTTPServer httpServer, HttpReader httpReader, Endpoint localEndpoint) { HTTPServer = httpServer; connectionStream = httpReader.Stream; LocalEndpoint = localEndpoint; RemoteEndpoint = httpReader.RemoteEndpoint; Method = httpReader.Method; Protocol = httpReader.Protocol; RequestURL = httpReader.URL; requestHeaders = new Dictionary(httpReader.Headers); requestCookies = new Dictionary(); requestParameters = new Dictionary(); Setup(); int clength = int.Parse(GetRequestHeader("content-length", "0")); requestBody = new byte[clength]; if (clength > 0) { int nread = httpReader.ReadRequestBody(requestBody, 0, clength); if (nread != clength) throw new HttpException(500, "failed to read request content"); } ContentStream = new MemoryStream(requestBody); } public void MakeCurrent() => current.Value = this; public static void ClearCurrent() => current.Value = null; private void Setup() { SetupResourceURI(); SetupCookies(); } /* * SetupResourceURI() * * Setup the following fields: * * - Hostname * - Port * - BaseURI * - URI * - Query * */ private void SetupResourceURI() { String host = GetRequestHeader("HOST"); String[] hostTokens = host.Split(':'); Hostname = hostTokens[0]; Port = (hostTokens.Length > 1) ? int.Parse(hostTokens[1]) : LocalEndpoint.Port; BaseURI = new UriBuilder("http", Hostname, Port).Uri; URI = new Uri(BaseURI, RequestURL); Query = new QueryStringParameters(URI.Query); } private void SetupCookies() { string cookies = GetRequestHeader("COOKIE"); foreach (String cookie in cookies.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { string[] c = cookie.Split(new char[] { '=' }, 2); string cn, cv; cn = c[0].Trim(); if (c.Length > 1) cv = c[1].Trim(); else cv = ""; if (!this.requestCookies.ContainsKey(cn)) this.requestCookies.Add(cn, cv); } } public override string ToString() { //return string.Format("[HttpRequest: RemoteEndpoint={0}, Hostname={1} Port={2} URI={4}, Method={4}, RequestURL={5}, Protocol={6} Query={7}]", RemoteEndpoint, URI, Method, RequestURL, Protocol, Hostname, Port,Query); return base.ToString(); } public String GetRequestHeader(String name) { return GetRequestHeader(name, ""); } public String GetRequestHeader(String name, String def) { name = name.ToUpper(); if (requestHeaders.ContainsKey(name)) return requestHeaders[name]; return def; } public String[] RequestHeaderNames => requestHeaders.Keys.ToArray(); public String[] CookieNames => requestCookies.Keys.ToArray(); public bool ContainsCookie(String name) { return this.requestCookies.ContainsKey(name); } public String GetCookie(String name) { return requestCookies[name]; } public String GetParameter(String parameterName) => GetParameter(parameterName, null); public String GetParameter(String parameterName,String defaultValue) { if (!requestParameters.TryGetValue(parameterName, out string value)) value = defaultValue; return value; } public void SetParameter(String parameterName,String parameterValue) => requestParameters[parameterName] = parameterValue; public IEnumerable ParameterNames => requestParameters.Keys; public string self() { return BaseURI.ToString(); } public HttpResponse Redirect(string location,int status = 307) { HttpResponse httpResponse = new HttpResponse(this); httpResponse.AddHeader("location", location); httpResponse.StatusCode = status; httpResponse.AddHeader("content-type", "text/plain"); httpResponse.ContentWriter.WriteLine("Redirect: {0}", location); return httpResponse; } public void Dispose() { contentReader?.Dispose(); ContentStream?.Dispose(); } } }