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

fix: don't put opts.headers into the agent itself #105

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

shunf4
Copy link

@shunf4 shunf4 commented Jun 28, 2024

This PR came about as I was looking into an error message complained by npm install using a http proxy. The header content-encoding: gzip was sent in a fetch to https://registry.npmmirror.com/tar/-/tar-6.2.1.tgz, causing a 400 Bad Request response, but it's not the case when no http proxy was set; the install simply succeeded.

A minimal reproduction:

$ node -v
v20.10.0

$ npm -v
10.2.3

# Though I think the latest code is affected also.

$ npm init
$ npm install [email protected] --registry=https://registry.npmmirror.com
$ rm -rf node_modules/tar
$ npm cache clean --force
$ HTTP_PROXY="${MY_HTTP_PROXY}" HTTPS_PROXY="${MY_HTTP_PROXY}" http_proxy="${MY_HTTP_PROXY}" https_proxy="${MY_HTTP_PROXY}" npm install

npm ERR! code E400
npm ERR! 400 Bad Request - GET https://registry.npmmirror.com/tar/-/tar-6.2.1.tgz

$ curl -L --http1.1 -H 'content-encoding: gzip' -H 'content-type: application/json' -i 'https://registry.npmmirror.com/tar/-/tar-6.2.1.tgz'

HTTP/1.1 400 Bad Request
Server: Tengine
Content-Type: text/html; charset=utf-8
Content-Length: 24
Connection: keep-alive
Strict-Transport-Security: max-age=5184000
Date: *
Vary: Origin
Via: *
Ali-Swift-Global-Savetime: *
X-Cache: MISS TCP_MISS dirn:-2:-2
X-Swift-Error: orig response 4XX error
X-Swift-SaveTime: *
X-Swift-CacheTime: 0
Timing-Allow-Origin: *
EagleId: *

<h2>400 Bad Request</h2>

Digging into the code I found that the request to https://registry.npmmirror.com/*/-/*-<version>.tgz was never meant to be using gzip: true, nor had the content-encoding: gzip header ever been passed in opts.header field. It's the agent, that (unnecessarily and unwantedly?) contained the content-encoding: gzip header passed from the first request (which was the one meant to be using gzipped request body), and reused by caching mechanism for following requests. However, agent.headers will be picked up if and only if an http proxy is configured:

proxy.setRequestProps(request, options)

The most obvious fix seems to be removing headers field in agent's normalizeOptions; an agent is not expected to remember the headers, in usual sense. I don't know the fix is good enough to be approved; it may bring unexpected implications or break some existing usages, since my situation is only a rather marginal case.

References

None

@shunf4 shunf4 requested a review from a team as a code owner June 28, 2024 16:06
@wraithgar wraithgar changed the title fix: Remove opts.headers in normalizeOptions, preventing possible usage in subsequent unrelated requests fix: don't put opts.headers into the agent itself Jun 28, 2024
@wraithgar
Copy link
Member

I think you stumbled upon a very sneaky but very real bug here. Great work.

@wraithgar
Copy link
Member

Looking through how we use the options elsewhere this seems like it should work. The headers aren't part of the cache key, and the two times we call this are both used for making a new Agent.

@wraithgar
Copy link
Member

We probably want to add a test (maybe to test/agent.js) that shows subsequent requests w/ differing headers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants