Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Cannot set headers after they are sent to the client" after returning h.abandon #3884

Closed
jchip opened this issue Nov 22, 2018 · 5 comments
Closed
Assignees
Labels
bug Bug or defect
Milestone

Comments

@jchip
Copy link

jchip commented Nov 22, 2018

Are you sure this is an issue with the hapi core module or are you just looking for some help?

Base on my understanding of the doc on h.abandon and the behavior I am able to repro, I believe so.

Is this a security related issue?

No

What are you trying to achieve or the steps to reproduce?

I encountered this issue while trying to use webpack-hot-middleware.

webpack-hot-middleware uses EventStream and established a keep alive connection on the request.raw.res object. In that case, it has taken over the request lifecycle. Instead of leaving Hapi hanging with an unresolved promise, I'd like to return h.abandon.

According to https://hapijs.com/api#h.abandon:

It is the developer's responsibility to write and end the response directly via request.raw.res.

I interpret that as Hapi would not try to do anything with response.

However, I get the following error after returning h.abandon:

(node:10352) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (_http_outgoing.js:470:11)
    at Object.internals.writeHead (/Users/xchen11/dev/pub/hapi-abandon/node_modules/hapi/lib/transmit.js:323:21)

I was able to repro the same behavior with this simple sample:

"use strict";

const Hapi = require("hapi");

// Create a server with a host and port
const server = Hapi.server({
  host: "localhost",
  port: 8000
});

server.ext({
  type: "onRequest",
  method: async (request, h) => {
    const res = request.raw.res;

    res.setHeader("content-type", "text/plain");
    res.end("hello");

    return h.abandon;
  }
});

// Start the server
async function start() {
  try {
    await server.start();
  } catch (err) {
    console.log(err);
    process.exit(1);
  }

  console.log("Server running at:", server.info.uri);
}

start();

What was the result you received?

(node:10352) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (_http_outgoing.js:470:11)
    at Object.internals.writeHead (/Users/xchen11/dev/pub/hapi-abandon/node_modules/hapi/lib/transmit.js:323:21)

What did you expect?

Hapi abandoning the request lifecycle and response and not attempt to set header on the response object.

Context

  • node version: 10.13.0

  • hapi version: 17.7.0

  • os: MacOS High Sierra 10.13.6

  • any other relevant information:

@yjwong
Copy link

yjwong commented Dec 12, 2018

Encountering the same problem with hapi version 17.8.1, with the exact use case.

Based on further investigation, it seems like response._invoke isn't calling this._setResponse with the h.abandon symbol, causing Transmit.send to be called here, when it should have returned earlier on.

@yjwong
Copy link

yjwong commented Dec 12, 2018

I found a hacky workaround for this problem.

server.ext('onRequest', async (request, h) => {
  try {
    await webpackDevMiddleware(request.raw.req, request.raw.res, (err: Error) => {
      // webpack-dev-middleware never calls the callback with an err object.
      // See node_modules/webpack-dev-middleware/lib/middleware.js.
    });
    (request as any)._isReplied = request.raw.res.finished;
    return request.raw.res.finished ? h.abandon : h.continue;
  } catch (err) {
    return err;
  }
});

Setting _isReplied to true causes request._reply to abort early and not send anything down the wire.

@hueniverse hueniverse self-assigned this Jan 6, 2019
@hueniverse hueniverse added the bug Bug or defect label Jan 6, 2019
@hueniverse hueniverse added this to the 18.0.0 milestone Jan 6, 2019
@jchip
Copy link
Author

jchip commented Jan 8, 2019

@hueniverse thanks for the fix. will there be a 17 release with this fix?

@hueniverse
Copy link
Contributor

v17 will be updated at some point in the next couple of months. I'm running on extremely limited resources right now and v18 is my priority. If you need it sooner, a PR backporting the fix to v17 would speed things up.

jchip pushed a commit to jchip/hapi that referenced this issue Jan 16, 2019
hueniverse added a commit that referenced this issue Mar 19, 2019
Handle signals in onRequest. Closes #3884
@lock
Copy link

lock bot commented Jan 9, 2020

This thread has been automatically locked due to inactivity. Please open a new issue for related bugs or questions following the new issue template instructions.

@lock lock bot locked as resolved and limited conversation to collaborators Jan 9, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Bug or defect
Projects
None yet
Development

No branches or pull requests

3 participants