Add default 404 handling, extend HttpResponse, fix several issues
ln.build - build0.waldrennach.l--n.de build job pending
Details
ln.build - build0.waldrennach.l--n.de build job pending
Details
parent
f868b0e998
commit
22c09b5b0a
|
@ -10,7 +10,7 @@ using ln.http.exceptions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using ln.type;
|
using ln.type;
|
||||||
using ln.http.router;
|
using ln.http.router;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace ln.http
|
namespace ln.http
|
||||||
{
|
{
|
||||||
|
@ -141,6 +141,7 @@ namespace ln.http
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
httpRequest = connection.ReadRequest(this);
|
httpRequest = connection.ReadRequest(this);
|
||||||
|
|
||||||
if (httpRequest == null)
|
if (httpRequest == null)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -151,23 +152,16 @@ namespace ln.http
|
||||||
}
|
}
|
||||||
catch (HttpException httpExc)
|
catch (HttpException httpExc)
|
||||||
{
|
{
|
||||||
response = new HttpResponse(httpRequest);
|
response = new HttpResponse((HttpStatusCode)httpExc.StatusCode).Content(httpExc.Message);
|
||||||
response.StatusCode = httpExc.StatusCode;
|
|
||||||
response.StatusMessage = httpExc.Message;
|
|
||||||
response.ContentWriter.WriteLine(httpExc.Message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response != null)
|
if (response == null)
|
||||||
{
|
response = HttpResponse.NotFound().Content(String.Format("The URI {0} could not be found on this server.", httpRequest.URI));
|
||||||
keepAlive = httpRequest.GetRequestHeader("connection", "keep-alive").Equals("keep-alive") && response.GetHeader("connection", "keep-alive").Equals("keep-alive");
|
|
||||||
response.SetHeader("connection", keepAlive ? "keep-alive" : "close");
|
|
||||||
|
|
||||||
connection.SendResponse(response);
|
keepAlive = httpRequest.GetRequestHeader("connection", "keep-alive").Equals("keep-alive") && response.GetHeader("connection", "keep-alive").Equals("keep-alive");
|
||||||
}
|
response.SetHeader("connection", keepAlive ? "keep-alive" : "close");
|
||||||
else
|
|
||||||
{
|
SendResponse(connection.GetStream(), httpRequest, response);
|
||||||
keepAlive = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
response?.ContentStream?.Dispose();
|
response?.ContentStream?.Dispose();
|
||||||
|
|
||||||
|
@ -176,17 +170,16 @@ namespace ln.http
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logging.Log(e);
|
Logging.Log(e);
|
||||||
if (httpRequest != null)
|
|
||||||
{
|
|
||||||
HttpResponse httpResponse = new HttpResponse(httpRequest);
|
|
||||||
httpResponse.StatusCode = 500;
|
|
||||||
httpResponse.ContentWriter.WriteLine("500 Internal Server Error");
|
|
||||||
httpResponse.ContentWriter.Flush();
|
|
||||||
|
|
||||||
connection.SendResponse(httpResponse);
|
SendResponse(
|
||||||
}
|
connection.GetStream(),
|
||||||
|
httpRequest,
|
||||||
|
HttpResponse
|
||||||
|
.InternalServerError()
|
||||||
|
.Content(e)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpRequest.ClearCurrent();
|
HttpRequest.ClearCurrent();
|
||||||
connection.GetStream().Close();
|
connection.GetStream().Close();
|
||||||
} finally
|
} finally
|
||||||
|
@ -196,13 +189,36 @@ namespace ln.http
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Log(DateTime startTime,double duration,HttpRequest httpRequest,HttpResponse httpResponse)
|
public static void SendResponse(Stream stream, HttpRequest request, HttpResponse response)
|
||||||
{
|
{
|
||||||
Logger.Log(LogLevel.INFO, "{0} {1} {2} {3}",startTime.ToString("yyyyMMdd-HH:mm:ss"),duration.ToString(CultureInfo.InvariantCulture),httpRequest.Hostname,httpRequest.RequestURL);
|
request.FinishRequest();
|
||||||
|
response.SetHeader("Content-Length", response.ContentStream.Length.ToString());
|
||||||
|
|
||||||
|
StreamWriter streamWriter = new StreamWriter(stream);
|
||||||
|
streamWriter.NewLine = "\r\n";
|
||||||
|
|
||||||
|
streamWriter.WriteLine("{0} {1} {2}", request.Protocol, (int)response.HttpStatusCode, response.HttpStatusCode.ToString());
|
||||||
|
foreach (String headerName in response.GetHeaderNames())
|
||||||
|
{
|
||||||
|
streamWriter.WriteLine("{0}: {1}", headerName, response.GetHeader(headerName));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (HttpCookie httpCookie in response.Cookies)
|
||||||
|
{
|
||||||
|
streamWriter.WriteLine("Set-Cookie: {0}", httpCookie.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
streamWriter.WriteLine();
|
||||||
|
streamWriter.Flush();
|
||||||
|
|
||||||
|
response.ContentStream.Position = 0;
|
||||||
|
response.ContentStream.CopyTo(stream);
|
||||||
|
response.ContentStream.Close();
|
||||||
|
response.ContentStream.Dispose();
|
||||||
|
|
||||||
|
stream.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static void StartSimpleServer(string[] arguments)
|
public static void StartSimpleServer(string[] arguments)
|
||||||
{
|
{
|
||||||
ArgumentContainer argumentContainer = new ArgumentContainer(new Argument[]
|
ArgumentContainer argumentContainer = new ArgumentContainer(new Argument[]
|
||||||
|
|
|
@ -7,44 +7,54 @@ namespace ln.http
|
||||||
{
|
{
|
||||||
public class HttpResponse
|
public class HttpResponse
|
||||||
{
|
{
|
||||||
public HttpRequest HttpRequest { get; }
|
//public HttpRequest HttpRequest { get; }
|
||||||
|
|
||||||
public Stream ContentStream { get; private set; }
|
public Stream ContentStream { get; private set; }
|
||||||
public TextWriter ContentWriter { get; private set; }
|
public TextWriter ContentWriter { get; private set; }
|
||||||
public bool HasCustomContentStream { get; private set; }
|
public bool HasCustomContentStream { get; private set; }
|
||||||
|
|
||||||
int statusCode;
|
|
||||||
string statusMessage;
|
|
||||||
Dictionary<string, List<String>> headers = new Dictionary<string, List<string>>();
|
Dictionary<string, List<String>> headers = new Dictionary<string, List<string>>();
|
||||||
List<HttpCookie> cookies = new List<HttpCookie>();
|
List<HttpCookie> cookies = new List<HttpCookie>();
|
||||||
|
|
||||||
public HttpResponse(HttpRequest httpRequest)
|
|
||||||
|
public HttpResponse() : this(HttpStatusCode.OK)
|
||||||
{
|
{
|
||||||
HttpRequest = httpRequest;
|
}
|
||||||
|
public HttpResponse(HttpStatusCode statusCode)
|
||||||
|
{
|
||||||
|
HttpStatusCode = statusCode;
|
||||||
ContentStream = new MemoryStream();
|
ContentStream = new MemoryStream();
|
||||||
ContentWriter = new StreamWriter(ContentStream);
|
ContentWriter = new StreamWriter(ContentStream);
|
||||||
|
|
||||||
StatusCode = 200;
|
|
||||||
SetHeader("content-type", "text/html");
|
SetHeader("content-type", "text/html");
|
||||||
}
|
}
|
||||||
public HttpResponse(HttpRequest httpRequest,string contentType)
|
|
||||||
:this(httpRequest)
|
[Obsolete]
|
||||||
|
public HttpResponse(HttpRequest httpRequest) : this() { }
|
||||||
|
[Obsolete]
|
||||||
|
public HttpResponse(HttpRequest httpRequest,string contentType) : this(contentType) { }
|
||||||
|
public HttpResponse(string contentType)
|
||||||
|
:this()
|
||||||
{
|
{
|
||||||
SetHeader("content-type", contentType);
|
SetHeader("content-type", contentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpResponse(HttpRequest httpRequest, Stream contentStream)
|
[Obsolete]
|
||||||
|
public HttpResponse(HttpRequest httpRequest, Stream contentStream) : this(contentStream) { }
|
||||||
|
public HttpResponse(Stream contentStream)
|
||||||
{
|
{
|
||||||
HttpRequest = httpRequest;
|
|
||||||
ContentStream = contentStream;
|
ContentStream = contentStream;
|
||||||
ContentWriter = null;
|
ContentWriter = null;
|
||||||
HasCustomContentStream = true;
|
HasCustomContentStream = true;
|
||||||
|
|
||||||
StatusCode = 200;
|
HttpStatusCode = HttpStatusCode.OK;
|
||||||
SetHeader("content-type", "text/html");
|
SetHeader("content-type", "text/html");
|
||||||
}
|
}
|
||||||
public HttpResponse(HttpRequest httpRequest, Stream contentStream,string contentType)
|
|
||||||
:this(httpRequest,contentStream)
|
[Obsolete]
|
||||||
|
public HttpResponse(HttpRequest httpRequest, Stream contentStream,string contentType) : this(contentStream, contentType){ }
|
||||||
|
public HttpResponse(Stream contentStream,string contentType)
|
||||||
|
:this(contentStream)
|
||||||
{
|
{
|
||||||
SetHeader("content-type", contentType);
|
SetHeader("content-type", contentType);
|
||||||
}
|
}
|
||||||
|
@ -80,56 +90,80 @@ namespace ln.http
|
||||||
|
|
||||||
headers[name].Add(value);
|
headers[name].Add(value);
|
||||||
}
|
}
|
||||||
public void RemoveHeader(String name)
|
public void RemoveHeader(String name) => headers.Remove(name.ToUpper());
|
||||||
{
|
public bool ContainsHeader(string headerName) => headers.ContainsKey(headerName.ToUpper());
|
||||||
headers.Remove(name.ToUpper());
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ContainsHeader(string headerName)
|
|
||||||
{
|
|
||||||
return headers.ContainsKey(headerName.ToUpper());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddCookie(string name,string value)
|
public void AddCookie(string name,string value)
|
||||||
{
|
{
|
||||||
AddCookie(new HttpCookie(name, value));
|
AddCookie(new HttpCookie(name, value));
|
||||||
}
|
}
|
||||||
public void AddCookie(HttpCookie httpCookie)
|
public void AddCookie(HttpCookie httpCookie) => cookies.Add(httpCookie);
|
||||||
{
|
public void RemoveCookie(HttpCookie httpCookie) => cookies.Remove(httpCookie);
|
||||||
cookies.Add(httpCookie);
|
|
||||||
}
|
|
||||||
public void RemoveCookie(HttpCookie httpCookie)
|
|
||||||
{
|
|
||||||
cookies.Remove(httpCookie);
|
|
||||||
}
|
|
||||||
public HttpCookie[] Cookies => cookies.ToArray();
|
public HttpCookie[] Cookies => cookies.ToArray();
|
||||||
|
|
||||||
|
public HttpStatusCode HttpStatusCode { get; set; }
|
||||||
|
|
||||||
|
[Obsolete]
|
||||||
public int StatusCode
|
public int StatusCode
|
||||||
{
|
{
|
||||||
get
|
get => (int)HttpStatusCode;
|
||||||
{
|
set => HttpStatusCode = (HttpStatusCode)value;
|
||||||
return statusCode;
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
statusCode = value;
|
|
||||||
statusMessage = HttpStatusCodes.GetStatusMessage(statusCode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
[Obsolete]
|
||||||
public String StatusMessage
|
public String StatusMessage
|
||||||
{
|
{
|
||||||
get {
|
get => HttpStatusCode.ToString();
|
||||||
return statusMessage;
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
statusMessage = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static HttpResponse OK() => new HttpResponse(HttpStatusCode.OK);
|
||||||
|
public static HttpResponse Created() => new HttpResponse(HttpStatusCode.Created);
|
||||||
|
public static HttpResponse Accepted() => new HttpResponse(HttpStatusCode.Accepted);
|
||||||
|
public static HttpResponse NoContent() => new HttpResponse(HttpStatusCode.NoContent);
|
||||||
|
|
||||||
|
public static HttpResponse MovedPermanently() => new HttpResponse(HttpStatusCode.MovedPermanently);
|
||||||
|
public static HttpResponse TemporaryRedirect() => new HttpResponse(HttpStatusCode.TemporaryRedirect);
|
||||||
|
public static HttpResponse PermanentRedirect() => new HttpResponse(HttpStatusCode.PermanentRedirect);
|
||||||
|
|
||||||
|
public static HttpResponse NotFound() => new HttpResponse(HttpStatusCode.NotFound);
|
||||||
|
public static HttpResponse BadRequest() => new HttpResponse(HttpStatusCode.BadRequest);
|
||||||
|
public static HttpResponse Unauthorized() => new HttpResponse(HttpStatusCode.Unauthorized);
|
||||||
|
public static HttpResponse Forbidden() => new HttpResponse(HttpStatusCode.Forbidden);
|
||||||
|
public static HttpResponse MethodNotAllowed() => new HttpResponse(HttpStatusCode.MethodNotAllowed);
|
||||||
|
public static HttpResponse RequestTimeout() => new HttpResponse(HttpStatusCode.RequestTimeout);
|
||||||
|
public static HttpResponse ImATeapot() => new HttpResponse(HttpStatusCode.ImATeapot);
|
||||||
|
public static HttpResponse UpgradeRequired() => new HttpResponse(HttpStatusCode.UpgradeRequired);
|
||||||
|
|
||||||
|
public static HttpResponse InternalServerError() => new HttpResponse(HttpStatusCode.InternalServerError);
|
||||||
|
public static HttpResponse NotImplemented() => new HttpResponse(HttpStatusCode.NotImplemented);
|
||||||
|
public static HttpResponse BadGateway() => new HttpResponse(HttpStatusCode.BadGateway);
|
||||||
|
public static HttpResponse ServiceUnavailable() => new HttpResponse(HttpStatusCode.ServiceUnavailable);
|
||||||
|
public static HttpResponse GatewayTimeout() => new HttpResponse(HttpStatusCode.GatewayTimeout);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public HttpResponse Content(Exception exception)
|
||||||
|
{
|
||||||
|
SetHeader("content-type", "text/plain");
|
||||||
|
ContentWriter.WriteLine("{0}", exception);
|
||||||
|
ContentWriter.Flush();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public HttpResponse Content(string text)
|
||||||
|
{
|
||||||
|
ContentWriter.Write(text);
|
||||||
|
ContentWriter.Flush();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ namespace ln.http
|
||||||
{ 403, "Access denied" },
|
{ 403, "Access denied" },
|
||||||
{ 404, "Not Found" },
|
{ 404, "Not Found" },
|
||||||
{ 500, "Internal Error" }
|
{ 500, "Internal Error" }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public static String GetStatusMessage(int code){
|
public static String GetStatusMessage(int code){
|
||||||
|
|
|
@ -55,37 +55,5 @@ namespace ln.http.connections
|
||||||
Listener = null;
|
Listener = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void SendResponse(HttpResponse response) => SendResponse(GetStream(), response);
|
|
||||||
|
|
||||||
public static void SendResponse(Stream stream, HttpResponse response)
|
|
||||||
{
|
|
||||||
response.HttpRequest.FinishRequest();
|
|
||||||
|
|
||||||
response.SetHeader("Content-Length", response.ContentStream.Length.ToString());
|
|
||||||
|
|
||||||
StreamWriter streamWriter = new StreamWriter(stream);
|
|
||||||
streamWriter.NewLine = "\r\n";
|
|
||||||
|
|
||||||
streamWriter.WriteLine("{0} {1} {2}", response.HttpRequest.Protocol, response.StatusCode, response.StatusMessage);
|
|
||||||
foreach (String headerName in response.GetHeaderNames())
|
|
||||||
{
|
|
||||||
streamWriter.WriteLine("{0}: {1}", headerName, response.GetHeader(headerName));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (HttpCookie httpCookie in response.Cookies)
|
|
||||||
{
|
|
||||||
streamWriter.WriteLine("Set-Cookie: {0}", httpCookie.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
streamWriter.WriteLine();
|
|
||||||
streamWriter.Flush();
|
|
||||||
|
|
||||||
response.ContentStream.Position = 0;
|
|
||||||
response.ContentStream.CopyTo(stream);
|
|
||||||
response.ContentStream.Close();
|
|
||||||
response.ContentStream.Dispose();
|
|
||||||
|
|
||||||
stream.Flush();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
<Version>0.2.1-test</Version>
|
<Version>0.2.2-test</Version>
|
||||||
<Authors>Harald Wolff-Thobaben</Authors>
|
<Authors>Harald Wolff-Thobaben</Authors>
|
||||||
<Company>l--n.de</Company>
|
<Company>l--n.de</Company>
|
||||||
<Description />
|
<Description />
|
||||||
|
|
|
@ -5,8 +5,13 @@ using System.Text;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
namespace ln.http.router
|
namespace ln.http.router
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public delegate bool RouterFilterDelegate(SimpleRouter sender, ref HttpRoutingContext routingContext, HttpRequest httpRequest, out HttpResponse httpResponse);
|
||||||
|
|
||||||
public class SimpleRouter : IHttpRouter
|
public class SimpleRouter : IHttpRouter
|
||||||
{
|
{
|
||||||
|
public event RouterFilterDelegate OnRoute;
|
||||||
|
|
||||||
List<SimpleRoute> routes = new List<SimpleRoute>();
|
List<SimpleRoute> routes = new List<SimpleRoute>();
|
||||||
public SimpleRoute[] Routes => routes.ToArray();
|
public SimpleRoute[] Routes => routes.ToArray();
|
||||||
|
|
||||||
|
@ -34,7 +39,7 @@ namespace ln.http.router
|
||||||
return string.Format("{0}", part);
|
return string.Format("{0}", part);
|
||||||
}).ToArray();
|
}).ToArray();
|
||||||
|
|
||||||
string reroute = string.Join("/", reparts);
|
string reroute = string.Format("{0}\\/?$", string.Join("/", reparts));
|
||||||
|
|
||||||
AddRoute(reroute, target, priority);
|
AddRoute(reroute, target, priority);
|
||||||
}
|
}
|
||||||
|
@ -52,6 +57,17 @@ namespace ln.http.router
|
||||||
|
|
||||||
public HttpResponse Route(HttpRoutingContext routingContext, HttpRequest httpRequest)
|
public HttpResponse Route(HttpRoutingContext routingContext, HttpRequest httpRequest)
|
||||||
{
|
{
|
||||||
|
HttpResponse httpResponse;
|
||||||
|
|
||||||
|
if (OnRoute != null)
|
||||||
|
{
|
||||||
|
foreach (RouterFilterDelegate filterDelegate in OnRoute.GetInvocationList())
|
||||||
|
{
|
||||||
|
if (filterDelegate(this, ref routingContext, httpRequest, out httpResponse))
|
||||||
|
return httpResponse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (SimpleRoute simpleRoute in routes.ToArray())
|
foreach (SimpleRoute simpleRoute in routes.ToArray())
|
||||||
{
|
{
|
||||||
Match match = simpleRoute.Route.Match(routingContext.Path);
|
Match match = simpleRoute.Route.Match(routingContext.Path);
|
||||||
|
@ -69,9 +85,9 @@ namespace ln.http.router
|
||||||
residual = "/" + group.Value;
|
residual = "/" + group.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse response = simpleRoute.Target.Route(routingContext.Routed(residual), httpRequest);
|
httpResponse = simpleRoute.Target.Route(routingContext.Routed(residual), httpRequest);
|
||||||
if (response != null)
|
if (httpResponse != null)
|
||||||
return response;
|
return httpResponse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -54,9 +54,7 @@ namespace ln.http.websocket
|
||||||
|
|
||||||
String wsKey = httpRequest.GetRequestHeader("Sec-WebSocket-Key");
|
String wsKey = httpRequest.GetRequestHeader("Sec-WebSocket-Key");
|
||||||
|
|
||||||
HttpResponse httpResponse = new HttpResponse(httpRequest);
|
HttpResponse httpResponse = new HttpResponse(HttpStatusCode.SwitchingProtocols);
|
||||||
httpResponse.StatusCode = 101;
|
|
||||||
httpResponse.StatusMessage = "Switching Protocols";
|
|
||||||
httpResponse.AddHeader("upgrade", "websocket");
|
httpResponse.AddHeader("upgrade", "websocket");
|
||||||
httpResponse.AddHeader("connection", "Upgrade");
|
httpResponse.AddHeader("connection", "Upgrade");
|
||||||
httpResponse.AddHeader("Sec-WebSocket-Version", "13");
|
httpResponse.AddHeader("Sec-WebSocket-Version", "13");
|
||||||
|
@ -68,7 +66,7 @@ namespace ln.http.websocket
|
||||||
Convert.ToBase64String(SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(acceptKey)))
|
Convert.ToBase64String(SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(acceptKey)))
|
||||||
);
|
);
|
||||||
|
|
||||||
Connection.SendResponse(Stream, httpResponse);
|
HTTPServer.SendResponse(Stream, httpRequest, httpResponse);
|
||||||
//HTTPServerConnection.Current.Value.AbortRequested += (connection) => Close();
|
//HTTPServerConnection.Current.Value.AbortRequested += (connection) => Close();
|
||||||
State = WebSocketState.OPEN;
|
State = WebSocketState.OPEN;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue