@@ -1,6 +1,8 @@
11package upstream
22
33import (
4+ "context"
5+ "net"
46 "net/http"
57 "net/http/httputil"
68 "net/url"
@@ -18,6 +20,7 @@ const (
1820
1921 httpScheme = "http"
2022 httpsScheme = "https"
23+ unixScheme = "unix"
2124)
2225
2326// SignatureHeaders contains the headers to be signed by the hmac algorithm
@@ -40,7 +43,10 @@ var SignatureHeaders = []string{
4043// to a single upstream host.
4144func newHTTPUpstreamProxy(upstream options.Upstream, u *url.URL, sigData *options.SignatureData, errorHandler ProxyErrorHandler) http.Handler {
4245 // Set path to empty so that request paths start at the server root
43− u.Path = ""
46+ // Unix scheme need the path to find the socket
47+ if u.Scheme != "unix" {
48+ u.Path = ""
49+ }
4450
4551 // Create a ReverseProxy
4652 proxy := newReverseProxy(u, upstream, errorHandler)
@@ -92,6 +98,25 @@ func (h *httpUpstreamProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request)
9298 }
9399}
94100
101+// Unix implementation of http.RoundTripper, required to register unix protocol in reverse proxy
102+type unixRoundTripper struct {
103+ Transport *http.Transport
104+}
105+
106+// Implementation of https://pkg.go.dev/net/http#RoundTripper interface to support http protocol over unix socket
107+func (t *unixRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
108+ // Inspired by https://github.com/tv42/httpunix
109+ // Not having a Host, even if not used, makes the reverseproxy fail with a "no Host in request URL"
110+ if req.Host == "" {
111+ req.Host = "localhost"
112+ }
113+ req.URL.Host = req.Host
114+ tt := t.Transport
115+ req = req.Clone(req.Context())
116+ req.URL.Scheme = "http"
117+ return tt.RoundTrip(req)
118+}
119+
95120// newReverseProxy creates a new reverse proxy for proxying requests to upstream
96121// servers based on the upstream configuration provided.
97122// The proxy should render an error page if there are failures connecting to the
@@ -102,6 +127,14 @@ func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler Pr
102127 // Inherit default transport options from Go's stdlib
103128 transport := http.DefaultTransport.(*http.Transport).Clone()
104129
130+ if target.Scheme == "unix" {
131+ transport.DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) {
132+ dialer := net.Dialer{}
133+ return dialer.DialContext(ctx, target.Scheme, target.Path)
134+ }
135+ transport.RegisterProtocol(target.Scheme, &unixRoundTripper{Transport: transport})
136+ }
137+
105138 // Change default duration for waiting for an upstream response
106139 if upstream.Timeout != nil {
107140 transport.ResponseHeaderTimeout = upstream.Timeout.Duration()