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: miss host header when serve as forward proxy. #16265

Closed
SunRunAway opened this issue Jul 5, 2016 · 5 comments

Comments

Projects
None yet
4 participants
@SunRunAway
Copy link

commented Jul 5, 2016

  1. What version of Go are you using (go version)?
    go version go1.6.2 darwin/amd64
  2. What operating system and processor architecture are you using?
    darwin/amd64
  3. What did you do?

run this program on local.

package main

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

func do(w http.ResponseWriter, req *http.Request) {
    fmt.Println("host header:", req.Host)
    b, _ := httputil.DumpRequest(req, false)
    fmt.Println(string(b))
}

func main() {
    http.HandleFunc("/", do)
    http.ListenAndServe(":12306", nil)
}

on another terminal, run:

curl -v 'http://www.qiniu.com/' -H 'Host: haha' -x 'http://localhost:12306'
*   Trying ::1...
* Connected to localhost (::1) port 12306 (#0)
> GET http://www.qiniu.com/ HTTP/1.1
> Host: haha
> User-Agent: curl/7.43.0
> Accept: */*
> Proxy-Connection: Keep-Alive
> 
< HTTP/1.1 200 OK
< Date: Tue, 05 Jul 2016 04:02:27 GMT
< Content-Length: 0
< Content-Type: text/plain; charset=utf-8
< 
* Connection #0 to host localhost left intact
  1. What did you expect to see?

I ran sudo tcpdump -i any -A port 12306 and it prints

GET http://www.qiniu.com/ HTTP/1.1
Host: haha
User-Agent: curl/7.43.0
Accept: */*
Proxy-Connection: Keep-Alive

  1. What did you see instead?

the program prints

host header: www.qiniu.com
GET http://www.qiniu.com/ HTTP/1.1
Accept: */*
Proxy-Connection: Keep-Alive
User-Agent: curl/7.43.0


Is it right that host header should be haha

@fraenkel

This comment has been minimized.

Copy link
Contributor

commented Jul 6, 2016

According to RFC 7230, Section 5.4 states

When a proxy receives a request with an absolute-form of request-target, the proxy MUST ignore the received Host header field (if any) and instead replace it with the host information of the request-target.

@SunRunAway

This comment has been minimized.

Copy link
Author

commented Jul 6, 2016

That is RFC definition for what a proxy must do, but server still need the Host imformation.
eg: enabling the proxy server to distinguish among diffrent proxy policies while servicing requests for multiple host names on a single IP address.

@bradfitz

This comment has been minimized.

Copy link
Member

commented Jul 6, 2016

https://tools.ietf.org/html/rfc7230#section-5.4 also says:

A client MUST send a Host header field in all HTTP/1.1 request
messages. If the target URI includes an authority component, then a
client MUST send a field-value for Host that is identical to that
authority component

Go is forcing the two values to be the same.

In fact, there's even an old comment in the code about this in net/http/request.go:

        // RFC 2616: Must treat                                                                                                               
        //      GET /index.html HTTP/1.1                                                                                                      
        //      Host: www.google.com                                                                                                          
        // and                                                                                                                                
        //      GET http://www.google.com/index.html HTTP/1.1                                                                                 
        //      Host: doesntmatter                                                                                                            
        // the same. In the second case, any Host line is ignored.                                                                            
        req.Host = req.URL.Host
        if req.Host == "" {
                req.Host = req.Header.get("Host")
        }

So I think this is working as intended. Feel free to clarify if I misunderstood something.

@bradfitz bradfitz closed this Jul 6, 2016

@SunRunAway

This comment has been minimized.

Copy link
Author

commented Jul 6, 2016

There is nothing conflict with RFC.

But that has no way for me to do
curl -H "Host: domain" "http://1.1.1.1/xxx" -x "http://proxyWrittenInGo"
through a proxy written in go.

Also, as an HTTP handler, it has no way to get the raw http header Host in an HTTP request.
And I think it's inconsistent that Host field which must be in an HTTP request presents in tcpdump but disapears in httputil.DumpRequest.

@bradfitz

This comment has been minimized.

Copy link
Member

commented Jul 6, 2016

Correct, Go does not permit you to get at a bogus Host header. It's bogus if it differs from an absolute request-target, as far as I understand from the spec.

If you need to test a proxy and curl doesn't let you generate the request you want to, you could probably generate it with a different program.

@golang golang locked and limited conversation to collaborators Jul 6, 2017

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
You can’t perform that action at this time.