Skip to content

Commit

Permalink
http server map methods single
Browse files Browse the repository at this point in the history
  • Loading branch information
alec1o committed Nov 18, 2023
1 parent 9454c57 commit a0bacb5
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 83 deletions.
178 changes: 97 additions & 81 deletions src/HTTP/HttpServer.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics.SymbolStore;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Netly.Core;

namespace Netly
{
public class HttpServer : IHttpServer
{
private HttpListener _listener;
private EventHandler<object> _onOpen, _onClose, _onError, _onPath, _onWebsocket;
private EventHandler<object> _onOpen, _onClose, _onError, _onWebsocket;

Check warning on line 15 in src/HTTP/HttpServer.cs

View workflow job for this annotation

GitHub Actions / build

The field 'HttpServer._onWebsocket' is never used

Check warning on line 15 in src/HTTP/HttpServer.cs

View workflow job for this annotation

GitHub Actions / build

The field 'HttpServer._onWebsocket' is never used
private bool _tryOpen, _tryClose;
private readonly List<(string url, bool isWebSocket)> _paths;

private readonly List<(string path, bool mapAllMethod, HttpMethod method, Action<Request, Response> callback)>
_httpMap;

private readonly List<(string path, Action<Request, WebSocketClient> callback)> _wsMap;

public bool IsOpen => _listener != null && _listener.IsListening;
public Host Host { get; private set; } = new Host(IPAddress.Any, 0);

public HttpServer()
{
_paths = new List<(string url, bool isWebSocket)>();
_httpMap =
new List<(string path, bool mapAllMethod, HttpMethod method, Action<Request, Response> callback)>();
_wsMap = new List<(string path, Action<Request, WebSocketClient> callback)>();
}

private struct PathContainer
Expand Down Expand Up @@ -126,43 +133,71 @@ public void OnClose(Action callback)
_onClose += (_, __) => MainThread.Add(() => callback?.Invoke());
}

public void On(string path, Action<Request, Response> callback)
public void MapAll(string path, Action<Request, Response> callback)
{
if (string.IsNullOrWhiteSpace(path)) return;
InternalMap(path, true, HttpMethod.Options, callback);
}

_paths.Add((path.Trim(), false));
public void MapGet(string path, Action<Request, Response> callback)
{
InternalMap(path, false, HttpMethod.Get, callback);
}

_onPath += (_, o) =>
{
var pathContainer = (PathContainer)o;
public void MapPut(string path, Action<Request, Response> callback)
{
InternalMap(path, false, HttpMethod.Put, callback);
}

if (pathContainer.Request.ComparePath(path))
{
MainThread.Add(() => callback?.Invoke(pathContainer.Request, pathContainer.Response));
}
};
public void MapHead(string path, Action<Request, Response> callback)
{
InternalMap(path, false, HttpMethod.Head, callback);
}

public void OnWebsocket(string path, Action<Request, WebSocketClient> callback)
public void MapPost(string path, Action<Request, Response> callback)
{
if (string.IsNullOrWhiteSpace(path)) return;
InternalMap(path, false, HttpMethod.Post, callback);
}

_paths.Add((path.Trim(), true));
public void MapPatch(string path, Action<Request, Response> callback)
{
// I DON'T KNOW WHY!
// But (HttpMethod.Patch) don't exist.
// Creating manual HttpMethod.
// TODO: Fix it for use HttpMethod.Patch
var method = new HttpMethod("PATCH");
InternalMap(path, false, method, callback);
}

_onWebsocket += (_, o) =>
{
var pathContainer = (PathContainer)o;
public void MapTrace(string path, Action<Request, Response> callback)
{
InternalMap(path, false, HttpMethod.Trace, callback);
}

if (pathContainer.Request.ComparePath(path))
{
MainThread.Add(() => callback?.Invoke(pathContainer.Request, pathContainer.WebSocket));
}
};
public void MapDelete(string path, Action<Request, Response> callback)
{
InternalMap(path, false, HttpMethod.Delete, callback);
}

public void MapOptions(string path, Action<Request, Response> callback)
{
InternalMap(path, false, HttpMethod.Options, callback);
}

private void InternalMap(string path, bool mapAllMethods, HttpMethod method, Action<Request, Response> callback)
{
if (string.IsNullOrWhiteSpace(path)) return;
_httpMap.Add((path.Trim(), mapAllMethods, method, callback));
}

public void MapWebSocket(string path, Action<Request, WebSocketClient> callback)
{
if (string.IsNullOrWhiteSpace(path)) return;
_wsMap.Add((path.Trim(), callback));
}

private void _ReceiveRequests()
{
ThreadPool.QueueUserWorkItem(_ =>
Task.Run(() =>
{
while (IsOpen)
{
Expand All @@ -171,6 +206,9 @@ private void _ReceiveRequests()
var context = _listener.GetContext();
var request = new Request(context.Request);
var response = new Response(context.Response);
var notFoundMessage = $"{request.RawRequest.HttpMethod.ToUpper()} {request.Path}";

#region Debug

// TODO: Only show on debug mode
{
Expand Down Expand Up @@ -205,71 +243,49 @@ private void _ReceiveRequests()
);
}

bool foundPath = false;
#endregion

if (request.IsWebSocket is false) // IS HTTP CONNECTION
if (request.IsWebSocket == false) // IS HTTP CONNECTION
{
foreach (var path in _paths)
{
if (request.ComparePath(path.url))
{
if (path.isWebSocket)
{
response.Send(426, "Only websocket connection for use this router");
}
else
{
_onPath?.Invoke(null, new PathContainer(request, response, null));
}

foundPath = true;
}
}
}
else
{
foreach (var path in _paths)
var paths = _httpMap.FindAll(x => request.ComparePath(x.path) && request.Method == x.method)
.ToArray();

if (paths.Length <= 0)
{
if (request.ComparePath(path.url))
{
if (path.isWebSocket)
{
foundPath = true;
}
}
response.Send(404, notFoundMessage);
continue;
}

if (foundPath)
foreach (var path in paths)
{
ThreadPool.QueueUserWorkItem(async __ =>
{
var ws = await context.AcceptWebSocketAsync("ws");

var websocket = new WebSocketClient(ws.WebSocket)
{
Headers = request.Headers,
Cookies = request.Cookies,
};

_onWebsocket?.Invoke(null, new PathContainer(request, response, websocket));
});
path.callback?.Invoke(request, response);
}
}

if (foundPath is false)
else // IS WEBSOCKET CONNECTION
{
if (request.IsWebSocket)
var paths = _wsMap.FindAll(x => x.path == request.Path);

if (paths.Count <= 0)
{
// TODO: Check best way for refuse websocket connection.
string data = $"{request.RawRequest.HttpMethod.ToUpper()} {request.Path}";
response.Send(404, data);
response.Send(404, notFoundMessage);
continue;
}
else

Task.Run(async () =>
{
// TODO: Check best may for refuse http connection.
string data = $"{request.RawRequest.HttpMethod.ToUpper()} {request.Path}";
response.Send(404, data);
}
var ws = await context.AcceptWebSocketAsync("ws");

var websocket = new WebSocketClient(ws.WebSocket)
{
Headers = request.Headers,
Cookies = request.Cookies,
};

foreach (var path in paths)
{
path.callback?.Invoke(request, websocket);
}
});
}
}
catch (Exception e)
Expand Down
13 changes: 11 additions & 2 deletions src/HTTP/IHttpServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,16 @@ internal interface IHttpServer
void Close();
void OnClose(Action callback);

void On(string path, Action<Request, Response> callback);
void OnWebsocket(string path, Action<Request, WebSocketClient> callback);
void MapAll(string path, Action<Request, Response> callback);
void MapGet(string path, Action<Request, Response> callback);
void MapPut(string path, Action<Request, Response> callback);
void MapHead(string path, Action<Request, Response> callback);
void MapPost(string path, Action<Request, Response> callback);
void MapPatch(string path, Action<Request, Response> callback);
void MapTrace(string path, Action<Request, Response> callback);
void MapDelete(string path, Action<Request, Response> callback);
void MapOptions(string path, Action<Request, Response> callback);

void MapWebSocket(string path, Action<Request, WebSocketClient> callback);
}
}

0 comments on commit a0bacb5

Please sign in to comment.