Reverse Proxy Handler

Forward requests with httputil.ReverseProxy, add a forwarded-host header, strip internal headers, and return 502 on failure.

GoData Fetching

Code

snippet
package main

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"net/url"
	"strings"
)

func NewReverseProxy(target *url.URL) *httputil.ReverseProxy {
	proxy := httputil.NewSingleHostReverseProxy(target)
	originalDirector := proxy.Director
	proxy.Director = func(request *http.Request) {
		originalDirector(request)
		request.Header.Set("X-Forwarded-Host", request.Host)
		request.Header.Del("X-Internal-Debug")
	}
	proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
		fmt.Println("proxy error:", err)
		http.Error(w, "upstream unavailable", http.StatusBadGateway)
	}
	return proxy
}

func main() {
	backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "path=%s host=%s", r.URL.Path, r.Header.Get("X-Forwarded-Host"))
	}))
	defer backend.Close()

	target, err := url.Parse(backend.URL)
	if err != nil {
		panic(err)
	}
	front := httptest.NewServer(NewReverseProxy(target))
	defer front.Close()

	response, err := http.Get(front.URL + "/api/items?page=2")
	if err != nil {
		panic(err)
	}
	defer response.Body.Close()
	body, err := io.ReadAll(response.Body)
	if err != nil {
		panic(err)
	}
	fmt.Println("status:", response.StatusCode)
	fmt.Println("body:", strings.TrimSpace(string(body)))
}