2020-11-17 23:46:07 +01:00
|
|
|
|
using System;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using ln.type;
|
|
|
|
|
using ln.http.exceptions;
|
|
|
|
|
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; }
|
|
|
|
|
|
2020-12-07 09:30:01 +01:00
|
|
|
|
[Obsolete]
|
|
|
|
|
public String Method { get => HttpMethod.ToString(); private set => throw new NotImplementedException(); }
|
|
|
|
|
public HttpMethod HttpMethod { get; private set; }
|
2020-11-17 23:46:07 +01:00
|
|
|
|
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();
|
2020-11-29 14:20:51 +01:00
|
|
|
|
|
|
|
|
|
if (requestLine == null)
|
|
|
|
|
throw new ConnectionClosedException("UnbufferedStreamReader.ReadLine() returned null");
|
|
|
|
|
|
2020-11-17 23:46:07 +01:00
|
|
|
|
string[] requestTokens = requestLine.Split(new char[0], StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
|
|
|
|
|
if (requestTokens.Length != 3)
|
|
|
|
|
throw new BadRequestException();
|
|
|
|
|
|
2020-12-07 09:30:01 +01:00
|
|
|
|
HttpMethod = (HttpMethod)Enum.Parse(typeof(HttpMethod), requestTokens[0]);
|
2020-11-17 23:46:07 +01:00
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|