Skip to content

Commit

Permalink
Added proxy support to custom HTTP endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
chrishamm committed May 28, 2024
1 parent 70ae662 commit 1e3caf3
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 34 deletions.
82 changes: 49 additions & 33 deletions src/CustomHttpEndpoint/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ public static class Program
/// </summary>
private static string? _cmd = null;

/// <summary>
/// URI to relay the incoming request to
/// </summary>
private static string? _uri = null;

/// <summary>
/// Command arguments for the binary to start
/// </summary>
Expand Down Expand Up @@ -67,6 +72,10 @@ public static async Task<int> Main(string[] args)
{
_cmd = arg;
}
else if (lastArg == "-u" || lastArg == "--uri")
{
_uri = arg;
}
else if (lastArg == "-a" || lastArg == "--args")
{
_args = arg;
Expand All @@ -84,6 +93,7 @@ public static async Task<int> Main(string[] args)
Console.WriteLine("-n, --namespace <namespace>: Namespace to use (defaults to custom-http-endpoint)");
Console.WriteLine("-p, --path <path>: HTTP query path (defaults to demo)");
Console.WriteLine("-e, --exec <executable>: Command to execute when an HTTP query is received, stdout and stderr are returned as the response body");
Console.WriteLine("-u, --uri <uri>: Tell the web server to relay the incoming request to another server specified by the URI");
Console.WriteLine("-a, --args <arguments>: Arguments for the executable command. Query values in % chars are replaced with query options (e.g. %myvalue%). Not applicable for WebSockets");
Console.WriteLine("-q, --quiet: Do not display info text");
Console.WriteLine("-h, --help: Displays this text");
Expand Down Expand Up @@ -238,39 +248,7 @@ private static async void OnHttpRequestReceived(HttpEndpointUnixSocket unixSocke
// Read the HTTP response from the client
ReceivedHttpRequest request = await requestConnection.ReadRequest();

if (string.IsNullOrWhiteSpace(_cmd))
{
// Write this event to the console if possible
if (!_quiet)
{
Console.WriteLine("Got new HTTP request from session {0}", request.SessionId);
}

// Only print a demo response in case no process is supposed to be started
string response = $"This demo text has been returned from a third-party application.\n\nMethod: {_method}\nSession ID: {request.SessionId}";
if (request.Headers.Count > 0)
{
response += "\n\nHeaders:";
foreach (var kv in request.Headers)
{
response += $"\n{kv.Key} = {kv.Value}";
}
}
if (request.Queries.Count > 0)
{
response += "\n\nQueries:";
foreach (var kv in request.Queries)
{
response += $"\n{kv.Key} = {kv.Value}";
}
}
if (!string.IsNullOrWhiteSpace(request.Body))
{
response += "\n\nBody:\n" + request.Body;
}
await requestConnection.SendResponse(200, response, HttpResponseType.PlainText);
}
else
if (!string.IsNullOrWhiteSpace(_cmd))
{
// Replace query values in the arguments
string args = _cmd;
Expand Down Expand Up @@ -306,6 +284,44 @@ private static async void OnHttpRequestReceived(HttpEndpointUnixSocket unixSocke
await requestConnection.SendResponse(501, "Failed to start process", HttpResponseType.StatusCode);
}
}
else if (!string.IsNullOrWhiteSpace(_uri))
{
// Tell DWS to relay this request to another URI
await requestConnection.SendResponse(200, _uri, HttpResponseType.URI);
}
else
{
// Write this event to the console if possible
if (!_quiet)
{
Console.WriteLine("Got new HTTP request from session {0}", request.SessionId);
}

// Only print a demo response in case no process is supposed to be started
string response = $"This demo text has been returned from a third-party application.\n\nMethod: {_method}\nSession ID: {request.SessionId}";
if (request.Headers.Count > 0)
{
response += "\n\nHeaders:";
foreach (var kv in request.Headers)
{
response += $"\n{kv.Key} = {kv.Value}";
}
}
if (request.Queries.Count > 0)
{
response += "\n\nQueries:";
foreach (var kv in request.Queries)
{
response += $"\n{kv.Key} = {kv.Value}";
}
}
if (!string.IsNullOrWhiteSpace(request.Body))
{
response += "\n\nBody:\n" + request.Body;
}
await requestConnection.SendResponse(200, response, HttpResponseType.PlainText);
}

}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ public enum HttpResponseType
/// <summary>
/// File content. Response must hold the absolute path to the file to return
/// </summary>
File
File,

/// <summary>
/// Send this request to another server (proxy). Response must hold the URI to send the request to
/// </summary>
URI
}
}
36 changes: 36 additions & 0 deletions src/DuetWebServer/Middleware/CustomEndpointMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
Expand Down Expand Up @@ -302,6 +306,38 @@ private static async Task ProcessRestRequst(HttpContext context, HttpEndpoint en
await using FileStream fs = new(httpResponse.Response, FileMode.Open, FileAccess.Read);
await fs.CopyToAsync(context.Response.Body);
}
else if (httpResponse.ResponseType == HttpResponseType.URI)
{
// Set up our own request
HttpRequestMessage requestMessage = new()
{
Method = new HttpMethod(context.Request.Method),
RequestUri = new Uri(httpResponse.Response)
};
requestMessage.Headers.Host = requestMessage.RequestUri.Host;
foreach (var header in context.Request.Headers)
{
requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
}
requestMessage.Content = new StringContent(body);

// Send it
using HttpClient client = new();
using HttpResponseMessage responseMessage = await client.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted);

// Send the response data back to the client
context.Response.StatusCode = (int)responseMessage.StatusCode;
foreach (var header in responseMessage.Headers)
{
context.Response.Headers[header.Key] = header.Value.ToArray();
}
foreach (var header in responseMessage.Content.Headers)
{
context.Response.Headers[header.Key] = header.Value.ToArray();
}
context.Response.Headers.Remove("transfer-encoding");
await responseMessage.Content.CopyToAsync(context.Response.Body);
}
else
{
switch (httpResponse.ResponseType)
Expand Down

0 comments on commit 1e3caf3

Please sign in to comment.