Skip to content

Commit 93ae418

Browse files
committed
feat: [US-010] improve http.Agent bridge compatibility
1 parent 8b80765 commit 93ae418

10 files changed

Lines changed: 857 additions & 124 deletions

File tree

.agent/contracts/node-bridge.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,3 +206,21 @@ Bridge globals routed through the `_loadPolyfill` dispatch multiplexer SHALL pre
206206
- **WHEN** a dispatch-multiplexed bridge handler throws a host error with `name` and `code` (for example `TypeError` + `ERR_INVALID_ARG_VALUE`)
207207
- **THEN** the sandbox-visible error MUST preserve that `name` and `code`
208208
- **AND** the bridge MUST NOT collapse the error to a plain `Error` with only a message
209+
210+
### Requirement: HTTP Agent Bridge Preserves Node Pooling Semantics
211+
Bridge-provided `http.Agent` behavior SHALL preserve the observable pooling state that Node.js userland and conformance tests inspect.
212+
213+
#### Scenario: Sandboxed code inspects agent bookkeeping
214+
- **WHEN** sandboxed code uses `http.Agent` or `require('_http_agent').Agent`
215+
- **THEN** the bridge MUST expose matching `Agent` constructors through both module paths
216+
- **AND** `getName()`, `requests`, `sockets`, `freeSockets`, and `totalSocketCount` MUST reflect request queueing and socket reuse state with Node-compatible key shapes
217+
218+
#### Scenario: Keepalive sockets are reused or discarded
219+
- **WHEN** sandboxed code enables `keepAlive` and reuses pooled HTTP connections
220+
- **THEN** the bridge MUST mark reused requests via `request.reusedSocket`
221+
- **AND** destroyed or remotely closed sockets MUST be removed from the pool instead of being reassigned to queued requests
222+
223+
#### Scenario: Total socket limits are configured
224+
- **WHEN** sandboxed code constructs an `http.Agent` with `maxSockets`, `maxFreeSockets`, or `maxTotalSockets`
225+
- **THEN** invalid argument types and ranges MUST throw Node-compatible `ERR_INVALID_ARG_TYPE` / `ERR_OUT_OF_RANGE` errors
226+
- **AND** queued requests across origins MUST respect both per-origin and total socket limits

docs/nodejs-conformance-report.mdx

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,22 @@ description: Node.js v22 test/parallel/ conformance results for the secure-exec
1212
| Node.js version | 22.14.0 |
1313
| Source | v22.14.0 (test/parallel/) |
1414
| Total tests | 3532 |
15-
| Passing (genuine) | 748 (21.2%) |
15+
| Passing (genuine) | 754 (21.3%) |
1616
| Passing (vacuous self-skip) | 33 |
17-
| Passing (total) | 781 (22.1%) |
18-
| Expected fail | 2680 |
17+
| Passing (total) | 787 (22.3%) |
18+
| Expected fail | 2674 |
1919
| Skip | 71 |
2020
| Last updated | 2026-03-26 |
2121

2222
## Failure Categories
2323

2424
| Category | Tests |
2525
| --- | --- |
26-
| implementation-gap | 1377 |
26+
| implementation-gap | 1372 |
2727
| unsupported-module | 738 |
2828
| requires-v8-flags | 239 |
2929
| requires-exec-path | 200 |
30-
| unsupported-api | 124 |
30+
| unsupported-api | 123 |
3131
| test-infra | 68 |
3232
| vacuous-skip | 33 |
3333
| native-addon | 3 |
@@ -123,7 +123,7 @@ description: Node.js v22 test/parallel/ conformance results for the secure-exec
123123
| heap | 11 | 0 | 11 | 0 | 0.0% |
124124
| heapdump | 1 | 1 | 0 | 0 | 100.0% |
125125
| heapsnapshot | 2 | 0 | 2 | 0 | 0.0% |
126-
| http | 377 | 237 (1 vacuous) | 139 | 1 | 63.0% |
126+
| http | 377 | 243 (1 vacuous) | 133 | 1 | 64.6% |
127127
| http2 | 256 | 4 | 252 | 0 | 1.6% |
128128
| https | 62 | 4 | 58 | 0 | 6.5% |
129129
| icu | 5 | 0 | 5 | 0 | 0.0% |
@@ -245,11 +245,11 @@ description: Node.js v22 test/parallel/ conformance results for the secure-exec
245245
| wrap | 4 | 0 | 4 | 0 | 0.0% |
246246
| x509 | 1 | 0 | 1 | 0 | 0.0% |
247247
| zlib | 53 | 17 | 33 | 3 | 34.0% |
248-
| **Total** | **3532** | **781** | **2680** | **71** | **22.6%** |
248+
| **Total** | **3532** | **787** | **2674** | **71** | **22.7%** |
249249

