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 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
package httptoo
import (
"bufio"
"encoding/gob"
"io"
"net"
"net/http"
"net/url"
"sync"
)
func deepCopy(dst, src interface{}) error {
r, w := io.Pipe()
e := gob.NewEncoder(w)
d := gob.NewDecoder(r)
var decErr, encErr error
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
decErr = d.Decode(dst)
r.Close()
}()
encErr = e.Encode(src)
// Always returns nil.
w.CloseWithError(encErr)
wg.Wait()
if encErr != nil {
return encErr
}
return decErr
}
// Takes a request, and alters its destination fields, for proxying.
func RedirectedRequest(r *http.Request, newUrl string) (ret *http.Request, err error) {
u, err := url.Parse(newUrl)
if err != nil {
return
}
ret = new(http.Request)
*ret = *r
ret.Header = nil
err = deepCopy(&ret.Header, r.Header)
if err != nil {
return
}
ret.URL = u
ret.RequestURI = ""
return
}
func CopyHeaders(w http.ResponseWriter, r *http.Response) {
for h, vs := range r.Header {
for _, v := range vs {
w.Header().Add(h, v)
}
}
}
func ForwardResponse(w http.ResponseWriter, r *http.Response) {
CopyHeaders(w, r)
w.WriteHeader(r.StatusCode)
// Errors frequently occur writing the body when the client hangs up.
io.Copy(w, r.Body)
r.Body.Close()
}
func SetOriginRequestForwardingHeaders(o, f *http.Request) {
xff := o.Header.Get("X-Forwarded-For")
hop, _, _ := net.SplitHostPort(f.RemoteAddr)
if xff == "" {
xff = hop
} else {
xff += "," + hop
}
o.Header.Set("X-Forwarded-For", xff)
o.Header.Set("X-Forwarded-Proto", OriginatingProtocol(f))
}
// w is for the client response. r is the request to send to the origin
// (already "forwarded"). originUrl is where to send the request.
func ReverseProxyUpgrade(w http.ResponseWriter, r *http.Request, originUrl string) (err error) {
u, err := url.Parse(originUrl)
if err != nil {
return
}
oc, err := net.Dial("tcp", u.Host)
if err != nil {
return
}
defer oc.Close()
err = r.Write(oc)
if err != nil {
return
}
originConnReadBuffer := bufio.NewReader(oc)
originResp, err := http.ReadResponse(originConnReadBuffer, r)
if err != nil {
return
}
if originResp.StatusCode != 101 {
ForwardResponse(w, originResp)
return
}
cc, _, err := w.(http.Hijacker).Hijack()
if err != nil {
return
}
defer cc.Close()
originResp.Write(cc)
go io.Copy(oc, cc)
// Let the origin connection control when this routine returns, as we
// should trust it more.
io.Copy(cc, originConnReadBuffer)
return
}
func ReverseProxy(w http.ResponseWriter, r *http.Request, originUrl string, client *http.Client) (err error) {
originRequest, err := RedirectedRequest(r, originUrl)
if err != nil {
return
}
SetOriginRequestForwardingHeaders(originRequest, r)
if r.Header.Get("Connection") == "Upgrade" {
return ReverseProxyUpgrade(w, originRequest, originUrl)
}
rt := client.Transport
if rt == nil {
rt = http.DefaultTransport
}
originResp, err := rt.RoundTrip(originRequest)
if err != nil {
return
}
ForwardResponse(w, originResp)
return
}
|