UNPKG

hapi

Version:

HTTP Server framework

581 lines (405 loc) 18.2 kB
// Load modules var Lab = require('lab'); var Request = require('request'); var Fs = require('fs'); var Http = require('http'); var Path = require('path'); var Stream = require('stream'); var Zlib = require('zlib'); var Hapi = require('../..'); // Declare internals var internals = {}; // Test shortcuts var expect = Lab.expect; var before = Lab.before; var after = Lab.after; var describe = Lab.experiment; var it = Lab.test; describe('Payload', function () { describe('raw mode', function () { it('returns an error on req socket error', function (done) { var handler = function (request) { expect(request).to.not.exist; // Must not be called }; var server = new Hapi.Server(); server.route({ method: 'POST', path: '/', config: { handler: handler } }); server.inject({ method: 'POST', url: '/', payload: 'test', simulate: { error: true } }, function (res) { expect(res.result).to.exist; expect(res.result.code).to.equal(500); done(); }); }); it('returns an error on req socket close', function (done) { var handler = function (request) { expect(request).to.not.exist; // Must not be called }; var server = new Hapi.Server(); server.route({ method: 'POST', path: '/', config: { handler: handler } }); server.inject({ method: 'POST', url: '/', payload: 'test', simulate: { close: true } }, function (res) { expect(res.result).to.exist; expect(res.result.code).to.equal(500); done(); }); }); it('returns a raw body', function (done) { var payload = '{"x":"1","y":"2","z":"3"}'; var handler = function (request) { expect(request.payload).to.not.exist; expect(request.rawPayload.toString()).to.equal(payload); request.reply(request.rawPayload); }; var server = new Hapi.Server(); server.route({ method: 'POST', path: '/', config: { handler: handler, payload: 'raw' } }); server.inject({ method: 'POST', url: '/', payload: payload }, function (res) { expect(res.result).to.exist; expect(res.result).to.equal(payload); done(); }); }); it('returns a parsed body and sets a raw body', function (done) { var payload = '{"x":"1","y":"2","z":"3"}'; var handler = function (request) { expect(request.payload).to.exist; expect(request.payload.z).to.equal('3'); expect(request.rawPayload.toString()).to.equal(payload); request.reply(request.payload); }; var server = new Hapi.Server(); server.route({ method: 'POST', path: '/', config: { handler: handler } }); server.inject({ method: 'POST', url: '/', payload: payload }, function (res) { expect(res.result).to.exist; expect(res.result.x).to.equal('1'); done(); }); }); it('doesn\'t set the request payload when the request is interrupted and its streaming', function (done) { var handler = function (request) { expect(request.payload).to.not.exist; request.reply('Success'); }; var server = new Hapi.Server('0.0.0.0', 0); server.route({ method: 'POST', path: '/', config: { handler: handler, payload: 'raw' } }); var s = new Stream.PassThrough(); server.start(function () { var options = { hostname: 'localhost', port: server.info.port, path: '/', method: 'POST' }; var iv = setInterval(function () { s.write('Hello'); }, 5); var req = Http.request(options, function (res) { }); req.on('error', function (err) { expect(err.code).to.equal('ECONNRESET'); done(); }); s.pipe(req); setTimeout(function () { req.abort(); clearInterval(iv); }, 15); }); }); it('doesn\'t set the request payload when the request is interrupted', function (done) { var handler = function (request) { expect(request.payload).to.not.exist; request.reply('Success'); }; var server = new Hapi.Server('0.0.0.0', 0); server.route({ method: 'POST', path: '/', config: { handler: handler, payload: 'raw' } }); server.start(function () { var options = { hostname: 'localhost', port: server.info.port, path: '/', method: 'POST', headers: { 'Content-Length': '10' } }; var req = Http.request(options, function (res) { }); req.write('Hello\n'); req.on('error', function (err) { expect(err.code).to.equal('ECONNRESET'); done(); }); setTimeout(function () { req.abort(); }, 15); }); }); it('returns the correct raw body when content-length is smaller than payload', function (done) { var payload = '{"x":"1","y":"2","z":"3"}'; var handler = function (request) { expect(request.payload).to.not.exist; expect(request.rawPayload.toString()).to.equal(payload); request.reply(request.rawPayload); }; var server = new Hapi.Server(); server.route({ method: 'POST', path: '/', config: { handler: handler, payload: 'raw' } }); server.inject({ method: 'POST', url: '/', payload: payload, headers: { 'Content-Length': '5' } }, function (res) { expect(res.result).to.exist; expect(res.result).to.equal(payload); done(); }); }); it('returns the correct raw body when content-length is larger than payload', function (done) { var payload = '{"x":"1","y":"2","z":"3"}'; var handler = function (request) { expect(request.payload).to.not.exist; expect(request.rawPayload.toString()).to.equal(payload); request.reply(request.rawPayload); }; var server = new Hapi.Server(); server.route({ method: 'POST', path: '/', config: { handler: handler, payload: 'raw' } }); server.inject({ method: 'POST', url: '/', payload: payload, headers: { 'Content-Length': '500' } }, function (res) { expect(res.result).to.exist; expect(res.result).to.equal(payload); done(); }); }); }); describe('stream mode', function () { var handler = function (request) { expect(request.payload).to.not.exist; request.reply('Success'); }; var server = new Hapi.Server('localhost', 0); server.route({ method: 'POST', path: '/', config: { handler: handler, payload: 'stream' } }); before(function (done) { server.start(done); }); it('doesn\'t set the request payload when streaming data in and the connection is interrupted', function (done) { var options = { hostname: 'localhost', port: server.info.port, path: '/', method: 'POST' }; var s = new Stream.PassThrough(); var iv = setInterval(function () { s.write('Hello'); }, 5); var req = Http.request(options, function (res) { expect(res.statusCode).to.equal(200); done(); }); req.on('error', function () { }); s.pipe(req); setTimeout(function () { req.abort(); clearInterval(iv); }, 25); }); }); describe('parse mode', function () { var handler = function (request) { request.reply(request.payload.key); }; var textHandler = function (request) { request.reply(request.payload + '+456'); }; var server = new Hapi.Server('localhost', 0, { timeout: { client: 50 } }); server.route({ method: 'POST', path: '/', config: { handler: handler, payload: 'parse' } }); server.route({ method: 'POST', path: '/text', config: { handler: textHandler } }); server.route({ method: '*', path: '/any', handler: handler }); before(function (done) { server.start(done); }); var TestStream = function () { Stream.Readable.call(this); }; Hapi.utils.inherits(TestStream, Stream.Readable); TestStream.prototype._read = function (size) { if (this.isDone) { return; } this.isDone = true; this.push('{ "key": "value" }'); this.push(null); }; it('sets parse mode when route methos is * and request is POST or PUT', function (done) { server.inject({ url: '/any', method: 'POST', headers: { 'Content-Type': 'application/json' }, payload: '{ "key": "09876" }' }, function (res) { expect(res.statusCode).to.equal(200); expect(res.result).to.equal('09876'); done(); }); }); it('sets the request payload with the streaming data', function (done) { var options = { uri: 'http://localhost:' + server.info.port + '/?x=1', method: 'POST', headers: { 'Content-Type': 'application/json' } }; var req = Request(options, function (err, res, body) { expect(res.statusCode).to.equal(200); expect(body).to.equal('value'); done(); }); var s = new TestStream(); s.pipe(req); }); it('times out when the request content-length is larger than payload', function (done) { var options = { hostname: 'localhost', port: server.info.port, path: '/?x=2', method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': '100' } }; var req = Http.request(options, function (res) { expect(res.statusCode).to.equal(408); done(); }); req.end('{ "key": "value" }'); }); it('resets connection when the request content-length is smaller than payload', function (done) { var options = { uri: 'http://localhost:' + server.info.port + '/?x=3', body: '{ "key": "value" }', method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': '1' } }; Request(options, function (err, res, body) { expect(err.message).to.equal('socket hang up'); done(); }); }); it('returns an error on unsupported mime type', function (done) { var options = { hostname: 'localhost', port: server.info.port, path: '/?x=4', method: 'POST', headers: { 'Content-Type': 'application/unknown', 'Content-Length': '18' } }; var req = Http.request(options, function (res) { expect(res.statusCode).to.equal(400); done(); }); req.end('{ "key": "value" }'); }); it('returns 200 on text mime type', function (done) { server.inject({ method: 'POST', url: '/text', payload: 'testing123', headers: { 'content-type': 'text/plain' } }, function (res) { expect(res.statusCode).to.equal(200); expect(res.result).to.equal('testing123+456'); done(); }); }); }); describe('unzip', function () { it('returns an error on malformed payload', function (done) { var payload = '7d8d78347h8347d58w347hd58w374d58w37h5d8w37hd4'; var handler = function () { throw new Error('never called'); }; var server = new Hapi.Server(); server.route({ method: 'POST', path: '/', config: { handler: handler } }); server.inject({ method: 'POST', url: '/', payload: payload, headers: { 'content-encoding': 'gzip' } }, function (res) { expect(res.result).to.exist; expect(res.result.code).to.equal(400); done(); }); }); it('doesn\'t return an error when the payload has the correct gzip header and gzipped payload', function (done) { var payload = '{"hi":"hello"}'; Zlib.gzip(payload, function (err, result) { var handler = function () { this.reply('Success'); }; var server = new Hapi.Server(); server.route({ method: 'POST', path: '/', config: { handler: handler } }); server.inject({ method: 'POST', url: '/', payload: result, headers: { 'content-encoding': 'gzip' } }, function (res) { expect(res.statusCode).to.equal(200); done(); }); }); }); }); describe('multi-part', function () { var invalidHandler = function (request) { expect(request).to.not.exist; // Must not be called }; var echo = function (request) { request.reply(request.payload); }; var _server = new Hapi.Server('0.0.0.0', 0); _server.route({ method: 'POST', path: '/invalid', handler: invalidHandler }); _server.route({ method: 'POST', path: '/echo', handler: echo }); var multipartPayload = '--AaB03x\r\n' + 'content-disposition: form-data; name="field1"\r\n' + '\r\n' + 'Joe Blow\r\nalmost tricked you!\r\n' + '--AaB03x\r\n' + 'content-disposition: form-data; name="field1"\r\n' + '\r\n' + 'Repeated name segment\r\n' + '--AaB03x\r\n' + 'content-disposition: form-data; name="pics"; filename="file1.txt"\r\n' + 'Content-Type: text/plain\r\n' + '\r\n' + '... contents of file1.txt ...\r\r\n' + '--AaB03x--\r\n'; it('returns an error on missing boundary in content-type header', function (done) { _server.inject({ method: 'POST', url: '/invalid', payload: multipartPayload, headers: { 'content-type': 'multipart/form-data' } }, function (res) { expect(res.result).to.exist; expect(res.result.code).to.equal(400); done(); }); }); it('returns an error on empty separator in content-type header', function (done) { _server.inject({ method: 'POST', url: '/invalid', payload: multipartPayload, headers: { 'content-type': 'multipart/form-data; boundary=' } }, function (res) { expect(res.result).to.exist; expect(res.result.code).to.equal(400); done(); }); }); it('returns parsed multipart data', function (done) { _server.inject({ method: 'POST', url: '/echo', payload: multipartPayload, headers: { 'content-type': 'multipart/form-data; boundary=AaB03x' } }, function (res) { expect(Object.keys(res.result).length).to.equal(2); expect(res.result.field1).to.exist; expect(res.result.field1.length).to.equal(2); expect(res.result.field1[1]).to.equal('Repeated name segment'); expect(res.result.pics).to.exist; done(); }); }); it('parses a file correctly', function (done) { var path = Path.join(__dirname, '../../images/hapi.png'); var stats = Fs.statSync(path); var fileStream = Fs.createReadStream(path); var fileContents = Fs.readFileSync(path, { encoding: 'binary' }); var fileHandler = function (request) { expect(request.raw.req.headers['content-type']).to.contain('multipart/form-data'); expect(request.payload['my_file'].size).to.equal(stats.size); var tmpContents = Fs.readFileSync(request.payload['my_file'].path, { encoding: 'binary' }); expect(fileContents).to.deep.equal(tmpContents); done(); }; var server = new Hapi.Server('0.0.0.0', 0); server.route({ method: 'POST', path: '/file', config: { handler: fileHandler } }); server.start(function () { var r = Request.post(server.info.uri + '/file'); var form = r.form(); form.append('my_file', fileStream); }); }); }); });