@heojeongbo/wirestage 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/entities/plugin/model/http.d.ts +12 -3
  2. package/dist/entities/plugin/model/http.d.ts.map +1 -1
  3. package/dist/entities/plugin/model/socket.d.ts +12 -7
  4. package/dist/entities/plugin/model/socket.d.ts.map +1 -1
  5. package/dist/entities/plugin/model/stream.d.ts +7 -4
  6. package/dist/entities/plugin/model/stream.d.ts.map +1 -1
  7. package/dist/features/sse/lib/create-sse.d.ts.map +1 -1
  8. package/dist/features/sse/lib/create-sse.js +2 -1
  9. package/dist/features/sse/lib/create-sse.js.map +1 -1
  10. package/dist/features/sse/model/types.d.ts +8 -2
  11. package/dist/features/sse/model/types.d.ts.map +1 -1
  12. package/dist/features/websocket/lib/proxy.d.ts.map +1 -1
  13. package/dist/features/websocket/lib/proxy.js +16 -3
  14. package/dist/features/websocket/lib/proxy.js.map +1 -1
  15. package/dist/features/websocket/model/types.d.ts +5 -1
  16. package/dist/features/websocket/model/types.d.ts.map +1 -1
  17. package/dist/shared/api/playwright/ports.d.ts +10 -1
  18. package/dist/shared/api/playwright/ports.d.ts.map +1 -1
  19. package/dist/shared/api/stream-server/stream-server.d.ts +3 -1
  20. package/dist/shared/api/stream-server/stream-server.d.ts.map +1 -1
  21. package/dist/shared/api/stream-server/stream-server.js +10 -3
  22. package/dist/shared/api/stream-server/stream-server.js.map +1 -1
  23. package/dist/widgets/stage/lib/create-stage.d.ts.map +1 -1
  24. package/dist/widgets/stage/lib/create-stage.js +2 -0
  25. package/dist/widgets/stage/lib/create-stage.js.map +1 -1
  26. package/dist/widgets/stage/lib/http-router.d.ts +11 -2
  27. package/dist/widgets/stage/lib/http-router.d.ts.map +1 -1
  28. package/dist/widgets/stage/lib/http-router.js +317 -76
  29. package/dist/widgets/stage/lib/http-router.js.map +1 -1
  30. package/dist/widgets/stage/lib/response-stream.d.ts +17 -3
  31. package/dist/widgets/stage/lib/response-stream.d.ts.map +1 -1
  32. package/dist/widgets/stage/lib/response-stream.js +88 -36
  33. package/dist/widgets/stage/lib/response-stream.js.map +1 -1
  34. package/dist/widgets/stage/lib/socket-router.d.ts.map +1 -1
  35. package/dist/widgets/stage/lib/socket-router.js +44 -15
  36. package/dist/widgets/stage/lib/socket-router.js.map +1 -1
  37. package/dist/widgets/stage/model/stage.d.ts +8 -6
  38. package/dist/widgets/stage/model/stage.d.ts.map +1 -1
  39. package/package.json +1 -1
  40. package/src/entities/plugin/model/http.ts +12 -3
  41. package/src/entities/plugin/model/socket.ts +12 -7
  42. package/src/entities/plugin/model/stream.ts +7 -4
  43. package/src/features/sse/lib/create-sse.ts +2 -1
  44. package/src/features/sse/model/types.ts +8 -2
  45. package/src/features/websocket/lib/proxy.ts +15 -3
  46. package/src/features/websocket/model/types.ts +5 -1
  47. package/src/shared/api/playwright/ports.ts +7 -2
  48. package/src/shared/api/stream-server/stream-server.ts +11 -3
  49. package/src/widgets/stage/lib/create-stage.ts +2 -0
  50. package/src/widgets/stage/lib/http-router.ts +425 -91
  51. package/src/widgets/stage/lib/response-stream.ts +110 -37
  52. package/src/widgets/stage/lib/socket-router.ts +45 -16
  53. package/src/widgets/stage/model/stage.ts +8 -6
@@ -4,53 +4,105 @@ import { WirestageError } from "../../../shared/lib/errors/index.js";
4
4
  /**
5
5
  * A Node response as the stream a rule writes: the adapter between the
6
6
  * stream server's sockets and the plugin contract, which knows nothing of
7
- * Node.
7
+ * Node. `onClose` hears how the response ended before `closed` resolves, so
8
+ * whoever awaits `closed` finds the end already on record.
8
9
  */
9
- export function createResponseStream(request, response) {
10
+ export function createResponseStream(request, response, onClose) {
10
11
  let state = "open";
12
+ let failed = false;
11
13
  const closed = createDeferred();
12
- response.once("close", () =>
13
- // Not ended and not cut: the page let go of it.
14
- closed.resolve(state === "open" ? "cancelled" : state));
14
+ response.once("close", () => {
15
+ // Not ended and not cut: the page let go of it.
16
+ const end = state === "open" ? "cancelled" : state;
17
+ onClose(end, failed);
18
+ closed.resolve(end);
19
+ });
20
+ /** The stream server's own headers (CORS, `connection`), before the rule's. */
21
+ const own = Object.entries(response.getHeaders());
22
+ /** Back to those: what a rule's head set is gone, what it replaced is back. */
23
+ const restore = () => {
24
+ for (const name of response.getHeaderNames())
25
+ response.removeHeader(name);
26
+ for (const [name, value] of own)
27
+ response.setHeader(name, value);
28
+ };
29
+ /**
30
+ * Whether a write can reach the page. Node drops a write — callback and
31
+ * all — into a socket destroyed or closed for writing, which is what the
32
+ * page letting go leaves, well before the response reports its close.
33
+ */
34
+ const writable = () => state === "open" && response.socket?.writable === true;
15
35
  const requestHeaders = {};
16
36
  for (const [name, value] of Object.entries(request.headers))
17
37
  requestHeaders[name] = String(value);
18
38
  return {
19
- requestHeaders,
20
- head(status, headers = {}) {
21
- response.writeHead(status, { ...headers });
22
- },
23
- flush() {
24
- response.flushHeaders();
25
- },
26
- write(chunk) {
27
- return new Promise((resolve, reject) => {
28
- if (state !== "open" || response.destroyed) {
29
- reject(new WirestageError("CLOSED", "the response stream is closed"));
30
- return;
39
+ stream: {
40
+ requestHeaders,
41
+ head(status, headers = {}) {
42
+ // Node checks a status only as the head goes out, and refused by
43
+ // `end()` it leaves the response broken: a 500 after it has no body.
44
+ if (!Number.isInteger(status) || status < 100 || status > 999)
45
+ throw new WirestageError("INVALID_ARGUMENT", `a response status is an integer from 100 to 999, got ${status}`);
46
+ // Kept off the wire until the first write or flush, so a rule that
47
+ // fails before then can still be answered with its 500.
48
+ restore();
49
+ response.statusCode = status;
50
+ for (const [name, value] of Object.entries(headers)) {
51
+ // The stream server's to say: one response per connection.
52
+ if (name.toLowerCase() === "connection")
53
+ continue;
54
+ // Several values, one per line (`set-cookie`): Node sends a
55
+ // header line for each, and refuses a value with a newline.
56
+ const values = value.split("\n");
57
+ response.setHeader(name, values.length > 1 ? values : value);
31
58
  }
32
- response.write(toBytes(chunk), (error) => error ? reject(error) : resolve());
33
- });
34
- },
35
- async end() {
36
- if (state !== "open")
37
- return;
38
- state = "ended";
39
- // A response the page already let go of never calls back from
40
- // end(): resolve on whichever comes first.
41
- if (!response.destroyed)
42
- await new Promise((resolve) => {
43
- response.once("close", resolve);
44
- response.end(resolve);
59
+ },
60
+ flush() {
61
+ response.flushHeaders();
62
+ },
63
+ write(chunk) {
64
+ return new Promise((resolve, reject) => {
65
+ if (!writable()) {
66
+ reject(new WirestageError("CLOSED", "the response stream is closed"));
67
+ return;
68
+ }
69
+ response.write(toBytes(chunk), (error) => error ? reject(error) : resolve());
45
70
  });
71
+ },
72
+ async end() {
73
+ if (writable()) {
74
+ state = "ended";
75
+ response.end();
76
+ }
77
+ await closed.promise;
78
+ },
79
+ cut() {
80
+ if (!writable())
81
+ return;
82
+ state = "cut";
83
+ response.destroy();
84
+ },
85
+ closed: closed.promise,
46
86
  },
47
- cut() {
48
- if (state !== "open")
49
- return;
50
- state = "cut";
51
- response.destroy();
87
+ get closing() {
88
+ return state !== "open" && !closed.settled;
89
+ },
90
+ fail(body) {
91
+ if (!writable())
92
+ return false;
93
+ failed = true;
94
+ if (response.headersSent) {
95
+ state = "cut";
96
+ response.destroy();
97
+ return true;
98
+ }
99
+ state = "ended";
100
+ restore();
101
+ response
102
+ .writeHead(500, { "content-type": "text/plain; charset=utf-8" })
103
+ .end(body);
104
+ return true;
52
105
  },
53
- closed: closed.promise,
54
106
  };
55
107
  }
56
108
  //# sourceMappingURL=response-stream.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"response-stream.js","sourceRoot":"","sources":["../../../../src/widgets/stage/lib/response-stream.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAErE;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CACnC,OAAwB,EACxB,QAAwB;IAExB,IAAI,KAAK,GAA6B,MAAM,CAAC;IAC7C,MAAM,MAAM,GAAG,cAAc,EAAa,CAAC;IAC3C,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;IAC3B,gDAAgD;IAChD,MAAM,CAAC,OAAO,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CACtD,CAAC;IACF,MAAM,cAAc,GAA2B,EAAE,CAAC;IAClD,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;QAC1D,cAAc,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACtC,OAAO;QACN,cAAc;QACd,IAAI,CAAC,MAAM,EAAE,OAAO,GAAG,EAAE;YACxB,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;QAC5C,CAAC;QACD,KAAK;YACJ,QAAQ,CAAC,YAAY,EAAE,CAAC;QACzB,CAAC;QACD,KAAK,CAAC,KAAK;YACV,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC5C,IAAI,KAAK,KAAK,MAAM,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;oBAC5C,MAAM,CAAC,IAAI,cAAc,CAAC,QAAQ,EAAE,+BAA+B,CAAC,CAAC,CAAC;oBACtE,OAAO;gBACR,CAAC;gBACD,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,CACxC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CACjC,CAAC;YACH,CAAC,CAAC,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,GAAG;YACR,IAAI,KAAK,KAAK,MAAM;gBAAE,OAAO;YAC7B,KAAK,GAAG,OAAO,CAAC;YAChB,8DAA8D;YAC9D,2CAA2C;YAC3C,IAAI,CAAC,QAAQ,CAAC,SAAS;gBACtB,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;oBACnC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;oBAChC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBACvB,CAAC,CAAC,CAAC;QACL,CAAC;QACD,GAAG;YACF,IAAI,KAAK,KAAK,MAAM;gBAAE,OAAO;YAC7B,KAAK,GAAG,KAAK,CAAC;YACd,QAAQ,CAAC,OAAO,EAAE,CAAC;QACpB,CAAC;QACD,MAAM,EAAE,MAAM,CAAC,OAAO;KACtB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"response-stream.js","sourceRoot":"","sources":["../../../../src/widgets/stage/lib/response-stream.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"socket-router.d.ts","sourceRoot":"","sources":["../../../../src/widgets/stage/lib/socket-router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,KAAK,EAEX,YAAY,EAIZ,MAAM,mCAAmC,CAAC;AAI3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AAExE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACvD,MAAM,CAAC,UAAU,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,MAAM,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI,CAAC;CAC1C;AA8CD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,kBAAkB,CA8L7E"}
1
+ {"version":3,"file":"socket-router.d.ts","sourceRoot":"","sources":["../../../../src/widgets/stage/lib/socket-router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,KAAK,EAEX,YAAY,EAIZ,MAAM,mCAAmC,CAAC;AAI3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AAExE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACvD,MAAM,CAAC,UAAU,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,MAAM,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI,CAAC;CAC1C;AA8CD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,kBAAkB,CA2N7E"}
@@ -64,19 +64,38 @@ export function createSocketRouter(deps) {
64
64
  entry.active = false;
65
65
  entries.splice(entries.indexOf(entry), 1);
66
66
  };
67
- const peer = (route, close = (options) => options) => ({
68
- send: (data) => route.send(typeof data === "string" ? data : toBuffer(data)),
69
- close: (options) => route.close(close(options)),
70
- onMessage: (handler) => route.onMessage(handler),
71
- onClose: (handler) => route.onClose(handler),
72
- });
73
67
  /**
74
- * A rule that throws is a bug in the test. Say which rule failed — in the
75
- * log, on the timeline, and in the reason the page's socket closes with —
76
- * and close both ends: the page's socket would otherwise sit open with
77
- * nobody answering it, and a server the rule connected would stay
78
- * connected. 1011 is a server's "internal error"; the server's end gets
79
- * 1000, as a script may not send 1011.
68
+ * One end, as a rule drives it. Playwright calls the rule's handlers from
69
+ * its own event dispatch, where a throw is an uncaught exception and a
70
+ * rejection an unhandled one — either surfacing in whichever test runs
71
+ * next. A handler that fails fails the rule instead, as `open` does.
72
+ */
73
+ const peer = (route, onFailure, close = (options) => options) => {
74
+ const guarded = (handler) => (...args) => {
75
+ try {
76
+ // Typed `void`, yet an async function is a valid handler.
77
+ const result = handler(...args);
78
+ if (result instanceof Promise)
79
+ result.catch(onFailure);
80
+ }
81
+ catch (error) {
82
+ void onFailure(error);
83
+ }
84
+ };
85
+ return {
86
+ send: (data) => route.send(typeof data === "string" ? data : toBuffer(data)),
87
+ close: (options) => route.close(close(options)),
88
+ onMessage: (handler) => route.onMessage(guarded(handler)),
89
+ onClose: (handler) => route.onClose(guarded(handler)),
90
+ };
91
+ };
92
+ /**
93
+ * A rule that throws, in `open` or in a handler it set, is a bug in the
94
+ * test. Say which rule failed — in the log, on the timeline, and in the
95
+ * reason the page's socket closes with — and close both ends: the page's
96
+ * socket would otherwise sit open with nobody answering it, and a server
97
+ * the rule connected would stay connected. 1011 is a server's "internal
98
+ * error"; the server's end gets 1000, as a script may not send 1011.
80
99
  */
81
100
  const fail = async (rule, view, server, error) => {
82
101
  const message = error instanceof Error ? error.message : String(error);
@@ -117,16 +136,26 @@ export function createSocketRouter(deps) {
117
136
  // the rule's own replaces.
118
137
  route.onMessage(drop);
119
138
  let server;
139
+ let failed = false;
140
+ // Once per socket, for whatever fails first. Marked before failing:
141
+ // the closes that follow are reported to the rule's handlers, and a
142
+ // broken one throws again.
143
+ const failOnce = async (error) => {
144
+ if (failed)
145
+ return;
146
+ failed = true;
147
+ await fail(entry.rule, view, server, error);
148
+ };
120
149
  const view = {
121
150
  correlation: deps.correlation(),
122
151
  url,
123
152
  context: attachment.id,
124
- page: peer(route),
153
+ page: peer(route, failOnce),
125
154
  connectToServer: () => {
126
155
  if (server === undefined) {
127
156
  const upstream = route.connectToServer();
128
157
  upstream.onMessage(drop);
129
- server = peer(upstream, forServer);
158
+ server = peer(upstream, failOnce, forServer);
130
159
  }
131
160
  return server;
132
161
  },
@@ -140,7 +169,7 @@ export function createSocketRouter(deps) {
140
169
  catch (error) {
141
170
  // Never rejected to Playwright, where it would surface as an
142
171
  // unhandled rejection in whichever test runs next.
143
- await fail(entry.rule, view, server, error);
172
+ await failOnce(error);
144
173
  }
145
174
  };
146
175
  const ensureRoute = (attachment) => {
@@ -1 +1 @@
1
- {"version":3,"file":"socket-router.js","sourceRoot":"","sources":["../../../../src/widgets/stage/lib/socket-router.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"socket-router.js","sourceRoot":"","sources":["../../../../src/widgets/stage/lib/socket-router.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAErE,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAwBvE,yEAAyE;AACzE,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAW,EAAE,CAC1C,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,CAAC;AAEjD,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;AAElC;;;GAGG;AACH,MAAM,SAAS,GAAG,CAAC,MAAc,EAAU,EAAE;AAC5C,yEAAyE;AACzE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAEvE;;;;;;;GAOG;AACH,MAAM,SAAS,GAAG,CAAC,OAAqB,EAAgB,EAAE;IACzD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,IAAI,EAAE,CAAC;IACvC,IAAI,IAAI,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,OAAO,CAAC;IAC/D,OAAO;QACN,IAAI,EAAE,IAAI,KAAK,SAAS,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;QACxD,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;KAC9D,CAAC;AACH,CAAC,CAAC;AAEF,2EAA2E;AAC3E,MAAM,IAAI,GAAG,GAAS,EAAE,GAAE,CAAC,CAAC;AAE5B;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAsB;IACxD,kCAAkC;IAClC,MAAM,OAAO,GAAY,EAAE,CAAC;IAC5B,MAAM,WAAW,GAAG,IAAI,GAAG,EAAmB,CAAC;IAC/C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkC,CAAC;IACzD,IAAI,QAAQ,GAAG,KAAK,CAAC;IAErB,oDAAoD;IACpD,MAAM,KAAK,GAAG,CAAC,GAAQ,EAAqB,EAAE,CAC7C,OAAO,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE;QAC1B,IAAI,CAAC;YACJ,OAAO,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,+DAA+D;YAC/D,yBAAyB;YACzB,IAAI,CAAC,GAAG,CAAC,KAAK,CACb,2BAA2B,EAC3B,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,EACzC,KAAK,CACL,CAAC;YACF,OAAO,KAAK,CAAC;QACd,CAAC;IACF,CAAC,CAAC,CAAC;IAEJ,MAAM,MAAM,GAAG,CAAC,KAAY,EAAE,EAAE;QAC/B,IAAI,CAAC,KAAK,CAAC,MAAM;YAAE,OAAO;QAC1B,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;QACrB,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3C,CAAC,CAAC;IAEF;;;;;OAKG;IACH,MAAM,IAAI,GAAG,CACZ,KAAyB,EACzB,SAA4C,EAC5C,QAAiD,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,EACxD,EAAE;QACf,MAAM,OAAO,GACZ,CAAyB,OAAgC,EAAE,EAAE,CAC7D,CAAC,GAAG,IAAU,EAAQ,EAAE;YACvB,IAAI,CAAC;gBACJ,0DAA0D;gBAC1D,MAAM,MAAM,GAAY,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;gBACzC,IAAI,MAAM,YAAY,OAAO;oBAAE,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YACxD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,KAAK,SAAS,CAAC,KAAK,CAAC,CAAC;YACvB,CAAC;QACF,CAAC,CAAC;QACH,OAAO;YACN,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CACd,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAC7D,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC/C,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACzD,OAAO,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;SACrD,CAAC;IACH,CAAC,CAAC;IAEF;;;;;;;OAOG;IACH,MAAM,IAAI,GAAG,KAAK,EACjB,IAAgB,EAChB,IAAgB,EAChB,MAA8B,EAC9B,KAAc,EACE,EAAE;QAClB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,IAAI,CAAC,GAAG,CAAC,KAAK,CACb,oBAAoB,EACpB,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EACxC,KAAK,CACL,CAAC;QACF,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;YACpB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,IAAI,EAAE,OAAO;YACb,SAAS,EAAE,MAAM;YACjB,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ;YAC3B,MAAM,EAAE,MAAM;YACd,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,IAAI,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE;SAChE,CAAC,CAAC;QACH,MAAM,OAAO,GAAG;YACf,IAAI,EAAE,IAAI;YACV,oEAAoE;YACpE,mDAAmD;YACnD,MAAM,EAAE,SAAS,CAAC,oBAAoB,IAAI,CAAC,KAAK,aAAa,OAAO,EAAE,CAAC;SACvE,CAAC;QACF,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACvE,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,KAAK,EACrB,UAA2B,EAC3B,KAAyB,EACT,EAAE;QAClB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;QACjC,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACnE,mEAAmE;QACnE,wDAAwD;QACxD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACzB,KAAK,CAAC,eAAe,EAAE,CAAC;YACxB,OAAO;QACR,CAAC;QACD,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;QAChB,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,EAAE,KAAK,CAAC,SAAS,KAAK,CAAC;YAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5E,qEAAqE;QACrE,oEAAoE;QACpE,sEAAsE;QACtE,2BAA2B;QAC3B,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACtB,IAAI,MAA8B,CAAC;QACnC,IAAI,MAAM,GAAG,KAAK,CAAC;QACnB,oEAAoE;QACpE,oEAAoE;QACpE,2BAA2B;QAC3B,MAAM,QAAQ,GAAG,KAAK,EAAE,KAAc,EAAiB,EAAE;YACxD,IAAI,MAAM;gBAAE,OAAO;YACnB,MAAM,GAAG,IAAI,CAAC;YACd,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;QAC7C,CAAC,CAAC;QACF,MAAM,IAAI,GAAe;YACxB,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE;YAC/B,GAAG;YACH,OAAO,EAAE,UAAU,CAAC,EAAE;YACtB,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC;YAC3B,eAAe,EAAE,GAAG,EAAE;gBACrB,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;oBAC1B,MAAM,QAAQ,GAAG,KAAK,CAAC,eAAe,EAAE,CAAC;oBACzC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;oBACzB,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;gBAC9C,CAAC;gBACD,OAAO,MAAM,CAAC;YACf,CAAC;SACD,CAAC;QACF,IAAI,CAAC;YACJ,gEAAgE;YAChE,iEAAiE;YACjE,qCAAqC;YACrC,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,6DAA6D;YAC7D,mDAAmD;YACnD,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC;IACF,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG,CAAC,UAA2B,EAAiB,EAAE;QAClE,IAAI,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACxC,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC9B,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,cAAc,CAC7C,CAAC,GAAG,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,SAAS,EAChE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC,CACtC,CAAC;YACF,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YACnC,mDAAmD;YACnD,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;QACnD,CAAC;QACD,OAAO,UAAU,CAAC;IACnB,CAAC,CAAC;IAEF,OAAO;QACN,KAAK,CAAC,GAAG,CAAC,IAAI;YACb,IACC,IAAI,CAAC,KAAK,KAAK,SAAS;gBACxB,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;gBAEjD,MAAM,IAAI,cAAc,CACvB,kBAAkB,EAClB,SAAS,IAAI,CAAC,KAAK,4CAA4C,IAAI,CAAC,KAAK,EAAE,CAC3E,CAAC;YACH,MAAM,KAAK,GAAU;gBACpB,IAAI;gBACJ,UAAU,EAAE,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC;gBACvC,SAAS,EAAE,IAAI,CAAC,KAAK;gBACrB,IAAI,EAAE,CAAC;gBACP,MAAM,EAAE,IAAI;aACZ,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACpB,IAAI,CAAC;gBACJ,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC;YACtD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,MAAM,CAAC,KAAK,CAAC,CAAC;gBACd,MAAM,KAAK,CAAC;YACb,CAAC;YACD,OAAO;gBACN,IAAI,IAAI;oBACP,OAAO,KAAK,CAAC,IAAI,CAAC;gBACnB,CAAC;gBACD,IAAI,MAAM;oBACT,OAAO,KAAK,CAAC,MAAM,CAAC;gBACrB,CAAC;gBACD,KAAK,CAAC,MAAM;oBACX,MAAM,CAAC,KAAK,CAAC,CAAC;gBACf,CAAC;aACD,CAAC;QACH,CAAC;QACD,OAAO;YACN,QAAQ,GAAG,IAAI,CAAC;QACjB,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,UAAU;YACtB,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAC5B,IAAI,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;gBAAE,MAAM,WAAW,CAAC,UAAU,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,CAAC,UAAU;YAChB,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YAC/B,kEAAkE;YAClE,yDAAyD;YACzD,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC3B,CAAC;KACD,CAAC;AACH,CAAC"}
@@ -16,9 +16,11 @@ export interface StageOptions<Plugins extends readonly AnyWirePlugin[]> {
16
16
  */
17
17
  readonly pageFlushMs?: number;
18
18
  /**
19
- * How long a page may take to answer a flush or a command before a read
20
- * goes on without it — and Playwright to take the page hooks out before a
21
- * detach goes on. Default 2000 ms.
19
+ * How long a page may take to answer a flush or a command, or the
20
+ * browser to hand over a response's body, before a read goes on without
21
+ * it; how long a live stream's request may take to reach the stream
22
+ * server before the stream is given up; and how long Playwright may take
23
+ * to take the page hooks out before a detach goes on. Default 2000 ms.
22
24
  */
23
25
  readonly pageTimeoutMs?: number;
24
26
  /** TLS for live streams on https pages, and the bind host. */
@@ -58,9 +60,9 @@ export interface StageCore {
58
60
  /** The id frames use for this page. */
59
61
  pageId(page: PagePort): string;
60
62
  /**
61
- * Bring the timeline up to date: flush every page's buffer and wait for
62
- * the responses already back from the network. The reads below do it for
63
- * you.
63
+ * Bring the timeline up to date: flush every page's buffer, and wait for
64
+ * the responses already back from the network and the ends of the
65
+ * streams a stub closed. The reads below do it for you.
64
66
  */
65
67
  sync(): Promise<void>;
66
68
  frames(filter?: FrameFilter): Promise<Frame[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"stage.d.ts","sourceRoot":"","sources":["../../../../src/widgets/stage/model/stage.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"stage.d.ts","sourceRoot":"","sources":["../../../../src/widgets/stage/model/stage.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heojeongbo/wirestage",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Record, fake and fault-inject browser wire traffic in Playwright: REST, Connect, gRPC-web, gRPC, SSE, WebSocket and WebRTC data channels on one timeline.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -49,7 +49,10 @@ export type AbortErrorCode =
49
49
  export type HttpOutcome =
50
50
  /**
51
51
  * Answer with a complete response. A header may carry several values
52
- * separated by a newline (`set-cookie`).
52
+ * separated by a newline: `set-cookie` is sent as a header line each,
53
+ * any other header as one value, `, `-separated. A header Chromium
54
+ * would refuse — a name that is not a token, a CR or NUL in a value — is
55
+ * the rule's error.
53
56
  */
54
57
  | {
55
58
  readonly type: "fulfill";
@@ -61,13 +64,19 @@ export type HttpOutcome =
61
64
  * Answer with a response that stays open (server streams, SSE): `open`
62
65
  * gets the stream to write. The request is redirected to the stage's
63
66
  * local stream server, which `route.fulfill` cannot stand in for because
64
- * it takes the whole body at once.
67
+ * it takes the whole body at once. An `open` that throws is the rule's
68
+ * error, as a throwing `handle` is: the page gets the 500 while nothing
69
+ * has gone out, and a cut connection after.
65
70
  */
66
71
  | {
67
72
  readonly type: "stream";
68
73
  open(stream: ResponseStream): void | Promise<void>;
69
74
  }
70
- /** Send to the network, optionally rewritten. Lower rules are skipped. */
75
+ /**
76
+ * Send to the network, optionally rewritten. Lower rules are skipped.
77
+ * `url` may be relative to the request's, and keeps its scheme:
78
+ * Playwright rewrites a URL within its scheme only.
79
+ */
71
80
  | {
72
81
  readonly type: "continue";
73
82
  readonly url?: string;
@@ -19,6 +19,8 @@ export interface SocketPeer {
19
19
  /**
20
20
  * Messages this end sends. One handler; a later call replaces it. Until
21
21
  * one is set, what this end sends is dropped, not passed on to the other.
22
+ * A handler that throws, or rejects, fails the rule (see
23
+ * `SocketRule.open`).
22
24
  */
23
25
  onMessage(handler: (data: string | Uint8Array) => void): void;
24
26
  /**
@@ -26,7 +28,8 @@ export interface SocketPeer {
26
28
  * the page starts is reported again once `close()` completes it, so a
27
29
  * handler must expect to run twice. Setting one turns off the automatic
28
30
  * forwarding of a close to the other end: the page's socket then hangs in
29
- * CLOSING until the handler closes the page's end itself.
31
+ * CLOSING until the handler closes the page's end itself. A handler that
32
+ * throws, or rejects, fails the rule.
30
33
  */
31
34
  onClose(
32
35
  handler: (code: number | undefined, reason: string | undefined) => void,
@@ -70,12 +73,14 @@ export interface SocketRule {
70
73
  * the handlers before awaiting anything: an end drops what it sends
71
74
  * until it has one.
72
75
  *
73
- * A rule that throws, or rejects, is a bug in the test: the stage logs
74
- * it, records a `fault` frame (`meta.reason` `rule-error`, `meta.rule`
75
- * the label, `meta.error` the message) and closes the page's end with
76
- * 1011 and a reason naming the rule — and the server's end too, if the
77
- * rule connected it. The ends' `onClose` handlers hear those closes as
78
- * they hear any other.
76
+ * A rule that throws, or rejects — here, or in a handler it set on
77
+ * either end — is a bug in the test: the stage logs it, records a
78
+ * `fault` frame (`meta.reason` `rule-error`, `meta.rule` the label,
79
+ * `meta.error` the message) and closes the page's end with 1011 and a
80
+ * reason naming the rule — and the server's end too, if the rule
81
+ * connected it. Once per socket: what fails after that is not reported
82
+ * again. The ends' `onClose` handlers hear those closes as they hear any
83
+ * other.
79
84
  */
80
85
  open(socket: SocketView): void | Promise<void>;
81
86
  }
@@ -21,23 +21,26 @@ export interface ResponseStream {
21
21
  readonly requestHeaders: Readonly<Record<string, string>>;
22
22
  /**
23
23
  * Set the status and headers. They go out with the first `write`, or at
24
- * once on `flush`. Without a call the response is a bare 200.
24
+ * once on `flush`, and a later call replaces them until then. Without a
25
+ * call the response is a bare 200. A header may carry several values
26
+ * separated by a newline (`set-cookie`). `connection` is the stream
27
+ * server's: it closes every connection after one response.
25
28
  */
26
29
  head(status: number, headers?: Readonly<Record<string, string>>): void;
27
30
  /** Send the head now, before any body: an EventSource opens on it. */
28
31
  flush(): void;
29
32
  /**
30
33
  * Write a chunk. Resolves once it is handed to the socket; rejects with
31
- * `CLOSED` when the response is over.
34
+ * `CLOSED` when the response is over, the page letting go included.
32
35
  */
33
36
  write(chunk: Uint8Array | string): Promise<void>;
34
- /** End the response cleanly. Resolves once it is closed. */
37
+ /** End the response cleanly. Resolves once it is closed, as `closed` does. */
35
38
  end(): Promise<void>;
36
39
  /**
37
40
  * Reset the connection mid-response: no terminator, no trailer — what a
38
41
  * network drop looks like to the page.
39
42
  */
40
43
  cut(): void;
41
- /** Resolves when the response is over, with how. */
44
+ /** Resolves when the response is over, with how, once that is on the timeline. */
42
45
  readonly closed: Promise<StreamEnd>;
43
46
  }
@@ -52,7 +52,6 @@ export function sse(): WirePlugin<"sse", SseApi> {
52
52
  stream.head(streamOptions.status ?? 200, {
53
53
  "content-type": "text/event-stream; charset=utf-8",
54
54
  "cache-control": "no-cache",
55
- connection: "keep-alive",
56
55
  ...streamOptions.headers,
57
56
  });
58
57
  // An EventSource opens on the headers, before any event.
@@ -84,6 +83,8 @@ export function sse(): WirePlugin<"sse", SseApi> {
84
83
  end: () => stream.end(),
85
84
  async cut() {
86
85
  stream.cut();
86
+ // The stage records the end before `closed` resolves.
87
+ await stream.closed;
87
88
  },
88
89
  closed: stream.closed,
89
90
  };
@@ -31,9 +31,15 @@ export interface SseConnection {
31
31
  readonly sent: number;
32
32
  /** Write an event. A string is sent as `data`. Resolves once written. */
33
33
  send(event: SseEvent | string): Promise<void>;
34
- /** Close cleanly; EventSource will reconnect after its retry delay. */
34
+ /**
35
+ * Close cleanly; EventSource will reconnect after its retry delay.
36
+ * Resolves once the end is on the timeline.
37
+ */
35
38
  end(): Promise<void>;
36
- /** Reset the connection, as a network drop would. */
39
+ /**
40
+ * Reset the connection, as a network drop would. Resolves once the cut is
41
+ * on the timeline.
42
+ */
37
43
  cut(): Promise<void>;
38
44
  readonly closed: Promise<SseEnd>;
39
45
  }
@@ -49,6 +49,9 @@ export async function createProxy(
49
49
  label: `message on ${socket.url.pathname}`,
50
50
  });
51
51
  const closed = createDeferred<WsCloseInfo>();
52
+ // Who closed, when neither end did: the test, or the stub — the
53
+ // stage closes the socket of a rule that throws.
54
+ let closedBy: "test" | "stub" | undefined;
52
55
  log("lifecycle", "none", "page", { event: "open", url: socket.url.href });
53
56
  const forward = (
54
57
  direction: "in" | "out",
@@ -57,7 +60,16 @@ export async function createProxy(
57
60
  ) => {
58
61
  const passing = toMessage(direction, raw, context.clock.now());
59
62
  messages.push(passing);
60
- if (options.drop?.(passing)) {
63
+ let dropped: boolean | undefined;
64
+ try {
65
+ dropped = options.drop?.(passing);
66
+ } catch (error) {
67
+ // The stage fails a rule that throws and closes both ends: the
68
+ // closes they report are the stub's, not the page's or server's.
69
+ closedBy ??= "stub";
70
+ throw error;
71
+ }
72
+ if (dropped) {
61
73
  log("fault", direction, "stub", { reason: "dropped" }, raw);
62
74
  return;
63
75
  }
@@ -77,7 +89,6 @@ export async function createProxy(
77
89
  // end to pass the close on, and the end that started it to complete
78
90
  // it — the page's socket hangs in CLOSING until its end is closed.
79
91
  // Once: each of those closes is reported back through a handler.
80
- let closedBy: "test" | undefined;
81
92
  const relay =
82
93
  (from: "page" | "network") =>
83
94
  (code: number | undefined, reason: string | undefined) => {
@@ -97,7 +108,8 @@ export async function createProxy(
97
108
  };
98
109
  page.onClose(relay("page"));
99
110
  server.onClose(relay("network"));
100
- // Over from the moment the test closes it, before either end hears.
111
+ // Over from the moment the test closes it or the rule fails, before
112
+ // either end hears.
101
113
  const over = () => closed.settled || closedBy !== undefined;
102
114
  connections.push({
103
115
  url: socket.url.href,
@@ -90,7 +90,11 @@ export interface WsMock {
90
90
  export interface WsProxyOptions {
91
91
  /** Proxy this many connections, then leave the rest to the next rule. */
92
92
  readonly times?: number;
93
- /** Drop a message instead of forwarding it. Dropped messages are recorded as faults. */
93
+ /**
94
+ * Drop a message instead of forwarding it. Dropped messages are recorded
95
+ * as faults. If it throws, the stage closes both ends, the page's with
96
+ * 1011, and records a `rule-error` fault.
97
+ */
94
98
  readonly drop?: (message: WsMessage) => boolean;
95
99
  }
96
100
 
@@ -30,6 +30,8 @@ export interface ResponsePort {
30
30
  status(): number;
31
31
  headers(): HeaderMap;
32
32
  body(): Promise<Buffer>;
33
+ /** Who sent it; null for a response a route made up. */
34
+ serverAddr(): Promise<{ ipAddress: string; port: number } | null>;
33
35
  }
34
36
 
35
37
  export interface RequestPort {
@@ -44,8 +46,11 @@ export interface RequestPort {
44
46
  allHeaders(): Promise<HeaderMap>;
45
47
  postDataBuffer(): Buffer | null;
46
48
  resourceType(): string;
47
- /** Throws for requests a service worker makes; callers must guard it. */
48
- frame(): { page(): PagePort };
49
+ /**
50
+ * The frame that sent it, the same object for every request of that
51
+ * frame. Throws for requests a service worker makes; callers must guard it.
52
+ */
53
+ frame(): { url(): string; page(): PagePort };
49
54
  response(): Promise<ResponsePort | null>;
50
55
  failure(): { errorText: string } | null;
51
56
  }
@@ -37,7 +37,9 @@ export interface StreamServer {
37
37
  /**
38
38
  * Register a handler and return the URL to redirect one request to.
39
39
  * `secure` is whether the page's request was https: Playwright only lets
40
- * `route.continue` change the URL within the same scheme.
40
+ * `route.continue` change the URL within the same scheme. Every response
41
+ * closes its connection (`connection: close`); a handler leaves that
42
+ * header alone.
41
43
  */
42
44
  open(
43
45
  handler: NodeHandler,
@@ -107,6 +109,11 @@ export function createStreamServer(
107
109
  };
108
110
 
109
111
  const onRequest = (request: IncomingMessage, response: ServerResponse) => {
112
+ // One response per connection. A browser re-sends a request that dies
113
+ // before its first byte on a connection it reused — a stream cut at
114
+ // once — and its session is gone by then: the page would get a 404
115
+ // where the test cut the stream.
116
+ response.setHeader("connection", "close");
110
117
  allowCors(request, response);
111
118
  if (request.method === "OPTIONS") {
112
119
  response.writeHead(204);
@@ -143,8 +150,9 @@ export function createStreamServer(
143
150
  return;
144
151
  }
145
152
  options.log?.error("stream handler failed", { session: id }, error);
146
- if (!response.headersSent) response.writeHead(500);
147
- response.end();
153
+ // After the head, a clean end would pass for the whole response.
154
+ if (response.headersSent) response.destroy();
155
+ else response.writeHead(500).end();
148
156
  });
149
157
  };
150
158
 
@@ -93,6 +93,8 @@ export function createStage<const Plugins extends readonly AnyWirePlugin[]>(
93
93
  streams: streamServer,
94
94
  pageId,
95
95
  correlation: createSequence("x"),
96
+ clock,
97
+ timeoutMs: options.pageTimeoutMs ?? 2_000,
96
98
  });
97
99
  const sockets = createSocketRouter({
98
100
  timeline,