44 "bytes"
55 "encoding/base64"
66 "io"
7+ "net"
78 "net/http"
89 "net/url"
910 "regexp"
@@ -22,6 +23,8 @@ import (
2223
2324var ProxyAuthorizationHeader string
2425
26+ const errProxyAuthRequired = "407 Proxy authentication required"
27+
2528// Creates goproxy.ProxyHttpServer and configures it to be used as a proxy for Hoverfly
2629// goproxy is given handlers that use the Hoverfly request processing
2730func NewProxy (hoverfly * Hoverfly ) * goproxy.ProxyHttpServer {
@@ -30,6 +33,27 @@ func NewProxy(hoverfly *Hoverfly) *goproxy.ProxyHttpServer {
3033 // creating proxy
3134 proxy := goproxy .NewProxyHttpServer ()
3235
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+
3357 if hoverfly .Cfg .AuthEnabled {
3458 log .Info ("Enabling proxy authentication" )
3559 proxyBasicAndBearer (proxy , "hoverfly" , func (user , password string ) bool {
@@ -201,25 +225,25 @@ func authFromHeader(req *http.Request, basicFunc func(user, passwd string) bool,
201225 authheader := strings .SplitN (headerValue , " " , 2 )
202226 req .Header .Del (ProxyAuthorizationHeader )
203227 if len (authheader ) != 2 {
204- return fmt .Errorf ("407 Proxy authentication required" )
228+ return fmt .Errorf (errProxyAuthRequired )
205229 }
206230 if authheader [0 ] == "Basic" {
207231 userpassraw , err := base64 .StdEncoding .DecodeString (authheader [1 ])
208232 if err != nil {
209- return fmt .Errorf ("407 Proxy authentication required" )
233+ return fmt .Errorf (errProxyAuthRequired )
210234 }
211235 userpass := strings .SplitN (string (userpassraw ), ":" , 2 )
212236 if len (userpass ) != 2 {
213- return fmt .Errorf ("407 Proxy authentication required" )
237+ return fmt .Errorf (errProxyAuthRequired )
214238 }
215239 result := basicFunc (userpass [0 ], userpass [1 ])
216240 if result == false {
217- return fmt .Errorf ("407 Proxy authentication required" )
241+ return fmt .Errorf (errProxyAuthRequired )
218242 }
219243 } else if authheader [0 ] == "Bearer" {
220244 result := bearerFunc (authheader [1 ])
221245 if result == false {
222- return fmt .Errorf ("407 Proxy authentication required" )
246+ return fmt .Errorf (errProxyAuthRequired )
223247 }
224248 } else {
225249 return fmt .Errorf ("407 Unknown authentication type `%v`, only `Basic` or `Bearer` are supported" , authheader [0 ])
0 commit comments