Skip to content

cloneRequest does not copy the header, so round trippers mutate the caller's request #981

Description

@SaiPisey2

cloneRequest in config/http_config.go intends a deep copy of the header but does not make one:

r2 := new(http.Request)
*r2 = *r
// Deep copy of the Header.
maps.Copy(r.Header, r2.Header)

After *r2 = *r, r2.Header is the same map as r.Header, so maps.Copy copies it onto itself. The clone shares the caller's header map.

Every round tripper that touches headers is affected, since they all clone first and then modify. headersRoundTripper uses Header.Add, so a reused request gains another copy of every configured header on each round trip. It grows without bound until the server rejects it — a downstream report in Alloy (grafana/alloy#7016) shows ~1000 identical X-Api-Key lines in one request and scrapes failing with 431.

There is a second effect: Header.Set on the clone of a request built without a header panics with assignment to entry in nil map, because the shared nil map is never replaced.

Introduced in 56870db ("Modernize Go"), which replaced the manual copy loop:

-	r2.Header = make(http.Header)
-	for k, s := range r.Header {
-		r2.Header[k] = s
-	}
+	maps.Copy(r.Header, r2.Header)

The arguments are also the wrong way round, but swapping them is not enough — the clone needs its own map.

Fix in #982.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions