@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.
- package/dist/entities/plugin/model/http.d.ts +12 -3
- package/dist/entities/plugin/model/http.d.ts.map +1 -1
- package/dist/entities/plugin/model/socket.d.ts +12 -7
- package/dist/entities/plugin/model/socket.d.ts.map +1 -1
- package/dist/entities/plugin/model/stream.d.ts +7 -4
- package/dist/entities/plugin/model/stream.d.ts.map +1 -1
- package/dist/features/sse/lib/create-sse.d.ts.map +1 -1
- package/dist/features/sse/lib/create-sse.js +2 -1
- package/dist/features/sse/lib/create-sse.js.map +1 -1
- package/dist/features/sse/model/types.d.ts +8 -2
- package/dist/features/sse/model/types.d.ts.map +1 -1
- package/dist/features/websocket/lib/proxy.d.ts.map +1 -1
- package/dist/features/websocket/lib/proxy.js +16 -3
- package/dist/features/websocket/lib/proxy.js.map +1 -1
- package/dist/features/websocket/model/types.d.ts +5 -1
- package/dist/features/websocket/model/types.d.ts.map +1 -1
- package/dist/shared/api/playwright/ports.d.ts +10 -1
- package/dist/shared/api/playwright/ports.d.ts.map +1 -1
- package/dist/shared/api/stream-server/stream-server.d.ts +3 -1
- package/dist/shared/api/stream-server/stream-server.d.ts.map +1 -1
- package/dist/shared/api/stream-server/stream-server.js +10 -3
- package/dist/shared/api/stream-server/stream-server.js.map +1 -1
- package/dist/widgets/stage/lib/create-stage.d.ts.map +1 -1
- package/dist/widgets/stage/lib/create-stage.js +2 -0
- package/dist/widgets/stage/lib/create-stage.js.map +1 -1
- package/dist/widgets/stage/lib/http-router.d.ts +11 -2
- package/dist/widgets/stage/lib/http-router.d.ts.map +1 -1
- package/dist/widgets/stage/lib/http-router.js +317 -76
- package/dist/widgets/stage/lib/http-router.js.map +1 -1
- package/dist/widgets/stage/lib/response-stream.d.ts +17 -3
- package/dist/widgets/stage/lib/response-stream.d.ts.map +1 -1
- package/dist/widgets/stage/lib/response-stream.js +88 -36
- package/dist/widgets/stage/lib/response-stream.js.map +1 -1
- package/dist/widgets/stage/lib/socket-router.d.ts.map +1 -1
- package/dist/widgets/stage/lib/socket-router.js +44 -15
- package/dist/widgets/stage/lib/socket-router.js.map +1 -1
- package/dist/widgets/stage/model/stage.d.ts +8 -6
- package/dist/widgets/stage/model/stage.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/entities/plugin/model/http.ts +12 -3
- package/src/entities/plugin/model/socket.ts +12 -7
- package/src/entities/plugin/model/stream.ts +7 -4
- package/src/features/sse/lib/create-sse.ts +2 -1
- package/src/features/sse/model/types.ts +8 -2
- package/src/features/websocket/lib/proxy.ts +15 -3
- package/src/features/websocket/model/types.ts +5 -1
- package/src/shared/api/playwright/ports.ts +7 -2
- package/src/shared/api/stream-server/stream-server.ts +11 -3
- package/src/widgets/stage/lib/create-stage.ts +2 -0
- package/src/widgets/stage/lib/http-router.ts +425 -91
- package/src/widgets/stage/lib/response-stream.ts +110 -37
- package/src/widgets/stage/lib/socket-router.ts +45 -16
- 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
|
-
|
|
14
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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":"
|
|
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,
|
|
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
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
|
|
79
|
-
|
|
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
|
|
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":"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,MAAM,IAAI,GAAG,CACZ,KAAyB,EACzB,QAAiD,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,EACxD,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"socket-router.js","sourceRoot":"","sources":["../../../../src/widgets/stage/lib/socket-router.ts"],"names":[],"mappings":"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"}
|
|
@@ -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
|
|
20
|
-
*
|
|
21
|
-
*
|
|
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
|
|
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":"AAAA,OAAO,KAAK,EACX,KAAK,EACL,WAAW,EACX,QAAQ,EACR,gBAAgB,EAChB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EACX,aAAa,EACb,UAAU,EACV,UAAU,EACV,cAAc,EACd,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EACX,WAAW,EACX,QAAQ,EACR,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AACtF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oCAAoC,CAAC;AAEhE,MAAM,WAAW,YAAY,CAAC,OAAO,SAAS,SAAS,aAAa,EAAE;IACrE,6EAA6E;IAC7E,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,mEAAmE;IACnE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,kDAAkD;IAClD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B
|
|
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.
|
|
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
|
|
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
|
-
/**
|
|
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
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* rule
|
|
78
|
-
*
|
|
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
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
|
|
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
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
48
|
-
|
|
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
|
-
|
|
147
|
-
response.
|
|
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,
|