CodeGenieApp / CodeGenieApp/serverless-express

Getting "stream is not readable" since 4.16.0

Open
#693 2 comments 12 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
5.3k
Forks
676
PR merge metrics
No merged PRs in 30d

Description

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: '\n' +
'\n' +
'\n' +
'\n' +
'Error\n' +
'\n' +
'\n' +
'

InternalServerError: stream is not readable
   at readStream (node_modules/raw-body/index.js:185:17)
   at getRawBody ... (same as below)' +
'\n' +
'\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:
```typescript
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](https://github.com/CodeGenieApp/serverless-express/blob/5944638c128e82ea41915fc724e72cb8b3189bbd/src/transport.js#L163) 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.

```typescript
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.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.