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; using ln.http.message; using ln.http.io; using ln.http.message.parser; namespace ln.http { public class HttpRequest : IDisposable { static ThreadLocal current = new ThreadLocal(); static public HttpRequest Current => current.Value; //Dictionary requestHeaders; HeaderContainer 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 HeaderContainer RequestHeaders => requestHeaders; MemoryStream contentStream; public MemoryStream ContentStream { get { if (contentStream == null) ReadRequestBody(); return contentStream; } } public TextReader ContentReader { get { if (contentReader == null) contentReader = new StreamReader(ContentStream); return contentReader; } } int requestBodyLength; byte[] requestBody; StreamReader contentReader; Stream connectionStream; UnbufferedStreamReader connectionReader; public Stream GetConnectionStream() => connectionStream; public HttpRequest(HTTPServer httpServer, Stream clientStream, Endpoint localEndpoint, Endpoint remoteEndpoint) { HTTPServer = httpServer; connectionStream = clientStream; connectionReader = new UnbufferedStreamReader(connectionStream); LocalEndpoint = localEndpoint; RemoteEndpoint = remoteEndpoint; ReadRequestLine(); requestHeaders = HTTP.ReadHeader(connectionReader); requestCookies = new Dictionary(); requestParameters = new Dictionary(); Setup(); requestBodyLength = int.Parse(GetRequestHeader("content-length", "0")); } void ReadRequestLine() { string requestLine = connectionReader.ReadLine(); string[] requestTokens = requestLine.Split(new char[0], StringSplitOptions.RemoveEmptyEntries); if (requestTokens.Length != 3) throw new BadRequestException(); Method = requestTokens[0]; RequestURL = requestTokens[1]; Protocol = requestTokens[2]; } public void ReadRequestBody() { requestBody = new byte[requestBodyLength]; if (requestBodyLength > 0) { int nRead = 0; int length = requestBodyLength; while (length > 0) { int nr = connectionStream.Read(requestBody, nRead, length); if (nr > 0) { nRead += nr; length -= nr; } } } contentStream = new MemoryStream(requestBody); } public void FinishRequest() { if ((requestBodyLength > 0) && (requestBody == null)) { int nRead = 0; int length = requestBodyLength; byte[] discard = new byte[8192]; while (length > 0) { int nr = connectionStream.Read(discard,0,length > discard.Length ? discard.Length : length); if (nr > 0) { nRead += nr; length -= nr; } } } } 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].Value; 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 bool ContainsParameter(string parameterName) => requestParameters.ContainsKey(parameterName); 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, params object[] p) => Redirect(303, location, p); public HttpResponse Redirect(int status, string location, params object[] p) { location = string.Format(location, p); HttpResponse response = new HttpResponse(this); response.StatusCode = status; response.SetHeader("location", location); return response; } public void Dispose() { contentReader?.Dispose(); ContentStream?.Dispose(); } } }