Nodejs server crash (ERR_HTTP_HEADERS_SENT)

OS info

  • Operating System : Mac OSX 17.7.0
  • Wappler Version : 3.9.2

Problem description

Nodejs server crash (ERR_HTTP_HEADERS_SENT) on auth setCookie line in provider.js

Steps to reproduce

  1. Create a nodejs webapp.
  2. Add a couple of pages protected with an API with a Security Restrict step and a login flow
  3. Publish to a remote server (happens in Heroku and DigitalOcean at least)
  4. Go to the site on an iPhone or iPad (Chrome or safari) and login to the site (everything works fine so far)
  5. If you close the app (Chrome or safari) by doble tapping and swiping up, and then reopen the browser app, the wappler created page loads and crashes the nodejs server. I had a hard time reproducing the error but I finally did. It crashes every time.

I know that on a remote server you shouldn’t enable debugging, but node crashes either way. This is the log output:

o-1 | 2021-03-29 16:46:13 2021-03-29T22:46:13.464Z server-connect:app Executing action step identify
do-1 | 2021-03-29 16:46:13 2021-03-29T22:46:13.464Z server-connect:app options: { provider: ‘site_security’ }
do-1 | 2021-03-29 16:46:13 2021-03-29T22:46:13.512Z server-connect:auth Login with cookie: { username: 'xxx, password: ‘xxx’ }
do-1 | 2021-03-29 16:46:13 2021-03-29T22:46:13.512Z server-connect:app Executing action step restrict
do-1 | 2021-03-29 16:46:13 2021-03-29T22:46:13.512Z server-connect:app options: { provider: ‘site_security’, permissions: }
do-1 | 2021-03-29 16:46:13 2021-03-29T22:46:13.543Z server-connect:auth setCookie xxx xxx xxx (redacted for privacy)
do-1 | 2021-03-29 16:46:13 Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
do-1 | 2021-03-29 16:46:13 at new NodeError (node:internal/errors:329:5)
do-1 | 2021-03-29 16:46:13 at ServerResponse.setHeader (node:_http_outgoing:573:11)
do-1 | 2021-03-29 16:46:13 at ServerResponse.header (/workspace/node_modules/express/lib/response.js:771:10)
do-1 | 2021-03-29 16:46:13 at ServerResponse.append (/workspace/node_modules/express/lib/response.js:732:15)
do-1 | 2021-03-29 16:46:13 at ServerResponse.res.cookie (/workspace/node_modules/express/lib/response.js:857:8)
do-1 | 2021-03-29 16:46:13 at App.setCookie (/workspace/lib/core/app.js:104:18)
do-1 | 2021-03-29 16:46:13 at DatabaseProvider.login (/workspace/lib/auth/provider.js:58:30)
do-1 | 2021-03-29 16:46:13 at runMicrotasks ()
do-1 | 2021-03-29 16:46:13 at processTicksAndRejections (node:internal/process/task_queues:94:5) {
do-1 | 2021-03-29 16:46:13 code: ‘ERR_HTTP_HEADERS_SENT’
do-1 | 2021-03-29 16:46:13 }

I managed to prevent the server from crashing by commenting this line in provider.js (line 57):
this.app.setCookie(this.name + ‘.auth’, this.encrypt({ username, password }), this.cookieOpts); but by doing that, the “site_security.auth” cookie never gets created.

So, I took line 57 and putted it inside a try/catch to prevent the server from crashing. Desktop sessions keep working normally; now when replaying the steps to trigger the bug, the server doesn’t crash, but the session becomes buggy… protected pages load properly but server component calls don’t respond.

I know this report is not as detailed as it could be, but I’m close to a deadline so I didi it as complete as possible… I hope you’re able to reproduce it.

Best regards.

Hello, I’ve now been able to reproduce the error on a computer (not only on iOS).

The problem: when the .sid cookie expires (it’s max-age is “session”) and the .auth cookie is sent to the server, the ‘ERR_HTTP_HEADERS_SENT’ happens.

Steps to reproduce:

  1. login with “remember” = true to get both .sid and .auth cookies set.
  2. manually delete the .sid cookie to simulate a “Session” expiration.
  3. Reload the page and check the server logs. (the nodejs error doesn’t seem to happen in localhost for some reason. It happens on non-localhost servers like Heroku, DigitalOcean,etc.) *I’m not using docker.

I will test this, so it seems to happen when it needs to set 2 cookies in a single request. When you delete the session cookie it will try to recreate it and the security provider logs the user in with the stored cookie and also sets it cookie again to update it and there is where the error is thrown.

Thanks Patrick!

The way I see it, “persistent” sessions (Logins with “remember” = 1) are not properly working. Is this appreciation correct? or do persistent sessions do work under other circumstances.

(When I manually delete the session cookie I’m just forcing the usual expiry = session behaviour).

I first thought it would be an issue with express setting multiple cookies in a request, but after testing that seems to work fine. So I’m still not sure what could cause the issue, perhaps it is Heroku and DigitalOcean related, since it works fine on localhost.

I agree that it is rather odd that on localhost the error doesn’t happen.

But it is strange that it always happens on remote servers. It happens on Heroku Dynos, DO App Platform and GCP App Engine… Maybe testing on Docker would be a good idea. I’m on an old Mac and haven’t been able to run Docker based - wappler projects.

Hi Patrick,

I’m now having this issue. It’s happening when I’m testing the Restrict on the private pages. It should redirect to the Login page, and does on localhost, but on DigitalOcean App Platform it crashes the server. Having looked at the logs I’ve found the difference between localhost and DO is the Node version. Locally I’m running Node v12.20.1 and DO is running Node v16.5.0. The local logs show the reason as follows:

(node:18) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)

(node:18) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

This is the error I am getting in my Stage environment on DO App Platform -

[logscans-staging] [2021-07-26 20:42:17] 2021-07-26T20:42:17.431Z server-connect:router Serving templateView /events
[logscans-staging] [2021-07-26 20:42:17] 2021-07-26T20:42:17.433Z server-connect:app Executing action step connect
[logscans-staging] [2021-07-26 20:42:17] 2021-07-26T20:42:17.433Z server-connect:app options: {
[logscans-staging] [2021-07-26 20:42:17] client: ‘mysql2’,
[logscans-staging] [2021-07-26 20:42:17] connection: {
[logscans-staging] [2021-07-26 20:42:17] host: ‘db-mysql-.db.ondigitalocean.com’,
[logscans-staging] [2021-07-26 20:42:17] port: '
’,
[logscans-staging] [2021-07-26 20:42:17] user: '
’,
[logscans-staging] [2021-07-26 20:42:17] password: '
’,
[logscans-staging] [2021-07-26 20:42:17] database: ‘logscans_stage’,
[logscans-staging] [2021-07-26 20:42:17] ssl: { rejectUnauthorized: false }
[logscans-staging] [2021-07-26 20:42:17] }
[logscans-staging] [2021-07-26 20:42:17] }
[logscans-staging] [2021-07-26 20:42:17] 2021-07-26T20:42:17.436Z server-connect:app Executing action step provider
[logscans-staging] [2021-07-26 20:42:17] 2021-07-26T20:42:17.436Z server-connect:app options: {
[logscans-staging] [2021-07-26 20:42:17] secret: '
’,
[logscans-staging] [2021-07-26 20:42:17] provider: ‘Database’,
[logscans-staging] [2021-07-26 20:42:17] connection: ‘logscans’,
[logscans-staging] [2021-07-26 20:42:17] users: {
[logscans-staging] [2021-07-26 20:42:17] table: ‘user’,
[logscans-staging] [2021-07-26 20:42:17] identity: ‘user_id’,
[logscans-staging] [2021-07-26 20:42:17] username: ‘user_name’,
[logscans-staging] [2021-07-26 20:42:17] password: ‘password’
[logscans-staging] [2021-07-26 20:42:17] },
[logscans-staging] [2021-07-26 20:42:17] permissions: {}
[logscans-staging] [2021-07-26 20:42:17] }
[logscans-staging] [2021-07-26 20:42:17] 2021-07-26T20:42:17.501Z server-connect:auth Login with cookie: { username: ‘demouser@logscans.io’, password: '
****************’ }
[logscans-staging] [2021-07-26 20:42:17] 2021-07-26T20:42:17.501Z server-connect:app Executing action step restrict
[logscans-staging] [2021-07-26 20:42:17] 2021-07-26T20:42:17.502Z server-connect:app options: { provider: ‘security’, loginUrl: ‘/login’, forbiddenUrl: ‘/’ }
[logscans-staging] [2021-07-26 20:42:17] 2021-07-26T20:42:17.517Z server-connect:auth setCookie 5 demouser@logscans.io ********************
[logscans-staging] [2021-07-26 20:42:17] node:internal/errors:463
[logscans-staging] [2021-07-26 20:42:17] ErrorCaptureStackTrace(err);
[logscans-staging] [2021-07-26 20:42:17] ^
[logscans-staging] [2021-07-26 20:42:17]
[logscans-staging] [2021-07-26 20:42:17] Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
[logscans-staging] [2021-07-26 20:42:17] at new NodeError (node:internal/errors:370:5)
[logscans-staging] [2021-07-26 20:42:17] at ServerResponse.setHeader (node:_http_outgoing:573:11)
[logscans-staging] [2021-07-26 20:42:17] at ServerResponse.header (/workspace/node_modules/express/lib/response.js:771:10)
[logscans-staging] [2021-07-26 20:42:17] at ServerResponse.append (/workspace/node_modules/express/lib/response.js:732:15)
[logscans-staging] [2021-07-26 20:42:17] at ServerResponse.res.cookie (/workspace/node_modules/express/lib/response.js:857:8)
[logscans-staging] [2021-07-26 20:42:17] at App.setCookie (/workspace/lib/core/app.js:104:18)
[logscans-staging] [2021-07-26 20:42:17] at DatabaseProvider.login (/workspace/lib/auth/provider.js:57:26)
[logscans-staging] [2021-07-26 20:42:17] at processTicksAndRejections (node:internal/process/task_queues:96:5) {
[logscans-staging] [2021-07-26 20:42:17] code: ‘ERR_HTTP_HEADERS_SENT’
[logscans-staging] [2021-07-26 20:42:17] }
[logscans-staging] [2021-07-26 20:42:17] [nodemon] app crashed - waiting for file changes before starting…

If I delete the auth cookie it works correctly and redirects, without error, to the Login page.

I’m using Wappler v4.0.1.

Thanks
Allan

Hi @patrick,

I’m still getting this issue on Wappler 4.2.0. Can you give me an update on how to resolve the issue?

Thanks
Allan

How do you restrict the page? The error is from the security provider which tries to create a new auth cookie after a successful login. The login was an automatic login using your previous auth cookie. For creating a cookie it sets an header for the response, but there is already some response send to the client and then the server throws this error.

You may also test following update, it should prevent the server crash. The update will not set a cookie when there were already some headers send and create a log entry instead.

app.zip (3.8 KB) Unzip the file to lib/core.

Hi @patrick,

This worked perfectly!

Thanks so much for the prompt response and getting this fixed. Much appreciated.

Thanks
Allan

This has been fixed in Wappler 4.2.1

This topic was automatically closed after 46 hours. New replies are no longer allowed.