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 · 4 comments

Comments

Projects
None yet
3 participants
@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

This comment has been minimized.

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

This comment has been minimized.

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 label Jan 6, 2019

@hueniverse hueniverse added this to the 18.0.0 milestone Jan 6, 2019

@hueniverse hueniverse closed this in 0c592b7 Jan 6, 2019

@jchip

This comment has been minimized.

Copy link

jchip commented Jan 8, 2019

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

@hueniverse

This comment has been minimized.

Copy link
Member

hueniverse commented Jan 10, 2019

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment