-
Notifications
You must be signed in to change notification settings - Fork 243
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 keepAlive
for http-proxy-agent
#169
Changes from all commits
3b50fc1
1467942
6f3bc0d
a546c05
1170a79
bac3018
2ddd92f
a841e14
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'http-proxy-agent': patch | ||
--- | ||
|
||
Fixed the keepAlive option for HttpProxyAgent |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,7 @@ interface HttpProxyAgentClientRequest extends http.ClientRequest { | |
}[]; | ||
_header?: string | null; | ||
_implicitHeader(): void; | ||
_updatedPropsForProxyAgent?: boolean; | ||
} | ||
|
||
function isHTTPS(protocol?: string | null): boolean { | ||
|
@@ -71,15 +72,12 @@ export class HttpProxyAgent<Uri extends string> extends Agent { | |
}; | ||
} | ||
|
||
async connect( | ||
setRequestProps( | ||
req: HttpProxyAgentClientRequest, | ||
opts: AgentConnectOpts | ||
): Promise<net.Socket> { | ||
const { proxy } = this; | ||
|
||
const protocol = opts.secureEndpoint ? 'https:' : 'http:'; | ||
): void { | ||
const hostname = req.getHeader('host') || 'localhost'; | ||
const base = `${protocol}//${hostname}`; | ||
const base = `http://${hostname}`; | ||
const url = new URL(req.path, base); | ||
if (opts.port !== 80) { | ||
url.port = String(opts.port); | ||
|
@@ -89,6 +87,8 @@ export class HttpProxyAgent<Uri extends string> extends Agent { | |
// to the absolute path of the URL that will be requested. | ||
req.path = String(url); | ||
|
||
const { proxy } = this; | ||
|
||
// Inject the `Proxy-Authorization` header if necessary. | ||
req._header = null; | ||
if (proxy.username || proxy.password) { | ||
|
@@ -108,6 +108,26 @@ export class HttpProxyAgent<Uri extends string> extends Agent { | |
); | ||
} | ||
|
||
req._updatedPropsForProxyAgent = true; | ||
} | ||
|
||
addRequest(req: HttpProxyAgentClientRequest, opts: AgentConnectOpts): void { | ||
this.setRequestProps(req, opts); | ||
// @ts-expect-error `addRequest()` isn't defined in `@types/node` | ||
super.addRequest(req, opts); | ||
} | ||
|
||
async connect( | ||
req: HttpProxyAgentClientRequest, | ||
opts: AgentConnectOpts | ||
): Promise<net.Socket> { | ||
if (!req._updatedPropsForProxyAgent) { | ||
// Maintain backwards compatibility with ProxyAgent and | ||
// PacProxyAgent, which both rely on HttpProxyAgent to set request | ||
// props inside the connect() function. | ||
this.setRequestProps(req, opts); | ||
} | ||
Comment on lines
+114
to
+129
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I changed this to work in a fully backwards-compatible way. |
||
|
||
// Create a socket connection to the proxy server. | ||
let socket: net.Socket; | ||
if (this.secureProxy) { | ||
|
@@ -124,7 +144,9 @@ export class HttpProxyAgent<Uri extends string> extends Agent { | |
let first: string; | ||
let endOfHeaders: number; | ||
debug('Regenerating stored HTTP header string for request'); | ||
req._implicitHeader(); | ||
if (!req._header) { | ||
req._implicitHeader(); | ||
} | ||
Comment on lines
-127
to
+149
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had to add this conditional because now that we are setting the headers before Alternatively, I could do just allow diff --git a/packages/http-proxy-agent/src/index.ts b/packages/http-proxy-agent/src/index.ts
index 16d5b51..807e4c5 100644
--- a/packages/http-proxy-agent/src/index.ts
+++ b/packages/http-proxy-agent/src/index.ts
@@ -29,7 +29,6 @@ interface HttpProxyAgentClientRequest extends http.ClientRequest {
}[];
_header?: string | null;
_implicitHeader(): void;
- _updatedPropsForProxyAgent?: boolean;
}
function isHTTPS(protocol?: string | null): boolean {
@@ -104,8 +103,6 @@ export class HttpProxyAgent<Uri extends string> extends Agent {
this.keepAlive ? 'Keep-Alive' : 'close'
);
}
-
- req._updatedPropsForProxyAgent = true;
}
addRequest(req: HttpProxyAgentClientRequest, opts: AgentConnectOpts): void {
@@ -118,12 +115,10 @@ export class HttpProxyAgent<Uri extends string> extends Agent {
req: HttpProxyAgentClientRequest,
opts: AgentConnectOpts
): Promise<net.Socket> {
- if (!req._updatedPropsForProxyAgent) {
- // Maintain backwards compatibility with ProxyAgent and
- // PacProxyAgent, which both rely on HttpProxyAgent to set request
- // props inside the connect() function.
- this.setRequestProps(req, opts);
- }
+ // Maintain backwards compatibility with ProxyAgent and
+ // PacProxyAgent, which both rely on HttpProxyAgent to set request
+ // props inside the connect() function.
+ this.setRequestProps(req, opts);
// Create a socket connection to the proxy server.
let socket: net.Socket;
@@ -141,9 +136,7 @@ export class HttpProxyAgent<Uri extends string> extends Agent {
let first: string;
let endOfHeaders: number;
debug('Regenerating stored HTTP header string for request');
- if (!req._header) {
- req._implicitHeader();
- }
+ req._implicitHeader();
if (req.outputData && req.outputData.length > 0) {
// Node >= 12
debug( What do you think? |
||
if (req.outputData && req.outputData.length > 0) { | ||
// Node >= 12 | ||
debug( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ import { listen } from 'async-listen'; | |
import { ProxyServer, createProxy } from 'proxy'; | ||
import { req, json } from 'agent-base'; | ||
import { PacProxyAgent } from '../src'; | ||
import { once } from 'events'; | ||
|
||
const sslOptions = { | ||
key: fs.readFileSync(`${__dirname}/ssl-cert-snakeoil.key`), | ||
|
@@ -244,6 +245,44 @@ describe('PacProxyAgent', () => { | |
assert.equal(httpServerUrl.host, data.host); | ||
assert(gotReq); | ||
}, 10000); // This test is slow on Windows :/ | ||
|
||
it('should work with `keepAlive: true`', async () => { | ||
httpServer.on('request', function (req, res) { | ||
res.end(JSON.stringify(req.headers)); | ||
}); | ||
|
||
function FindProxyForURL() { | ||
return 'PROXY localhost:PORT;'; | ||
} | ||
|
||
const uri = `data:,${encodeURIComponent( | ||
FindProxyForURL.toString().replace('PORT', proxyServerUrl.port) | ||
)}`; | ||
const agent = new PacProxyAgent(uri, { keepAlive: true }); | ||
|
||
try { | ||
const res = await req(new URL('/test', httpServerUrl), { | ||
agent, | ||
}); | ||
expect(res.headers.connection).toEqual('keep-alive'); | ||
expect(res.statusCode).toEqual(200); | ||
res.resume(); | ||
const s1 = res.socket; | ||
await once(s1, 'free'); | ||
|
||
const res2 = await req(new URL('/test', httpServerUrl), { | ||
agent, | ||
}); | ||
expect(res2.headers.connection).toEqual('keep-alive'); | ||
// expect(res2.statusCode).toEqual(200); // TODO: this assertion will cause this test to fail; uncomment in a follow-on PR to fix keepAlive for PacProxyAgent+HTTP | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I left the new tests and test assertions intact. |
||
res2.resume(); | ||
const s2 = res2.socket; | ||
assert(s1 === s2); | ||
await once(s2, 'free'); | ||
} finally { | ||
agent.destroy(); | ||
} | ||
}); | ||
}); | ||
|
||
describe('"https" module', () => { | ||
|
@@ -358,5 +397,46 @@ describe('PacProxyAgent', () => { | |
assert.equal(proxyCount, 4); | ||
assert(gotReq); | ||
}, 10000); // This test is slow on Windows :/ | ||
|
||
it('should work with `keepAlive: true`', async () => { | ||
httpsServer.on('request', function (req, res) { | ||
res.end(JSON.stringify(req.headers)); | ||
}); | ||
|
||
function FindProxyForURL() { | ||
return 'PROXY localhost:PORT;'; | ||
} | ||
|
||
const uri = `data:,${encodeURIComponent( | ||
FindProxyForURL.toString().replace('PORT', proxyServerUrl.port) | ||
)}`; | ||
const agent = new PacProxyAgent(uri, { | ||
keepAlive: true, | ||
rejectUnauthorized: false, | ||
}); | ||
|
||
try { | ||
const res = await req(new URL('/test', httpsServerUrl), { | ||
agent, | ||
}); | ||
expect(res.headers.connection).toEqual('keep-alive'); | ||
expect(res.statusCode).toEqual(200); | ||
res.resume(); | ||
const s1 = res.socket; | ||
await once(s1, 'free'); | ||
|
||
const res2 = await req(new URL('/test', httpsServerUrl), { | ||
agent, | ||
}); | ||
expect(res2.headers.connection).toEqual('keep-alive'); | ||
expect(res2.statusCode).toEqual(200); | ||
res2.resume(); | ||
const s2 = res2.socket; | ||
assert(s1 === s2); | ||
await once(s2, 'free'); | ||
} finally { | ||
agent.destroy(); | ||
} | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seemed redundant to have
const protocol = opts.secureEndpoint ? 'https:' : 'http:';
and use that to generate the URL, when HttpProxyAgent should only be used for HTTP URLs. In any case, thesecureEndpoint
option is always undefinedAgent.addRequest()
(except for when ProxyAgent is used).I simplified it here by hard-coding the protocol, I think that's OK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're going to do this, then the function should throw an error when
opts.secureEndpoint === true
. However, I think it is valid to do HTTPS requests (though less ideal, especially over an external network). I believe squid works when passing an https URL for example.