250250
## Expectations Detail
251251

252-
### implementation-gap (696 entries)
252+
### implementation-gap (691 entries)
253253

254254
**Glob patterns:**
255255

@@ -260,7 +260,7 @@ description: Node.js v22 test/parallel/ conformance results for the secure-exec
260260
- `test-https-*.js` — https depends on tls — most tests fail on missing TLS fixture files or crypto API gaps
261261
- `test-http2-*.js` — http2 module bridged via kernel — most tests fail on API gaps, missing fixtures, or protocol handling
262262

263-
*690 individual tests — see expectations.json for full list.*
263+
*685 individual tests — see expectations.json for full list.*
264264

265265
### unsupported-module (191 entries)
266266

@@ -465,15 +465,15 @@ description: Node.js v22 test/parallel/ conformance results for the secure-exec
465465

466466
</Accordion>
467467

468-
### unsupported-api (79 entries)
468+
### unsupported-api (78 entries)
469469

470470
**Glob patterns:**
471471

472472
- `test-snapshot-*.js` — V8 snapshot/startup features not available in sandbox
473473
- `test-shadow-*.js` — ShadowRealm is experimental and not supported in sandbox
474474
- `test-compile-*.js` — V8 compile cache/code cache features not available in sandbox
475475

476-
<Accordion title="76 individual tests">
476+
<Accordion title="75 individual tests">
477477

478478
| Test | Reason |
479479
| --- | --- |
@@ -505,7 +505,6 @@ description: Node.js v22 test/parallel/ conformance results for the secure-exec
505505
| `test-fs-promises-file-handle-writeFile.js` | Readable.from is not available in the browser — stream.Readable.from() factory not implemented in sandbox stream polyfill |
506506
| `test-fs-promises-writefile.js` | Readable.from is not available in the browser — stream.Readable.from() factory not implemented; used by writeFile() Readable/iterable overload |
507507
| `test-http-addrequest-localaddress.js` | TypeError: agent.addRequest is not a function — http.Agent.addRequest() internal method not implemented in http polyfill |
508-
| `test-http-agent-getname.js` | TypeError: agent.getName() is not a function — http.Agent.getName() not implemented in http polyfill |
509508
| `test-http-header-validators.js` | TypeError: Cannot read properties of undefined (reading 'constructor') — validateHeaderName/validateHeaderValue not exported from http polyfill module |
510509
| `test-http-import-websocket.js` | ReferenceError: WebSocket is not defined — WebSocket global not available in sandbox; undici WebSocket not polyfilled as a global |
511510
| `test-http-incoming-matchKnownFields.js` | TypeError: incomingMessage._addHeaderLine is not a function — http.IncomingMessage._addHeaderLine() internal method not implemented in http polyfill |

packages/core/isolate-runtime/src/inject/require-setup.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2651,6 +2651,17 @@
26512651
return _httpModule;
26522652
}
26532653

2654+
if (name === '_http_agent') {
2655+
if (__internalModuleCache['_http_agent']) return __internalModuleCache['_http_agent'];
2656+
const httpAgentModule = {
2657+
Agent: _httpModule.Agent,
2658+
globalAgent: _httpModule.globalAgent,
2659+
};
2660+
__internalModuleCache['_http_agent'] = httpAgentModule;
2661+
_debugRequire('loaded', name, 'http-agent-special');
2662+
return httpAgentModule;
2663+
}
2664+
26542665
// Special handling for https module
26552666
if (name === 'https') {
26562667
if (__internalModuleCache['https']) return __internalModuleCache['https'];

packages/core/src/generated/isolate-runtime.ts

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)