Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
gwik opened this issue Apr 3, 2015 · 5 comments
Closed

Comments

@gwik
Copy link
Contributor

gwik commented Apr 3, 2015

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))

}
@ianlancetaylor ianlancetaylor changed the title net/http/httputil.NewSingleHostReverseProxy fails to transparently send requests net/http/httputil: NewSingleHostReverseProxy fails to transparently send requests Apr 3, 2015
@ianlancetaylor
Copy link
Contributor

CC @bradfitz

@minux
Copy link
Member

minux commented Apr 3, 2015

I think you misunderstand what "reverse proxy" means here.
See http://en.wikipedia.org/wiki/Reverse_proxy

In short, the reverse proxy is actually acting as the server for
the requested URL. It uses another server to process the
request and pass the response back to the user. Both the
reverse proxy and the actual server (target) are serving the
same domain, so the reverse proxy doesn't change the Host.

@minux minux closed this as completed Apr 3, 2015
@gwik
Copy link
Contributor Author

gwik commented Apr 4, 2015

@minux I understand what a reverse proxy is. I may have misunderstand the purpose of NewSingleHostReverseProxy.

@eaigner
Copy link
Contributor

eaigner commented Oct 7, 2015

Had the same problem. Doesn't seem quite clear that the host doesn't change. Maybe a docfix?

@bradfitz bradfitz self-assigned this Oct 8, 2015
@bradfitz bradfitz reopened this Oct 8, 2015
@gopherbot
Copy link

CL https://golang.org/cl/15630 mentions this issue.

@golang golang locked and limited conversation to collaborators Oct 9, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants