diff --git a/http/http.go b/http/http.go index f53fbf40..81d0e785 100644 --- a/http/http.go +++ b/http/http.go @@ -30,7 +30,7 @@ func New(cfg *Config) (*httpProxy, error) { return nil, errors.Wrap(err, "error listening") } - proxy, err := util.NewProxy(cfg.EndpointAddress) + proxy, err := NewProxy(cfg.EndpointAddress) if err != nil { return nil, err } diff --git a/http/proxy.go b/http/proxy.go new file mode 100644 index 00000000..3848d535 --- /dev/null +++ b/http/proxy.go @@ -0,0 +1,28 @@ +package http + +import ( + "github.com/sirupsen/logrus" + "net/http" + "net/http/httputil" + "net/url" +) + +func NewProxy(target string) (*httputil.ReverseProxy, error) { + targetURL, err := url.Parse(target) + if err != nil { + return nil, err + } + + proxy := httputil.NewSingleHostReverseProxy(targetURL) + director := proxy.Director + proxy.Director = func(req *http.Request) { + director(req) + logrus.Debugf("-> %v", req.URL.String()) + req.Header.Set("X-Proxy", "zrok") + } + proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) { + logrus.Errorf("error proxying: %v", err) + } + + return proxy, nil +} diff --git a/proxy/proxy.go b/proxy/proxy.go index 2091476a..2fc46041 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -20,7 +20,7 @@ func Run(cfg *Config) error { zTransport := http.DefaultTransport.(*http.Transport).Clone() zTransport.DialContext = zDialCtx.Dial - proxy, err := util.NewServiceProxy(&resolver{}) + proxy, err := NewServiceProxy(&resolver{}) if err != nil { return err } diff --git a/proxy/service.go b/proxy/service.go new file mode 100644 index 00000000..97d2fb91 --- /dev/null +++ b/proxy/service.go @@ -0,0 +1,89 @@ +package proxy + +import ( + "fmt" + "github.com/sirupsen/logrus" + "net/http" + "net/http/httputil" + "net/url" + "strings" +) + +type ProxyServiceResolver interface { + Service(host string) string +} + +func NewServiceProxy(p ProxyServiceResolver) (*httputil.ReverseProxy, error) { + proxy := hostTargetReverseProxy(p) + director := proxy.Director + proxy.Director = func(req *http.Request) { + director(req) + logrus.Debugf("-> %v", req.URL.String()) + req.Header.Set("X-Proxy", "zrok") + } + proxy.ModifyResponse = func(resp *http.Response) error { + return nil + } + proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) { + logrus.Errorf("error proxying: %v", err) + } + + return proxy, nil +} + +func hostTargetReverseProxy(r ProxyServiceResolver) *httputil.ReverseProxy { + director := func(req *http.Request) { + targetSvc := r.Service(req.Host) + if target, err := url.Parse(fmt.Sprintf("http://%v", targetSvc)); err == nil { + targetQuery := target.RawQuery + req.URL.Scheme = target.Scheme + req.URL.Host = target.Host + req.URL.Path, req.URL.RawPath = joinURLPath(target, req.URL) + if targetQuery == "" || req.URL.RawQuery == "" { + req.URL.RawQuery = targetQuery + req.URL.RawQuery + } else { + req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery + } + if _, ok := req.Header["User-Agent"]; !ok { + // explicitly disable User-Agent so it's not set to default value + req.Header.Set("User-Agent", "") + } + } else { + logrus.Errorf("error proxying: %v", err) + } + } + return &httputil.ReverseProxy{Director: director} +} + +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 +} + +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 +} diff --git a/util/proxy.go b/util/proxy.go index 7960b146..8991fecd 100644 --- a/util/proxy.go +++ b/util/proxy.go @@ -1,60 +1,11 @@ package util import ( - "fmt" - "github.com/sirupsen/logrus" "net/http" "net/http/httputil" - "net/url" - "strings" "sync/atomic" ) -type ProxyServiceResolver interface { - Service(host string) string -} - -func NewProxy(target string) (*httputil.ReverseProxy, error) { - targetURL, err := url.Parse(target) - if err != nil { - return nil, err - } - - proxy := httputil.NewSingleHostReverseProxy(targetURL) - director := proxy.Director - proxy.Director = func(req *http.Request) { - director(req) - logrus.Debugf("-> %v", req.URL.String()) - req.Header.Set("X-Proxy", "zrok") - } - proxy.ModifyResponse = func(resp *http.Response) error { - return nil - } - proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) { - logrus.Errorf("error proxying: %v", err) - } - - return proxy, nil -} - -func NewServiceProxy(p ProxyServiceResolver) (*httputil.ReverseProxy, error) { - proxy := hostTargetReverseProxy(p) - director := proxy.Director - proxy.Director = func(req *http.Request) { - director(req) - logrus.Debugf("-> %v", req.URL.String()) - req.Header.Set("X-Proxy", "zrok") - } - proxy.ModifyResponse = func(resp *http.Response) error { - return nil - } - proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) { - logrus.Errorf("error proxying: %v", err) - } - - return proxy, nil -} - type proxyHandler struct { proxy *httputil.ReverseProxy requests int32 @@ -79,60 +30,3 @@ func (self *proxyHandler) Requests() int32 { func (self *proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { self.proxy.ServeHTTP(w, r) } - -func hostTargetReverseProxy(r ProxyServiceResolver) *httputil.ReverseProxy { - director := func(req *http.Request) { - targetSvc := r.Service(req.Host) - if target, err := url.Parse(fmt.Sprintf("http://%v", targetSvc)); err == nil { - targetQuery := target.RawQuery - req.URL.Scheme = target.Scheme - req.URL.Host = target.Host - req.URL.Path, req.URL.RawPath = joinURLPath(target, req.URL) - if targetQuery == "" || req.URL.RawQuery == "" { - req.URL.RawQuery = targetQuery + req.URL.RawQuery - } else { - req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery - } - if _, ok := req.Header["User-Agent"]; !ok { - // explicitly disable User-Agent so it's not set to default value - req.Header.Set("User-Agent", "") - } - } else { - logrus.Errorf("error proxying: %v", err) - } - } - return &httputil.ReverseProxy{Director: director} -} - -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 -} - -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 -}