From 322716053f4cd2130a7d5bb30f0a48ebcd69d9d9 Mon Sep 17 00:00:00 2001 From: Farid Neshat Date: Mon, 17 Feb 2014 00:11:08 +0800 Subject: [PATCH] http: Improve HTTP Expect header handling Just following the spec: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.20 Fixes #4651 --- doc/api/http.markdown | 11 +++++++++ lib/_http_server.js | 22 +++++++++++------ test/simple/test-http-expect.js | 43 +++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 test/simple/test-http-expect.js diff --git a/doc/api/http.markdown b/doc/api/http.markdown index 6f748b75b5ef..895d0aa42fb0 100644 --- a/doc/api/http.markdown +++ b/doc/api/http.markdown @@ -115,6 +115,17 @@ request body. Note that when this event is emitted and handled, the `request` event will not be emitted. +### Event: 'checkExpectation' + +`function (request, response) { }` + +Emitted each time a request with an http Expect header is received, where the +value is not 100-continue. If this event isn't listened for, the server will +automatically respond with a 417 Expectation Failed as appropriate. + +Note that when this event is emitted and handled, the `request` event will +not be emitted. + ### Event: 'connect' `function (request, socket, head) { }` diff --git a/lib/_http_server.js b/lib/_http_server.js index 4cb0c4b2f3b9..9c58191211c5 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -492,14 +492,22 @@ function connectionListener(socket) { } if (!util.isUndefined(req.headers.expect) && - (req.httpVersionMajor == 1 && req.httpVersionMinor == 1) && - continueExpression.test(req.headers['expect'])) { - res._expect_continue = true; - if (EventEmitter.listenerCount(self, 'checkContinue') > 0) { - self.emit('checkContinue', req, res); + (req.httpVersionMajor == 1 && req.httpVersionMinor == 1)) { + if (continueExpression.test(req.headers['expect'])) { + res._expect_continue = true; + if (EventEmitter.listenerCount(self, 'checkContinue') > 0) { + self.emit('checkContinue', req, res); + } else { + res.writeContinue(); + self.emit('request', req, res); + } } else { - res.writeContinue(); - self.emit('request', req, res); + if (EventEmitter.listenerCount(self, 'checkExpectation') > 0) { + self.emit('checkExpectation', req, res); + } else { + res.writeHead(417); + res.end(); + } } } else { self.emit('request', req, res); diff --git a/test/simple/test-http-expect.js b/test/simple/test-http-expect.js new file mode 100644 index 000000000000..2ebb7eab3ec1 --- /dev/null +++ b/test/simple/test-http-expect.js @@ -0,0 +1,43 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); +var http = require('http'); +var server = http.createServer(function (req, res) { + throw new Error('This shouldn\'t have been called'); +}).listen(common.PORT); + +server.on('listening', function() { + var options = { + port: common.PORT, + headers: { 'Expect': 'meoww' } + }; + http.request(options).on('response', function(res) { + assert.equal(417, res.statusCode, 'Final status code was ' + res.statusCode + ', not 417.'); + server.on('checkExpectation', common.mustCall(function checkExpectaionHandler(req, res) { + res.end(); + })); + http.request(options).on('response', function(res) { + process.exit(); + }); + }); +});