Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HttpServer: Support for Unix Sockets #250

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"version": "0.2.0",
"configurations": [
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/examples/HttpServer/bin/Debug/net7.0/HttpServer.dll",
"args": [],
"cwd": "${workspaceFolder}/examples/HttpServer",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
}
]
}
41 changes: 41 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/examples/HttpServer/HttpServer.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/examples/HttpServer/HttpServer.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/examples/HttpServer/HttpServer.csproj"
],
"problemMatcher": "$msCompile"
}
]
}
12 changes: 12 additions & 0 deletions examples/HttpServerUnixSocket/HttpServer.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\source\NetCoreServer\NetCoreServer.csproj" />
</ItemGroup>

</Project>
23 changes: 23 additions & 0 deletions examples/HttpServerUnixSocket/NetCoreServer.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Sample proxy configuration for running nginx for development. Make sure to fix paths
error_log /home/username/.nginx/nginx.log warn;
pid /home/username/.nginx/nginx.pid;

daemon off;

events {
worker_connections 1024;
}

http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" "$http_cookie" "$sent_http_content_type"';
access_log /home/username/.nginx/nginx-access.log main buffer=32k;

server {
listen 127.0.0.1:8088;
location / {
proxy_pass http://unix:/tmp/test.sock;
}
}
}
206 changes: 206 additions & 0 deletions examples/HttpServerUnixSocket/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Text;
using NetCoreServer;

namespace HttpServer
{
class CommonCache
{
public static CommonCache GetInstance()
{
if (_instance == null)
_instance = new CommonCache();
return _instance;
}

public string GetAllCache()
{
var result = new StringBuilder();
result.Append("[\n");
foreach (var item in _cache)
{
result.Append(" {\n");
result.AppendFormat($" \"key\": \"{item.Key}\",\n");
result.AppendFormat($" \"value\": \"{item.Value}\",\n");
result.Append(" },\n");
}
result.Append("]\n");
return result.ToString();
}

public bool GetCacheValue(string key, out string value)
{
return _cache.TryGetValue(key, out value);
}

public void PutCacheValue(string key, string value)
{
_cache[key] = value;
}

public bool DeleteCacheValue(string key, out string value)
{
return _cache.TryRemove(key, out value);
}

private readonly ConcurrentDictionary<string, string> _cache = new ConcurrentDictionary<string, string>();
private static CommonCache _instance;
}

class HttpCacheSession : HttpSession
{
public HttpCacheSession(NetCoreServer.HttpServer server) : base(server) { }

protected override void OnReceivedRequest(HttpRequest request)
{
// Show HTTP request content
Console.WriteLine(request);

// Process HTTP request methods
if (request.Method == "HEAD")
SendResponseAsync(Response.MakeHeadResponse());
else if (request.Method == "GET")
{
string key = request.Url;

// Decode the key value
key = Uri.UnescapeDataString(key);
key = key.Replace("/api/cache", "", StringComparison.InvariantCultureIgnoreCase);
key = key.Replace("?key=", "", StringComparison.InvariantCultureIgnoreCase);

if (string.IsNullOrEmpty(key))
{
// Response with all cache values
SendResponseAsync(Response.MakeGetResponse(CommonCache.GetInstance().GetAllCache(), "application/json; charset=UTF-8"));
}
// Get the cache value by the given key
else if (CommonCache.GetInstance().GetCacheValue(key, out var value))
{
// Response with the cache value
SendResponseAsync(Response.MakeGetResponse(value));
}
else
SendResponseAsync(Response.MakeErrorResponse(404, "Required cache value was not found for the key: " + key));
}
else if ((request.Method == "POST") || (request.Method == "PUT"))
{
string key = request.Url;
string value = request.Body;

// Decode the key value
key = Uri.UnescapeDataString(key);
key = key.Replace("/api/cache", "", StringComparison.InvariantCultureIgnoreCase);
key = key.Replace("?key=", "", StringComparison.InvariantCultureIgnoreCase);

// Put the cache value
CommonCache.GetInstance().PutCacheValue(key, value);

// Response with the cache value
SendResponseAsync(Response.MakeOkResponse());
}
else if (request.Method == "DELETE")
{
string key = request.Url;

// Decode the key value
key = Uri.UnescapeDataString(key);
key = key.Replace("/api/cache", "", StringComparison.InvariantCultureIgnoreCase);
key = key.Replace("?key=", "", StringComparison.InvariantCultureIgnoreCase);

// Delete the cache value
if (CommonCache.GetInstance().DeleteCacheValue(key, out var value))
{
// Response with the cache value
SendResponseAsync(Response.MakeGetResponse(value));
}
else
SendResponseAsync(Response.MakeErrorResponse(404, "Deleted cache value was not found for the key: " + key));
}
else if (request.Method == "OPTIONS")
SendResponseAsync(Response.MakeOptionsResponse());
else if (request.Method == "TRACE")
SendResponseAsync(Response.MakeTraceResponse(request));
else
SendResponseAsync(Response.MakeErrorResponse("Unsupported HTTP method: " + request.Method));
}

protected override void OnReceivedRequestError(HttpRequest request, string error)
{
Console.WriteLine($"Request error: {error}");
}

protected override void OnError(SocketError error)
{
Console.WriteLine($"HTTP session caught an error: {error}");
}
}

class HttpCacheServer : NetCoreServer.HttpServer
{
public HttpCacheServer(IPAddress address, int port) : base(address, port) { }

public HttpCacheServer(UnixDomainSocketEndPoint endpoint) : base(endpoint) { }

protected override TcpSession CreateSession() { return new HttpCacheSession(this); }

protected override void OnError(SocketError error)
{
Console.WriteLine($"HTTP session caught an error: {error}");
}
}

class Program
{
static void Main(string[] args)
{
// HTTP server port
var unixSocketPath = "/tmp/test.sock";
// HTTP server content path
string www = "../../../../../www/api";
if (args.Length > 1)
www = args[1];

Console.WriteLine($"HTTP server socket path: {unixSocketPath}");
Console.WriteLine($"HTTP server static content path: {www}");
Console.WriteLine($"HTTP server test with curl --unix-socket {unixSocketPath} http://localhost/api/index.html");
Console.WriteLine($"You may also setup run nginx -c $PWD/NetCoreServer.conf to test the server with a browser (make sure all paths in the conf file exists)");

Console.WriteLine();

// Create a new HTTP server
var server = new HttpCacheServer(new UnixDomainSocketEndPoint(unixSocketPath));
server.AddStaticContent(www, "/api");

// Start the server
Console.Write("Server starting...");
server.Start();
Console.WriteLine("Done!");

Console.WriteLine("Press Enter to stop the server or '!' to restart the server...");

// Perform text input
for (; ; )
{
string line = Console.ReadLine();
if (string.IsNullOrEmpty(line))
break;

// Restart the server
if (line == "!")
{
Console.Write("Server restarting...");
server.Restart();
Console.WriteLine("Done!");
}
}

// Stop the server
Console.Write("Server stopping...");
server.Stop();
Console.WriteLine("Done!");
}
}
}
Loading