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 ln.type;
|
||||
using ln.http.router;
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace ln.http
|
||||
{
|
||||
|
@ -141,6 +141,7 @@ namespace ln.http
|
|||
do
|
||||
{
|
||||
httpRequest = connection.ReadRequest(this);
|
||||
|
||||
if (httpRequest == null)
|
||||
break;
|
||||
|
||||
|
@ -151,23 +152,16 @@ namespace ln.http
|
|||
}
|
||||
catch (HttpException httpExc)
|
||||
{
|
||||
response = new HttpResponse(httpRequest);
|
||||
response.StatusCode = httpExc.StatusCode;
|
||||
response.StatusMessage = httpExc.Message;
|
||||
response.ContentWriter.WriteLine(httpExc.Message);
|
||||
response = new HttpResponse((HttpStatusCode)httpExc.StatusCode).Content(httpExc.Message);
|
||||
}
|
||||
|
||||
if (response != null)
|
||||
{
|
||||
keepAlive = httpRequest.GetRequestHeader("connection", "keep-alive").Equals("keep-alive") && response.GetHeader("connection", "keep-alive").Equals("keep-alive");
|
||||
response.SetHeader("connection", keepAlive ? "keep-alive" : "close");
|
||||
if (response == null)
|
||||
response = HttpResponse.NotFound().Content(String.Format("The URI {0} could not be found on this server.", httpRequest.URI));
|
||||
|
||||
connection.SendResponse(response);
|
||||
}
|
||||
else
|
||||
{
|
||||
keepAlive = false;
|
||||
}
|
||||
keepAlive = httpRequest.GetRequestHeader("connection", "keep-alive").Equals("keep-alive") && response.GetHeader("connection", "keep-alive").Equals("keep-alive");
|
||||
response.SetHeader("connection", keepAlive ? "keep-alive" : "close");
|
||||
|
||||
SendResponse(connection.GetStream(), httpRequest, response);
|
||||
|
||||
response?.ContentStream?.Dispose();
|
||||
|
||||
|
@ -176,17 +170,16 @@ namespace ln.http
|
|||
catch (Exception 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();
|
||||
connection.GetStream().Close();
|
||||
} 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)
|
||||
{
|
||||
ArgumentContainer argumentContainer = new ArgumentContainer(new Argument[]
|
||||
|
|
|
@ -7,44 +7,54 @@ namespace ln.http
|
|||
{
|
||||
public class HttpResponse
|
||||
{
|
||||
public HttpRequest HttpRequest { get; }
|
||||
//public HttpRequest HttpRequest { get; }
|
||||
|
||||
public Stream ContentStream { get; private set; }
|
||||
public TextWriter ContentWriter { get; private set; }
|
||||
public bool HasCustomContentStream { get; private set; }
|
||||
|
||||
int statusCode;
|
||||
string statusMessage;
|
||||
Dictionary<string, List<String>> headers = new Dictionary<string, List<string>>();
|
||||
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();
|
||||
ContentWriter = new StreamWriter(ContentStream);
|
||||
|
||||
StatusCode = 200;
|
||||
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);
|
||||
}
|
||||
|
||||
public HttpResponse(HttpRequest httpRequest, Stream contentStream)
|
||||
[Obsolete]
|
||||
public HttpResponse(HttpRequest httpRequest, Stream contentStream) : this(contentStream) { }
|
||||
public HttpResponse(Stream contentStream)
|
||||
{
|
||||
HttpRequest = httpRequest;
|
||||
ContentStream = contentStream;
|
||||
ContentWriter = null;
|
||||
HasCustomContentStream = true;
|
||||
|
||||
StatusCode = 200;
|
||||
HttpStatusCode = HttpStatusCode.OK;
|
||||
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);
|
||||
}
|
||||
|
@ -80,56 +90,80 @@ namespace ln.http
|
|||
|
||||
headers[name].Add(value);
|
||||
}
|
||||
public void RemoveHeader(String name)
|
||||
{
|
||||
headers.Remove(name.ToUpper());
|
||||
}
|
||||
|
||||
public bool ContainsHeader(string headerName)
|
||||
{
|
||||
return headers.ContainsKey(headerName.ToUpper());
|
||||
}
|
||||
public void RemoveHeader(String name) => headers.Remove(name.ToUpper());
|
||||
public bool ContainsHeader(string headerName) => headers.ContainsKey(headerName.ToUpper());
|
||||
|
||||
public void AddCookie(string name,string value)
|
||||
{
|
||||
AddCookie(new HttpCookie(name, value));
|
||||
}
|
||||
public void AddCookie(HttpCookie httpCookie)
|
||||
{
|
||||
cookies.Add(httpCookie);
|
||||
}
|
||||
public void RemoveCookie(HttpCookie httpCookie)
|
||||
{
|
||||
cookies.Remove(httpCookie);
|
||||
}
|
||||
public void AddCookie(HttpCookie httpCookie) => cookies.Add(httpCookie);
|
||||
public void RemoveCookie(HttpCookie httpCookie) => cookies.Remove(httpCookie);
|
||||
public HttpCookie[] Cookies => cookies.ToArray();
|
||||
|
||||
|
||||
|
||||
public HttpStatusCode HttpStatusCode { get; set; }
|
||||
|
||||
[Obsolete]
|
||||
public int StatusCode
|
||||
{
|
||||
get
|
||||
{
|
||||
return statusCode;
|
||||
}
|
||||
set {
|
||||
statusCode = value;
|
||||
statusMessage = HttpStatusCodes.GetStatusMessage(statusCode);
|
||||
}
|
||||
get => (int)HttpStatusCode;
|
||||
set => HttpStatusCode = (HttpStatusCode)value;
|
||||
}
|
||||
[Obsolete]
|
||||
public String StatusMessage
|
||||
{
|
||||
get {
|
||||
return statusMessage;
|
||||
}
|
||||
set {
|
||||
statusMessage = value;
|
||||
}
|
||||
get => HttpStatusCode.ToString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
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" },
|
||||
{ 404, "Not Found" },
|
||||
{ 500, "Internal Error" }
|
||||
|
||||
};
|
||||
|
||||
public static String GetStatusMessage(int code){
|
||||
|
|
|
@ -55,37 +55,5 @@ namespace ln.http.connections
|
|||
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>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Version>0.2.1-test</Version>
|
||||
<Version>0.2.2-test</Version>
|
||||
<Authors>Harald Wolff-Thobaben</Authors>
|
||||
<Company>l--n.de</Company>
|
||||
<Description />
|
||||
|
|
|
@ -5,8 +5,13 @@ using System.Text;
|
|||
using System.Linq;
|
||||
namespace ln.http.router
|
||||
{
|
||||
|
||||
public delegate bool RouterFilterDelegate(SimpleRouter sender, ref HttpRoutingContext routingContext, HttpRequest httpRequest, out HttpResponse httpResponse);
|
||||
|
||||
public class SimpleRouter : IHttpRouter
|
||||
{
|
||||
public event RouterFilterDelegate OnRoute;
|
||||
|
||||
List<SimpleRoute> routes = new List<SimpleRoute>();
|
||||
public SimpleRoute[] Routes => routes.ToArray();
|
||||
|
||||
|
@ -34,7 +39,7 @@ namespace ln.http.router
|
|||
return string.Format("{0}", part);
|
||||
}).ToArray();
|
||||
|
||||
string reroute = string.Join("/", reparts);
|
||||
string reroute = string.Format("{0}\\/?$", string.Join("/", reparts));
|
||||
|
||||
AddRoute(reroute, target, priority);
|
||||
}
|
||||
|
@ -52,6 +57,17 @@ namespace ln.http.router
|
|||
|
||||
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())
|
||||
{
|
||||
Match match = simpleRoute.Route.Match(routingContext.Path);
|
||||
|
@ -69,9 +85,9 @@ namespace ln.http.router
|
|||
residual = "/" + group.Value;
|
||||
}
|
||||
|
||||
HttpResponse response = simpleRoute.Target.Route(routingContext.Routed(residual), httpRequest);
|
||||
if (response != null)
|
||||
return response;
|
||||
httpResponse = simpleRoute.Target.Route(routingContext.Routed(residual), httpRequest);
|
||||
if (httpResponse != null)
|
||||
return httpResponse;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -54,9 +54,7 @@ namespace ln.http.websocket
|
|||
|
||||
String wsKey = httpRequest.GetRequestHeader("Sec-WebSocket-Key");
|
||||
|
||||
HttpResponse httpResponse = new HttpResponse(httpRequest);
|
||||
httpResponse.StatusCode = 101;
|
||||
httpResponse.StatusMessage = "Switching Protocols";
|
||||
HttpResponse httpResponse = new HttpResponse(HttpStatusCode.SwitchingProtocols);
|
||||
httpResponse.AddHeader("upgrade", "websocket");
|
||||
httpResponse.AddHeader("connection", "Upgrade");
|
||||
httpResponse.AddHeader("Sec-WebSocket-Version", "13");
|
||||
|
@ -68,7 +66,7 @@ namespace ln.http.websocket
|
|||
Convert.ToBase64String(SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(acceptKey)))
|
||||
);
|
||||
|
||||
Connection.SendResponse(Stream, httpResponse);
|
||||
HTTPServer.SendResponse(Stream, httpRequest, httpResponse);
|
||||
//HTTPServerConnection.Current.Value.AbortRequested += (connection) => Close();
|
||||
State = WebSocketState.OPEN;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue