net/http: set cookie host to Request.Host when available

When both Request.URL and Request.Host are set, the host in URL
is used for connecting at the transport level, while Host is used
for the request host line. Cookies should be set for the request,
not the underlying connection destination.

Fixes #38988

Change-Id: I09053b87ccac67081f6038d205837d9763701526
Reviewed-on: https://go-review.googlesource.com/c/go/+/710335
Reviewed-by: Damien Neil <dneil@google.com>
Auto-Submit: Damien Neil <dneil@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Sean Liao 2025-10-08 22:33:24 +01:00 committed by Gopher Robot
parent 6f4c63ba63
commit 69e8279632
2 changed files with 37 additions and 2 deletions

View file

@ -172,8 +172,13 @@ func refererForURL(lastReq, newReq *url.URL, explicitRef string) string {
// didTimeout is non-nil only if err != nil.
func (c *Client) send(req *Request, deadline time.Time) (resp *Response, didTimeout func() bool, err error) {
cookieURL := req.URL
if req.Host != "" {
cookieURL = cloneURL(cookieURL)
cookieURL.Host = req.Host
}
if c.Jar != nil {
for _, cookie := range c.Jar.Cookies(req.URL) {
for _, cookie := range c.Jar.Cookies(cookieURL) {
req.AddCookie(cookie)
}
}
@ -183,7 +188,7 @@ func (c *Client) send(req *Request, deadline time.Time) (resp *Response, didTime
}
if c.Jar != nil {
if rc := resp.Cookies(); len(rc) > 0 {
c.Jar.SetCookies(req.URL, rc)
c.Jar.SetCookies(cookieURL, rc)
}
}
return resp, nil, nil

View file

@ -585,6 +585,36 @@ var echoCookiesRedirectHandler = HandlerFunc(func(w ResponseWriter, r *Request)
}
})
func TestHostMismatchCookies(t *testing.T) { run(t, testHostMismatchCookies) }
func testHostMismatchCookies(t *testing.T, mode testMode) {
ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
for _, c := range r.Cookies() {
c.Value = "SetOnServer"
SetCookie(w, c)
}
})).ts
reqURL, _ := url.Parse(ts.URL)
hostURL := *reqURL
hostURL.Host = "cookies.example.com"
c := ts.Client()
c.Jar = new(TestJar)
c.Jar.SetCookies(reqURL, []*Cookie{{Name: "First", Value: "SetOnClient"}})
c.Jar.SetCookies(&hostURL, []*Cookie{{Name: "Second", Value: "SetOnClient"}})
req, _ := NewRequest("GET", ts.URL, NoBody)
req.Host = hostURL.Host
resp, err := c.Do(req)
if err != nil {
t.Fatalf("Get: %v", err)
}
resp.Body.Close()
matchReturnedCookies(t, []*Cookie{{Name: "First", Value: "SetOnClient"}}, c.Jar.Cookies(reqURL))
matchReturnedCookies(t, []*Cookie{{Name: "Second", Value: "SetOnServer"}}, c.Jar.Cookies(&hostURL))
}
func TestClientSendsCookieFromJar(t *testing.T) {
defer afterTest(t)
tr := &recordingTransport{}