ln.http/HttpRequest.cs

273 lines
8.3 KiB
C#

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<HttpRequest> current = new ThreadLocal<HttpRequest>();
static public HttpRequest Current => current.Value;
//Dictionary<String, String> requestHeaders;
HeaderContainer requestHeaders;
Dictionary<String, String> requestCookies;
Dictionary<string, String> 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<string, string>();
requestParameters = new Dictionary<string, string>();
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<string> 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();
}
}
}