Skip to content

Commit f060e2e

Browse files
committed
Handle breaking changes to connection header in Node v20
1 parent 7ac336a commit f060e2e

File tree

5 files changed

+39
-21
lines changed

5 files changed

+39
-21
lines changed

test/integration/proxying/http-proxying.spec.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ import {
1717
getDeferred,
1818
Deferred,
1919
sendRawRequest,
20-
makeAbortableRequest
20+
makeAbortableRequest,
21+
defaultNodeConnectionHeader
2122
} from "../../test-utils";
2223
import { isLocalIPv6Available } from "../../../src/util/socket-util";
2324

@@ -103,15 +104,15 @@ nodeOnly(() => {
103104
'dupe-header': ['A', 'B'],
104105
uppercaseheader: 'VALUE',
105106
host: `localhost:${remoteServer.port}`,
106-
connection: 'close'
107+
connection: defaultNodeConnectionHeader()
107108
});
108109

109110
expect(req.rawHeaders).to.deep.equal([
110111
['Dupe-Header', 'A'],
111112
['UPPERCASEHEADER', 'VALUE'],
112113
['Dupe-Header', 'B'],
113114
['Host', `localhost:${remoteServer.port}`],
114-
['Connection', 'close' ] // Added by node in initial request
115+
['Connection', defaultNodeConnectionHeader()] // Added by node in initial request
115116
]);
116117
return {};
117118
});
@@ -322,7 +323,7 @@ nodeOnly(() => {
322323
let resultingRequest = (await remoteEndpoint.getSeenRequests())[0];
323324
expect(resultingRequest.headers).to.deep.equal({
324325
'host': `localhost:${remoteServer.port}`,
325-
'connection': 'close'
326+
'connection': defaultNodeConnectionHeader()
326327
});
327328
});
328329

