2016-10-10 11:50:04 -07:00
|
|
|
import * as http from 'http';
|
|
|
|
import { parse } from 'url';
|
|
|
|
import * as zlib from 'zlib';
|
|
|
|
import * as stream from 'stream';
|
|
|
|
import { multipart as Multipart } from 'parted';
|
|
|
|
|
2017-07-02 09:32:48 -07:00
|
|
|
let convert;
|
|
|
|
try { convert = require('encoding').convert; } catch(e) {}
|
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
export default class TestServer {
|
|
|
|
constructor() {
|
|
|
|
this.server = http.createServer(this.router);
|
|
|
|
this.port = 30001;
|
|
|
|
this.hostname = 'localhost';
|
2017-06-03 03:01:13 -07:00
|
|
|
// node 8 default keepalive timeout is 5000ms
|
|
|
|
// make it shorter here as we want to close server quickly at the end of tests
|
|
|
|
this.server.keepAliveTimeout = 1000;
|
2016-10-10 11:50:04 -07:00
|
|
|
this.server.on('error', function(err) {
|
|
|
|
console.log(err.stack);
|
|
|
|
});
|
|
|
|
this.server.on('connection', function(socket) {
|
|
|
|
socket.setTimeout(1500);
|
|
|
|
});
|
2015-01-26 05:58:52 -08:00
|
|
|
}
|
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
start(cb) {
|
|
|
|
this.server.listen(this.port, this.hostname, cb);
|
2015-01-27 07:33:06 -08:00
|
|
|
}
|
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
stop(cb) {
|
|
|
|
this.server.close(cb);
|
2016-03-19 03:24:08 -07:00
|
|
|
}
|
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
router(req, res) {
|
|
|
|
let p = parse(req.url).pathname;
|
2015-01-27 07:33:06 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/hello') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'text/plain');
|
|
|
|
res.end('world');
|
|
|
|
}
|
2015-01-27 07:33:06 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/plain') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'text/plain');
|
|
|
|
res.end('text');
|
|
|
|
}
|
2015-01-27 07:33:06 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/options') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Allow', 'GET, HEAD, OPTIONS');
|
|
|
|
res.end('hello world');
|
|
|
|
}
|
2015-01-27 07:33:06 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/html') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'text/html');
|
|
|
|
res.end('<html></html>');
|
|
|
|
}
|
2016-08-03 02:31:46 -07:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/json') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'application/json');
|
|
|
|
res.end(JSON.stringify({
|
|
|
|
name: 'value'
|
|
|
|
}));
|
|
|
|
}
|
2015-01-27 21:01:10 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/gzip') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'text/plain');
|
|
|
|
res.setHeader('Content-Encoding', 'gzip');
|
|
|
|
zlib.gzip('hello world', function(err, buffer) {
|
|
|
|
res.end(buffer);
|
|
|
|
});
|
|
|
|
}
|
2015-09-28 06:58:45 -07:00
|
|
|
|
2017-02-22 00:05:55 -08:00
|
|
|
if (p === '/gzip-truncated') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'text/plain');
|
|
|
|
res.setHeader('Content-Encoding', 'gzip');
|
|
|
|
zlib.gzip('hello world', function(err, buffer) {
|
|
|
|
// truncate the CRC checksum and size check at the end of the stream
|
|
|
|
res.end(buffer.slice(0, buffer.length - 8));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/deflate') {
|
2015-01-27 07:33:06 -08:00
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'text/plain');
|
2016-10-10 11:50:04 -07:00
|
|
|
res.setHeader('Content-Encoding', 'deflate');
|
|
|
|
zlib.deflate('hello world', function(err, buffer) {
|
|
|
|
res.end(buffer);
|
|
|
|
});
|
|
|
|
}
|
2015-01-27 07:33:06 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/deflate-raw') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'text/plain');
|
|
|
|
res.setHeader('Content-Encoding', 'deflate');
|
|
|
|
zlib.deflateRaw('hello world', function(err, buffer) {
|
|
|
|
res.end(buffer);
|
|
|
|
});
|
|
|
|
}
|
2015-04-16 22:26:55 -07:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/sdch') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'text/plain');
|
|
|
|
res.setHeader('Content-Encoding', 'sdch');
|
|
|
|
res.end('fake sdch string');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p === '/invalid-content-encoding') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'text/plain');
|
|
|
|
res.setHeader('Content-Encoding', 'gzip');
|
|
|
|
res.end('fake gzip string');
|
|
|
|
}
|
2015-01-27 09:00:53 -08:00
|
|
|
|
2018-03-25 12:29:28 -07:00
|
|
|
if (p === '/invalid-header') {
|
|
|
|
res.setHeader('Content-Type', 'text/plain');
|
|
|
|
res.writeHead(200);
|
|
|
|
// HACK: add a few invalid headers to the generated header string before
|
|
|
|
// it is actually sent to the socket.
|
|
|
|
res._header = res._header.replace(/\r\n$/, [
|
|
|
|
'Invalid-Header : abc\r\n',
|
|
|
|
'Invalid-Header-Value: \x07k\r\n',
|
|
|
|
'Set-Cookie: \x07k\r\n',
|
|
|
|
'Set-Cookie: \x07kk\r\n',
|
|
|
|
].join('') + '\r\n');
|
|
|
|
res.end('hello world\n');
|
|
|
|
}
|
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/timeout') {
|
|
|
|
setTimeout(function() {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'text/plain');
|
|
|
|
res.end('text');
|
|
|
|
}, 1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p === '/slow') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'text/plain');
|
2015-01-28 06:56:25 -08:00
|
|
|
res.write('test');
|
2016-10-10 11:50:04 -07:00
|
|
|
setTimeout(function() {
|
|
|
|
res.end('test');
|
|
|
|
}, 1000);
|
|
|
|
}
|
2015-01-28 06:56:25 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/cookie') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Set-Cookie', ['a=1', 'b=1']);
|
|
|
|
res.end('cookie');
|
|
|
|
}
|
2015-01-28 06:56:25 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/size/chunk') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'text/plain');
|
|
|
|
setTimeout(function() {
|
|
|
|
res.write('test');
|
|
|
|
}, 50);
|
|
|
|
setTimeout(function() {
|
|
|
|
res.end('test');
|
|
|
|
}, 100);
|
|
|
|
}
|
2015-01-27 09:00:53 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/size/long') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'text/plain');
|
|
|
|
res.end('testtest');
|
|
|
|
}
|
2015-01-27 09:00:53 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/encoding/gbk') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'text/html');
|
|
|
|
res.end(convert('<meta charset="gbk"><div>中文</div>', 'gbk'));
|
|
|
|
}
|
2015-01-27 09:00:53 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/encoding/gb2312') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'text/html');
|
|
|
|
res.end(convert('<meta http-equiv="Content-Type" content="text/html; charset=gb2312"><div>中文</div>', 'gb2312'));
|
|
|
|
}
|
2015-01-28 06:56:25 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/encoding/shift-jis') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'text/html; charset=Shift-JIS');
|
|
|
|
res.end(convert('<div>日本語</div>', 'Shift_JIS'));
|
|
|
|
}
|
2015-01-27 21:01:10 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/encoding/euc-jp') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'text/xml');
|
|
|
|
res.end(convert('<?xml version="1.0" encoding="EUC-JP"?><title>日本語</title>', 'EUC-JP'));
|
|
|
|
}
|
2015-03-23 21:31:04 -07:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/encoding/utf8') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.end('中文');
|
|
|
|
}
|
2015-03-23 21:31:04 -07:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/encoding/order1') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'charset=gbk; text/plain');
|
|
|
|
res.end(convert('中文', 'gbk'));
|
2016-03-19 01:17:14 -07:00
|
|
|
}
|
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/encoding/order2') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'text/plain; charset=gbk; qs=1');
|
|
|
|
res.end(convert('中文', 'gbk'));
|
2016-03-19 03:06:33 -07:00
|
|
|
}
|
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/encoding/chunked') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'text/html');
|
|
|
|
res.setHeader('Transfer-Encoding', 'chunked');
|
2017-02-26 14:12:45 -08:00
|
|
|
res.write('a'.repeat(10));
|
2016-10-10 11:50:04 -07:00
|
|
|
res.end(convert('<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS" /><div>日本語</div>', 'Shift_JIS'));
|
|
|
|
}
|
2015-01-27 07:33:06 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/encoding/invalid') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'text/html');
|
|
|
|
res.setHeader('Transfer-Encoding', 'chunked');
|
2017-02-26 14:12:45 -08:00
|
|
|
res.write('a'.repeat(1200));
|
2016-10-10 11:50:04 -07:00
|
|
|
res.end(convert('中文', 'gbk'));
|
|
|
|
}
|
2015-01-27 07:33:06 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/redirect/301') {
|
|
|
|
res.statusCode = 301;
|
|
|
|
res.setHeader('Location', '/inspect');
|
|
|
|
res.end();
|
|
|
|
}
|
2015-01-27 07:33:06 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/redirect/302') {
|
|
|
|
res.statusCode = 302;
|
|
|
|
res.setHeader('Location', '/inspect');
|
|
|
|
res.end();
|
|
|
|
}
|
2015-01-27 07:33:06 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/redirect/303') {
|
|
|
|
res.statusCode = 303;
|
|
|
|
res.setHeader('Location', '/inspect');
|
|
|
|
res.end();
|
|
|
|
}
|
2015-01-27 07:33:06 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/redirect/307') {
|
|
|
|
res.statusCode = 307;
|
|
|
|
res.setHeader('Location', '/inspect');
|
|
|
|
res.end();
|
|
|
|
}
|
2015-01-27 07:33:06 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/redirect/308') {
|
|
|
|
res.statusCode = 308;
|
|
|
|
res.setHeader('Location', '/inspect');
|
|
|
|
res.end();
|
|
|
|
}
|
2015-01-27 09:00:53 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/redirect/chain') {
|
|
|
|
res.statusCode = 301;
|
|
|
|
res.setHeader('Location', '/redirect/301');
|
|
|
|
res.end();
|
|
|
|
}
|
2015-01-27 09:00:53 -08:00
|
|
|
|
2018-03-04 19:29:59 -08:00
|
|
|
if (p === '/redirect/no-location') {
|
2016-10-10 11:50:04 -07:00
|
|
|
res.statusCode = 301;
|
|
|
|
res.end();
|
|
|
|
}
|
2016-08-02 21:55:58 -07:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/error/400') {
|
|
|
|
res.statusCode = 400;
|
|
|
|
res.setHeader('Content-Type', 'text/plain');
|
|
|
|
res.end('client error');
|
|
|
|
}
|
2015-01-27 09:00:53 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/error/404') {
|
|
|
|
res.statusCode = 404;
|
|
|
|
res.setHeader('Content-Encoding', 'gzip');
|
|
|
|
res.end();
|
|
|
|
}
|
2015-01-27 09:00:53 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/error/500') {
|
|
|
|
res.statusCode = 500;
|
|
|
|
res.setHeader('Content-Type', 'text/plain');
|
|
|
|
res.end('server error');
|
|
|
|
}
|
2015-01-27 21:01:10 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/error/reset') {
|
|
|
|
res.destroy();
|
|
|
|
}
|
2015-01-27 21:01:10 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/error/json') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'application/json');
|
|
|
|
res.end('invalid json');
|
|
|
|
}
|
2016-05-25 09:46:11 -07:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/no-content') {
|
|
|
|
res.statusCode = 204;
|
|
|
|
res.end();
|
|
|
|
}
|
2016-05-25 09:46:11 -07:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/no-content/gzip') {
|
|
|
|
res.statusCode = 204;
|
|
|
|
res.setHeader('Content-Encoding', 'gzip');
|
|
|
|
res.end();
|
|
|
|
}
|
2016-05-25 09:46:11 -07:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/not-modified') {
|
|
|
|
res.statusCode = 304;
|
|
|
|
res.end();
|
|
|
|
}
|
2015-01-27 07:33:06 -08:00
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
if (p === '/not-modified/gzip') {
|
|
|
|
res.statusCode = 304;
|
|
|
|
res.setHeader('Content-Encoding', 'gzip');
|
|
|
|
res.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p === '/inspect') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'application/json');
|
|
|
|
let body = '';
|
|
|
|
req.on('data', function(c) { body += c });
|
|
|
|
req.on('end', function() {
|
|
|
|
res.end(JSON.stringify({
|
|
|
|
method: req.method,
|
|
|
|
url: req.url,
|
|
|
|
headers: req.headers,
|
|
|
|
body
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p === '/multipart') {
|
|
|
|
res.statusCode = 200;
|
|
|
|
res.setHeader('Content-Type', 'application/json');
|
|
|
|
const parser = new Multipart(req.headers['content-type']);
|
|
|
|
let body = '';
|
|
|
|
parser.on('part', function(field, part) {
|
|
|
|
body += field + '=' + part;
|
|
|
|
});
|
|
|
|
parser.on('end', function() {
|
|
|
|
res.end(JSON.stringify({
|
|
|
|
method: req.method,
|
|
|
|
url: req.url,
|
|
|
|
headers: req.headers,
|
|
|
|
body: body
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
req.pipe(parser);
|
|
|
|
}
|
2015-07-22 00:40:52 -07:00
|
|
|
}
|
2015-01-26 05:58:52 -08:00
|
|
|
}
|
2016-11-05 09:39:01 -07:00
|
|
|
|
|
|
|
if (require.main === module) {
|
|
|
|
const server = new TestServer;
|
|
|
|
server.start(() => {
|
|
|
|
console.log(`Server started listening at port ${server.port}`);
|
|
|
|
});
|
|
|
|
}
|