Getting "stream is not readable" since 4.16.0
lightpriest opened this issue · 0 comments
The #692 PR introduced a bug when using with express and an async middleware: the IncomingMessage/Request (stream) is marked as not readable and fails when used with body-parser (for example).
Affected version: 4.16.0
Express version: 4 (didn't test it on 5)
Body parser version: 1.20.3
For the code attached, running with 4.15.0 yields:
{
statusCode: 200,
body: 'Hello World!',
isBase64Encoded: false,
headers: {
'x-powered-by': 'Express',
'content-type': 'text/html; charset=utf-8',
'content-length': '12',
etag: 'W/"c-Lve95gjOVATpfV8EL5X4nxwjKHE"'
}
}
Running with 4.16.0 yields:
{
statusCode: 500,
body: '<!DOCTYPE html>\n' +
'<html lang="en">\n' +
'<head>\n' +
'<meta charset="utf-8">\n' +
'<title>Error</title>\n' +
'</head>\n' +
'<body>\n' +
'<pre>InternalServerError: stream is not readable<br> at readStream (node_modules/raw-body/index.js:185:17)<br> at getRawBody ... (same as below)' +
'</body>\n' +
'</html>\n',
isBase64Encoded: false,
headers: {
'x-powered-by': 'Express',
'content-security-policy': "default-src 'none'",
'x-content-type-options': 'nosniff',
'content-type': 'text/html; charset=utf-8',
'content-length': 1262
}
}
InternalServerError: stream is not readable
at readStream (node_modules/raw-body/index.js:185:17)
at getRawBody (node_modules/raw-body/index.js:116:12)
at read (node_modules/body-parser/lib/read.js:79:3)
at jsonParser (node_modules/body-parser/lib/types/json.js:138:5)
at Layer.handle [as handle_request] (node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (node_modules/express/lib/router/index.js:328:13)
at node_modules/express/lib/router/index.js:286:9
at Function.process_params (node_modules/express/lib/router/index.js:346:12)
at next (node_modules/express/lib/router/index.js:280:10)
at src/example.ts:10:3
Code to reproduce:
import serverlessExpress from '@codegenie/serverless-express';
import express from 'express';
import bodyParser from 'body-parser';
import { promisify } from 'node:util';
const app = express();
app.use(async (req, res, next) => {
// Remove this line to "remove the problem"
await promisify(setTimeout)(1000);
next();
});
app.use(bodyParser.json());
app.use((req, res) => {
res.send('Hello World!');
});
const serverlessExpressInstance = serverlessExpress({ app });
export async function handler(event: any, context: any, callback: any) {
return serverlessExpressInstance(event, context, callback);
}
handler(
{
version: '2.0',
rawPath: '/path/to/resource',
headers: {
"Content-Type": "application/json",
"Content-Length": "2",
},
requestContext: {
http: {
method: 'POST',
path: '/path/to/resource',
protocol: 'HTTP/1.1',
sourceIp: '192.168.0.1/32',
userAgent: 'agent',
},
},
body: "{}",
isBase64Encoded: false,
},
{} as any,
() => {},
)
.then(console.log.bind(console))
.catch(console.error.bind(console));
I believe this is the culprit. Express doesn't return a promise from the handle function, so if there's a middleware that starts an async operation the function will return and the stream will be marked as not readable.
await framework.sendRequest({ app, request, response })
markHttpRequestAsCompleted(request) // <-- maybe this should be moved down to after the the stream completes?
await waitForStreamComplete(response)
Small note, initially I had trouble finding this because I thought we were actually using vendia's serverless-express version. After fiddling with it for a couple of hours I realized it was actually "exporting" codegenie's version. So leaving this note here, in case someone else thinks they're using vendia's.