package reverseproxy import ( "context" "net/http" "net/http/httputil" "net/url" "strings" ) type ReverseProxy struct { context.Context *httputil.ReverseProxy } type CtxKey string func New(ctx context.Context, host string) *ReverseProxy { ctxKey := CtxKey("host") ctx = context.WithValue(ctx, ctxKey, host) reverseProxySTDLIB := &httputil.ReverseProxy{ Director: func(r *http.Request) { r = r.WithContext(ctx) hostFromCtx := ctx.Value(ctxKey).(string) target, _ := url.Parse(hostFromCtx) targetQuery := target.RawQuery r.URL.Scheme = target.Scheme r.URL.Host = target.Host r.URL.Path, r.URL.RawPath = joinURLPath(target, r.URL) if targetQuery == "" || r.URL.RawQuery == "" { r.URL.RawQuery = targetQuery + r.URL.RawQuery } else { r.URL.RawQuery = targetQuery + "&" + r.URL.RawQuery } }, } reverseProxy := &ReverseProxy{} reverseProxy.Context = ctx reverseProxy.ReverseProxy = reverseProxySTDLIB return reverseProxy } func (revereProxy *ReverseProxy) SetContext(ctx context.Context) { revereProxy.Context = ctx } func (revereProxy *ReverseProxy) DirectorFunc(df func(jup JoinURLPathFunc) DirectorFunc) { d := df(joinURLPath) revereProxy.Director = d } func singleJoiningSlash(a, b string) string { aslash := strings.HasSuffix(a, "/") bslash := strings.HasPrefix(b, "/") switch { case aslash && bslash: return a + b[1:] case !aslash && !bslash: return a + "/" + b } return a + b } func joinURLPath(a, b *url.URL) (path, rawpath string) { if a.RawPath == "" && b.RawPath == "" { return singleJoiningSlash(a.Path, b.Path), "" } // Same as singleJoiningSlash, but uses EscapedPath to determine // whether a slash should be added apath := a.EscapedPath() bpath := b.EscapedPath() aslash := strings.HasSuffix(apath, "/") bslash := strings.HasPrefix(bpath, "/") switch { case aslash && bslash: return a.Path + b.Path[1:], apath + bpath[1:] case !aslash && !bslash: return a.Path + "/" + b.Path, apath + "/" + bpath } return a.Path + b.Path, apath + bpath } type JoinURLPathFunc func(*url.URL, *url.URL) (string, string) // type SingleJoiningSlashFunc func(string, string) string type DirectorFunc func(*http.Request)