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:

Espacially I did liked that one:

https://www.youtube.com/watch?v=tWSmUsYLiE4