Skip to content

Getting "stream is not readable" since 4.16.0 #693

Open
@lightpriest

Description

@lightpriest

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> &nbsp; &nbsp;at readStream (node_modules/raw-body/index.js:185:17)<br> &nbsp; &nbsp;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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions