Match Node's socket encodings, rejected writes and event loop liveness (#2599)

This commit is contained in:
Chris Tate
2026-10-30 00:09:13 -05:00
committed by GitHub
29 changed files with 983 additions and 92 deletions
@@ -1,3 +1,3 @@
After `socket.setEncoding("utf8")`, an inline `'data'` listener receives strings, as in Node, whatever type its chunk parameter declares.
After `socket.setEncoding()`, an inline `'data'` listener receives strings, as in Node, whatever type its chunk parameter declares.
A listener passed by reference, such as a named function whose parameter is typed `Buffer`, keeps its `Buffer` parameter, so a string chunk throws `TypeError: expected Uint8Array at $[0], got string`.
Declare that parameter as `string` or `string | Buffer`, or pass an inline function.
@@ -54788,6 +54788,12 @@
"tests/corpus/2986-http-static-client-socket-finish.ts",
"tests/corpus/2987-http-static-timeout-errors.ts",
"tests/corpus/2988-http-static-client-abort.ts",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/fixtures/server/cases/http-trailers/main.ts",
"tests/fixtures/server/cases/http2-connect/main.ts",
"tests/fixtures/server/cases/upgrade-both/main.ts"
@@ -56336,7 +56342,13 @@
"evidence": "compiler-dedicated:server.close",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -56419,7 +56431,13 @@
"evidence": "compiler-dedicated:server.listen",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -59448,7 +59466,13 @@
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/2672-http-request-response-callback.ts",
"tests/corpus/2689-http-server-timeout-properties.ts"
"tests/corpus/2689-http-server-timeout-properties.ts",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs"
]
},
"anchorSource": "chapter"
@@ -61706,7 +61730,13 @@
"evidence": "compiler-dedicated:server.close",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -62014,7 +62044,13 @@
"evidence": "compiler-dedicated:server.close",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -63842,7 +63878,13 @@
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/2672-http-request-response-callback.ts",
"tests/corpus/2689-http-server-timeout-properties.ts"
"tests/corpus/2689-http-server-timeout-properties.ts",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs"
]
},
"anchorSource": "chapter"
@@ -63938,7 +63980,13 @@
"evidence": "compiler-dedicated:server.close",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -64034,7 +64082,13 @@
"evidence": "compiler-dedicated:server.listen",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -72029,6 +72083,12 @@
"tests/corpus/2672-http-request-response-callback.ts",
"tests/corpus/2689-http-server-timeout-properties.ts",
"tests/corpus/5659-net-blocklist-socketaddress.ts",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/fixtures/node-types/net-blocklist.ts"
]
},
@@ -72637,7 +72697,13 @@
"evidence": "compiler-dedicated:server.address",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -72659,7 +72725,13 @@
"evidence": "compiler-dedicated:server.close",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -72717,7 +72789,13 @@
"evidence": "compiler-dedicated:server.listen",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -72739,7 +72817,13 @@
"evidence": "compiler-dedicated:server.listen",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -72761,7 +72845,13 @@
"evidence": "compiler-dedicated:server.listen",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -72783,7 +72873,13 @@
"evidence": "compiler-dedicated:server.listen",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -72805,7 +72901,13 @@
"evidence": "compiler-dedicated:server.listen",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -73148,7 +73250,13 @@
"evidence": "compiler-dedicated:socket.destroy",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -73188,7 +73296,13 @@
"evidence": "compiler-dedicated:socket.end",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -73210,7 +73324,13 @@
"evidence": "compiler-dedicated:socket.pause",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -73271,7 +73391,13 @@
"evidence": "compiler-dedicated:socket.resume",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -73293,7 +73419,13 @@
"evidence": "compiler-dedicated:socket.setEncoding",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -73387,7 +73519,13 @@
"evidence": "compiler-dedicated:socket.setTimeout",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -73466,7 +73604,13 @@
"evidence": "compiler-dedicated:socket.write",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -74110,7 +74254,13 @@
"evidence": "compiler-dedicated:net.connect",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -74132,7 +74282,13 @@
"evidence": "compiler-dedicated:net.connect",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -74154,7 +74310,13 @@
"evidence": "compiler-dedicated:net.connect",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -74176,7 +74338,13 @@
"evidence": "compiler-dedicated:net.connect",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -74198,7 +74366,13 @@
"evidence": "compiler-dedicated:net.createConnection",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -74220,7 +74394,13 @@
"evidence": "compiler-dedicated:net.createConnection",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -74242,7 +74422,13 @@
"evidence": "compiler-dedicated:net.createConnection",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -74264,7 +74450,13 @@
"evidence": "compiler-dedicated:net.createConnection",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -74286,7 +74478,13 @@
"evidence": "compiler-dedicated:net.createServer",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -74344,7 +74542,13 @@
"evidence": "compiler-dedicated:net.getDefaultAutoSelectFamilyAttemptTimeout",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -74366,7 +74570,13 @@
"evidence": "compiler-dedicated:net.setDefaultAutoSelectFamilyAttemptTimeout",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -75823,6 +76033,12 @@
"tests/corpus/5214-perf-entry-inspect.ts",
"tests/corpus/5215-perf-timing-validation.js",
"tests/corpus/6397-event-loop-metrics.ts",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/fixtures/node-types/event-loop-metrics.ts"
]
},
@@ -76126,7 +76342,13 @@
"evidence": "compiler-dedicated:net.connect",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -96067,6 +96289,12 @@
"tests/corpus/7440-tls-server-add-context.ts",
"tests/corpus/7441-tls-client-error.ts",
"tests/corpus/7442-tls-server-contexts.mjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/fixtures/node-types/tls-client-certificates.ts",
"tests/fixtures/node-types/tls-peer-certificates.ts",
"tests/fixtures/node-types/tls-server-contexts.ts",
@@ -96471,7 +96699,13 @@
"evidence": "compiler-dedicated:server.address",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -96498,7 +96732,13 @@
"evidence": "compiler-dedicated:server.close",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
@@ -96548,7 +96788,13 @@
"evidence": "compiler-dedicated:server.listen",
"tests": [
"tests/corpus/2500-net-autosel-timeout.ts",
"tests/corpus/2596-net-arg-ladders.cjs"
"tests/corpus/2596-net-arg-ladders.cjs",
"tests/corpus/net-socket-set-encoding-names.ts",
"tests/corpus/net-socket-write-rejected.cjs",
"tests/corpus/net-socket-method-values.cjs",
"tests/corpus/net-paused-socket-exit.cjs",
"tests/corpus/net-server-socket-peer-close.cjs",
"tests/corpus/net-socket-encoding-pipe.cjs"
]
},
"anchorSource": "exact"
+1 -1
View File
@@ -479,7 +479,7 @@
{ "symbols": ["setImmediate", "setInterval", "setTimeout", "clearImmediate", "clearInterval", "clearTimeout", "timersPromises.setTimeout", "timersPromises.setImmediate", "timersPromises.setInterval", "immediate.hasRef", "immediate.ref", "immediate.unref", "timeout.hasRef", "timeout.ref", "timeout.unref", "timeout.refresh"], "status": "partial", "evidence": ["tests/corpus/1804-timers-module.ts", "tests/corpus/1805-timer-callback-args.ts", "tests/corpus/2093-timers-promises.ts", "tests/corpus/2852-timers-promises-interval.ts", "tests/corpus/5830-timers-promises-signal.ts"] },
{ "symbols": ["timersPromises.scheduler.wait", "timersPromises.scheduler.yield"], "status": "partial", "evidence": ["tests/corpus/5839-timers-promises-scheduler.ts", "tests/corpus/5889-timers-scheduler-signal.ts"] },
{ "symbols": ["stream.finished", "stream.pipeline", "stream.getDefaultHighWaterMark", "readable.push", "readable.unshift", "readable.read", "readable.pause", "readable.resume", "readable.isPaused", "readable.setEncoding", "readable.pipe", "readable.unpipe", "readable.destroy", "writable.write", "writable.end", "writable.cork", "writable.uncork", "writable.setDefaultEncoding"], "status": "partial", "evidence": ["tests/corpus/1685-stream-readable-basics.ts", "tests/corpus/1688-stream-writable-basics.ts", "tests/corpus/1814-stream-pipeline.cjs", "tests/corpus/5302-stream-promises-options.ts", "tests/corpus/5303-stream-finished-callback-opts.cjs", "tests/corpus/5425-pipeline-generator-stage.ts", "tests/corpus/5428-pipeline-generator-stages.cjs", "tests/corpus/5617-pipeline-function-ends.ts", "tests/corpus/5619-pipeline-function-ends.cjs", "tests/corpus/writable-error-callbacks.ts"] },
{ "symbols": ["net.createServer", "net.connect", "net.createConnection", "net.getDefaultAutoSelectFamilyAttemptTimeout", "net.setDefaultAutoSelectFamilyAttemptTimeout", "server.address", "server.close", "server.listen", "socket.destroy", "socket.end", "socket.pause", "socket.resume", "socket.setEncoding", "socket.setTimeout", "socket.write"], "status": "partial", "evidence": ["tests/corpus/2500-net-autosel-timeout.ts", "tests/corpus/2596-net-arg-ladders.cjs"] },
{ "symbols": ["net.createServer", "net.connect", "net.createConnection", "net.getDefaultAutoSelectFamilyAttemptTimeout", "net.setDefaultAutoSelectFamilyAttemptTimeout", "server.address", "server.close", "server.listen", "socket.destroy", "socket.end", "socket.pause", "socket.resume", "socket.setEncoding", "socket.setTimeout", "socket.write"], "status": "partial", "evidence": ["tests/corpus/2500-net-autosel-timeout.ts", "tests/corpus/2596-net-arg-ladders.cjs", "tests/corpus/net-socket-set-encoding-names.ts", "tests/corpus/net-socket-write-rejected.cjs", "tests/corpus/net-socket-method-values.cjs", "tests/corpus/net-paused-socket-exit.cjs", "tests/corpus/net-server-socket-peer-close.cjs", "tests/corpus/net-socket-encoding-pipe.cjs"] },
{ "symbols": ["dgram.createSocket", "socket.address", "socket.bind", "socket.close", "socket.connect", "socket.ref", "socket.remoteAddress", "socket.send", "socket.unref"], "status": "partial", "evidence": ["tests/corpus/2597-dgram-send-ladders.cjs"] },
{ "symbols": ["net.SocketAddress", "net.BlockList"], "status": "partial", "evidence": ["tests/corpus/5659-net-blocklist-socketaddress.ts", "tests/fixtures/node-types/net-blocklist.ts"] },
{ "symbols": ["SocketAddress.parse", "BlockList.isBlockList", "blockList.addAddress", "blockList.addAddresses", "blockList.addCIDR", "blockList.addCIDRs", "blockList.addRange", "blockList.addSubnet", "blockList.check", "blockList.clear", "blockList.fromJSON", "blockList.removeAddress", "blockList.removeCIDR", "blockList.removeRange", "blockList.removeSubnet", "blockList.toJSON"], "status": "supported", "evidence": ["tests/corpus/5659-net-blocklist-socketaddress.ts", "tests/fixtures/node-types/net-blocklist.ts"] },
@@ -0,0 +1,6 @@
{
"order": [
"<repo>/tests/corpus/net-paused-socket-exit.cjs"
],
"diags": []
}
@@ -0,0 +1,6 @@
{
"order": [
"<repo>/tests/corpus/net-server-socket-peer-close.cjs"
],
"diags": []
}
@@ -0,0 +1,6 @@
{
"order": [
"<repo>/tests/corpus/net-socket-encoding-pipe.cjs"
],
"diags": []
}
@@ -0,0 +1,6 @@
{
"order": [
"<repo>/tests/corpus/net-socket-method-values.cjs"
],
"diags": []
}
@@ -0,0 +1,6 @@
{
"order": [
"<repo>/tests/corpus/net-socket-set-encoding-names.ts"
],
"diags": []
}
@@ -0,0 +1,6 @@
{
"order": [
"<repo>/tests/corpus/net-socket-write-rejected.cjs"
],
"diags": []
}
+1
View File
@@ -2308,6 +2308,7 @@ ScrDyn *scr_gen_receiver(void);
* firing site opens/closes it around a 'data' pass; scr_dyn_new_chunk
* answers a Buffer-flavored bytes box, or a string inside the window. */
void scr_dyn_chunk_enc(bool utf8);
void scr_dyn_chunk_text(ScrStr *text); /* a decoded string instead; borrowed, NULL closes */
ScrDyn *scr_dyn_new_chunk(const ScrBytes *b);
/* The JSON text of a dyn value: the JSON.stringify text (+1),
+5
View File
@@ -6430,10 +6430,15 @@ ScrDyn *scr_dyn_bytes_key_get(const ScrDyn *value, const ScrStr *key) {
* bodies — the overwhelming test shape — are exact. */
static SCR_TL bool scr_dyn_chunk_utf8;
void scr_dyn_chunk_enc(bool utf8) { scr_dyn_chunk_utf8 = utf8; }
/* A firing site that decodes with a StringDecoder (net sockets) names
* the decoded string instead (borrowed; NULL closes the window). */
static SCR_TL ScrStr *scr_dyn_chunk_str;
void scr_dyn_chunk_text(ScrStr *text) { scr_dyn_chunk_str = text; }
/* One 'data' payload as the dyn value the current window dictates:
* a Buffer-flavored bytes box, or a string inside a setEncoding window. */
ScrDyn *scr_dyn_new_chunk(const ScrBytes *b) {
if (scr_dyn_chunk_str != NULL) return scr_dyn_new_str(scr_dyn_chunk_str);
if (scr_dyn_chunk_utf8) {
ScrStr *s = scr_bytes_string(b, 0, b->len);
ScrDyn *d = scr_dyn_new_str(s);
+231 -42
View File
@@ -506,7 +506,12 @@ struct ScrNetSocket {
bool unrefd; /* socket.unref(): does not keep the loop alive */
bool connecting;
bool rd_eof; /* peer FIN seen (or read half abandoned) */
bool enc_utf8; /* setEncoding('utf8'): 'data' delivers strings */
bool fin_seen; /* peer FIN seen: 'end' fired (Node's writeAfterFIN state) */
/* setEncoding(enc): the canonical encoding (+1, NULL without one) and the
* StringDecoder's packed partial sequence (scr_strdec_*). 'data' then
* delivers the decoded strings. */
ScrStr *encoding;
double dec_pending;
bool wr_ending; /* end() called: FIN when the buffer drains */
bool wr_done; /* FIN sent (or write half dead) */
bool had_error;
@@ -604,6 +609,10 @@ struct ScrNetSocket {
* finish_ls waits for the FIN (wr_done); wcb_ls waits for the write
* buffer to drain (this surface's flush moment). */
ScrNetLs finish_ls, wcb_ls;
/* The error of the last user write when the socket's state rejected it
* (static text, NULL after an accepted write): that write's callback
* receives it on the next tick. */
const char *write_err_code, *write_err_msg;
bool finish_pending; /* wr_done seen: fire finish_ls at the next sweep */
ScrNetLs ready_ls; /* 'ready': right after the client's 'connect' listeners */
size_t bytes_read; /* socket.bytesRead: every byte delivered from the peer */
@@ -777,6 +786,7 @@ void scr_net_sock_release(ScrNetSocket *s) {
scr_als_ctx_release(s->als);
scr_als_ctx_release(s->end_ctx);
scr_str_release(s->pending_err);
scr_str_release(s->encoding);
scr_str_release(s->remote_cache);
for (size_t i = 0; i < s->dial_n; i++) scr_str_release(s->dial_ips[i]);
free(s->dial_ips);
@@ -1325,11 +1335,45 @@ static bool scr_net_sock_write_open(ScrNetSocket *w) {
* is set. A write the kernel takes at once answers true whatever its
* size. A write to an ended or closed socket answers false, and so does
* one whose send fails and closes the socket. */
/* A user write the socket's state rejects, as Node's Socket.write and
* Writable.write answer it: after the peer's FIN (Node's writeAfterFIN,
* allowHalfOpen false) EPIPE "This socket has been ended by the other
* party"; after end() ERR_STREAM_WRITE_AFTER_END; after destroy()
* ERR_STREAM_DESTROYED. The write's callback receives the error on the
* next tick. The first two destroy a socket that is not destroyed yet with
* the error, so 'error' and 'close' follow. */
static void scr_net_sock_write_rejected(ScrNetSocket *w) {
bool destroy = true;
if (w->fin_seen) {
w->write_err_code = "EPIPE";
w->write_err_msg = "This socket has been ended by the other party";
} else if (w->wr_ending) {
w->write_err_code = "ERR_STREAM_WRITE_AFTER_END";
w->write_err_msg = "write after end";
} else {
w->write_err_code = "ERR_STREAM_DESTROYED";
w->write_err_msg = "Cannot call write after a stream was destroyed";
destroy = false;
}
if (!destroy || w->fd < 0 || w->close_emitted) return;
if (!w->pending_err) {
w->pending_err = scr_str_new(w->write_err_msg, strlen(w->write_err_msg));
w->pending_code = w->write_err_code;
}
w->had_error = true;
scr_net_sock_close_fd(w);
}
static bool scr_net_sock_user_write(ScrNetSocket *s, const char *data, size_t len) {
ScrNetSocket *w = s->adopted_by != NULL ? s->adopted_by : s;
w->write_err_code = w->write_err_msg = NULL;
bool open = scr_net_sock_write_open(w);
if (!open) {
scr_net_sock_write_rejected(w);
return false;
}
scr_net_sock_write_raw(s, data, len);
if (!open || !scr_net_sock_write_open(w)) return false;
if (!scr_net_sock_write_open(w)) return false;
bool ok = w->wpending < SCR_NET_WRITE_HWM;
if (!ok) w->need_drain = true;
return ok;
@@ -1491,16 +1535,66 @@ static void scr_net_sock_flush(ScrNetSocket *s) {
}
}
/* The decoded string of the 'data' emit in progress (borrowed, NULL for a
* Buffer emit): the string-typed listener adapter answers it. */
static SCR_TL ScrStr *scr_net_chunk_text = NULL;
/* One 'data' emit to the listeners: the chunk as a Buffer, or `text` (the
* decoded string after setEncoding). Exactly one of chunk and text is
* non-NULL. */
static void scr_net_sock_fire_data(ScrNetSocket *s, ScrBytes *chunk, ScrStr *text) {
ScrBytes *empty = NULL;
if (chunk == NULL) chunk = empty = scr_bytes_new(SCR_BYTES_U8, 0);
ScrNetL *snap;
size_t nl = scr_net_ls_snapshot(&s->data_ls, &snap);
scr_dyn_this_push(s, SCR_DYNH_NET_SOCKET);
ScrStr *outer = scr_net_chunk_text;
scr_net_chunk_text = text;
scr_dyn_chunk_text(text);
for (size_t i = 0; i < nl; i++) {
if (!scr_exc_pending()) ((ScrNetDataFn)snap[i].fn)(snap[i].cb, chunk);
scr_closure_release(snap[i].cb);
}
scr_dyn_chunk_text(NULL);
scr_net_chunk_text = outer;
scr_dyn_this_pop();
free(snap);
scr_bytes_release(empty);
}
/* One decoded 'data' string after setEncoding: the pipe destination
* writes it, as Node's pipe writes the string chunk (UTF-8), and the
* 'data' listeners receive it. */
static void scr_net_sock_emit_text(ScrNetSocket *s, ScrStr *text) {
if (s->pipe_dst) {
char *owned;
const char *bytes = scr_str_utf8(text, &owned);
scr_net_sock_write_raw(s->pipe_dst, bytes, text->len);
scr_str_utf8_end(owned);
}
if (s->data_ls.n > 0) scr_net_sock_fire_data(s, NULL, text);
}
/* EOF: 'end' fires now (directly — Node's poll-phase emit), then the
* write half ends (allowHalfOpen false), and the fd closes once the FIN
* is out. 'close' waits for the sweep. */
static void scr_net_sock_eof(ScrNetSocket *s) {
s->rd_eof = true;
s->fin_seen = true;
scr_net_sock_update_read(s);
if (s->native_eof) {
s->native_eof(s->native_ctx);
if (scr_exc_pending()) return;
}
if (s->encoding != NULL && (s->data_ls.n > 0 || s->pipe_dst != NULL)) {
/* the decoder's end(): a held partial sequence is the last 'data',
* and the pipe destination receives it before its end() */
ScrStr *tail = scr_strdec_end(s->encoding, s->dec_pending);
s->dec_pending = 0;
if (tail->len > 0) scr_net_sock_emit_text(s, tail);
scr_str_release(tail);
if (scr_exc_pending()) return;
}
if (s->pipe_dst) {
/* pipe's end-the-destination default */
ScrNetSocket *dst = s->pipe_dst;
@@ -1531,22 +1625,25 @@ static bool scr_net_sock_deliver(ScrNetSocket *s, const char *buf, size_t n) {
* next chunk takes the listener path */
return true;
}
if (s->encoding != NULL && (s->pipe_dst != NULL || s->data_ls.n > 0)) {
/* the decoder answers the complete characters; a partial sequence
* waits for the next chunk, and an empty answer emits nothing */
ScrBytes *chunk = scr_bytes_new(SCR_BYTES_U8, (double)n);
chunk->is_buffer = true;
memcpy(chunk->data, buf, n);
ScrStr *text = scr_strdec_write(s->encoding, s->dec_pending, chunk);
s->dec_pending = scr_strdec_next(s->encoding, s->dec_pending, chunk);
scr_bytes_release(chunk);
if (text->len > 0) scr_net_sock_emit_text(s, text);
scr_str_release(text);
return !scr_exc_pending();
}
if (s->pipe_dst) scr_net_sock_write_raw(s->pipe_dst, buf, n);
if (s->data_ls.n > 0) {
ScrBytes *chunk = scr_bytes_new(SCR_BYTES_U8, (double)n);
chunk->is_buffer = true; /* Node delivers a Buffer */
memcpy(chunk->data, buf, n);
ScrNetL *snap;
size_t nl = scr_net_ls_snapshot(&s->data_ls, &snap);
scr_dyn_this_push(s, SCR_DYNH_NET_SOCKET);
scr_dyn_chunk_enc(s->enc_utf8);
for (size_t i = 0; i < nl; i++) {
if (!scr_exc_pending()) ((ScrNetDataFn)snap[i].fn)(snap[i].cb, chunk);
scr_closure_release(snap[i].cb);
}
scr_dyn_chunk_enc(false);
scr_dyn_this_pop();
free(snap);
scr_net_sock_fire_data(s, chunk, NULL);
scr_bytes_release(chunk);
if (scr_exc_pending()) return false;
}
@@ -2577,6 +2674,9 @@ static void scr_net_server_accept(ScrNetServer *srv) {
}
scr_dyn_this_pop();
free(snap);
/* With no consumer yet the socket still reads ahead, as Node's
* stream does from the start, so a peer's FIN or reset is seen. */
if (!srv->native_conn && !scr_exc_pending()) scr_net_sock_update_read(sock);
}
scr_net_sock_release(sock); /* registry + server-count refs remain */
if (scr_exc_pending()) return;
@@ -3625,6 +3725,11 @@ static void scr_net_sock_add_write_cb(ScrNetSocket *s, ScrClosure *cb, void *fn)
/* write(chunk, callback): fires when the write buffer drains (this
* surface's flush moment), from the sweep. */
void scr_net_sock_on_write_flush(ScrNetSocket *s, ScrClosure *cb /*moves*/) {
ScrNetSocket *w = s->adopted_by != NULL ? s->adopted_by : s;
if (w->write_err_code != NULL) {
scr_next_tick(cb); /* the rejected write's callback */
return;
}
if (s->close_emitted) {
scr_closure_release(cb);
return;
@@ -5029,7 +5134,33 @@ void scr_net_server_close_dyn(ScrNetServer *s, const ScrDyn *listener /*borrowed
scr_net_server_close(s, cb);
}
/* The rejected write's callback: callback(error) on the next tick. */
static void scr_net_write_err_fire(ScrClosure *self) {
ScrDyn *fn = (ScrDyn *)scr_box_get_ref(self->caps[0]);
ScrDyn *err = (ScrDyn *)scr_box_get_ref(self->caps[1]);
ScrDyn *args[1] = { err };
scr_dyn_release(scr_dyn_call(fn, args, 1, "callback"));
scr_dyn_release(err);
scr_dyn_release(fn);
}
void scr_net_sock_on_write_flush_dyn(ScrNetSocket *s, const ScrDyn *listener /*borrowed*/) {
ScrNetSocket *w = s->adopted_by != NULL ? s->adopted_by : s;
if (w->write_err_code != NULL) {
ScrStr *msg = scr_str_new(w->write_err_msg, strlen(w->write_err_msg));
ScrError *e = scr_error_new(SCR_ERR_ERROR, msg);
scr_str_release(msg);
scr_error_set_code(e, w->write_err_code);
ScrClosure *clo = scr_dyn_listener_closure_fn(listener, (void *)&scr_net_write_err_fire);
ScrClosure *job = scr_closure_new((void *)&scr_net_write_err_fire, 2);
job->caps[0] = scr_box_retain(clo->caps[0]);
job->caps[1] = scr_box_new_obj(scr_dyn_retain_v, scr_dyn_release_v, scr_dyn_trace_v);
scr_box_set_ref(job->caps[1], scr_dyn_from_error(e));
scr_error_release(e);
scr_closure_release(clo);
scr_next_tick(job);
return;
}
if (s->close_emitted) return;
scr_net_sock_add_write_cb(s, scr_dyn_listener_closure_fn(listener, (void *)&scr_net_dyn_fire_arg),
(void *)&scr_net_dyn_fire_arg);
@@ -5270,31 +5401,17 @@ void scr_net_server_emit_connection(ScrNetServer *srv, ScrNetSocket *sock) {
* firing server-level events ('request': Node's this === server). */
ScrNetServer *scr_net_sock_server(ScrNetSocket *s) { return s->server; }
/* socket.setEncoding(enc) — the req twin (scr_http_req_set_encoding's
* contract: utf8 flips 'data' to strings, real-but-unsupported encodings
* throw the 'not supported yet' Error, unknown names throw
* ERR_UNKNOWN_ENCODING). */
/* socket.setEncoding(enc): Node's Readable.setEncoding with a new
* StringDecoder — any Buffer encoding name in any ASCII case; an unknown
* name throws ERR_UNKNOWN_ENCODING. 'data' then delivers the decoded
* strings, and a partial sequence held at EOF is the last 'data'. */
void scr_net_sock_set_encoding(ScrNetSocket *s, ScrStr *enc /*borrowed*/) {
if ((enc->len == 4 && memcmp(enc->data, "utf8", 4) == 0) ||
(enc->len == 5 && memcmp(enc->data, "utf-8", 5) == 0)) {
s->enc_utf8 = true;
return;
}
static const char *const known[] = { "ascii", "latin1", "binary", "base64",
"base64url", "hex", "ucs2", "ucs-2", "utf16le", "utf-16le", NULL };
for (size_t i = 0; known[i]; i++) {
if (enc->len == strlen(known[i]) && memcmp(enc->data, known[i], enc->len) == 0) {
char msg[128];
int n = snprintf(msg, sizeof msg, "setEncoding('%s') is not supported yet (only 'utf8' here)",
known[i]);
scr_throw_error_msg(SCR_ERR_ERROR, msg, (size_t)n);
return;
}
}
char msg[128];
int n = snprintf(msg, sizeof msg, "Unknown encoding: %.*s",
(int)(enc->len < 64 ? enc->len : 64), enc->data);
scr_throw_error_msg_code(SCR_ERR_TYPE, msg, (size_t)n, "ERR_UNKNOWN_ENCODING");
/* Node's normalizeEncoding answers utf8 for the empty name */
ScrStr *canonical = enc->len == 0 ? scr_str_new("utf8", 4) : scr_bytes_normalize_encoding(enc);
if (canonical == NULL) return; /* ERR_UNKNOWN_ENCODING pending */
scr_str_release(s->encoding);
s->encoding = canonical;
s->dec_pending = 0;
}
/* pipe(dst): every arrived chunk forwards to dst; src EOF end()s dst
@@ -5382,6 +5499,10 @@ void scr_net_data_thunk_dyn(ScrClosure *cb, ScrBytes *chunk) {
* a string-typed listener there mistypes its own input, and the decode
* is the honest reading of the annotation. */
void scr_net_data_thunk_str(ScrClosure *cb, ScrBytes *chunk) {
if (scr_net_chunk_text != NULL) {
((void (*)(ScrClosure *, ScrStr *))cb->fn)(cb, scr_str_retain(scr_net_chunk_text));
return;
}
ScrStr *enc = scr_str_new("utf8", 4);
ScrStr *text = scr_bytes_to_str(chunk, enc);
scr_str_release(enc);
@@ -5669,6 +5790,7 @@ void scr_net_sock_transport_report(ScrNetSocket *s, const char *msg, const char
void scr_net_sock_transport_ended(ScrNetSocket *s) {
s->rd_eof = true;
s->fin_seen = true;
scr_net_fire0_this(&s->end_ls, s, SCR_DYNH_NET_SOCKET);
}
@@ -6023,12 +6145,24 @@ static void scr_net_sweep(void) {
/* ── the loop hooks (scr_async.c) ────────────────────────────────────── */
/* Loop liveness: a ref'd server or socket, or pending protocol work. */
/* Whether a socket keeps the loop alive, as libuv's active TCP handle
* does: a ref'd socket that is connecting, reading or writing, or that has
* events still to deliver. A connected socket whose reads are off (pause(),
* or a full read-ahead buffer) and that has nothing to write does not, so a
* program whose only open handle is such a socket exits, as under Node. */
static bool scr_net_sock_keeps_alive(ScrNetSocket *s) {
if (s->unrefd) return false;
if (s->fd < 0 || s->connecting || s->lookup_wait || scr_net_sock_has_work(s, true)) return true;
return s->read_armed || s->write_armed || s->wlen > s->whead || (s->wr_ending && !s->wr_done);
}
/* Loop liveness: a ref'd server, an active socket, or pending protocol
* work. */
static bool scr_net_pending(void) {
for (ScrNetServer *srv = scr_net_servers; srv; srv = srv->next)
if (!srv->unrefd) return true;
for (ScrNetSocket *sock = scr_net_socks; sock; sock = sock->next)
if (!sock->unrefd) return true;
if (scr_net_sock_keeps_alive(sock)) return true;
return scr_net_proto_pending();
}
@@ -6337,8 +6471,9 @@ static ScrDyn *scr_net_dynh_sock_invoke(void *h, ScrDyn *self, const char *metho
bool ok = chunk->kind == SCR_DYN_STR ? scr_net_sock_write_str(s, chunk->v.str)
: scr_net_sock_write_bytes(s, chunk->v.bytes);
if (argc > 1 && args[1]->kind == SCR_DYN_FUNC) {
/* write(chunk, cb): fires when the buffer drains (sweep-deferred) */
scr_net_sock_on_write_flush(s, scr_dyn_listener_closure0(args[1]));
/* write(chunk, cb): cb(null) when the buffer drains (sweep-deferred),
* or cb(error) for a rejected write */
scr_net_sock_on_write_flush_dyn(s, args[1]);
}
return scr_dyn_new_bool(ok);
}
@@ -6478,6 +6613,60 @@ static ScrDyn *scr_net_dynh_sock_invoke(void *h, ScrDyn *self, const char *metho
return NULL;
}
/* The Socket members a property read answers as functions (`typeof
* socket.setKeepAlive`, `const write = socket.write`): the names
* scr_net_dynh_sock_invoke dispatches or reports as not supported yet.
* Each name is one function value, as Node's prototype methods are, and
* calls the member on the Socket it is called on. */
static const char *const scr_net_dynh_sock_methods[] = {
"on", "once", "addListener", "write", "end", "pause", "resume", "setNoDelay",
"setKeepAlive", "destroySoon", "destroy", "setTimeout", "setEncoding", "pipe", "toString",
"ref", "unref", "address", "resetAndDestroy", "connect", "cork", "uncork", "read", "unpipe",
"off", "removeListener", "removeAllListeners", "emit", "prependListener",
"prependOnceListener", "listenerCount", "listeners",
};
#define SCR_NET_DYNH_SOCK_METHODS (sizeof scr_net_dynh_sock_methods / sizeof *scr_net_dynh_sock_methods)
static SCR_TL ScrDyn *scr_net_dynh_sock_method_fns[SCR_NET_DYNH_SOCK_METHODS];
static void scr_net_dynh_sock_methods_release(void) {
for (size_t i = 0; i < SCR_NET_DYNH_SOCK_METHODS; i++) {
scr_dyn_release(scr_net_dynh_sock_method_fns[i]);
scr_net_dynh_sock_method_fns[i] = NULL;
}
}
static ScrDyn *scr_net_dynh_sock_method_call(ScrClosure *clo, ScrDyn *const *args, size_t argc) {
const char *method = scr_net_dynh_sock_methods[(size_t)scr_box_get_f64(clo->caps[0])];
ScrDyn *self = scr_dyn_this_get();
ScrDyn *result = NULL;
if (self->kind == SCR_DYN_HANDLE && self->v.handle.tag == SCR_DYNH_NET_SOCKET)
result = scr_net_dynh_sock_invoke(self->v.handle.ptr, self, method, args, argc, method);
else
scr_net_dynh_unsupported(method, "called on a value other than a Socket");
scr_dyn_release(self);
return result;
}
static ScrDyn *scr_net_dynh_sock_method_value(const char *key) {
for (size_t i = 0; i < SCR_NET_DYNH_SOCK_METHODS; i++) {
if (strcmp(key, scr_net_dynh_sock_methods[i]) != 0) continue;
if (scr_net_dynh_sock_method_fns[i] == NULL) {
bool first = true;
for (size_t j = 0; j < SCR_NET_DYNH_SOCK_METHODS; j++)
if (scr_net_dynh_sock_method_fns[j] != NULL) first = false;
if (first) scr_atexit(scr_net_dynh_sock_methods_release);
ScrClosure *clo = scr_closure_new(NULL, 1);
clo->caps[0] = scr_box_new(SCR_BOX_F64);
scr_box_set_f64(clo->caps[0], (double)i);
scr_net_dynh_sock_method_fns[i] =
scr_dyn_new_func(clo, &scr_net_dynh_sock_method_call, 0, "(...args)",
scr_net_dynh_sock_methods[i]);
}
return scr_dyn_retain(scr_net_dynh_sock_method_fns[i]);
}
return NULL;
}
static ScrDyn *scr_net_dynh_sock_get(void *h, const char *key, size_t key_len) {
ScrNetSocket *s = (ScrNetSocket *)h;
(void)key_len;
@@ -6539,7 +6728,7 @@ static ScrDyn *scr_net_dynh_sock_get(void *h, const char *key, size_t key_len) {
}
}
}
return NULL;
return scr_net_dynh_sock_method_value(key);
}
static bool scr_net_dynh_sock_set(void *h, const char *key, size_t key_len, const ScrDyn *value) {
+24
View File
@@ -0,0 +1,24 @@
// A connected socket that is paused and has nothing to write does not
// keep the process alive: with the server and its connection unref'd,
// the process exits before the unref'd timer would fire.
"use strict";
const net = require("net");
net
.createServer((connection) => {
connection.unref();
})
.listen(0, "127.0.0.1", function () {
const client = net.connect(this.address().port, "127.0.0.1");
client.on("connect", () => {
console.log("connected");
client.pause();
});
setTimeout(() => {
console.log("timer fired");
process.exit(1);
}, 2000).unref();
})
.unref();
process.on("exit", (code) => console.log("exit", code));
@@ -0,0 +1,29 @@
// An accepted socket reads ahead before it has a consumer, as Node's
// stream does, so it sees the peer's FIN or reset: with only 'end' and
// 'close' listeners it emits 'end' and 'close' when the client ends, and
// 'error' (ECONNRESET) and 'close' when the client calls
// resetAndDestroy().
"use strict";
const net = require("net");
function run(label, finish, done) {
const server = net.createServer((socket) => {
socket.on("end", () => console.log(label, "server socket end"));
socket.on("error", (err) => console.log(label, "server socket error", err.code, err.message));
socket.on("close", (hadError) => {
console.log(label, "server socket close", hadError);
server.close(done);
});
});
server.listen(0, "127.0.0.1", () => {
const client = net.connect(server.address().port, "127.0.0.1", () => {
setTimeout(() => finish(client), 20);
});
});
}
run("end:", (client) => client.end(), () =>
run("destroy:", (client) => client.destroy(), () =>
run("reset:", (client) => client.resetAndDestroy(), () => console.log("done")),
),
);
+44
View File
@@ -0,0 +1,44 @@
// socket.pipe(destination) after socket.setEncoding(enc) writes the
// decoded strings to the destination, as Node's pipe writes each string
// chunk: the destination receives their UTF-8 bytes, a split character
// arrives whole, and a partial character held at EOF is written before the
// destination ends.
"use strict";
const net = require("net");
const source = net.createServer((socket) => {
socket.on("error", () => {});
socket.write(Buffer.from([0x61, 0xc3]));
setTimeout(() => socket.end(Buffer.from([0xa9, 0x62, 0xe2, 0x82])), 50);
});
const received = new Map();
const collector = net.createServer((socket) => {
const chunks = [];
let label = null;
socket.on("data", (chunk) => chunks.push(chunk));
socket.on("end", () => {
const all = Buffer.concat(chunks);
const newline = all.indexOf(0x0a);
label = all.subarray(0, newline).toString();
received.set(label, all.subarray(newline + 1).toString("hex"));
if (received.size === encodings.length) {
for (const name of encodings) console.log(name, received.get(name));
source.close();
collector.close();
}
});
});
const encodings = ["utf8", "hex", "base64", "latin1", "ucs2"];
source.listen(0, "127.0.0.1", () => {
collector.listen(0, "127.0.0.1", () => {
for (const encoding of encodings) {
const destination = net.connect(collector.address().port, "127.0.0.1");
destination.write(encoding + "\n");
const client = net.connect(source.address().port, "127.0.0.1");
client.setEncoding(encoding);
client.pipe(destination);
}
});
});
+41
View File
@@ -0,0 +1,41 @@
// A socket read through an untyped binding answers its methods as
// functions: typeof reports "function", every socket answers the same
// function for a name, as Node's prototype methods are, and calling the
// function with a socket as `this` runs the method on that socket.
"use strict";
const net = require("net");
const names = ["write", "end", "setKeepAlive", "setNoDelay", "setTimeout", "setEncoding", "pause", "resume", "destroy", "on", "once", "address", "ref", "unref"];
let first;
const server = net.createServer((socket) => {
console.log(names.map((name) => `${name}:${typeof socket[name]}`).join(" "));
console.log("missing:", typeof socket.notAMethod);
if (first === undefined) {
first = socket;
return;
}
console.log("same function:", names.every((name) => socket[name] === first[name]));
const write = socket.write;
write.call(socket, "via call\n");
socket.end.call(socket);
first.end();
});
server.listen(0, "127.0.0.1", () => {
const { port } = server.address();
let open = 2;
const texts = [];
for (let i = 0; i < 2; i++) {
const client = net.connect(port, "127.0.0.1");
let text = "";
client.setEncoding("utf8");
client.on("data", (chunk) => (text += chunk));
client.on("end", () => {
texts.push(JSON.stringify(text));
if (--open === 0) {
console.log("clients:", texts.sort().join(" "));
server.close();
}
});
}
});
@@ -0,0 +1,46 @@
// socket.setEncoding takes any Buffer encoding name in any ASCII case and
// decodes 'data' with a StringDecoder: a character split across two
// chunks arrives whole, a chunk that completes no character emits no
// 'data', and a partial character held at the end is the last 'data'.
// An unknown name throws ERR_UNKNOWN_ENCODING.
import * as net from "node:net";
const server = net.createServer((socket) => {
socket.on("error", () => {});
// "a", then the two bytes of "é" split across writes, then "b" and the
// first two bytes of "€", which never complete.
socket.write(Buffer.from([0x61, 0xc3]));
setTimeout(() => socket.end(Buffer.from([0xa9, 0x62, 0xe2, 0x82])), 50);
});
server.listen(0, "127.0.0.1", () => {
const { port } = server.address() as net.AddressInfo;
const encodings = ["UTF8", "Utf-8", "latin1", "BINARY", "hex", "base64", "ascii", "ucs2", "UTF16LE"];
let left = encodings.length;
const results = new Map<string, string>();
for (const encoding of encodings) {
const chunks: string[] = [];
const client = net.connect(port, "127.0.0.1");
client.setEncoding(encoding as BufferEncoding);
client.on("data", (chunk: string) => chunks.push(`${typeof chunk}:${JSON.stringify(chunk)}`));
client.on("end", () => {
results.set(encoding, chunks.join(" | "));
if (--left === 0) {
for (const name of encodings) console.log(name, results.get(name));
server.close();
}
});
}
const probe = net.connect(port, "127.0.0.1");
probe.on("error", () => {});
for (const name of ["nope", "utf-16", ""]) {
try {
probe.setEncoding(name as BufferEncoding);
console.log(JSON.stringify(name), "accepted");
} catch (error) {
const e = error as NodeJS.ErrnoException;
console.log(JSON.stringify(name), e.name, e.code, e.message);
}
}
probe.destroy();
});
@@ -0,0 +1,76 @@
// A write the socket's state rejects calls its callback with Node's error
// on the next tick: EPIPE after the peer's FIN, ERR_STREAM_WRITE_AFTER_END
// after end(), and ERR_STREAM_DESTROYED after destroy(). The first two
// destroy a socket that is still open, so 'error' and 'close' follow; a
// destroyed socket emits no 'error'.
"use strict";
const net = require("net");
const describe = (err) => (err ? `${err.constructor.name} ${err.code} ${err.message}` : String(err));
const steps = [];
const log = (line) => steps.push(line);
function afterFin(done) {
let serverSocket;
const server = net.createServer((socket) => {
serverSocket = socket;
socket.resume();
socket.on("error", (err) => log(`fin: error ${describe(err)}`));
});
server.listen(0, "127.0.0.1", () => {
const client = net.connect(server.address().port, "127.0.0.1", () => {
client.on("end", () => {
const ok = serverSocket.write("late", (err) => {
log(`fin: callback ${describe(err)}`);
server.close(done);
});
log(`fin: write answered ${ok}`);
});
client.end();
});
});
}
function afterEnd(done) {
const server = net.createServer((socket) => {
socket.on("error", (err) => log(`end: error ${describe(err)}`));
socket.on("close", (hadError) => {
log(`end: close ${hadError}`);
server.close(done);
});
socket.end("bye");
const ok = socket.write("late", (err) => log(`end: callback ${describe(err)}`));
log(`end: write answered ${ok}`);
});
server.listen(0, "127.0.0.1", () => {
const client = net.connect(server.address().port, "127.0.0.1");
client.on("error", () => {});
client.resume();
});
}
function afterDestroy(done) {
const server = net.createServer((socket) => {
socket.on("error", (err) => log(`destroy: error ${describe(err)}`));
socket.on("close", (hadError) => {
log(`destroy: close ${hadError}`);
server.close(done);
});
socket.destroy();
const ok = socket.write("late", (err) => log(`destroy: callback ${describe(err)}`));
log(`destroy: write answered ${ok}`);
});
server.listen(0, "127.0.0.1", () => {
const client = net.connect(server.address().port, "127.0.0.1");
client.on("error", () => {});
client.resume();
});
}
afterFin(() =>
afterEnd(() =>
afterDestroy(() => {
for (const step of steps) console.log(step);
}),
),
);
@@ -0,0 +1 @@
{ "plain": 2100, "san": 3200 }
@@ -0,0 +1 @@
{ "plain": 2100, "san": 3200 }
@@ -0,0 +1 @@
{ "plain": 2100, "san": 3200 }
@@ -0,0 +1 @@
{ "plain": 2100, "san": 3200 }
@@ -0,0 +1 @@
{ "plain": 2100, "san": 3200 }
@@ -0,0 +1 @@
{ "plain": 2100, "san": 3200 }
+58 -2
View File
@@ -24,13 +24,17 @@ test("runs that end by different signals, or by a signal and an exit, differ", (
expect(termination({ code: 0, signal: null })).toBe(0);
});
test("a relative --node path links the checkout's fixtures into the minimal tree", () => {
test("a relative --node path links the checkout's fixtures and common helpers into the minimal tree", () => {
const checkout = join(scratch, "node");
mkdirSync(join(checkout, "test", "parallel"), { recursive: true });
mkdirSync(join(checkout, "test", "fixtures"), { recursive: true });
mkdirSync(join(checkout, "test", "common"), { recursive: true });
writeFileSync(join(checkout, "test", "fixtures", "marker.txt"), "fixture\n");
writeFileSync(join(checkout, "test", "common", "fixtures.js"), "module.exports = {};\n");
for (const helper of ["fixtures.js", "dns.js", "internet.js"])
writeFileSync(
join(checkout, "test", "common", helper),
`module.exports = ${JSON.stringify(helper)};\n`,
);
const out = join(scratch, "out");
const result = spawnSync(
process.execPath,
@@ -49,4 +53,56 @@ test("a relative --node path links the checkout's fixtures into the minimal tree
const linked = join(out, "tree", "test", "fixtures", "marker.txt");
expect(existsSync(linked)).toBe(true);
expect(readFileSync(linked, "utf8")).toBe("fixture\n");
for (const helper of ["dns.js", "internet.js"])
expect(readFileSync(join(out, "tree", "test", "common", helper), "utf8")).toBe(
`module.exports = ${JSON.stringify(helper)};\n`,
);
expect(existsSync(join(out, "tree", "test", "common", "tmpdir.js"))).toBe(true);
});
test("concurrent files get their own test/common tmpdir", () => {
const checkout = join(scratch, "node-tmpdir");
mkdirSync(join(checkout, "test", "parallel"), { recursive: true });
mkdirSync(join(checkout, "test", "fixtures"), { recursive: true });
mkdirSync(join(checkout, "test", "common"), { recursive: true });
for (const helper of ["fixtures.js", "dns.js", "internet.js"])
writeFileSync(join(checkout, "test", "common", helper), "module.exports = {};\n");
// Each file refreshes the tmpdir, writes a marker and reads it back after
// the other file has refreshed its own: with a shared directory the
// marker is gone and the file exits with 1.
const body = `const fs = require("fs");
const tmpdir = require("../common/tmpdir");
tmpdir.refresh();
fs.writeFileSync(tmpdir.resolve("marker"), __filename);
setTimeout(() => {
process.exit(fs.existsSync(tmpdir.resolve("marker")) ? 0 : 1);
}, 500);
`;
for (const name of ["test-tmp-a.js", "test-tmp-b.js"])
writeFileSync(join(checkout, "test", "parallel", name), body);
const failingCli = join(scratch, "failing-cli.mjs");
writeFileSync(failingCli, "process.exit(1);\n");
const out = join(scratch, "out-tmpdir");
const result = spawnSync(
process.execPath,
[
join(repo, "tests/node-parallel/run.mjs"),
"--node",
checkout,
"--pattern",
"^test-tmp-",
"--out",
out,
"--cli",
failingCli,
"--jobs",
"2",
],
{ cwd: scratch, encoding: "utf8" },
);
expect(result.status, result.stderr).toBe(0);
const results = JSON.parse(readFileSync(join(out, "results.json"), "utf8")) as {
nodeExit: number;
}[];
expect(results.map((r) => r.nodeExit)).toEqual([0, 0]);
});
+2 -2
View File
@@ -12,12 +12,12 @@ Options:
- `--node`: a Node.js checkout, or its `test` directory. Use the version in `.node-version`.
- `--pattern`: a regular expression over file names in `test/parallel`.
- `--common`: `minimal` (the default) or `node`.
- `minimal` copies the selected files into `<out>/tree/test/parallel` and uses `common/index.cjs` and `common/wpt.cjs` from this directory as `test/common/index.js` and `test/common/wpt.js`, with the checkout's `test/fixtures` and `test/common/fixtures.js`. The minimal common has the helpers these files use (`mustCall`, `mustNotCall`, `skip`, `expectWarning`, `invalidArgTypeHelper`, `spawnPromisified`, `hasIntl`, `isWindows`, ...) with Node's semantics, so a verdict measures the module under test rather than Node's full `test/common`.
- `minimal` copies the selected files into `<out>/tree/test/parallel` and uses `common/index.cjs`, `common/wpt.cjs` and `common/tmpdir.cjs` from this directory as `test/common/index.js`, `test/common/wpt.js` and `test/common/tmpdir.js`, with the checkout's `test/fixtures`, `test/common/fixtures.js`, `test/common/dns.js` and `test/common/internet.js`. The minimal common has the helpers these files use (`mustCall`, `mustNotCall`, `mustSucceed`, `skip`, `expectWarning`, `expectsError`, `invalidArgTypeHelper`, `spawnPromisified`, `hasIntl`, `isWindows`, `PIPE`, ...) with Node's semantics, so a verdict measures the module under test rather than Node's full `test/common`.
- `node` builds the files in place against Node's own `test/common`.
- `--out`: where executables and results go (default `node_modules/.cache/scriptc-node-parallel`).
- `--cli`: the scriptc CLI entry (default `packages/cli/dist/main.js`).
- `--jobs`: concurrent files (default 4).
Node runs each file with its `// Flags:` line. Results are written to `<out>/results.json` and `<out>/results.md`.
Node runs each file with its `// Flags:` line. Each job runs its files with its own `TEST_THREAD_ID`, as Node's test runner does, so `test/common/tmpdir` gives concurrent files different directories. Results are written to `<out>/results.json` and `<out>/results.md`.
Before a lane relies on the minimal common, run its files under Node in `<out>/tree/test/parallel`: they should all pass there.
+50
View File
@@ -13,6 +13,7 @@ const isWindows = process.platform === "win32";
const isAIX = os.type() === "AIX";
const isIBMi = os.type() === "OS400";
const isSunOS = process.platform === "sunos";
const hasCrypto = Boolean(process.versions.openssl);
const mustCallChecks = [];
function runCallChecks(exitCode) {
@@ -60,6 +61,13 @@ function mustCallAtLeast(fn, minimum) {
return mustCallInner(fn, minimum, "minimum");
}
function mustSucceed(fn, exact) {
return mustCall(function (err, ...args) {
assert.ifError(err);
if (typeof fn === "function") return fn.apply(this, args);
}, exact);
}
function mustNotCall(msg) {
return function mustNotCall() {
assert.fail(msg || "function should not have been called");
@@ -89,6 +97,23 @@ function expectWarning(name, expected, code) {
});
}
function expectsError(validator, exact) {
return mustCall((...args) => {
if (args.length !== 1) assert.fail(`Expected one argument, got ${inspect(args)}`);
const error = args.pop();
assert.strictEqual(Object.prototype.propertyIsEnumerable.call(error, "message"), false);
assert.throws(() => {
throw error;
}, validator);
return true;
}, exact);
}
function platformTimeout(ms) {
if (isAIX || isIBMi) return (typeof ms === "bigint" ? 2n : 2) * ms;
return ms;
}
function invalidArgTypeHelper(input) {
if (input == null) return ` Received ${input}`;
if (typeof input === "function") return ` Received function ${input.name}`;
@@ -122,6 +147,7 @@ function spawnPromisified(...args) {
}
module.exports = {
hasCrypto,
hasIntl,
isAIX,
isDebug,
@@ -131,8 +157,32 @@ module.exports = {
mustCall,
mustCallAtLeast,
mustNotCall,
mustSucceed,
skip,
expectWarning,
expectsError,
invalidArgTypeHelper,
platformTimeout,
spawnPromisified,
get hasIPv6() {
const re = isWindows ? /Loopback Pseudo-Interface/ : isIBMi ? /\*LOOPBACK/ : /lo/;
const interfaces = os.networkInterfaces();
return Object.keys(interfaces).some(
(name) => re.test(name) && interfaces[name].some(({ family }) => family === "IPv6"),
);
},
inFreeBSDJail: false,
localhostIPv4: "127.0.0.1",
// The helpers below load their modules on first use, so that files of other
// lanes do not load node:net or the tmpdir helper.
get defaultAutoSelectFamilyAttemptTimeout() {
return require("net").getDefaultAutoSelectFamilyAttemptTimeout();
},
get PIPE() {
const path = require("path");
const pipePrefix = isWindows
? "\\\\.\\pipe\\"
: path.relative(process.cwd(), `${require("./tmpdir").path}/`);
return path.join(pipePrefix, `node-test.${process.pid}.sock`);
},
};
+36
View File
@@ -0,0 +1,36 @@
"use strict";
// test/common/tmpdir.js for the minimal test/common: a per-run directory
// beside test/parallel, with the semantics of Node's helper.
const fs = require("fs");
const path = require("path");
const testRoot = path.resolve(__dirname, "..");
let tmpPath = path.join(testRoot, `.tmp.${process.env.TEST_SERIAL_ID || process.env.TEST_THREAD_ID || "0"}`);
let firstRefresh = true;
function refresh() {
fs.rmSync(tmpPath, { maxRetries: 3, recursive: true, force: true });
fs.mkdirSync(tmpPath);
if (firstRefresh) {
firstRefresh = false;
process.on("exit", () => {
process.chdir(testRoot);
fs.rmSync(tmpPath, { maxRetries: 3, recursive: true, force: true });
});
}
}
function resolve(...paths) {
return path.resolve(tmpPath, ...paths);
}
module.exports = {
refresh,
resolve,
get path() {
return tmpPath;
},
set path(newPath) {
tmpPath = path.resolve(newPath);
},
};
+13 -7
View File
@@ -42,8 +42,8 @@ const out = resolve(values.out);
mkdirSync(out, { recursive: true });
// The minimal lane copies the selected files into a tree whose test/common is
// this directory's index.cjs and wpt.cjs, next to the checkout's fixtures and
// common/fixtures.js.
// this directory's index.cjs, wpt.cjs and tmpdir.cjs, next to the checkout's
// fixtures, common/fixtures.js, common/dns.js and common/internet.js.
let testRoot = nodeTest;
const files = readdirSync(join(nodeTest, "parallel"))
.filter((name) => name.endsWith(".js") && pattern.test(name))
@@ -59,6 +59,9 @@ if (values.common === "minimal") {
cpSync(join(nodeTest, "common", "fixtures.js"), join(testRoot, "common", "fixtures.js"));
cpSync(join(here, "common", "index.cjs"), join(testRoot, "common", "index.js"));
cpSync(join(here, "common", "wpt.cjs"), join(testRoot, "common", "wpt.js"));
cpSync(join(here, "common", "tmpdir.cjs"), join(testRoot, "common", "tmpdir.js"));
for (const helper of ["dns.js", "internet.js"])
cpSync(join(nodeTest, "common", helper), join(testRoot, "common", helper));
}
function run(cmd, args, opts) {
@@ -86,11 +89,14 @@ const firstCause = (text) =>
.trim()
.slice(0, 300);
async function one(file) {
// Each job runs its files with its own TEST_THREAD_ID, as Node's test runner
// does, so test/common/tmpdir gives concurrent files different directories.
async function one(file, job) {
const env = { ...process.env, TEST_THREAD_ID: String(job) };
const dir = join(testRoot, "parallel");
const source = readFileSync(join(dir, file), "utf8");
const flags = (/^\/\/ Flags: (.*)$/m.exec(source)?.[1] ?? "").split(/\s+/).filter(Boolean);
const node = await run(process.execPath, [...flags, file], { cwd: dir, timeout: 120_000 });
const node = await run(process.execPath, [...flags, file], { cwd: dir, env, timeout: 120_000 });
const exe = join(out, "bin", `${file}.exe`);
mkdirSync(join(out, "bin"), { recursive: true });
const build = await run(process.execPath, [values.cli, "build", file, "-o", exe], {
@@ -103,7 +109,7 @@ async function one(file) {
result.cause = firstCause(build.stdout + build.stderr);
return result;
}
const native = await run(exe, [], { cwd: dir, timeout: 120_000 });
const native = await run(exe, [], { cwd: dir, env, timeout: 120_000 });
result.exit = termination(native);
result.verdict = sameTermination(node, native) ? "pass" : "fail";
if (result.verdict !== "pass") result.cause = firstCause(native.stderr + native.stdout);
@@ -113,9 +119,9 @@ async function one(file) {
const results = [];
const queue = [...files];
await Promise.all(
Array.from({ length: Number(values.jobs) }, async () => {
Array.from({ length: Number(values.jobs) }, async (_, job) => {
for (let file = queue.shift(); file !== undefined; file = queue.shift()) {
const result = await one(file);
const result = await one(file, job);
results.push(result);
console.log(`${result.verdict.padEnd(8)} ${file}${result.cause ? ` ${result.cause}` : ""}`);
}