Self made reverse proxies
Do not remember why and for what but from backup have two samples of simple reverse proxies
In both cases it will be a nano reverse proxy which will proxy requests to X-Forwarded-Host
and add Accept-Language
header
dotnet reverse proxy
var builder = WebApplication.CreateBuilder(args);
var httpClient = new HttpClient();
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.MapMethods("/{*path}", new [] {"GET", "POST"}, async (HttpRequest httpRequest, HttpResponse httpResponse, string path, CancellationToken cancellationToken) => {
var httpRequestMessage = new HttpRequestMessage();
// request: method
httpRequestMessage.Method = new HttpMethod(httpRequest.Method);
// request: url, note: for path and query use something like: `httpRequest.GetEncodedPathAndQuery()` instead of `path`
httpRequestMessage.RequestUri = new Uri($"{httpRequest.Headers["X-Forwarded-Proto"]}://{httpRequest.Headers["X-Forwarded-Host"]}/{path}");
// request: body
if (HttpMethods.IsPost(httpRequest.Method))
{
httpRequestMessage.Content = new StreamContent(httpRequest.Body);
}
// request: headers
foreach (var header in httpRequest.Headers)
{
if (!httpRequestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()) && httpRequestMessage.Content != null)
{
httpRequestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
}
}
httpRequestMessage.Headers.Host = httpRequest.Headers["X-Forwarded-Host"];
// request: override
httpRequestMessage.Headers.TryAddWithoutValidation("Accept-Language", "en");
using var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
// response: status
httpResponse.StatusCode = (int)httpResponseMessage.StatusCode;
// response: headers
foreach(var header in httpResponseMessage.Headers) {
httpResponse.Headers[header.Key] = header.Value.ToArray();
}
foreach (var header in httpResponseMessage.Content.Headers)
{
httpResponse.Headers[header.Key] = header.Value.ToArray();
}
httpResponse.Headers.Remove("transfer-encoding"); // SendAsync removes chunking from the response. This removes the header so it doesn't expect a chunked response.
// response: body
await httpResponseMessage.Content.CopyToAsync(httpResponse.Body, cancellationToken);
});
app.Run();
usage example:
curl -X POST -H "X-Forwarded-Proto: https" -H "X-Forwarded-Host: gateway.mac-blog.org.ua" http://localhost:5000/graphql -H "Content-Type: application/json" -d '{"query": "{ city(id:1) { name }}"}'
Links:
golang reverse proxy
The fun fact the same can be acomplished in go lang literally in few lines of code
package main
import (
"log"
"net/http"
"net/http/httputil"
"net/url"
)
func main() {
proxy := httputil.NewSingleHostReverseProxy(&url.URL{})
originalDirector := proxy.Director
proxy.Director = func(req *http.Request) {
originalDirector(req)
req.URL.Host = req.Header.Get("X-Forwarded-Host") // "apply-api.rabota.ua"
req.URL.Scheme = req.Header.Get("X-Forwarded-Proto") // "https"
req.Host = req.Header.Get("X-Forwarded-Host") // "apply-api.rabota.ua"
req.Header.Set("Accept-Language", "en") // overriding header
}
log.Fatal(http.ListenAndServe(":8080", proxy))
}
usage example:
curl -X POST -H "X-Forwarded-Proto: https" -H "X-Forwarded-Host: gateway.mac-blog.org.ua" http://localhost:8080/graphql -H "Content-Type: application/json" -d '{"query": "{ city(id:1) { name }}"}'
Links:
- HttpProxy.go
- simple_proxy.go
- The Right Use of ReverseProxy in Golang
- FOSDEM 2019: How to write a reverse proxy with Go in 25 minutes
Espacially I did liked that one: