261 lines
8.4 KiB
C#
261 lines
8.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using ln.logging;
|
|
using ln.threading;
|
|
using ln.application;
|
|
using ln.http.listener;
|
|
using ln.http.connections;
|
|
using System.Globalization;
|
|
using ln.http.exceptions;
|
|
using System.Threading;
|
|
using ln.type;
|
|
using ln.http.router;
|
|
using System.IO;
|
|
|
|
namespace ln.http
|
|
{
|
|
public class HTTPServer
|
|
{
|
|
public static int backlog = 5;
|
|
public static int defaultPort = 8080;
|
|
public static bool exclusivePortListener = false;
|
|
|
|
public IHttpRouter Router { get; set; }
|
|
|
|
public bool IsRunning => !shutdown && (threadPool.CurrentPoolSize > 0);
|
|
public Logger Logger { get; set; }
|
|
|
|
bool shutdown = false;
|
|
|
|
List<Listener> listeners = new List<Listener>();
|
|
public Listener[] Listeners => listeners.ToArray();
|
|
|
|
DynamicPool threadPool;
|
|
public DynamicPool ThreadPool => threadPool;
|
|
|
|
HashSet<Connection> currentConnections = new HashSet<Connection>();
|
|
public IEnumerable<Connection> CurrentConnections => currentConnections;
|
|
|
|
public HTTPServer()
|
|
{
|
|
Logger = Logger.Default;
|
|
threadPool = new DynamicPool(1024);
|
|
}
|
|
public HTTPServer(IHttpRouter router)
|
|
: this()
|
|
{
|
|
Router = router;
|
|
}
|
|
public HTTPServer(Listener listener, IHttpRouter router)
|
|
: this(router)
|
|
{
|
|
AddListener(listener);
|
|
}
|
|
public HTTPServer(Endpoint endpoint, IHttpRouter router)
|
|
: this(new HttpListener(endpoint), router) { }
|
|
|
|
public void AddListener(Listener listener)
|
|
{
|
|
listeners.Add(listener);
|
|
if (IsRunning)
|
|
StartListener(listener);
|
|
}
|
|
|
|
public void StartListener(Listener listener)
|
|
{
|
|
listener.Open();
|
|
|
|
threadPool.Enqueue(
|
|
() => listener.AcceptMany(
|
|
(connection) => threadPool.Enqueue(
|
|
() => this.HandleConnection(connection)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
public void StopListener(Listener listener)
|
|
{
|
|
listener.Close();
|
|
}
|
|
|
|
|
|
public void AddEndpoint(Endpoint endpoint)
|
|
{
|
|
AddListener(new HttpListener(endpoint));
|
|
}
|
|
|
|
public void Start()
|
|
{
|
|
threadPool.Start();
|
|
|
|
foreach (Listener listener in listeners)
|
|
StartListener(listener);
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
lock (this)
|
|
{
|
|
this.shutdown = true;
|
|
}
|
|
foreach (Listener listener in listeners)
|
|
StopListener(listener);
|
|
|
|
for (int n = 0; n < 150; n++)
|
|
{
|
|
lock (currentConnections)
|
|
{
|
|
if (currentConnections.Count == 0)
|
|
break;
|
|
if ((n % 20) == 0)
|
|
{
|
|
Logging.Log(LogLevel.INFO, "HTTPServer: still waiting for {0} connections to close", currentConnections.Count);
|
|
}
|
|
}
|
|
Thread.Sleep(100);
|
|
}
|
|
|
|
lock (currentConnections)
|
|
{
|
|
foreach (Connection connection in currentConnections)
|
|
{
|
|
connection.Close();
|
|
}
|
|
}
|
|
|
|
threadPool.Stop(true);
|
|
}
|
|
|
|
private void HandleConnection(Connection connection)
|
|
{
|
|
lock (this.currentConnections)
|
|
currentConnections.Add(connection);
|
|
|
|
try
|
|
{
|
|
HttpRequest httpRequest = null;
|
|
bool keepAlive = true;
|
|
try
|
|
{
|
|
do
|
|
{
|
|
httpRequest = connection.ReadRequest(this);
|
|
|
|
if (httpRequest == null)
|
|
break;
|
|
|
|
HttpResponse response;
|
|
try
|
|
{
|
|
response = Router.Route(new HttpRoutingContext(httpRequest),httpRequest);
|
|
}
|
|
catch (HttpException httpExc)
|
|
{
|
|
response = new HttpResponse((HttpStatusCode)httpExc.StatusCode).Content(httpExc.Message);
|
|
}
|
|
|
|
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");
|
|
|
|
SendResponse(connection.GetStream(), httpRequest, response);
|
|
|
|
response?.ContentStream?.Dispose();
|
|
|
|
} while (keepAlive);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Logging.Log(e);
|
|
HttpResponse
|
|
.InternalServerError()
|
|
.Content(e)
|
|
.SendResponse(connection.GetStream(), httpRequest);
|
|
}
|
|
|
|
HttpRequest.ClearCurrent();
|
|
connection.Close();
|
|
} finally
|
|
{
|
|
lock (currentConnections)
|
|
currentConnections.Remove(connection);
|
|
}
|
|
}
|
|
|
|
public static void SendResponse(Stream stream, HttpRequest request, HttpResponse response)
|
|
{
|
|
response.SendResponse(stream, request);
|
|
/*
|
|
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[]
|
|
{
|
|
new Argument('p',"port",8080),
|
|
new Argument('l',"listen","127.0.0.1"),
|
|
new Argument('c', "catch",null)
|
|
});
|
|
|
|
argumentContainer.Parse(ref arguments);
|
|
|
|
SimpleRouter router = new SimpleRouter();
|
|
router.AddSimpleRoute("/*", new RouterTarget((request) =>
|
|
{
|
|
HttpResponse response = new HttpResponse(request);
|
|
response.StatusCode = 404;
|
|
response.SetHeader("content-type", "text/plain");
|
|
response.ContentWriter.WriteLine("404 Not Found");
|
|
response.ContentWriter.Flush();
|
|
return response;
|
|
}), -100);
|
|
|
|
foreach (String path in arguments)
|
|
{
|
|
StaticRouter staticRouter = new StaticRouter(path);
|
|
staticRouter.AddIndex("index.html");
|
|
staticRouter.AddIndex("index.htm");
|
|
router.AddSimpleRoute("/*", staticRouter);
|
|
}
|
|
|
|
if (argumentContainer['c'].Value != null)
|
|
router.AddSimpleRoute("/*", new RouterTarget((request) => router.Route(new HttpRoutingContext(request, argumentContainer['c'].Value), request)), 0);
|
|
|
|
HTTPServer server = new HTTPServer(new Endpoint(IPv6.Parse(argumentContainer['l'].Value),int.Parse(argumentContainer['p'].Value)),
|
|
new LoggingRouter(router));
|
|
server.Start();
|
|
}
|
|
|
|
}
|
|
}
|