-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Description
Are you sure this is an issue with hapi or are you just looking for some help?
Everything points to this being an error in hapi (or perhaps Node streams), but I've not yet been able to reproduce it outside of Heroku's environment.
What are you trying to achieve or the steps to reproduce?
One of our clients came to us experiencing many H18 errors with their Hapi app in production. They kindly created a minimal example of the issue, which is a Hapi hello-world server: https://github.com/kimmobrunfeldt/heroku-hapi-503-bug You can deploy this to Heroku to try it yourself with one click.
To trigger the error you can curl the service with a small data payload:
curl -d'a' https://heroku-hapi-503-bug.herokuapp.com
What was the result you received?
About 30% of the time this will work as expected, and about 70% of the time this will result in a 503 error. Weird.
Concerned that this was a bug in our router I escalated this to our Platform team. One thing that immediately jumped out was that attaching strace to the Node instance immediately resolved the issue producing only 404s. Tricky.
tcpdump
So we ran tcpdump on the response generated by the application.
Here's what's happening during a 503:
Compared to what happens during a 404:
The server sends a RST packet instead of a FIN packet to close the response
Full packet capture is available here: 462013.pcap.zip
streams
Playing around with the internals, I ended up making the problem go away by modifying lib/transmit.js https://github.com/hapijs/hapi/blob/master/lib/transmit.js#L298 by adding an additional stream that did nothing more than pipe the data along:
const through = require('through');
const donothing = through(
function (data) {
this.emit('data', data);
},
function () {
this.emit('end');
}
);
const tap = response._tap();
const preview = (tap ? source.pipe(tap) : source);
const compressed = (compressor ? preview.pipe(compressor) : preview);
const ranged = (ranger ? compressed.pipe(ranger) : compressed);
ranged.pipe(donothing).pipe(request.raw.res);
Though I'm unsure of what the implications of that are.
git bisect
Finally, I realized that even if we don't understand the underlying cause, we have a reproducible test case. I tested an older version of Hapi that did not suffer from the same issue and created a git bisect script to narrow down when this might have been introduced: https://github.com/jmorrell/heroku-hapi-H18-bisect
This points to this commit as the one that introduced this odd behavior.
98d34046392006540b64a799b6ca58150d08df2c is the first bad commit
commit 98d34046392006540b64a799b6ca58150d08df2c
Author: Eran Hammer <[email protected]>
Date: Mon Oct 26 00:49:23 2015 -0700
Skip most lifecycle on not found and bad path. Closes #2867
:100755 100755 1cd1680739ce6bf95b76abe496e26534d094beaa 2decb33f2a69bb7d22862c6c5b54fef83c437e21 M API.md
:040000 040000 87075a6a7a8ab1d85b4b4d259ce14a2b16870dab 481e5da93ba66490cc62ca1fc242d6f153daa701 M lib
:040000 040000 84db0ba536bb4f4e8290b5c29ce88d7f6ce1c1c7 00ed8d03c69ce0759262669c81c882dc1c20d890 M test
bisect run success
Context
- node version: 7.9.0
- hapi version: Many affected
- os:
~ $ uname -a
Linux 3d243743-fcf2-4103-8c24-72657c71ef36 3.13.0-112-generic #159-Ubuntu SMP Fri Mar 3 15:26:07 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

