Skip to content

net/http/httputil: NewSingleHostReverseProxy fails to transparently send requests #10342

@gwik

Description

@gwik

I seems that NewSingleHostReverseProxy does not work, at least not how I would expected it to.
The program below demonstrates the correct and incorrect behavior.
The problem is that the director function do not reset the Host field of the request received by the proxy.
This is ok if the server is used as a real proxy:

 curl -x http://localhost:8000/ http://golang.org/

works perfectly well.
However it was my understanding that the goal of NewSingleHostReverseProxy was to transparently proxy requests to a given http server:

 curl http://localhost:8000/

won't work as localhost:8000 is sent as the Host header of the request to the proxied server.

package main

import (
    "fmt"
    "net/http"
    "net/http/httputil"
    "net/url"
    "strings"
)

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 NewSingleHostReverseProxy(target *url.URL) *httputil.ReverseProxy {
    targetQuery := target.RawQuery
    director := func(req *http.Request) {
        req.URL.Scheme = target.Scheme
        req.URL.Host = target.Host
        req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
        fmt.Println(req.Host)
        // this fixes the bug
        req.Host = ""
        // --
        if targetQuery == "" || req.URL.RawQuery == "" {
            req.URL.RawQuery = targetQuery + req.URL.RawQuery
        } else {
            req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
        }
    }
    return &httputil.ReverseProxy{Director: director}
}

func main() {
    url, _ := url.Parse("http://golang.org/")

    go func() {
        http.ListenAndServe(":8001", NewSingleHostReverseProxy(url))
    }()
    http.ListenAndServe(":8000", httputil.NewSingleHostReverseProxy(url))

}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions