/* eslint-env mocha */ var assert = require('assert') var tls = require('tls') var net = require('net') var https = require('https') var transport = require('spdy-transport') var util = require('util') var fixtures = require('./fixtures') var spdy = require('../') describe('SPDY Server', function () { fixtures.everyConfig(function (protocol, alpn, version, plain) { var server var client beforeEach(function (done) { server = spdy.createServer(Object.assign({ spdy: { 'x-forwarded-for': true, plain: plain } }, fixtures.keys)) server.listen(fixtures.port, function () { var socket = (plain ? net : tls).connect({ rejectUnauthorized: false, port: fixtures.port, ALPNProtocols: [alpn] }, function () { client = transport.connection.create(socket, { protocol: protocol, isServer: false }) client.start(version) done() }) }) }) afterEach(function (done) { client.socket.destroy() server.close(done) }) it('should process GET request', function (done) { var stream = client.request({ method: 'GET', path: '/get', headers: { a: 'b' } }, function (err) { assert(!err) stream.on('error', (err) => { done(err) }) stream.on('response', function (status, headers) { assert.strictEqual(status, 200) assert.strictEqual(headers.ok, 'yes') fixtures.expectData(stream, 'response', done) }) stream.end() }) server.on('request', function (req, res) { assert.strictEqual(req.isSpdy, res.isSpdy) assert.strictEqual(req.spdyVersion, res.spdyVersion) assert(req.isSpdy) if (!plain) { assert(req.socket.encrypted) assert(req.socket.getPeerCertificate()) } // Auto-detection if (version === 3.1) { assert(req.spdyVersion >= 3 && req.spdyVersion <= 3.1) } else { assert.strictEqual(req.spdyVersion, version) } assert(req.spdyStream) assert(res.spdyStream) assert.strictEqual(req.method, 'GET') assert.strictEqual(req.url, '/get') assert.deepStrictEqual(req.headers, { a: 'b', host: 'localhost' }) req.on('end', function () { res.writeHead(200, { ok: 'yes' }) res.end('response') assert(res.finished, 'res.finished should be set') }) req.resume() }) }) it('should process POST request', function (done) { var stream = client.request({ method: 'POST', path: '/post' }, function (err) { assert(!err) stream.on('response', function (status, headers) { assert.strictEqual(status, 200) assert.strictEqual(headers.ok, 'yes') fixtures.expectData(stream, 'response', next) }) stream.end('request') }) server.on('request', function (req, res) { assert.strictEqual(req.method, 'POST') assert.strictEqual(req.url, '/post') res.writeHead(200, { ok: 'yes' }) res.end('response') fixtures.expectData(req, 'request', next) }) var waiting = 2 function next () { if (--waiting === 0) { return done() } } }) it('should process expect-continue request', function (done) { var stream = client.request({ method: 'GET', path: '/get', headers: { Expect: '100-continue' } }, function (err) { assert(!err) stream.on('response', function (status, headers) { assert.strictEqual(status, 100) fixtures.expectData(stream, 'response', done) }) stream.end() }) server.on('request', function (req, res) { req.on('end', function () { res.end('response') }) req.resume() }) }) it('should emit `checkContinue` request', function (done) { var stream = client.request({ method: 'GET', path: '/get', headers: { Expect: '100-continue' } }, function (err) { assert(!err) stream.on('response', function (status, headers) { assert.strictEqual(status, 100) fixtures.expectData(stream, 'response', done) }) stream.end() }) server.on('checkContinue', function (req, res) { req.on('end', function () { res.writeContinue() res.end('response') }) req.resume() }) }) it('should send PUSH_PROMISE', function (done) { var stream = client.request({ method: 'POST', path: '/page' }, function (err) { assert(!err) stream.on('pushPromise', function (push) { assert.strictEqual(push.path, '/push') assert.strictEqual(push.headers.yes, 'push') fixtures.expectData(push, 'push', next) fixtures.expectData(stream, 'response', next) }) stream.end('request') }) server.on('request', function (req, res) { assert.strictEqual(req.method, 'POST') assert.strictEqual(req.url, '/page') res.writeHead(200, { ok: 'yes' }) var push = res.push('/push', { request: { yes: 'push' } }) push.end('push') res.end('response') fixtures.expectData(req, 'request', next) }) var waiting = 3 function next () { if (--waiting === 0) { return done() } } }) it('should receive trailing headers', function (done) { var stream = client.request({ method: 'POST', path: '/post' }, function (err) { assert(!err) stream.sendHeaders({ trai: 'ler' }) stream.end() stream.on('response', function (status, headers) { assert.strictEqual(status, 200) assert.strictEqual(headers.ok, 'yes') fixtures.expectData(stream, 'response', done) }) }) server.on('request', function (req, res) { var gotHeaders = false req.on('trailers', function (headers) { gotHeaders = true assert.strictEqual(headers.trai, 'ler') }) req.on('end', function () { assert(gotHeaders) res.writeHead(200, { ok: 'yes' }) res.end('response') }) req.resume() }) }) it('should call .writeHead() automatically', function (done) { var stream = client.request({ method: 'POST', path: '/post' }, function (err) { assert(!err) stream.on('response', function (status, headers) { assert.strictEqual(status, 300) fixtures.expectData(stream, 'response', done) }) stream.end() }) server.on('request', function (req, res) { req.on('end', function () { res.statusCode = 300 res.end('response') }) req.resume() }) }) it('should not crash on .writeHead() after socket close', function (done) { var stream = client.request({ method: 'POST', path: '/post' }, function (err) { assert(!err) setTimeout(function () { client.socket.destroy() }, 50) stream.on('error', function () {}) stream.end() }) server.on('request', function (req, res) { req.connection.on('close', function () { assert.doesNotThrow(function () { res.writeHead(200) res.end('response') }) done() }) }) }) it('should not crash on .push() after socket close', function (done) { var stream = client.request({ method: 'POST', path: '/post' }, function (err) { assert(!err) setTimeout(function () { client.socket.destroy() }, 50) stream.on('error', function () {}) stream.end() }) server.on('request', function (req, res) { req.connection.on('close', function () { assert.doesNotThrow(function () { assert.strictEqual(res.push('/push', {}), undefined) res.end('response') }) done() }) }) }) it('should end response after writing everything down', function (done) { var stream = client.request({ method: 'GET', path: '/post' }, function (err) { assert(!err) stream.on('response', function (status, headers) { assert.strictEqual(status, 200) fixtures.expectData(stream, 'hello world, what\'s up?', done) }) stream.end() }) server.on('request', function (req, res) { req.resume() res.writeHead(200) res.write('hello ') res.write('world') res.write(', what\'s') res.write(' up?') res.end() }) }) it('should handle x-forwarded-for', function (done) { client.sendXForwardedFor('1.2.3.4') var stream = client.request({ method: 'GET', path: '/post' }, function (err) { assert(!err) stream.resume() stream.on('end', done) stream.end() }) server.on('request', function (req, res) { assert.strictEqual(req.headers['x-forwarded-for'], '1.2.3.4') req.resume() res.end() }) }) it('should destroy request after end', function (done) { var stream = client.request({ method: 'POST', path: '/post' }, function (err) { assert(!err) }) stream.end() stream.on('error', function () {}) server.on('request', function (req, res) { res.end() res.destroy() res.socket.on('close', function () { done() }) }) }) }) it('should respond to http/1.1', function (done) { var server = spdy.createServer(fixtures.keys, function (req, res) { assert.strictEqual(req.isSpdy, res.isSpdy) assert.strictEqual(req.spdyVersion, res.spdyVersion) assert(!req.isSpdy) assert.strictEqual(req.spdyVersion, 1) res.writeHead(200) res.end() }) server.listen(fixtures.port, function () { var req = https.request({ agent: false, rejectUnauthorized: false, NPNProtocols: ['http/1.1'], port: fixtures.port, method: 'GET', path: '/' }, function (res) { assert.strictEqual(res.statusCode, 200) res.resume() res.on('end', function () { server.close(done) }) }) req.end() }) }) it('should support custom base', function (done) { function Pseuver (options, listener) { https.Server.call(this, options, listener) } util.inherits(Pseuver, https.Server) var server = spdy.createServer(Pseuver, fixtures.keys, function (req, res) { assert.strictEqual(req.isSpdy, res.isSpdy) assert.strictEqual(req.spdyVersion, res.spdyVersion) assert(!req.isSpdy) assert.strictEqual(req.spdyVersion, 1) res.writeHead(200) res.end() }) server.listen(fixtures.port, function () { var req = https.request({ agent: false, rejectUnauthorized: false, NPNProtocols: ['http/1.1'], port: fixtures.port, method: 'GET', path: '/' }, function (res) { assert.strictEqual(res.statusCode, 200) res.resume() res.on('end', function () { server.close(done) }) }) req.end() }) }) })