@@ -336,7 +337,7 @@ nodeOnly(() => {
336337
beforeRequest: (req) => {
337338
expect(req.headers).to.deep.equal({
338339
'host': `localhost:${remoteServer.port}`,
339-
'connection': 'close',
340+
'connection': defaultNodeConnectionHeader(),
340341
'uppercase-header': 'UPPERCASE-VALUE',
341342
'multival': ['value 1', 'value 2']
342343
});
@@ -346,7 +347,7 @@ nodeOnly(() => {
346347
['multival', 'value 1'],
347348
['multival', 'value 2'],
348349
['host', `localhost:${remoteServer.port}`],
349-
['Connection', 'close']
350+
['Connection', defaultNodeConnectionHeader()]
350351
]);
351352

352353
return {
@@ -374,7 +375,7 @@ nodeOnly(() => {
374375
beforeRequest: (req) => {
375376
expect(req.headers).to.deep.equal({
376377
'host': `localhost:${remoteServer.port}`,
377-
'connection': 'close'
378+
'connection': defaultNodeConnectionHeader()
378379
});
379380
return {
380381
headers: Object.assign({}, req.headers, { 'x-test-header': 'test' })
@@ -396,7 +397,7 @@ nodeOnly(() => {
396397
beforeRequest: (req) => {
397398
expect(req.headers).to.deep.equal({
398399
'host': `localhost:${remoteServer.port}`,
399-
'connection': 'close'
400+
'connection': defaultNodeConnectionHeader()
400401
});
401402

402403
req.headers['x-test-header'] = 'test';
@@ -517,7 +518,7 @@ nodeOnly(() => {
517518
method: 'POST',
518519
headers: {
519520
'host': `localhost:${remoteServer.port}`,
520-
'connection': 'close',
521+
'connection': defaultNodeConnectionHeader(),
521522
'content-encoding': 'gzip',
522523
'content-length': '37',
523524
'custom-header': 'a-value'

test/integration/proxying/proxy-transforms.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import request = require("request-promise-native");
44
import * as zlib from 'zlib';
55

66
import { getLocal, Mockttp } from "../../..";
7-
import { expect, nodeOnly } from "../../test-utils";
7+
import { defaultNodeConnectionHeader, expect, nodeOnly } from "../../test-utils";
88

99
const INITIAL_ENV = _.cloneDeep(process.env);
1010

@@ -128,7 +128,7 @@ nodeOnly(() => {
128128
'host': `localhost:${remoteServer.port}`,
129129
'accept': 'application/json',
130130
'content-type': 'application/json',
131-
'connection': 'close'
131+
'connection': defaultNodeConnectionHeader()
132132
});
133133

134134
it("does nothing with an empty transform", async () => {
@@ -254,7 +254,7 @@ nodeOnly(() => {
254254
method: 'POST',
255255
headers: {
256256
// Required unavoidable headers:
257-
'connection': 'close',
257+
'connection': defaultNodeConnectionHeader(),
258258
'transfer-encoding': 'chunked', // Because we removed content-length
259259
// No other headers, only injected value:
260260
'custom-header': 'replaced-value'

test/integration/remote-client.spec.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,15 @@ import {
1515
CompletedRequest,
1616
MOCKTTP_PARAM_REF
1717
} from "../..";
18-
import { expect, fetch, nodeOnly, browserOnly, delay, getDeferred } from "../test-utils";
18+
import {
19+
expect,
20+
fetch,
21+
nodeOnly,
22+
browserOnly,
23+
delay,
24+
getDeferred,
25+
defaultNodeConnectionHeader
26+
} from "../test-utils";
1927
import type { MockttpClient } from "../../dist/client/mockttp-client";
2028

2129
browserOnly(() => {
@@ -171,7 +179,7 @@ nodeOnly(() => {
171179
host: `localhost:${targetServer.port}`,
172180
accept: 'application/json',
173181
'content-length': '12',
174-
connection: 'close'
182+
connection: defaultNodeConnectionHeader()
175183
},
176184
body: 'request-body'
177185
});
@@ -232,7 +240,7 @@ nodeOnly(() => {
232240
accept: 'application/json',
233241
'content-type': 'application/json',
234242
'content-length': '25',
235-
connection: 'close',
243+
connection: defaultNodeConnectionHeader(),
236244
'injected-header': 'injected-value' // Only injected header remains
237245
},
238246
body: JSON.stringify({
@@ -316,7 +324,7 @@ nodeOnly(() => {
316324
accept: 'application/json',
317325
'content-type': 'application/json',
318326
'content-length': '21',
319-
connection: 'close'
327+
connection: defaultNodeConnectionHeader()
320328
},
321329
body: JSON.stringify({
322330
// Request body was separately transformed:

test/integration/subscriptions/request-events.spec.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import {
1414
nodeOnly,
1515
getDeferred,
1616
sendRawRequest,
17-
isNode
17+
isNode,
18+
defaultNodeConnectionHeader
1819
} from "../../test-utils";
1920
import { TimingEvents } from "../../../dist/types";
2021

@@ -126,12 +127,12 @@ describe("Request initiated subscriptions", () => {
126127
['UPPERCASEHEADER', 'VALUE'],
127128
['Dupe-Header', 'A'],
128129
['Dupe-Header', 'B'],
129-
['Connection', 'close']
130+
['Connection', defaultNodeConnectionHeader()]
130131
]);
131132

132133
// Parsed format:
133134
expect(seenRequest.headers).to.deep.equal({
134-
connection: 'close',
135+
connection: defaultNodeConnectionHeader(),
135136
uppercaseheader: 'VALUE',
136137
'dupe-header': ['A', 'B']
137138
});
@@ -220,12 +221,12 @@ describe("Request initiated subscriptions", () => {
220221
['UPPERCASEHEADER', 'VALUE'],
221222
['Dupe-Header', 'A'],
222223
['Dupe-Header', 'B'],
223-
['Connection', 'close']
224+
['Connection', defaultNodeConnectionHeader()]
224225
]);
225226

226227
// Parsed format:
227228
expect(seenRequest.headers).to.deep.equal({
228-
connection: 'close',
229+
connection: defaultNodeConnectionHeader(),
229230
uppercaseheader: 'VALUE',
230231
'dupe-header': ['A', 'B']
231232
});

test/test-utils.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
File as FilePolyfill
1717
} from "formdata-node";
1818
import { RequestPromise } from 'request-promise-native';
19+
import * as semver from 'semver';
1920

2021
import chai = require("chai");
2122
import chaiAsPromised = require("chai-as-promised");
@@ -258,6 +259,13 @@ export const HTTP_ABORTSIGNAL_SUPPORTED = ">=14.17";
258259
export const OLD_TLS_SUPPORTED = "<17"; // In 17+ TLS < v1.2 is only available with legacy OpenSSL flag
259260
export const SOCKET_RESET_SUPPORTED = "^16.17 || >=18.3";
260261
export const BROKEN_H2_TUNNELLING = "^18.8"; // Some H1-over-H2 tests fail in Node 18 (but not 19)
262+
export const DEFAULT_KEEP_ALIVE = ">=19";
263+
export const FIXED_KEEP_ALIVE_BEHAVIOUR = ">=20";
264+
265+
export const defaultNodeConnectionHeader = () =>
266+
semver.satisfies(process.version, DEFAULT_KEEP_ALIVE)
267+
? 'keep-alive'
268+
: 'close';
261269

262270
type Http2ResponseHeaders = http2.IncomingHttpHeaders & http2.IncomingHttpStatusHeader;
263271

0 commit comments

Comments
 (0)