-
Notifications
You must be signed in to change notification settings - Fork 0
/
reverseproxy.go
113 lines (100 loc) · 2.43 KB
/
reverseproxy.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package utils
import (
"io"
"log"
"net/http"
"net/http/httputil"
"net/url"
"os"
"regexp"
"strings"
)
// AsURL expands :port and hostname to http://localhost:port & http://hostname respectively
func AsURL(s string) string {
if isPort(s) {
s = "localhost" + s
}
// google.com will be parsed as URL{Path: google.com} without an explicit protocol
// hence the hack
if !strings.Contains(s, "://") {
s = "http://" + s
}
return s
}
func isPort(s string) bool {
match, _ := regexp.MatchString(`^:\d{1,5}$`, s)
return match
}
// ReverseProxy
//
// - upstream: http://user:pass@example.com
// behavior: use upstream credential by default
// - upstream: http://example.com
// behavior: passthrough client credential if any
// - upstream: http://-@example.com
// behavior: don't pass any credential to upstream
func ReverseProxy(addr string) http.Handler {
addr = AsURL(addr)
upstream, err := url.Parse(addr)
if err != nil {
panic(err)
}
rewrite := func(r *httputil.ProxyRequest) {
r.SetURL(upstream)
r.SetXForwarded()
// passthrough Host from client
if os.Getenv("PASS_HOST") != "" {
r.Out.Host = r.In.Host
}
// ignore credential when default upstream user set to -
if upstream.User.String() == "-" {
r.Out.Header.Del("Authorization")
return
}
// use client credentials if available
if r.In.Header.Get("Authorization") != "" {
return
}
// otherwise use credentials from upstream
if upstream.User != nil {
user := upstream.User.Username()
pass, _ := upstream.User.Password()
r.Out.SetBasicAuth(user, pass)
}
}
modify := func(r *http.Response) error {
r.Header.Del("Content-Security-Policy")
return nil
}
rp := &httputil.ReverseProxy{
Rewrite: rewrite,
ModifyResponse: modify,
ErrorLog: ReverseProxyLogger(),
}
return rp
}
func ReverseProxyLogger() *log.Logger {
if os.Getenv("REVERSEPROXY_LOG") == "" {
return log.New(io.Discard, "", 0) // discard logger
}
return nil // default logger
}
// TransparentProxy is a reverse proxy that preserves the original Host header
func TransparentProxy(addr string) http.Handler {
addr = AsURL(addr)
upstream, err := url.Parse(addr)
if err != nil {
panic(err)
}
rewrite := func(r *httputil.ProxyRequest) {
r.SetURL(upstream)
r.SetXForwarded()
// passthrough Host from client
r.Out.Host = r.In.Host
}
rp := &httputil.ReverseProxy{
Rewrite: rewrite,
ErrorLog: ReverseProxyLogger(),
}
return rp
}