Skip to content

Commit 3f32586

Browse files
anonrigjoshthoward
authored andcommitted
increase test coverage of node:http (#4535)
1 parent 7b1af56 commit 3f32586

16 files changed

+919
-259
lines changed

justfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ stream-test *args:
3636

3737
# e.g. just node-test zlib
3838
node-test test_name *args:
39-
just stream-test //src/workerd/api/node:tests/{{test_name}}-nodejs-test {{args}}
39+
just stream-test //src/workerd/api/node/tests:{{test_name}}-nodejs-test {{args}}
4040

4141
# e.g. just wpt-test urlpattern
4242
wpt-test test_name:

src/node/http.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
import { METHODS, STATUS_CODES } from 'node-internal:internal_http_constants';
1111
import { ClientRequest } from 'node-internal:internal_http_client';
1212
import { OutgoingMessage } from 'node-internal:internal_http_outgoing';
13+
import { IncomingMessage } from 'node-internal:internal_http_incoming';
1314
import { Agent, globalAgent } from 'node-internal:internal_http_agent';
1415
import type { IncomingMessageCallback } from 'node-internal:internal_http_util';
1516
import type { RequestOptions } from 'node:http';
@@ -44,6 +45,7 @@ export {
4445
OutgoingMessage,
4546
Agent,
4647
globalAgent,
48+
IncomingMessage,
4749
};
4850
export default {
4951
validateHeaderName,
@@ -57,4 +59,5 @@ export default {
5759
OutgoingMessage,
5860
Agent,
5961
globalAgent,
62+
IncomingMessage,
6063
};

src/node/internal/internal_http_client.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@
44
// Copyright Joyent and Node contributors. All rights reserved. MIT license.
55

66
import { _checkIsHttpToken as checkIsHttpToken } from 'node-internal:internal_http';
7-
import {
8-
kHeadersSent,
9-
kOutHeaders,
10-
} from 'node-internal:internal_http_outgoing';
7+
import { kOutHeaders } from 'node-internal:internal_http_outgoing';
118
import { Buffer } from 'node-internal:internal_buffer';
129
import { urlToHttpOptions, isURL } from 'node-internal:internal_url';
1310
import {
@@ -223,6 +220,9 @@ export class ClientRequest extends OutgoingMessage implements _ClientRequest {
223220
const headers = options.headers;
224221
if (!Array.isArray(headers)) {
225222
if (headers != null) {
223+
if ('host' in headers) {
224+
validateString(headers.host, 'host');
225+
}
226226
for (const [key, value] of Object.entries(headers)) {
227227
this.setHeader(key, value as unknown as string);
228228
}
@@ -336,7 +336,8 @@ export class ClientRequest extends OutgoingMessage implements _ClientRequest {
336336
url.port = this.port;
337337
url.pathname = this.path;
338338

339-
this[kHeadersSent] = true;
339+
// Sets headersSent to true
340+
this._header = '';
340341

341342
// Our fetch implementation has the following limitations.
342343
//

src/node/internal/internal_http_outgoing.ts

Lines changed: 103 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
ERR_HTTP_HEADERS_SENT,
99
ERR_INVALID_ARG_TYPE,
1010
ERR_STREAM_CANNOT_PIPE,
11+
ERR_STREAM_DESTROYED,
1112
ERR_METHOD_NOT_IMPLEMENTED,
1213
} from 'node-internal:internal_errors';
1314
import {
@@ -16,13 +17,16 @@ import {
1617
} from 'node-internal:internal_http';
1718
import { Writable } from 'node-internal:streams_writable';
1819
import { EventEmitter } from 'node-internal:events';
19-
import type { OutgoingMessage as _OutgoingMessage } from 'node:http';
20+
import type {
21+
OutgoingMessage as _OutgoingMessage,
22+
OutgoingHttpHeaders,
23+
} from 'node:http';
2024

2125
export const kUniqueHeaders = Symbol('kUniqueHeaders');
2226
export const kHighWaterMark = Symbol('kHighWaterMark');
2327
export const kNeedDrain = Symbol('kNeedDrain');
2428
export const kOutHeaders = Symbol('kOutHeaders');
25-
export const kHeadersSent = Symbol('kHeadersSent');
29+
export const kErrored = Symbol('kErrored');
2630

2731
export function parseUniqueHeadersOption(
2832
headers?: string[] | null
@@ -39,11 +43,30 @@ export function parseUniqueHeadersOption(
3943
}
4044

4145
export class OutgoingMessage extends Writable implements _OutgoingMessage {
42-
[kHeadersSent]: boolean = false;
43-
[kOutHeaders]: Record<string, Record<string, string | string[]>> = {};
46+
[kOutHeaders]: Record<string, { name: string; value: string | string[] }> =
47+
{};
48+
[kErrored]: Error | null = null;
49+
50+
#header: unknown;
51+
52+
override writable = true;
53+
override destroyed = false;
54+
finished = false;
55+
56+
get _header(): unknown {
57+
return this.#header;
58+
}
59+
60+
set _header(value: unknown) {
61+
this.#header = value;
62+
}
63+
64+
get header(): unknown {
65+
return this.#header;
66+
}
4467

4568
setHeader(name: string, value: string | string[]): this {
46-
if (this[kHeadersSent]) {
69+
if (this.headersSent) {
4770
throw new ERR_HTTP_HEADERS_SENT('set');
4871
}
4972
validateHeaderName(name);
@@ -61,7 +84,7 @@ export class OutgoingMessage extends Writable implements _OutgoingMessage {
6184
setHeaders(
6285
headers: Headers | Map<string, string | number | readonly string[]>
6386
): this {
64-
if (this[kHeadersSent]) {
87+
if (this.headersSent) {
6588
throw new ERR_HTTP_HEADERS_SENT('set');
6689
}
6790

@@ -103,7 +126,7 @@ export class OutgoingMessage extends Writable implements _OutgoingMessage {
103126
}
104127

105128
appendHeader(name: string, value: string | string[]): this {
106-
if (this[kHeadersSent]) {
129+
if (this.headersSent) {
107130
throw new ERR_HTTP_HEADERS_SENT('append');
108131
}
109132

@@ -118,7 +141,7 @@ export class OutgoingMessage extends Writable implements _OutgoingMessage {
118141

119142
// Prepare the field for appending, if required
120143
if (!Array.isArray(headers[field].value)) {
121-
headers[field].value = [headers[field].value as string];
144+
headers[field].value = [headers[field].value];
122145
}
123146

124147
const existingValues = headers[field].value;
@@ -144,7 +167,7 @@ export class OutgoingMessage extends Writable implements _OutgoingMessage {
144167
removeHeader(name: string): void {
145168
validateString(name, 'name');
146169

147-
if (this[kHeadersSent]) {
170+
if (this.headersSent) {
148171
throw new ERR_HTTP_HEADERS_SENT('remove');
149172
}
150173

@@ -165,11 +188,24 @@ export class OutgoingMessage extends Writable implements _OutgoingMessage {
165188
}
166189

167190
flushHeaders(): void {
168-
// Not implemented
191+
if (!this._header) {
192+
this._implicitHeader();
193+
}
194+
195+
// Force-flush the headers.
196+
this._send('');
197+
}
198+
199+
getHeaders(): OutgoingHttpHeaders {
200+
const headers: Record<string, string | string[]> = {};
201+
for (const [_key, entry] of Object.entries(this[kOutHeaders])) {
202+
headers[entry.name] = entry.value;
203+
}
204+
return headers;
169205
}
170206

171207
get headersSent(): boolean {
172-
return this[kHeadersSent];
208+
return this.#header != null;
173209
}
174210

175211
// @ts-expect-error TS2416 Unnecessary type assertion
@@ -184,4 +220,60 @@ export class OutgoingMessage extends Writable implements _OutgoingMessage {
184220
_implicitHeader(): void {
185221
throw new ERR_METHOD_NOT_IMPLEMENTED('_implicitHeader()');
186222
}
223+
224+
_renderHeaders(): Record<string, string | string[]> {
225+
if (this.headersSent) {
226+
throw new ERR_HTTP_HEADERS_SENT('render');
227+
}
228+
const headers: Record<string, string | string[]> = {};
229+
for (const [_key, entry] of Object.entries(this[kOutHeaders])) {
230+
headers[entry.name] = entry.value;
231+
}
232+
return headers;
233+
}
234+
235+
_send(_val: unknown): void {
236+
// Unimplemented.
237+
}
238+
239+
override _write(
240+
_chunk: any, // eslint-disable-line @typescript-eslint/no-explicit-any
241+
_encoding: BufferEncoding,
242+
cb: (error?: Error | null) => void
243+
): void {
244+
// The only reason for us to override this method is to increase the Node.js test coverage.
245+
// Otherwise, we don't implement _write yet.
246+
if (this.destroyed) {
247+
cb(new ERR_STREAM_DESTROYED('_write'));
248+
return;
249+
}
250+
251+
throw new ERR_METHOD_NOT_IMPLEMENTED('_write');
252+
}
253+
254+
override destroy(err?: unknown, _cb?: (err?: unknown) => void): this {
255+
if (this.destroyed) {
256+
return this;
257+
}
258+
this.destroyed = true;
259+
this[kErrored] = err as Error;
260+
261+
return this;
262+
}
263+
264+
// @ts-expect-error TS2611 Property accessor.
265+
get writableObjectMode(): boolean {
266+
return false;
267+
}
268+
269+
// @ts-expect-error TS2611 Property accessor.
270+
get errored(): Error | null {
271+
return this[kErrored];
272+
}
273+
274+
// @ts-expect-error TS2611 Property accessor.
275+
get writableEnded(): boolean {
276+
// eslint-disable-next-line @typescript-eslint/no-deprecated
277+
return this.finished;
278+
}
187279
}

src/workerd/api/node/tests/BUILD.bazel

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -325,48 +325,70 @@ wd_test(
325325
data = ["streams-nodejs-test.js"],
326326
)
327327

328+
js_binary(
329+
name = "http-nodejs-server",
330+
entry_point = "http-nodejs-server.js",
331+
)
332+
328333
wd_test(
329334
src = "http-nodejs-test.wd-test",
330335
args = ["--experimental"],
331336
data = ["http-nodejs-test.js"],
337+
sidecar = "http-nodejs-server",
338+
sidecar_port_bindings = [
339+
"PONG_SERVER_PORT",
340+
"ASD_SERVER_PORT",
341+
"TIMEOUT_SERVER_PORT",
342+
"HELLO_WORLD_SERVER_PORT",
343+
"HEADER_VALIDATION_SERVER_PORT",
344+
],
345+
tags = ["requires-network"],
346+
)
347+
348+
js_binary(
349+
name = "http-outgoing-nodejs-server",
350+
entry_point = "http-outgoing-nodejs-server.js",
332351
)
333352

334353
wd_test(
335354
src = "http-outgoing-nodejs-test.wd-test",
336355
args = ["--experimental"],
337356
data = ["http-outgoing-nodejs-test.js"],
357+
sidecar = "http-outgoing-nodejs-server",
358+
sidecar_port_bindings = [
359+
"FINISH_WRITABLE_PORT",
360+
"WRITABLE_FINISHED_PORT",
361+
"PROPERTIES_PORT",
362+
],
338363
)
339364

340365
js_binary(
341-
name = "http-client-server",
342-
entry_point = "http-client-server.js",
366+
name = "http-client-nodejs-server",
367+
entry_point = "http-client-nodejs-server.js",
343368
)
344369

345370
wd_test(
346371
src = "http-client-nodejs-test.wd-test",
347372
args = ["--experimental"],
348373
data = ["http-client-nodejs-test.js"],
349-
sidecar = "http-client-server",
374+
sidecar = "http-client-nodejs-server",
350375
sidecar_port_bindings = [
351376
"PONG_SERVER_PORT",
352377
"ASD_SERVER_PORT",
353-
"TIMEOUT_SERVER_PORT",
354-
"HELLO_WORLD_SERVER_PORT",
355-
"HEADER_VALIDATION_SERVER_PORT",
378+
"DEFAULT_HEADERS_EXIST_PORT",
356379
],
357-
tags = ["requires-network"],
358380
)
359381

360382
js_binary(
361-
name = "http-agent-server",
362-
entry_point = "http-agent-server.js",
383+
name = "http-agent-nodejs-server",
384+
entry_point = "http-agent-nodejs-server.js",
363385
)
364386

365387
wd_test(
366388
src = "http-agent-nodejs-test.wd-test",
367389
args = ["--experimental"],
368390
data = ["http-agent-nodejs-test.js"],
369-
sidecar = "http-agent-server",
391+
sidecar = "http-agent-nodejs-server",
370392
sidecar_port_bindings = [
371393
"PONG_SERVER_PORT",
372394
],

src/workerd/api/node/tests/http-agent-nodejs-test.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// https://opensource.org/licenses/Apache-2.0
44

55
import http from 'node:http';
6+
import https from 'node:https';
67
import { strictEqual, ok } from 'node:assert';
78

89
export const checkPortsSetCorrectly = {
@@ -75,3 +76,54 @@ export const testHttpAgentNull = {
7576
await promise;
7677
},
7778
};
79+
80+
// Test is taken from test/parallel/test-https-agent-constructor.js
81+
export const testHttpsAgentConstructor = {
82+
async test() {
83+
ok(new https.Agent() instanceof https.Agent);
84+
strictEqual(typeof https.request, 'function');
85+
strictEqual(typeof http.get, 'function');
86+
},
87+
};
88+
89+
// Tests covering http-agent
90+
// - [ ] test/parallel/test-http-agent-abort-controller.js
91+
// - [ ] test/parallel/test-http-agent-close.js
92+
// - [ ] test/parallel/test-http-agent-destroyed-socket.js
93+
// - [ ] test/parallel/test-http-agent-domain-reused-gc.js
94+
// - [ ] test/parallel/test-http-agent-error-on-idle.js
95+
// - [ ] test/parallel/test-http-agent-false.js
96+
// - [x] test/parallel/test-http-agent-getname.js
97+
// - [ ] test/parallel/test-http-agent-keepalive-delay.js
98+
// - [ ] test/parallel/test-http-agent-keepalive.js
99+
// - [ ] test/parallel/test-http-agent-maxsockets-respected.js
100+
// - [ ] test/parallel/test-http-agent-maxsockets.js
101+
// - [ ] test/parallel/test-http-agent-maxtotalsockets.js
102+
// - [ ] test/parallel/test-http-agent-no-protocol.js
103+
// - [x] test/parallel/test-http-agent-null.js
104+
// - [ ] test/parallel/test-http-agent-remove.js
105+
// - [ ] test/parallel/test-http-agent-reuse-drained-socket-only.js
106+
// - [ ] test/parallel/test-http-agent-scheduling.js
107+
// - [ ] test/parallel/test-http-agent-timeout-option.js
108+
// - [ ] test/parallel/test-http-agent-timeout.js
109+
// - [ ] test/parallel/test-http-agent-uninitialized-with-handle.js
110+
// - [ ] test/parallel/test-http-agent.js
111+
// - [ ] test/parallel/test-https-agent-abort-controller.js
112+
// - [ ] test/parallel/test-https-agent-additional-options.js
113+
// - [x] test/parallel/test-https-agent-constructor.js
114+
// - [ ] test/parallel/test-https-agent-create-connection.js
115+
// - [ ] test/parallel/test-https-agent-disable-session-reuse.js
116+
// - [ ] test/parallel/test-https-agent-getname.js
117+
// - [ ] test/parallel/test-https-agent-keylog.js
118+
// - [ ] test/parallel/test-https-agent-servername.js
119+
// - [ ] test/parallel/test-https-agent-session-eviction.js
120+
// - [ ] test/parallel/test-https-agent-session-injection.js
121+
// - [ ] test/parallel/test-https-agent-session-reuse.js
122+
// - [ ] test/parallel/test-https-agent-sni.js
123+
// - [ ] test/parallel/test-https-agent-sockets-leak.js
124+
// - [ ] test/parallel/test-https-agent-unref-socket.js
125+
// - [ ] test/parallel/test-https-agent.js
126+
127+
// Tests doesn't make sense for workerd:
128+
//
129+
// - [ ] test/parallel/test-http-agent-uninitialized.js

0 commit comments

Comments
 (0)