Creates goproxy.ProxyHttpServer and configures it to be used as a proxy for Hoverfly goproxy is given handlers that use the Hoverfly request processing
(hoverfly *Hoverfly)
| 28 | // Creates goproxy.ProxyHttpServer and configures it to be used as a proxy for Hoverfly |
| 29 | // goproxy is given handlers that use the Hoverfly request processing |
| 30 | func NewProxy(hoverfly *Hoverfly) *goproxy.ProxyHttpServer { |
| 31 | ProxyAuthorizationHeader = hoverfly.Cfg.ProxyAuthorizationHeader |
| 32 | |
| 33 | // creating proxy |
| 34 | proxy := goproxy.NewProxyHttpServer() |
| 35 | |
| 36 | // Fix #774: HTTP/1.1 clients may send requests with a relative URL (path only) and a Host header. |
| 37 | // goproxy's default NonproxyHandler returns 500 for these. Reconstruct the absolute URL from the |
| 38 | // Host header so the request is processed normally, matching the behaviour of NewWebserverProxy. |
| 39 | // |
| 40 | // Guard: if the Host header targets the proxy's own port the client is connecting directly to |
| 41 | // Hoverfly (not using it as a proxy). In that case keep the original 500 "is a proxy server" |
| 42 | // response — forwarding to ourselves would cause infinite recursion or a panic. |
| 43 | proxy.NonproxyHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 44 | if r.Host == "" { |
| 45 | http.Error(w, "Cannot handle requests without Host header, e.g., HTTP 1.0", http.StatusBadRequest) |
| 46 | return |
| 47 | } |
| 48 | if _, port, err := net.SplitHostPort(r.Host); err == nil && port == hoverfly.Cfg.ProxyPort { |
| 49 | http.Error(w, "This is a proxy server. Does not respond to non-proxy requests.", http.StatusInternalServerError) |
| 50 | return |
| 51 | } |
| 52 | r.URL.Scheme = "http" |
| 53 | r.URL.Host = r.Host |
| 54 | proxy.ServeHTTP(w, r) |
| 55 | }) |
| 56 | |
| 57 | if hoverfly.Cfg.AuthEnabled { |
| 58 | log.Info("Enabling proxy authentication") |
| 59 | proxyBasicAndBearer(proxy, "hoverfly", func(user, password string) bool { |
| 60 | |
| 61 | proxyUser := &backends.User{ |
| 62 | Username: user, |
| 63 | Password: password, |
| 64 | } |
| 65 | |
| 66 | responseStatus, _ := authentication.Login(proxyUser, hoverfly.Authentication, nil, 0) |
| 67 | |
| 68 | return responseStatus == http.StatusOK |
| 69 | }, func(headerToken string) bool { |
| 70 | return authentication.IsJwtTokenValid(headerToken, hoverfly.Authentication, hoverfly.Cfg.SecretKey, hoverfly.Cfg.JWTExpirationDelta) |
| 71 | }) |
| 72 | } |
| 73 | |
| 74 | proxy.OnRequest(matchesFilter(hoverfly.Cfg.Destination)). |
| 75 | HandleConnect(goproxy.FuncHttpsHandler(func(host string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) { |
| 76 | if hoverfly.Cfg.PlainHttpTunneling && !strings.HasSuffix(host, ":443") { |
| 77 | return goproxy.HTTPMitmConnect, host |
| 78 | } |
| 79 | return goproxy.MitmConnect, host |
| 80 | })) |
| 81 | |
| 82 | // processing connections |
| 83 | proxy.OnRequest(matchesFilter(hoverfly.Cfg.Destination)).DoFunc( |
| 84 | func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { |
| 85 | startTime := time.Now() |
| 86 | resp, journalIDChannel := hoverfly.processRequest(r) |
| 87 | id, _ := hoverfly.Journal.NewEntry(r, resp, hoverfly.Cfg.Mode, startTime) |