Skip to content

Commit 3e6ef7c

Browse files
committed
chore: updated stream response handler to match AWS's new behaviour
1 parent 2e3dbfb commit 3e6ef7c

2 files changed

Lines changed: 41 additions & 41 deletions

File tree

src/lib/runtime/streamResponse.ts

Lines changed: 17 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,6 @@
11
import { Writable } from "stream";
22
import type { WritableOptions } from "stream";
33

4-
const invalidArg = (type: string, messages?: string[], cause?: any) => {
5-
let msg = [`The "chunk" argument must be of type string or an instance of Buffer or Uint8Array. Received ${type}`];
6-
7-
if (messages) {
8-
msg = msg.concat(messages);
9-
}
10-
const err = new TypeError(msg.join("\n"));
11-
if (cause) {
12-
err.cause = cause;
13-
}
14-
return err;
15-
};
16-
174
type writeCb = (error: Error | null | undefined) => void;
185
// custom Writable interace to exclude AWS's undefined properties
196
// currently TS's Omit dont handle this special case
@@ -26,7 +13,7 @@ interface IResponseStream {
2613
* @deprecated
2714
*/
2815
destroy: Writable["destroy"];
29-
end: (chunk?: string | Buffer | Uint8Array) => void;
16+
end: Writable["end"];
3017
uncork: Writable["uncork"];
3118
write: Writable["write"];
3219
addListener: Writable["addListener"];
@@ -92,10 +79,8 @@ interface IMetadata {
9279

9380
export class ResponseStream extends Writable {
9481
#isSent = false;
95-
#isEnd = false;
9682
#__write;
9783
#invalidContentType = new TypeError('Invalid value "undefined" for header "Content-Type"');
98-
#multipleEnd = new Error("write after end");
9984
_onBeforeFirstWrite?: (write: Writable["write"]) => any;
10085
constructor(opts: Partial<WritableOptions>) {
10186
super({ highWaterMark: opts.highWaterMark, write: opts.write });
@@ -117,28 +102,24 @@ export class ResponseStream extends Writable {
117102
return writeResponse;
118103
};
119104

120-
// @ts-ignore
121-
this.end = (chunk: any) => {
122-
if (this.#isEnd) {
123-
throw this.#multipleEnd;
124-
}
125-
// simple if(chunk) will not work as 0 must throw an error
126-
const typeofChunk = typeof chunk;
127-
if (chunk !== null && typeofChunk != "undefined" && typeofChunk !== "string" && !Buffer.isBuffer(chunk) && chunk?.constructor !== Uint8Array) {
128-
throw invalidArg("an instance of Object", ["Try responseStream.write(yourObject);", "Then responseStream.end();"], chunk);
129-
}
105+
const orgEnd = this.end.bind(this);
130106

131-
if (typeofChunk != "undefined" && !this.#isSent && typeof this._onBeforeFirstWrite == "function") {
132-
this._onBeforeFirstWrite((_chunk: any) => this.#__write(_chunk));
133-
}
134-
135-
this.#isEnd = true;
136-
137-
if (typeofChunk != "undefined") {
138-
this.#__write(chunk);
107+
// @ts-ignore
108+
this.end = (...params) => {
109+
if (params.length) {
110+
const [chunk] = params;
111+
if (chunk && typeof chunk != "function") {
112+
this.write(chunk);
113+
114+
orgEnd();
115+
} else {
116+
// @ts-ignore
117+
orgEnd(...params);
118+
}
119+
} else {
120+
// @ts-ignore
121+
orgEnd(...params);
139122
}
140-
141-
this.destroy();
142123
};
143124
}
144125
#wrapeChunk = (chunk: any) => {

src/plugins/lambda/functionUrlInvoke.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,29 @@ const createStreamResponseHandler = (res: ServerResponse, foundHandler: any) =>
5757
if (serverRes.headersSent) {
5858
return originalWrite(chunk, encoding, cb);
5959
} else if (sendHeadersBefore) {
60-
sendHeaders();
61-
return originalWrite(chunk, encoding, cb);
60+
try {
61+
const chunkString = BufferedStreamResponse.codec.decode(chunk);
62+
63+
const out = JSON.parse(chunkString);
64+
if (Array.isArray(out)) {
65+
throw new Error("Invalid stream response");
66+
}
67+
68+
if (out && typeof out == "object") {
69+
sendHeaders();
70+
return originalWrite(chunk, encoding, cb);
71+
}
72+
73+
res.writeHead(200, { "Content-Type": CommonEventGenerator.contentType.octet });
74+
return res.end();
75+
} catch (error: any) {
76+
const err = new Error(
77+
`When using HttpResponseStream first chunk of .write() must be valid JSON and not be Array. Number and null will respones with 200.\n'${foundHandler.name}'`
78+
);
79+
err.cause = error.message;
80+
console.error(err);
81+
return internalServerError(res);
82+
}
6283
} else {
6384
// first bytes to be written to body
6485
if (isHttpIntegrationResponse) {
@@ -77,9 +98,7 @@ const createStreamResponseHandler = (res: ServerResponse, foundHandler: any) =>
7798
throw new Error(chunkString);
7899
}
79100
} catch (error: any) {
80-
const err = new Error(
81-
`When 'Content-Type' is 'application/json' first chunk of .write() must be parsable JSON and not be null or number.\n${foundHandler.name} => .write(${error.message})`
82-
);
101+
const err = new Error(`When 'Content-Type' is 'application/json' first chunk of .write() must be parsable JSON and not be null or number.\n'${foundHandler.name}'`);
83102
err.cause = error.message;
84103
console.error(err);
85104
return internalServerError(res);

0 commit comments

Comments
 (0)