diff --git a/src/CustomHttpEndpoint/Program.cs b/src/CustomHttpEndpoint/Program.cs
index 2ec691bd7..516a4e17d 100644
--- a/src/CustomHttpEndpoint/Program.cs
+++ b/src/CustomHttpEndpoint/Program.cs
@@ -22,6 +22,11 @@ public static class Program
///
private static string? _cmd = null;
+ ///
+ /// URI to relay the incoming request to
+ ///
+ private static string? _uri = null;
+
///
/// Command arguments for the binary to start
///
@@ -67,6 +72,10 @@ public static async Task Main(string[] args)
{
_cmd = arg;
}
+ else if (lastArg == "-u" || lastArg == "--uri")
+ {
+ _uri = arg;
+ }
else if (lastArg == "-a" || lastArg == "--args")
{
_args = arg;
@@ -84,6 +93,7 @@ public static async Task Main(string[] args)
Console.WriteLine("-n, --namespace : Namespace to use (defaults to custom-http-endpoint)");
Console.WriteLine("-p, --path : HTTP query path (defaults to demo)");
Console.WriteLine("-e, --exec : Command to execute when an HTTP query is received, stdout and stderr are returned as the response body");
+ Console.WriteLine("-u, --uri : Tell the web server to relay the incoming request to another server specified by the URI");
Console.WriteLine("-a, --args : 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");
@@ -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;
@@ -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);
+ }
+
}
}
diff --git a/src/DuetAPI/Commands/HttpEndpoints/SendHttpResponse/HttpResponseType.cs b/src/DuetAPI/Commands/HttpEndpoints/SendHttpResponse/HttpResponseType.cs
index a33b18573..b72139943 100644
--- a/src/DuetAPI/Commands/HttpEndpoints/SendHttpResponse/HttpResponseType.cs
+++ b/src/DuetAPI/Commands/HttpEndpoints/SendHttpResponse/HttpResponseType.cs
@@ -27,6 +27,11 @@ public enum HttpResponseType
///
/// File content. Response must hold the absolute path to the file to return
///
- File
+ File,
+
+ ///
+ /// Send this request to another server (proxy). Response must hold the URI to send the request to
+ ///
+ URI
}
}
diff --git a/src/DuetWebServer/Middleware/CustomEndpointMiddleware.cs b/src/DuetWebServer/Middleware/CustomEndpointMiddleware.cs
index 1afcada29..e83541a69 100644
--- a/src/DuetWebServer/Middleware/CustomEndpointMiddleware.cs
+++ b/src/DuetWebServer/Middleware/CustomEndpointMiddleware.cs
@@ -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;
@@ -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)