mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-05 07:18:39 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
575
testing/xpcshell/node-http2/test/compressor.js
Normal file
575
testing/xpcshell/node-http2/test/compressor.js
Normal file
|
|
@ -0,0 +1,575 @@
|
|||
var expect = require('chai').expect;
|
||||
var util = require('./util');
|
||||
|
||||
var compressor = require('../lib/protocol/compressor');
|
||||
var HeaderTable = compressor.HeaderTable;
|
||||
var HuffmanTable = compressor.HuffmanTable;
|
||||
var HeaderSetCompressor = compressor.HeaderSetCompressor;
|
||||
var HeaderSetDecompressor = compressor.HeaderSetDecompressor;
|
||||
var Compressor = compressor.Compressor;
|
||||
var Decompressor = compressor.Decompressor;
|
||||
|
||||
var test_integers = [{
|
||||
N: 5,
|
||||
I: 10,
|
||||
buffer: new Buffer([10])
|
||||
}, {
|
||||
N: 0,
|
||||
I: 10,
|
||||
buffer: new Buffer([10])
|
||||
}, {
|
||||
N: 5,
|
||||
I: 1337,
|
||||
buffer: new Buffer([31, 128 + 26, 10])
|
||||
}, {
|
||||
N: 0,
|
||||
I: 1337,
|
||||
buffer: new Buffer([128 + 57, 10])
|
||||
}];
|
||||
|
||||
var test_strings = [{
|
||||
string: 'www.foo.com',
|
||||
buffer: new Buffer('89f1e3c2f29ceb90f4ff', 'hex')
|
||||
}, {
|
||||
string: 'éáűőúöüó€',
|
||||
buffer: new Buffer('13c3a9c3a1c5b1c591c3bac3b6c3bcc3b3e282ac', 'hex')
|
||||
}];
|
||||
|
||||
test_huffman_request = {
|
||||
'GET': 'c5837f',
|
||||
'http': '9d29af',
|
||||
'/': '63',
|
||||
'www.foo.com': 'f1e3c2f29ceb90f4ff',
|
||||
'https': '9d29ad1f',
|
||||
'www.bar.com': 'f1e3c2f18ec5c87a7f',
|
||||
'no-cache': 'a8eb10649cbf',
|
||||
'/custom-path.css': '6096a127a56ac699d72211',
|
||||
'custom-key': '25a849e95ba97d7f',
|
||||
'custom-value': '25a849e95bb8e8b4bf'
|
||||
};
|
||||
|
||||
test_huffman_response = {
|
||||
'302': '6402',
|
||||
'private': 'aec3771a4b',
|
||||
'Mon, 21 OCt 2013 20:13:21 GMT': 'd07abe941054d5792a0801654102e059b820a98b46ff',
|
||||
': https://www.bar.com': 'b8a4e94d68b8c31e3c785e31d8b90f4f',
|
||||
'200': '1001',
|
||||
'Mon, 21 OCt 2013 20:13:22 GMT': 'd07abe941054d5792a0801654102e059b821298b46ff',
|
||||
'https://www.bar.com': '9d29ad171863c78f0bc63b1721e9',
|
||||
'gzip': '9bd9ab',
|
||||
'foo=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAALASDJKHQKBZXOQWEOPIUAXQWEOIUAXLJKHQWOEIUAL\
|
||||
QWEOIUAXLQEUAXLLKJASDQWEOUIAXN1234LASDJKHQKBZXOQWEOPIUAXQWEOIUAXLJKH\
|
||||
QWOEIUALQWEOIUAXLQEUAXLLKJASDQWEOUIAXN1234LASDJKHQKBZXOQWEOPIUAXQWEO\
|
||||
IUAXLJKHQWOEIUALQWEOIUAXLQEUAXLLKJASDQWEOUIAXN1234LASDJKHQKBZXOQWEOP\
|
||||
IUAXQWEOIUAXLJKHQWOEIUALQWEOIUAXLQEUAXLLKJASDQWEOUIAXN1234ZZZZZZZZZZ\
|
||||
ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ1234 m\
|
||||
ax-age=3600; version=1': '94e7821861861861861861861861861861861861861861861861861861861861861861861861861861861861861861861861861861861861873c3bafe5cd8f666bbfbf9ab672c1ab5e4e10fe6ce583564e10fe67cb9b1ece5ab064e10e7d9cb06ac9c21fccfb307087f33e7cd961dd7f672c1ab86487f34844cb59e1dd7f2e6c7b335dfdfcd5b3960d5af27087f3672c1ab27087f33e5cd8f672d583270873ece583564e10fe67d983843f99f3e6cb0eebfb3960d5c3243f9a42265acf0eebf97363d99aefefe6ad9cb06ad793843f9b3960d593843f99f2e6c7b396ac1938439f672c1ab27087f33ecc1c21fccf9f3658775fd9cb06ae1921fcd21132d678775fcb9b1eccd77f7f356ce58356bc9c21fcd9cb06ac9c21fccf97363d9cb560c9c21cfb3960d593843f99f660e10fe67cf9b2c3bafece583570c90fe6908996bf7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f42265a5291f9587316065c003ed4ee5b1063d5007f',
|
||||
'foo=ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ\
|
||||
ZZZZZZZZZZZZZZZZZZZZZZZZZZLASDJKHQKBZXOQWEOPIUAXQWEOIUAXLJKHQWOEIUAL\
|
||||
QWEOIUAXLQEUAXLLKJASDQWEOUIAXN1234LASDJKHQKBZXOQWEOPIUAXQWEOIUAXLJKH\
|
||||
QWOEIUALQWEOIUAXLQEUAXLLKJASDQWEOUIAXN1234LASDJKHQKBZXOQWEOPIUAXQWEO\
|
||||
IUAXLJKHQWOEIUALQWEOIUAXLQEUAXLLKJASDQWEOUIAXN1234LASDJKHQKBZXOQWEOP\
|
||||
IUAXQWEOIUAXLJKHQWOEIUALQWEOIUAXLQEUAXLLKJASDQWEOUIAXN1234AAAAAAAAAA\
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1234 m\
|
||||
ax-age=3600; version=1': '94e783f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f73c3bafe5cd8f666bbfbf9ab672c1ab5e4e10fe6ce583564e10fe67cb9b1ece5ab064e10e7d9cb06ac9c21fccfb307087f33e7cd961dd7f672c1ab86487f34844cb59e1dd7f2e6c7b335dfdfcd5b3960d5af27087f3672c1ab27087f33e5cd8f672d583270873ece583564e10fe67d983843f99f3e6cb0eebfb3960d5c3243f9a42265acf0eebf97363d99aefefe6ad9cb06ad793843f9b3960d593843f99f2e6c7b396ac1938439f672c1ab27087f33ecc1c21fccf9f3658775fd9cb06ae1921fcd21132d678775fcb9b1eccd77f7f356ce58356bc9c21fcd9cb06ac9c21fccf97363d9cb560c9c21cfb3960d593843f99f660e10fe67cf9b2c3bafece583570c90fe6908996a1861861861861861861861861861861861861861861861861861861861861861861861861861861861861861861861861861861861842265a5291f9587316065c003ed4ee5b1063d5007f'
|
||||
};
|
||||
|
||||
var test_headers = [{
|
||||
// index
|
||||
header: {
|
||||
name: 1,
|
||||
value: 1,
|
||||
index: false,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('82', 'hex')
|
||||
}, {
|
||||
// index
|
||||
header: {
|
||||
name: 5,
|
||||
value: 5,
|
||||
index: false,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('86', 'hex')
|
||||
}, {
|
||||
// index
|
||||
header: {
|
||||
name: 3,
|
||||
value: 3,
|
||||
index: false,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('84', 'hex')
|
||||
}, {
|
||||
// literal w/index, name index
|
||||
header: {
|
||||
name: 0,
|
||||
value: 'www.foo.com',
|
||||
index: true,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('41' + '89f1e3c2f29ceb90f4ff', 'hex')
|
||||
}, {
|
||||
// indexed
|
||||
header: {
|
||||
name: 1,
|
||||
value: 1,
|
||||
index: false,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('82', 'hex')
|
||||
}, {
|
||||
// indexed
|
||||
header: {
|
||||
name: 6,
|
||||
value: 6,
|
||||
index: false,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('87', 'hex')
|
||||
}, {
|
||||
// indexed
|
||||
header: {
|
||||
name: 3,
|
||||
value: 3,
|
||||
index: false,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('84', 'hex')
|
||||
}, {
|
||||
// literal w/index, name index
|
||||
header: {
|
||||
name: 0,
|
||||
value: 'www.bar.com',
|
||||
index: true,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('41' + '89f1e3c2f18ec5c87a7f', 'hex')
|
||||
}, {
|
||||
// literal w/index, name index
|
||||
header: {
|
||||
name: 23,
|
||||
value: 'no-cache',
|
||||
index: true,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('58' + '86a8eb10649cbf', 'hex')
|
||||
}, {
|
||||
// index
|
||||
header: {
|
||||
name: 1,
|
||||
value: 1,
|
||||
index: false,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('82', 'hex')
|
||||
}, {
|
||||
// index
|
||||
header: {
|
||||
name: 6,
|
||||
value: 6,
|
||||
index: false,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('87', 'hex')
|
||||
}, {
|
||||
// literal w/index, name index
|
||||
header: {
|
||||
name: 3,
|
||||
value: '/custom-path.css',
|
||||
index: true,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('44' + '8b6096a127a56ac699d72211', 'hex')
|
||||
}, {
|
||||
// index
|
||||
header: {
|
||||
name: 63,
|
||||
value: 63,
|
||||
index: false,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('C0', 'hex')
|
||||
}, {
|
||||
// literal w/index, new name & value
|
||||
header: {
|
||||
name: 'custom-key',
|
||||
value: 'custom-value',
|
||||
index: true,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('40' + '8825a849e95ba97d7f' + '8925a849e95bb8e8b4bf', 'hex')
|
||||
}, {
|
||||
// index
|
||||
header: {
|
||||
name: 1,
|
||||
value: 1,
|
||||
index: false,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('82', 'hex')
|
||||
}, {
|
||||
// index
|
||||
header: {
|
||||
name: 6,
|
||||
value: 6,
|
||||
index: false,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('87', 'hex')
|
||||
}, {
|
||||
// index
|
||||
header: {
|
||||
name: 62,
|
||||
value: 62,
|
||||
index: false,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('BF', 'hex')
|
||||
}, {
|
||||
// index
|
||||
header: {
|
||||
name: 65,
|
||||
value: 65,
|
||||
index: false,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('C2', 'hex')
|
||||
}, {
|
||||
// index
|
||||
header: {
|
||||
name: 64,
|
||||
value: 64,
|
||||
index: false,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('C1', 'hex')
|
||||
}, {
|
||||
// index
|
||||
header: {
|
||||
name: 61,
|
||||
value: 61,
|
||||
index: false,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('BE', 'hex')
|
||||
}, {
|
||||
// Literal w/o index, name index
|
||||
header: {
|
||||
name: 6,
|
||||
value: "whatever",
|
||||
index: false,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('07' + '86f138d25ee5b3', 'hex')
|
||||
}, {
|
||||
// Literal w/o index, new name & value
|
||||
header: {
|
||||
name: "foo",
|
||||
value: "bar",
|
||||
index: false,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('00' + '8294e7' + '03626172', 'hex')
|
||||
}, {
|
||||
// Literal never indexed, name index
|
||||
header: {
|
||||
name: 6,
|
||||
value: "whatever",
|
||||
index: false,
|
||||
mustNeverIndex: true,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('17' + '86f138d25ee5b3', 'hex')
|
||||
}, {
|
||||
// Literal never indexed, new name & value
|
||||
header: {
|
||||
name: "foo",
|
||||
value: "bar",
|
||||
index: false,
|
||||
mustNeverIndex: true,
|
||||
contextUpdate: false,
|
||||
newMaxSize: 0
|
||||
},
|
||||
buffer: new Buffer('10' + '8294e7' + '03626172', 'hex')
|
||||
}, {
|
||||
header: {
|
||||
name: -1,
|
||||
value: -1,
|
||||
index: false,
|
||||
mustNeverIndex: false,
|
||||
contextUpdate: true,
|
||||
newMaxSize: 100
|
||||
},
|
||||
buffer: new Buffer('3F45', 'hex')
|
||||
}];
|
||||
|
||||
var test_header_sets = [{
|
||||
headers: {
|
||||
':method': 'GET',
|
||||
':scheme': 'http',
|
||||
':path': '/',
|
||||
':authority': 'www.foo.com'
|
||||
},
|
||||
buffer: util.concat(test_headers.slice(0, 4).map(function(test) { return test.buffer; }))
|
||||
}, {
|
||||
headers: {
|
||||
':method': 'GET',
|
||||
':scheme': 'https',
|
||||
':path': '/',
|
||||
':authority': 'www.bar.com',
|
||||
'cache-control': 'no-cache'
|
||||
},
|
||||
buffer: util.concat(test_headers.slice(4, 9).map(function(test) { return test.buffer; }))
|
||||
}, {
|
||||
headers: {
|
||||
':method': 'GET',
|
||||
':scheme': 'https',
|
||||
':path': '/custom-path.css',
|
||||
':authority': 'www.bar.com',
|
||||
'custom-key': 'custom-value'
|
||||
},
|
||||
buffer: util.concat(test_headers.slice(9, 14).map(function(test) { return test.buffer; }))
|
||||
}, {
|
||||
headers: {
|
||||
':method': 'GET',
|
||||
':scheme': 'https',
|
||||
':path': '/custom-path.css',
|
||||
':authority': ['www.foo.com', 'www.bar.com'],
|
||||
'custom-key': 'custom-value'
|
||||
},
|
||||
buffer: util.concat(test_headers.slice(14, 19).map(function(test) { return test.buffer; }))
|
||||
}];
|
||||
|
||||
describe('compressor.js', function() {
|
||||
describe('HeaderTable', function() {
|
||||
});
|
||||
|
||||
describe('HuffmanTable', function() {
|
||||
describe('method encode(buffer)', function() {
|
||||
it('should return the Huffman encoded version of the input buffer', function() {
|
||||
var table = HuffmanTable.huffmanTable;
|
||||
for (var decoded in test_huffman_request) {
|
||||
var encoded = test_huffman_request[decoded];
|
||||
expect(table.encode(new Buffer(decoded)).toString('hex')).to.equal(encoded);
|
||||
}
|
||||
table = HuffmanTable.huffmanTable;
|
||||
for (decoded in test_huffman_response) {
|
||||
encoded = test_huffman_response[decoded];
|
||||
expect(table.encode(new Buffer(decoded)).toString('hex')).to.equal(encoded);
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('method decode(buffer)', function() {
|
||||
it('should return the Huffman decoded version of the input buffer', function() {
|
||||
var table = HuffmanTable.huffmanTable;
|
||||
for (var decoded in test_huffman_request) {
|
||||
var encoded = test_huffman_request[decoded];
|
||||
expect(table.decode(new Buffer(encoded, 'hex')).toString()).to.equal(decoded);
|
||||
}
|
||||
table = HuffmanTable.huffmanTable;
|
||||
for (decoded in test_huffman_response) {
|
||||
encoded = test_huffman_response[decoded];
|
||||
expect(table.decode(new Buffer(encoded, 'hex')).toString()).to.equal(decoded);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('HeaderSetCompressor', function() {
|
||||
describe('static method .integer(I, N)', function() {
|
||||
it('should return an array of buffers that represent the N-prefix coded form of the integer I', function() {
|
||||
for (var i = 0; i < test_integers.length; i++) {
|
||||
var test = test_integers[i];
|
||||
test.buffer.cursor = 0;
|
||||
expect(util.concat(HeaderSetCompressor.integer(test.I, test.N))).to.deep.equal(test.buffer);
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('static method .string(string)', function() {
|
||||
it('should return an array of buffers that represent the encoded form of the string', function() {
|
||||
var table = HuffmanTable.huffmanTable;
|
||||
for (var i = 0; i < test_strings.length; i++) {
|
||||
var test = test_strings[i];
|
||||
expect(util.concat(HeaderSetCompressor.string(test.string, table))).to.deep.equal(test.buffer);
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('static method .header({ name, value, index })', function() {
|
||||
it('should return an array of buffers that represent the encoded form of the header', function() {
|
||||
var table = HuffmanTable.huffmanTable;
|
||||
for (var i = 0; i < test_headers.length; i++) {
|
||||
var test = test_headers[i];
|
||||
expect(util.concat(HeaderSetCompressor.header(test.header, table))).to.deep.equal(test.buffer);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('HeaderSetDecompressor', function() {
|
||||
describe('static method .integer(buffer, N)', function() {
|
||||
it('should return the parsed N-prefix coded number and increase the cursor property of buffer', function() {
|
||||
for (var i = 0; i < test_integers.length; i++) {
|
||||
var test = test_integers[i];
|
||||
test.buffer.cursor = 0;
|
||||
expect(HeaderSetDecompressor.integer(test.buffer, test.N)).to.equal(test.I);
|
||||
expect(test.buffer.cursor).to.equal(test.buffer.length);
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('static method .string(buffer)', function() {
|
||||
it('should return the parsed string and increase the cursor property of buffer', function() {
|
||||
var table = HuffmanTable.huffmanTable;
|
||||
for (var i = 0; i < test_strings.length; i++) {
|
||||
var test = test_strings[i];
|
||||
test.buffer.cursor = 0;
|
||||
expect(HeaderSetDecompressor.string(test.buffer, table)).to.equal(test.string);
|
||||
expect(test.buffer.cursor).to.equal(test.buffer.length);
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('static method .header(buffer)', function() {
|
||||
it('should return the parsed header and increase the cursor property of buffer', function() {
|
||||
var table = HuffmanTable.huffmanTable;
|
||||
for (var i = 0; i < test_headers.length; i++) {
|
||||
var test = test_headers[i];
|
||||
test.buffer.cursor = 0;
|
||||
expect(HeaderSetDecompressor.header(test.buffer, table)).to.deep.equal(test.header);
|
||||
expect(test.buffer.cursor).to.equal(test.buffer.length);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Decompressor', function() {
|
||||
describe('method decompress(buffer)', function() {
|
||||
it('should return the parsed header set in { name1: value1, name2: [value2, value3], ... } format', function() {
|
||||
var decompressor = new Decompressor(util.log, 'REQUEST');
|
||||
for (var i = 0; i < test_header_sets.length - 1; i++) {
|
||||
var header_set = test_header_sets[i];
|
||||
expect(decompressor.decompress(header_set.buffer)).to.deep.equal(header_set.headers);
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('transform stream', function() {
|
||||
it('should emit an error event if a series of header frames is interleaved with other frames', function() {
|
||||
var decompressor = new Decompressor(util.log, 'REQUEST');
|
||||
var error_occured = false;
|
||||
decompressor.on('error', function() {
|
||||
error_occured = true;
|
||||
});
|
||||
decompressor.write({
|
||||
type: 'HEADERS',
|
||||
flags: {
|
||||
END_HEADERS: false
|
||||
},
|
||||
data: new Buffer(5)
|
||||
});
|
||||
decompressor.write({
|
||||
type: 'DATA',
|
||||
flags: {},
|
||||
data: new Buffer(5)
|
||||
});
|
||||
expect(error_occured).to.be.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('invariant', function() {
|
||||
describe('decompressor.decompress(compressor.compress(headerset)) === headerset', function() {
|
||||
it('should be true for any header set if the states are synchronized', function() {
|
||||
var compressor = new Compressor(util.log, 'REQUEST');
|
||||
var decompressor = new Decompressor(util.log, 'REQUEST');
|
||||
var n = test_header_sets.length;
|
||||
for (var i = 0; i < 10; i++) {
|
||||
var headers = test_header_sets[i%n].headers;
|
||||
var compressed = compressor.compress(headers);
|
||||
var decompressed = decompressor.decompress(compressed);
|
||||
expect(decompressed).to.deep.equal(headers);
|
||||
expect(compressor._table).to.deep.equal(decompressor._table);
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('source.pipe(compressor).pipe(decompressor).pipe(destination)', function() {
|
||||
it('should behave like source.pipe(destination) for a stream of frames', function(done) {
|
||||
var compressor = new Compressor(util.log, 'RESPONSE');
|
||||
var decompressor = new Decompressor(util.log, 'RESPONSE');
|
||||
var n = test_header_sets.length;
|
||||
compressor.pipe(decompressor);
|
||||
for (var i = 0; i < 10; i++) {
|
||||
compressor.write({
|
||||
type: i%2 ? 'HEADERS' : 'PUSH_PROMISE',
|
||||
flags: {},
|
||||
headers: test_header_sets[i%n].headers
|
||||
});
|
||||
}
|
||||
setTimeout(function() {
|
||||
for (var j = 0; j < 10; j++) {
|
||||
expect(decompressor.read().headers).to.deep.equal(test_header_sets[j%n].headers);
|
||||
}
|
||||
done();
|
||||
}, 10);
|
||||
});
|
||||
});
|
||||
describe('huffmanTable.decompress(huffmanTable.compress(buffer)) === buffer', function() {
|
||||
it('should be true for any buffer', function() {
|
||||
for (var i = 0; i < 10; i++) {
|
||||
var buffer = [];
|
||||
while (Math.random() > 0.1) {
|
||||
buffer.push(Math.floor(Math.random() * 256))
|
||||
}
|
||||
buffer = new Buffer(buffer);
|
||||
var table = HuffmanTable.huffmanTable;
|
||||
var result = table.decode(table.encode(buffer));
|
||||
expect(result).to.deep.equal(buffer);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
237
testing/xpcshell/node-http2/test/connection.js
Normal file
237
testing/xpcshell/node-http2/test/connection.js
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
var expect = require('chai').expect;
|
||||
var util = require('./util');
|
||||
|
||||
var Connection = require('../lib/protocol/connection').Connection;
|
||||
|
||||
var settings = {
|
||||
SETTINGS_MAX_CONCURRENT_STREAMS: 100,
|
||||
SETTINGS_INITIAL_WINDOW_SIZE: 100000
|
||||
};
|
||||
|
||||
var MAX_PRIORITY = Math.pow(2, 31) - 1;
|
||||
var MAX_RANDOM_PRIORITY = 10;
|
||||
|
||||
function randomPriority() {
|
||||
return Math.floor(Math.random() * (MAX_RANDOM_PRIORITY + 1));
|
||||
}
|
||||
|
||||
function expectPriorityOrder(priorities) {
|
||||
priorities.forEach(function(bucket, priority) {
|
||||
bucket.forEach(function(stream) {
|
||||
expect(stream._priority).to.be.equal(priority);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('connection.js', function() {
|
||||
describe('Connection class', function() {
|
||||
describe('method ._insert(stream)', function() {
|
||||
it('should insert the stream in _streamPriorities in a place determined by stream._priority', function() {
|
||||
var streams = [];
|
||||
var connection = Object.create(Connection.prototype, { _streamPriorities: { value: streams }});
|
||||
var streamCount = 10;
|
||||
|
||||
for (var i = 0; i < streamCount; i++) {
|
||||
var stream = { _priority: randomPriority() };
|
||||
connection._insert(stream, stream._priority);
|
||||
expect(connection._streamPriorities[stream._priority]).to.include(stream);
|
||||
}
|
||||
|
||||
expectPriorityOrder(connection._streamPriorities);
|
||||
});
|
||||
});
|
||||
describe('method ._reprioritize(stream)', function() {
|
||||
it('should eject and then insert the stream in _streamPriorities in a place determined by stream._priority', function() {
|
||||
var streams = [];
|
||||
var connection = Object.create(Connection.prototype, { _streamPriorities: { value: streams }});
|
||||
var streamCount = 10;
|
||||
var oldPriority, newPriority, stream;
|
||||
|
||||
for (var i = 0; i < streamCount; i++) {
|
||||
oldPriority = randomPriority();
|
||||
while ((newPriority = randomPriority()) === oldPriority);
|
||||
stream = { _priority: oldPriority };
|
||||
connection._insert(stream, oldPriority);
|
||||
connection._reprioritize(stream, newPriority);
|
||||
stream._priority = newPriority;
|
||||
|
||||
expect(connection._streamPriorities[newPriority]).to.include(stream);
|
||||
expect(connection._streamPriorities[oldPriority] || []).to.not.include(stream);
|
||||
}
|
||||
|
||||
expectPriorityOrder(streams);
|
||||
});
|
||||
});
|
||||
describe('invalid operation', function() {
|
||||
describe('unsolicited ping answer', function() {
|
||||
it('should be ignored', function() {
|
||||
var connection = new Connection(util.log, 1, settings);
|
||||
|
||||
connection._receivePing({
|
||||
stream: 0,
|
||||
type: 'PING',
|
||||
flags: {
|
||||
'PONG': true
|
||||
},
|
||||
data: new Buffer(8)
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('test scenario', function() {
|
||||
var c, s;
|
||||
beforeEach(function() {
|
||||
c = new Connection(util.log.child({ role: 'client' }), 1, settings);
|
||||
s = new Connection(util.log.child({ role: 'client' }), 2, settings);
|
||||
c.pipe(s).pipe(c);
|
||||
});
|
||||
|
||||
describe('connection setup', function() {
|
||||
it('should work as expected', function(done) {
|
||||
setTimeout(function() {
|
||||
// If there are no exception until this, then we're done
|
||||
done();
|
||||
}, 10);
|
||||
});
|
||||
});
|
||||
describe('sending/receiving a request', function() {
|
||||
it('should work as expected', function(done) {
|
||||
// Request and response data
|
||||
var request_headers = {
|
||||
':method': 'GET',
|
||||
':path': '/'
|
||||
};
|
||||
var request_data = new Buffer(0);
|
||||
var response_headers = {
|
||||
':status': '200'
|
||||
};
|
||||
var response_data = new Buffer('12345678', 'hex');
|
||||
|
||||
// Setting up server
|
||||
s.on('stream', function(server_stream) {
|
||||
server_stream.on('headers', function(headers) {
|
||||
expect(headers).to.deep.equal(request_headers);
|
||||
server_stream.headers(response_headers);
|
||||
server_stream.end(response_data);
|
||||
});
|
||||
});
|
||||
|
||||
// Sending request
|
||||
var client_stream = c.createStream();
|
||||
client_stream.headers(request_headers);
|
||||
client_stream.end(request_data);
|
||||
|
||||
// Waiting for answer
|
||||
done = util.callNTimes(2, done);
|
||||
client_stream.on('headers', function(headers) {
|
||||
expect(headers).to.deep.equal(response_headers);
|
||||
done();
|
||||
});
|
||||
client_stream.on('data', function(data) {
|
||||
expect(data).to.deep.equal(response_data);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('server push', function() {
|
||||
it('should work as expected', function(done) {
|
||||
var request_headers = { ':method': 'get', ':path': '/' };
|
||||
var response_headers = { ':status': '200' };
|
||||
var push_request_headers = { ':method': 'get', ':path': '/x' };
|
||||
var push_response_headers = { ':status': '200' };
|
||||
var response_content = new Buffer(10);
|
||||
var push_content = new Buffer(10);
|
||||
|
||||
done = util.callNTimes(5, done);
|
||||
|
||||
s.on('stream', function(response) {
|
||||
response.headers(response_headers);
|
||||
|
||||
var pushed = response.promise(push_request_headers);
|
||||
pushed.headers(push_response_headers);
|
||||
pushed.end(push_content);
|
||||
|
||||
response.end(response_content);
|
||||
});
|
||||
|
||||
var request = c.createStream();
|
||||
request.headers(request_headers);
|
||||
request.end();
|
||||
request.on('headers', function(headers) {
|
||||
expect(headers).to.deep.equal(response_headers);
|
||||
done();
|
||||
});
|
||||
request.on('data', function(data) {
|
||||
expect(data).to.deep.equal(response_content);
|
||||
done();
|
||||
});
|
||||
request.on('promise', function(pushed, headers) {
|
||||
expect(headers).to.deep.equal(push_request_headers);
|
||||
pushed.on('headers', function(headers) {
|
||||
expect(headers).to.deep.equal(response_headers);
|
||||
done();
|
||||
});
|
||||
pushed.on('data', function(data) {
|
||||
expect(data).to.deep.equal(push_content);
|
||||
done();
|
||||
});
|
||||
pushed.on('end', done);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('ping from client', function() {
|
||||
it('should work as expected', function(done) {
|
||||
c.ping(function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('ping from server', function() {
|
||||
it('should work as expected', function(done) {
|
||||
s.ping(function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('creating two streams and then using them in reverse order', function() {
|
||||
it('should not result in non-monotonous local ID ordering', function() {
|
||||
var s1 = c.createStream();
|
||||
var s2 = c.createStream();
|
||||
s2.headers({ ':method': 'get', ':path': '/' });
|
||||
s1.headers({ ':method': 'get', ':path': '/' });
|
||||
});
|
||||
});
|
||||
describe('creating two promises and then using them in reverse order', function() {
|
||||
it('should not result in non-monotonous local ID ordering', function(done) {
|
||||
s.on('stream', function(response) {
|
||||
response.headers({ ':status': '200' });
|
||||
|
||||
var p1 = s.createStream();
|
||||
var p2 = s.createStream();
|
||||
response.promise(p2, { ':method': 'get', ':path': '/p2' });
|
||||
response.promise(p1, { ':method': 'get', ':path': '/p1' });
|
||||
p2.headers({ ':status': '200' });
|
||||
p1.headers({ ':status': '200' });
|
||||
});
|
||||
|
||||
var request = c.createStream();
|
||||
request.headers({ ':method': 'get', ':path': '/' });
|
||||
|
||||
done = util.callNTimes(2, done);
|
||||
request.on('promise', function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('closing the connection on one end', function() {
|
||||
it('should result in closed streams on both ends', function(done) {
|
||||
done = util.callNTimes(2, done);
|
||||
c.on('end', done);
|
||||
s.on('end', done);
|
||||
|
||||
c.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
41
testing/xpcshell/node-http2/test/endpoint.js
Normal file
41
testing/xpcshell/node-http2/test/endpoint.js
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
var expect = require('chai').expect;
|
||||
var util = require('./util');
|
||||
|
||||
var endpoint = require('../lib/protocol/endpoint');
|
||||
var Endpoint = endpoint.Endpoint;
|
||||
|
||||
var settings = {
|
||||
SETTINGS_MAX_CONCURRENT_STREAMS: 100,
|
||||
SETTINGS_INITIAL_WINDOW_SIZE: 100000
|
||||
};
|
||||
|
||||
describe('endpoint.js', function() {
|
||||
describe('scenario', function() {
|
||||
describe('connection setup', function() {
|
||||
it('should work as expected', function(done) {
|
||||
var c = new Endpoint(util.log.child({ role: 'client' }), 'CLIENT', settings);
|
||||
var s = new Endpoint(util.log.child({ role: 'client' }), 'SERVER', settings);
|
||||
|
||||
util.log.debug('Test initialization over, starting piping.');
|
||||
c.pipe(s).pipe(c);
|
||||
|
||||
setTimeout(function() {
|
||||
// If there are no exception until this, then we're done
|
||||
done();
|
||||
}, 10);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('bunyan serializer', function() {
|
||||
describe('`e`', function() {
|
||||
var format = endpoint.serializers.e;
|
||||
it('should assign a unique ID to each endpoint', function() {
|
||||
var c = new Endpoint(util.log.child({ role: 'client' }), 'CLIENT', settings);
|
||||
var s = new Endpoint(util.log.child({ role: 'client' }), 'SERVER', settings);
|
||||
expect(format(c)).to.not.equal(format(s));
|
||||
expect(format(c)).to.equal(format(c));
|
||||
expect(format(s)).to.equal(format(s));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
260
testing/xpcshell/node-http2/test/flow.js
Normal file
260
testing/xpcshell/node-http2/test/flow.js
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
var expect = require('chai').expect;
|
||||
var util = require('./util');
|
||||
|
||||
var Flow = require('../lib/protocol/flow').Flow;
|
||||
|
||||
var MAX_PAYLOAD_SIZE = 16384;
|
||||
|
||||
function createFlow(log) {
|
||||
var flowControlId = util.random(10, 100);
|
||||
var flow = new Flow(flowControlId);
|
||||
flow._log = util.log.child(log || {});
|
||||
return flow;
|
||||
}
|
||||
|
||||
describe('flow.js', function() {
|
||||
describe('Flow class', function() {
|
||||
var flow;
|
||||
beforeEach(function() {
|
||||
flow = createFlow();
|
||||
});
|
||||
|
||||
describe('._receive(frame, callback) method', function() {
|
||||
it('is called when there\'s a frame in the input buffer to be consumed', function(done) {
|
||||
var frame = { type: 'PRIORITY', flags: {}, priority: 1 };
|
||||
flow._receive = function _receive(receivedFrame, callback) {
|
||||
expect(receivedFrame).to.equal(frame);
|
||||
callback();
|
||||
};
|
||||
flow.write(frame, done);
|
||||
});
|
||||
it('has to be overridden by the child class, otherwise it throws', function() {
|
||||
expect(flow._receive.bind(flow)).to.throw(Error);
|
||||
});
|
||||
});
|
||||
describe('._send() method', function() {
|
||||
it('is called when the output buffer should be filled with more frames and the flow' +
|
||||
'control queue is empty', function() {
|
||||
var notFlowControlledFrame = { type: 'PRIORITY', flags: {}, priority: 1 };
|
||||
flow._send = function _send() {
|
||||
this.push(notFlowControlledFrame);
|
||||
};
|
||||
expect(flow.read()).to.equal(notFlowControlledFrame);
|
||||
|
||||
flow._window = 0;
|
||||
flow._queue.push({ type: 'DATA', flags: {}, data: { length: 1 } });
|
||||
var frame = flow.read();
|
||||
while (frame.type === notFlowControlledFrame.type) frame = flow.read();
|
||||
expect(frame.type).to.equal('BLOCKED');
|
||||
expect(flow.read()).to.equal(null);
|
||||
});
|
||||
it('has to be overridden by the child class, otherwise it throws', function() {
|
||||
expect(flow._send.bind(flow)).to.throw(Error);
|
||||
});
|
||||
});
|
||||
describe('._increaseWindow(size) method', function() {
|
||||
it('should increase `this._window` by `size`', function() {
|
||||
flow._send = util.noop;
|
||||
flow._window = 0;
|
||||
|
||||
var increase1 = util.random(0,100);
|
||||
var increase2 = util.random(0,100);
|
||||
flow._increaseWindow(increase1);
|
||||
flow._increaseWindow(increase2);
|
||||
expect(flow._window).to.equal(increase1 + increase2);
|
||||
|
||||
flow._increaseWindow(Infinity);
|
||||
expect(flow._window).to.equal(Infinity);
|
||||
});
|
||||
it('should emit error when increasing with a finite `size` when `_window` is infinite', function() {
|
||||
flow._send = util.noop;
|
||||
flow._increaseWindow(Infinity);
|
||||
var increase = util.random(1,100);
|
||||
|
||||
expect(flow._increaseWindow.bind(flow, increase)).to.throw('Uncaught, unspecified "error" event.');
|
||||
});
|
||||
it('should emit error when `_window` grows over the window limit', function() {
|
||||
var WINDOW_SIZE_LIMIT = Math.pow(2, 31) - 1;
|
||||
flow._send = util.noop;
|
||||
flow._window = 0;
|
||||
|
||||
flow._increaseWindow(WINDOW_SIZE_LIMIT);
|
||||
expect(flow._increaseWindow.bind(flow, 1)).to.throw('Uncaught, unspecified "error" event.');
|
||||
|
||||
});
|
||||
});
|
||||
describe('.read() method', function() {
|
||||
describe('when the flow control queue is not empty', function() {
|
||||
it('should return the first item in the queue if the window is enough', function() {
|
||||
var priorityFrame = { type: 'PRIORITY', flags: {}, priority: 1 };
|
||||
var dataFrame = { type: 'DATA', flags: {}, data: { length: 10 } };
|
||||
flow._send = util.noop;
|
||||
flow._window = 10;
|
||||
flow._queue = [priorityFrame, dataFrame];
|
||||
|
||||
expect(flow.read()).to.equal(priorityFrame);
|
||||
expect(flow.read()).to.equal(dataFrame);
|
||||
});
|
||||
it('should also split DATA frames when needed', function() {
|
||||
var buffer = new Buffer(10);
|
||||
var dataFrame = { type: 'DATA', flags: {}, stream: util.random(0, 100), data: buffer };
|
||||
flow._send = util.noop;
|
||||
flow._window = 5;
|
||||
flow._queue = [dataFrame];
|
||||
|
||||
var expectedFragment = { flags: {}, type: 'DATA', stream: dataFrame.stream, data: buffer.slice(0,5) };
|
||||
expect(flow.read()).to.deep.equal(expectedFragment);
|
||||
expect(dataFrame.data).to.deep.equal(buffer.slice(5));
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('.push(frame) method', function() {
|
||||
it('should push `frame` into the output queue or the flow control queue', function() {
|
||||
var priorityFrame = { type: 'PRIORITY', flags: {}, priority: 1 };
|
||||
var dataFrame = { type: 'DATA', flags: {}, data: { length: 10 } };
|
||||
flow._window = 10;
|
||||
|
||||
flow.push(dataFrame); // output queue
|
||||
flow.push(dataFrame); // flow control queue, because of depleted window
|
||||
flow.push(priorityFrame); // flow control queue, because it's not empty
|
||||
|
||||
expect(flow.read()).to.be.equal(dataFrame);
|
||||
expect(flow._queue[0]).to.be.equal(dataFrame);
|
||||
expect(flow._queue[1]).to.be.equal(priorityFrame);
|
||||
});
|
||||
});
|
||||
describe('.write() method', function() {
|
||||
it('call with a DATA frame should trigger sending WINDOW_UPDATE if remote flow control is not' +
|
||||
'disabled', function(done) {
|
||||
flow._window = 100;
|
||||
flow._send = util.noop;
|
||||
flow._receive = function(frame, callback) {
|
||||
callback();
|
||||
};
|
||||
|
||||
var buffer = new Buffer(util.random(10, 100));
|
||||
flow.write({ type: 'DATA', flags: {}, data: buffer });
|
||||
flow.once('readable', function() {
|
||||
expect(flow.read()).to.be.deep.equal({
|
||||
type: 'WINDOW_UPDATE',
|
||||
flags: {},
|
||||
stream: flow._flowControlId,
|
||||
window_size: buffer.length
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('test scenario', function() {
|
||||
var flow1, flow2;
|
||||
beforeEach(function() {
|
||||
flow1 = createFlow({ flow: 1 });
|
||||
flow2 = createFlow({ flow: 2 });
|
||||
flow1._flowControlId = flow2._flowControlId;
|
||||
flow1._send = flow2._send = util.noop;
|
||||
flow1._receive = flow2._receive = function(frame, callback) { callback(); };
|
||||
});
|
||||
|
||||
describe('sending a large data stream', function() {
|
||||
it('should work as expected', function(done) {
|
||||
// Sender side
|
||||
var frameNumber = util.random(5, 8);
|
||||
var input = [];
|
||||
flow1._send = function _send() {
|
||||
if (input.length >= frameNumber) {
|
||||
this.push({ type: 'DATA', flags: { END_STREAM: true }, data: new Buffer(0) });
|
||||
this.push(null);
|
||||
} else {
|
||||
var buffer = new Buffer(util.random(1000, 100000));
|
||||
input.push(buffer);
|
||||
this.push({ type: 'DATA', flags: {}, data: buffer });
|
||||
}
|
||||
};
|
||||
|
||||
// Receiver side
|
||||
var output = [];
|
||||
flow2._receive = function _receive(frame, callback) {
|
||||
if (frame.type === 'DATA') {
|
||||
expect(frame.data.length).to.be.lte(MAX_PAYLOAD_SIZE);
|
||||
output.push(frame.data);
|
||||
}
|
||||
if (frame.flags.END_STREAM) {
|
||||
this.emit('end_stream');
|
||||
}
|
||||
callback();
|
||||
};
|
||||
|
||||
// Checking results
|
||||
flow2.on('end_stream', function() {
|
||||
input = util.concat(input);
|
||||
output = util.concat(output);
|
||||
|
||||
expect(input).to.deep.equal(output);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
// Start piping
|
||||
flow1.pipe(flow2).pipe(flow1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when running out of window', function() {
|
||||
it('should send a BLOCKED frame', function(done) {
|
||||
// Sender side
|
||||
var frameNumber = util.random(5, 8);
|
||||
var input = [];
|
||||
flow1._send = function _send() {
|
||||
if (input.length >= frameNumber) {
|
||||
this.push({ type: 'DATA', flags: { END_STREAM: true }, data: new Buffer(0) });
|
||||
this.push(null);
|
||||
} else {
|
||||
var buffer = new Buffer(util.random(1000, 100000));
|
||||
input.push(buffer);
|
||||
this.push({ type: 'DATA', flags: {}, data: buffer });
|
||||
}
|
||||
};
|
||||
|
||||
// Receiver side
|
||||
// Do not send WINDOW_UPDATESs except when the other side sends BLOCKED
|
||||
var output = [];
|
||||
flow2._restoreWindow = util.noop;
|
||||
flow2._receive = function _receive(frame, callback) {
|
||||
if (frame.type === 'DATA') {
|
||||
expect(frame.data.length).to.be.lte(MAX_PAYLOAD_SIZE);
|
||||
output.push(frame.data);
|
||||
}
|
||||
if (frame.flags.END_STREAM) {
|
||||
this.emit('end_stream');
|
||||
}
|
||||
if (frame.type === 'BLOCKED') {
|
||||
setTimeout(function() {
|
||||
this._push({
|
||||
type: 'WINDOW_UPDATE',
|
||||
flags: {},
|
||||
stream: this._flowControlId,
|
||||
window_size: this._received
|
||||
});
|
||||
this._received = 0;
|
||||
}.bind(this), 20);
|
||||
}
|
||||
callback();
|
||||
};
|
||||
|
||||
// Checking results
|
||||
flow2.on('end_stream', function() {
|
||||
input = util.concat(input);
|
||||
output = util.concat(output);
|
||||
|
||||
expect(input).to.deep.equal(output);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
// Start piping
|
||||
flow1.pipe(flow2).pipe(flow1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
395
testing/xpcshell/node-http2/test/framer.js
Normal file
395
testing/xpcshell/node-http2/test/framer.js
Normal file
|
|
@ -0,0 +1,395 @@
|
|||
var expect = require('chai').expect;
|
||||
var util = require('./util');
|
||||
|
||||
var framer = require('../lib/protocol/framer');
|
||||
var Serializer = framer.Serializer;
|
||||
var Deserializer = framer.Deserializer;
|
||||
|
||||
var frame_types = {
|
||||
DATA: ['data'],
|
||||
HEADERS: ['priority_information', 'data'],
|
||||
PRIORITY: ['priority_information'],
|
||||
RST_STREAM: ['error'],
|
||||
SETTINGS: ['settings'],
|
||||
PUSH_PROMISE: ['promised_stream', 'data'],
|
||||
PING: ['data'],
|
||||
GOAWAY: ['last_stream', 'error'],
|
||||
WINDOW_UPDATE: ['window_size'],
|
||||
CONTINUATION: ['data'],
|
||||
ALTSVC: ['protocolID', 'host', 'port', 'origin', 'maxAge']
|
||||
};
|
||||
|
||||
var test_frames = [{
|
||||
frame: {
|
||||
type: 'DATA',
|
||||
flags: { END_STREAM: false, RESERVED2: false, RESERVED4: false,
|
||||
PADDED: false },
|
||||
stream: 10,
|
||||
|
||||
data: new Buffer('12345678', 'hex')
|
||||
},
|
||||
// length + type + flags + stream + content
|
||||
buffer: new Buffer('000004' + '00' + '00' + '0000000A' + '12345678', 'hex')
|
||||
|
||||
}, {
|
||||
frame: {
|
||||
type: 'HEADERS',
|
||||
flags: { END_STREAM: false, RESERVED2: false, END_HEADERS: false,
|
||||
PADDED: false, RESERVED5: false, PRIORITY: false },
|
||||
stream: 15,
|
||||
|
||||
data: new Buffer('12345678', 'hex')
|
||||
},
|
||||
buffer: new Buffer('000004' + '01' + '00' + '0000000F' + '12345678', 'hex')
|
||||
|
||||
}, {
|
||||
frame: {
|
||||
type: 'HEADERS',
|
||||
flags: { END_STREAM: false, RESERVED2: false, END_HEADERS: false,
|
||||
PADDED: false, RESERVED5: false, PRIORITY: true },
|
||||
stream: 15,
|
||||
priorityDependency: 10,
|
||||
priorityWeight: 5,
|
||||
exclusiveDependency: false,
|
||||
|
||||
data: new Buffer('12345678', 'hex')
|
||||
},
|
||||
buffer: new Buffer('000009' + '01' + '20' + '0000000F' + '0000000A' + '05' + '12345678', 'hex')
|
||||
|
||||
|
||||
}, {
|
||||
frame: {
|
||||
type: 'HEADERS',
|
||||
flags: { END_STREAM: false, RESERVED2: false, END_HEADERS: false,
|
||||
PADDED: false, RESERVED5: false, PRIORITY: true },
|
||||
stream: 15,
|
||||
priorityDependency: 10,
|
||||
priorityWeight: 5,
|
||||
exclusiveDependency: true,
|
||||
|
||||
data: new Buffer('12345678', 'hex')
|
||||
},
|
||||
buffer: new Buffer('000009' + '01' + '20' + '0000000F' + '8000000A' + '05' + '12345678', 'hex')
|
||||
|
||||
}, {
|
||||
frame: {
|
||||
type: 'PRIORITY',
|
||||
flags: { },
|
||||
stream: 10,
|
||||
|
||||
priorityDependency: 9,
|
||||
priorityWeight: 5,
|
||||
exclusiveDependency: false
|
||||
},
|
||||
buffer: new Buffer('000005' + '02' + '00' + '0000000A' + '00000009' + '05', 'hex')
|
||||
|
||||
}, {
|
||||
frame: {
|
||||
type: 'PRIORITY',
|
||||
flags: { },
|
||||
stream: 10,
|
||||
|
||||
priorityDependency: 9,
|
||||
priorityWeight: 5,
|
||||
exclusiveDependency: true
|
||||
},
|
||||
buffer: new Buffer('000005' + '02' + '00' + '0000000A' + '80000009' + '05', 'hex')
|
||||
|
||||
}, {
|
||||
frame: {
|
||||
type: 'RST_STREAM',
|
||||
flags: { },
|
||||
stream: 10,
|
||||
|
||||
error: 'INTERNAL_ERROR'
|
||||
},
|
||||
buffer: new Buffer('000004' + '03' + '00' + '0000000A' + '00000002', 'hex')
|
||||
|
||||
}, {
|
||||
frame: {
|
||||
type: 'SETTINGS',
|
||||
flags: { ACK: false },
|
||||
stream: 10,
|
||||
|
||||
settings: {
|
||||
SETTINGS_HEADER_TABLE_SIZE: 0x12345678,
|
||||
SETTINGS_ENABLE_PUSH: true,
|
||||
SETTINGS_MAX_CONCURRENT_STREAMS: 0x01234567,
|
||||
SETTINGS_INITIAL_WINDOW_SIZE: 0x89ABCDEF,
|
||||
SETTINGS_MAX_FRAME_SIZE: 0x00010000
|
||||
}
|
||||
},
|
||||
buffer: new Buffer('00001E' + '04' + '00' + '0000000A' + '0001' + '12345678' +
|
||||
'0002' + '00000001' +
|
||||
'0003' + '01234567' +
|
||||
'0004' + '89ABCDEF' +
|
||||
'0005' + '00010000', 'hex')
|
||||
|
||||
}, {
|
||||
frame: {
|
||||
type: 'PUSH_PROMISE',
|
||||
flags: { RESERVED1: false, RESERVED2: false, END_PUSH_PROMISE: false,
|
||||
PADDED: false },
|
||||
stream: 15,
|
||||
|
||||
promised_stream: 3,
|
||||
data: new Buffer('12345678', 'hex')
|
||||
},
|
||||
buffer: new Buffer('000008' + '05' + '00' + '0000000F' + '00000003' + '12345678', 'hex')
|
||||
|
||||
}, {
|
||||
frame: {
|
||||
type: 'PING',
|
||||
flags: { ACK: false },
|
||||
stream: 15,
|
||||
|
||||
data: new Buffer('1234567887654321', 'hex')
|
||||
},
|
||||
buffer: new Buffer('000008' + '06' + '00' + '0000000F' + '1234567887654321', 'hex')
|
||||
|
||||
}, {
|
||||
frame: {
|
||||
type: 'GOAWAY',
|
||||
flags: { },
|
||||
stream: 10,
|
||||
|
||||
last_stream: 0x12345678,
|
||||
error: 'PROTOCOL_ERROR'
|
||||
},
|
||||
buffer: new Buffer('000008' + '07' + '00' + '0000000A' + '12345678' + '00000001', 'hex')
|
||||
|
||||
}, {
|
||||
frame: {
|
||||
type: 'WINDOW_UPDATE',
|
||||
flags: { },
|
||||
stream: 10,
|
||||
|
||||
window_size: 0x12345678
|
||||
},
|
||||
buffer: new Buffer('000004' + '08' + '00' + '0000000A' + '12345678', 'hex')
|
||||
}, {
|
||||
frame: {
|
||||
type: 'CONTINUATION',
|
||||
flags: { RESERVED1: false, RESERVED2: false, END_HEADERS: true },
|
||||
stream: 10,
|
||||
|
||||
data: new Buffer('12345678', 'hex')
|
||||
},
|
||||
// length + type + flags + stream + content
|
||||
buffer: new Buffer('000004' + '09' + '04' + '0000000A' + '12345678', 'hex')
|
||||
}, {
|
||||
frame: {
|
||||
type: 'ALTSVC',
|
||||
flags: { },
|
||||
stream: 0,
|
||||
|
||||
maxAge: 31536000,
|
||||
port: 4443,
|
||||
protocolID: "h2",
|
||||
host: "altsvc.example.com",
|
||||
origin: ""
|
||||
},
|
||||
buffer: new Buffer(new Buffer('00002B' + '0A' + '00' + '00000000' + '0000', 'hex') + new Buffer('h2="altsvc.example.com:4443"; ma=31536000', 'ascii'))
|
||||
}, {
|
||||
frame: {
|
||||
type: 'ALTSVC',
|
||||
flags: { },
|
||||
stream: 0,
|
||||
|
||||
maxAge: 31536000,
|
||||
port: 4443,
|
||||
protocolID: "h2",
|
||||
host: "altsvc.example.com",
|
||||
origin: "https://onlyme.example.com"
|
||||
},
|
||||
buffer: new Buffer(new Buffer('000045' + '0A' + '00' + '00000000' + '001A', 'hex') + new Buffer('https://onlyme.example.comh2="altsvc.example.com:4443"; ma=31536000', 'ascii'))
|
||||
|
||||
}, {
|
||||
frame: {
|
||||
type: 'BLOCKED',
|
||||
flags: { },
|
||||
stream: 10
|
||||
},
|
||||
buffer: new Buffer('000000' + '0B' + '00' + '0000000A', 'hex')
|
||||
}];
|
||||
|
||||
var deserializer_test_frames = test_frames.slice(0);
|
||||
var padded_test_frames = [{
|
||||
frame: {
|
||||
type: 'DATA',
|
||||
flags: { END_STREAM: false, RESERVED2: false, RESERVED4: false,
|
||||
PADDED: true },
|
||||
stream: 10,
|
||||
data: new Buffer('12345678', 'hex')
|
||||
},
|
||||
// length + type + flags + stream + pad length + content + padding
|
||||
buffer: new Buffer('00000B' + '00' + '08' + '0000000A' + '06' + '12345678' + '000000000000', 'hex')
|
||||
|
||||
}, {
|
||||
frame: {
|
||||
type: 'HEADERS',
|
||||
flags: { END_STREAM: false, RESERVED2: false, END_HEADERS: false,
|
||||
PADDED: true, RESERVED5: false, PRIORITY: false },
|
||||
stream: 15,
|
||||
|
||||
data: new Buffer('12345678', 'hex')
|
||||
},
|
||||
// length + type + flags + stream + pad length + data + padding
|
||||
buffer: new Buffer('00000B' + '01' + '08' + '0000000F' + '06' + '12345678' + '000000000000', 'hex')
|
||||
|
||||
}, {
|
||||
frame: {
|
||||
type: 'HEADERS',
|
||||
flags: { END_STREAM: false, RESERVED2: false, END_HEADERS: false,
|
||||
PADDED: true, RESERVED5: false, PRIORITY: true },
|
||||
stream: 15,
|
||||
priorityDependency: 10,
|
||||
priorityWeight: 5,
|
||||
exclusiveDependency: false,
|
||||
|
||||
data: new Buffer('12345678', 'hex')
|
||||
},
|
||||
// length + type + flags + stream + pad length + priority dependency + priority weight + data + padding
|
||||
buffer: new Buffer('000010' + '01' + '28' + '0000000F' + '06' + '0000000A' + '05' + '12345678' + '000000000000', 'hex')
|
||||
|
||||
}, {
|
||||
frame: {
|
||||
type: 'HEADERS',
|
||||
flags: { END_STREAM: false, RESERVED2: false, END_HEADERS: false,
|
||||
PADDED: true, RESERVED5: false, PRIORITY: true },
|
||||
stream: 15,
|
||||
priorityDependency: 10,
|
||||
priorityWeight: 5,
|
||||
exclusiveDependency: true,
|
||||
|
||||
data: new Buffer('12345678', 'hex')
|
||||
},
|
||||
// length + type + flags + stream + pad length + priority dependency + priority weight + data + padding
|
||||
buffer: new Buffer('000010' + '01' + '28' + '0000000F' + '06' + '8000000A' + '05' + '12345678' + '000000000000', 'hex')
|
||||
|
||||
}, {
|
||||
frame: {
|
||||
type: 'PUSH_PROMISE',
|
||||
flags: { RESERVED1: false, RESERVED2: false, END_PUSH_PROMISE: false,
|
||||
PADDED: true },
|
||||
stream: 15,
|
||||
|
||||
promised_stream: 3,
|
||||
data: new Buffer('12345678', 'hex')
|
||||
},
|
||||
// length + type + flags + stream + pad length + promised stream + data + padding
|
||||
buffer: new Buffer('00000F' + '05' + '08' + '0000000F' + '06' + '00000003' + '12345678' + '000000000000', 'hex')
|
||||
|
||||
}];
|
||||
for (var idx = 0; idx < padded_test_frames.length; idx++) {
|
||||
deserializer_test_frames.push(padded_test_frames[idx]);
|
||||
}
|
||||
|
||||
|
||||
describe('framer.js', function() {
|
||||
describe('Serializer', function() {
|
||||
describe('static method .commonHeader({ type, flags, stream }, buffer_array)', function() {
|
||||
it('should add the appropriate 9 byte header buffer in front of the others', function() {
|
||||
for (var i = 0; i < test_frames.length; i++) {
|
||||
var test = test_frames[i];
|
||||
var buffers = [test.buffer.slice(9)];
|
||||
var header_buffer = test.buffer.slice(0,9);
|
||||
Serializer.commonHeader(test.frame, buffers);
|
||||
expect(buffers[0]).to.deep.equal(header_buffer);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Object.keys(frame_types).forEach(function(type) {
|
||||
var tests = test_frames.filter(function(test) { return test.frame.type === type; });
|
||||
var frame_shape = '{ ' + frame_types[type].join(', ') + ' }';
|
||||
describe('static method .' + type + '(' + frame_shape + ', buffer_array)', function() {
|
||||
it('should push buffers to the array that make up a ' + type + ' type payload', function() {
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
var test = tests[i];
|
||||
var buffers = [];
|
||||
Serializer[type](test.frame, buffers);
|
||||
expect(util.concat(buffers)).to.deep.equal(test.buffer.slice(9));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('transform stream', function() {
|
||||
it('should transform frame objects to appropriate buffers', function() {
|
||||
var stream = new Serializer(util.log);
|
||||
|
||||
for (var i = 0; i < test_frames.length; i++) {
|
||||
var test = test_frames[i];
|
||||
stream.write(test.frame);
|
||||
var chunk, buffer = new Buffer(0);
|
||||
while (chunk = stream.read()) {
|
||||
buffer = util.concat([buffer, chunk]);
|
||||
}
|
||||
expect(buffer).to.be.deep.equal(test.buffer);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Deserializer', function() {
|
||||
describe('static method .commonHeader(header_buffer, frame)', function() {
|
||||
it('should augment the frame object with these properties: { type, flags, stream })', function() {
|
||||
for (var i = 0; i < deserializer_test_frames.length; i++) {
|
||||
var test = deserializer_test_frames[i], frame = {};
|
||||
Deserializer.commonHeader(test.buffer.slice(0,9), frame);
|
||||
expect(frame).to.deep.equal({
|
||||
type: test.frame.type,
|
||||
flags: test.frame.flags,
|
||||
stream: test.frame.stream
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Object.keys(frame_types).forEach(function(type) {
|
||||
var tests = deserializer_test_frames.filter(function(test) { return test.frame.type === type; });
|
||||
var frame_shape = '{ ' + frame_types[type].join(', ') + ' }';
|
||||
describe('static method .' + type + '(payload_buffer, frame)', function() {
|
||||
it('should augment the frame object with these properties: ' + frame_shape, function() {
|
||||
for (var i = 0; i < tests.length; i++) {
|
||||
var test = tests[i];
|
||||
var frame = {
|
||||
type: test.frame.type,
|
||||
flags: test.frame.flags,
|
||||
stream: test.frame.stream
|
||||
};
|
||||
Deserializer[type](test.buffer.slice(9), frame);
|
||||
expect(frame).to.deep.equal(test.frame);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('transform stream', function() {
|
||||
it('should transform buffers to appropriate frame object', function() {
|
||||
var stream = new Deserializer(util.log);
|
||||
|
||||
var shuffled = util.shuffleBuffers(deserializer_test_frames.map(function(test) { return test.buffer; }));
|
||||
shuffled.forEach(stream.write.bind(stream));
|
||||
|
||||
for (var j = 0; j < deserializer_test_frames.length; j++) {
|
||||
expect(stream.read()).to.be.deep.equal(deserializer_test_frames[j].frame);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('bunyan formatter', function() {
|
||||
describe('`frame`', function() {
|
||||
var format = framer.serializers.frame;
|
||||
it('should assign a unique ID to each frame', function() {
|
||||
var frame1 = { type: 'DATA', data: new Buffer(10) };
|
||||
var frame2 = { type: 'PRIORITY', priority: 1 };
|
||||
expect(format(frame1).id).to.be.equal(format(frame1));
|
||||
expect(format(frame2).id).to.be.equal(format(frame2));
|
||||
expect(format(frame1)).to.not.be.equal(format(frame2));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
793
testing/xpcshell/node-http2/test/http.js
Normal file
793
testing/xpcshell/node-http2/test/http.js
Normal file
|
|
@ -0,0 +1,793 @@
|
|||
var expect = require('chai').expect;
|
||||
var util = require('./util');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var url = require('url');
|
||||
var net = require('net');
|
||||
|
||||
var http2 = require('../lib/http');
|
||||
var https = require('https');
|
||||
|
||||
var serverOptions = {
|
||||
key: fs.readFileSync(path.join(__dirname, '../example/localhost.key')),
|
||||
cert: fs.readFileSync(path.join(__dirname, '../example/localhost.crt')),
|
||||
rejectUnauthorized: true,
|
||||
log: util.serverLog
|
||||
};
|
||||
|
||||
var agentOptions = {
|
||||
key: serverOptions.key,
|
||||
ca: serverOptions.cert,
|
||||
rejectUnauthorized: true,
|
||||
log: util.clientLog
|
||||
};
|
||||
|
||||
var globalAgent = new http2.Agent(agentOptions);
|
||||
|
||||
describe('http.js', function() {
|
||||
beforeEach(function() {
|
||||
http2.globalAgent = globalAgent;
|
||||
});
|
||||
describe('Server', function() {
|
||||
describe('new Server(options)', function() {
|
||||
it('should throw if called without \'plain\' or TLS options', function() {
|
||||
expect(function() {
|
||||
new http2.Server();
|
||||
}).to.throw(Error);
|
||||
expect(function() {
|
||||
http2.createServer(util.noop);
|
||||
}).to.throw(Error);
|
||||
});
|
||||
});
|
||||
describe('method `listen()`', function () {
|
||||
it('should emit `listening` event', function (done) {
|
||||
var server = http2.createServer(serverOptions);
|
||||
|
||||
server.on('listening', function () {
|
||||
server.close();
|
||||
|
||||
done();
|
||||
})
|
||||
|
||||
server.listen(0);
|
||||
});
|
||||
it('should emit `error` on failure', function (done) {
|
||||
var server = http2.createServer(serverOptions);
|
||||
|
||||
// This TCP server is used to explicitly take a port to make
|
||||
// server.listen() fails.
|
||||
var net = require('net').createServer();
|
||||
|
||||
server.on('error', function () {
|
||||
net.close()
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
net.listen(0, function () {
|
||||
server.listen(this.address().port);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('property `timeout`', function() {
|
||||
it('should be a proxy for the backing HTTPS server\'s `timeout` property', function() {
|
||||
var server = new http2.Server(serverOptions);
|
||||
var backingServer = server._server;
|
||||
var newTimeout = 10;
|
||||
server.timeout = newTimeout;
|
||||
expect(server.timeout).to.be.equal(newTimeout);
|
||||
expect(backingServer.timeout).to.be.equal(newTimeout);
|
||||
});
|
||||
});
|
||||
describe('method `setTimeout(timeout, [callback])`', function() {
|
||||
it('should be a proxy for the backing HTTPS server\'s `setTimeout` method', function() {
|
||||
var server = new http2.Server(serverOptions);
|
||||
var backingServer = server._server;
|
||||
var newTimeout = 10;
|
||||
var newCallback = util.noop;
|
||||
backingServer.setTimeout = function(timeout, callback) {
|
||||
expect(timeout).to.be.equal(newTimeout);
|
||||
expect(callback).to.be.equal(newCallback);
|
||||
};
|
||||
server.setTimeout(newTimeout, newCallback);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Agent', function() {
|
||||
describe('property `maxSockets`', function() {
|
||||
it('should be a proxy for the backing HTTPS agent\'s `maxSockets` property', function() {
|
||||
var agent = new http2.Agent({ log: util.clientLog });
|
||||
var backingAgent = agent._httpsAgent;
|
||||
var newMaxSockets = backingAgent.maxSockets + 1;
|
||||
agent.maxSockets = newMaxSockets;
|
||||
expect(agent.maxSockets).to.be.equal(newMaxSockets);
|
||||
expect(backingAgent.maxSockets).to.be.equal(newMaxSockets);
|
||||
});
|
||||
});
|
||||
describe('method `request(options, [callback])`', function() {
|
||||
it('should use a new agent for request-specific TLS settings', function(done) {
|
||||
var path = '/x';
|
||||
var message = 'Hello world';
|
||||
|
||||
var server = http2.createServer(serverOptions, function(request, response) {
|
||||
expect(request.url).to.equal(path);
|
||||
response.end(message);
|
||||
});
|
||||
|
||||
server.listen(1234, function() {
|
||||
var options = url.parse('https://localhost:1234' + path);
|
||||
options.key = agentOptions.key;
|
||||
options.ca = agentOptions.ca;
|
||||
options.rejectUnauthorized = true;
|
||||
|
||||
http2.globalAgent = new http2.Agent({ log: util.clientLog });
|
||||
http2.get(options, function(response) {
|
||||
response.on('data', function(data) {
|
||||
expect(data.toString()).to.equal(message);
|
||||
server.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should throw when trying to use with \'http\' scheme', function() {
|
||||
expect(function() {
|
||||
var agent = new http2.Agent({ log: util.clientLog });
|
||||
agent.request({ protocol: 'http:' });
|
||||
}).to.throw(Error);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('OutgoingRequest', function() {
|
||||
function testFallbackProxyMethod(name, originalArguments, done) {
|
||||
var request = new http2.OutgoingRequest();
|
||||
|
||||
// When in HTTP/2 mode, this call should be ignored
|
||||
request.stream = { reset: util.noop };
|
||||
request[name].apply(request, originalArguments);
|
||||
delete request.stream;
|
||||
|
||||
// When in fallback mode, this call should be forwarded
|
||||
request[name].apply(request, originalArguments);
|
||||
var mockFallbackRequest = { on: util.noop };
|
||||
mockFallbackRequest[name] = function() {
|
||||
expect(Array.prototype.slice.call(arguments)).to.deep.equal(originalArguments);
|
||||
done();
|
||||
};
|
||||
request._fallback(mockFallbackRequest);
|
||||
}
|
||||
describe('method `setNoDelay(noDelay)`', function() {
|
||||
it('should act as a proxy for the backing HTTPS agent\'s `setNoDelay` method', function(done) {
|
||||
testFallbackProxyMethod('setNoDelay', [true], done);
|
||||
});
|
||||
});
|
||||
describe('method `setSocketKeepAlive(enable, initialDelay)`', function() {
|
||||
it('should act as a proxy for the backing HTTPS agent\'s `setSocketKeepAlive` method', function(done) {
|
||||
testFallbackProxyMethod('setSocketKeepAlive', [true, util.random(10, 100)], done);
|
||||
});
|
||||
});
|
||||
describe('method `setTimeout(timeout, [callback])`', function() {
|
||||
it('should act as a proxy for the backing HTTPS agent\'s `setTimeout` method', function(done) {
|
||||
testFallbackProxyMethod('setTimeout', [util.random(10, 100), util.noop], done);
|
||||
});
|
||||
});
|
||||
describe('method `abort()`', function() {
|
||||
it('should act as a proxy for the backing HTTPS agent\'s `abort` method', function(done) {
|
||||
testFallbackProxyMethod('abort', [], done);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('OutgoingResponse', function() {
|
||||
it('should throw error when writeHead is called multiple times on it', function() {
|
||||
var called = false;
|
||||
var stream = { _log: util.log, headers: function () {
|
||||
if (called) {
|
||||
throw new Error('Should not send headers twice');
|
||||
} else {
|
||||
called = true;
|
||||
}
|
||||
}, once: util.noop };
|
||||
var response = new http2.OutgoingResponse(stream);
|
||||
|
||||
response.writeHead(200);
|
||||
response.writeHead(404);
|
||||
});
|
||||
it('field finished should be Boolean', function(){
|
||||
var stream = { _log: util.log, headers: function () {}, once: util.noop };
|
||||
var response = new http2.OutgoingResponse(stream);
|
||||
expect(response.finished).to.be.a('Boolean');
|
||||
});
|
||||
it('field finished should initially be false and then go to true when response completes',function(done){
|
||||
var res;
|
||||
var server = http2.createServer(serverOptions, function(request, response) {
|
||||
res = response;
|
||||
expect(res.finished).to.be.false;
|
||||
response.end('HiThere');
|
||||
});
|
||||
server.listen(1236, function() {
|
||||
http2.get('https://localhost:1236/finished-test', function(response) {
|
||||
response.on('data', function(data){
|
||||
var sink = data; //
|
||||
});
|
||||
response.on('end',function(){
|
||||
expect(res.finished).to.be.true;
|
||||
server.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('test scenario', function() {
|
||||
describe('simple request', function() {
|
||||
it('should work as expected', function(done) {
|
||||
var path = '/x';
|
||||
var message = 'Hello world';
|
||||
|
||||
var server = http2.createServer(serverOptions, function(request, response) {
|
||||
expect(request.url).to.equal(path);
|
||||
response.end(message);
|
||||
});
|
||||
|
||||
server.listen(1234, function() {
|
||||
http2.get('https://localhost:1234' + path, function(response) {
|
||||
response.on('data', function(data) {
|
||||
expect(data.toString()).to.equal(message);
|
||||
server.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('2 simple request in parallel', function() {
|
||||
it('should work as expected', function(originalDone) {
|
||||
var path = '/x';
|
||||
var message = 'Hello world';
|
||||
var done = util.callNTimes(2, function() {
|
||||
server.close();
|
||||
originalDone();
|
||||
});
|
||||
|
||||
var server = http2.createServer(serverOptions, function(request, response) {
|
||||
expect(request.url).to.equal(path);
|
||||
response.end(message);
|
||||
});
|
||||
|
||||
server.listen(1234, function() {
|
||||
http2.get('https://localhost:1234' + path, function(response) {
|
||||
response.on('data', function(data) {
|
||||
expect(data.toString()).to.equal(message);
|
||||
done();
|
||||
});
|
||||
});
|
||||
http2.get('https://localhost:1234' + path, function(response) {
|
||||
response.on('data', function(data) {
|
||||
expect(data.toString()).to.equal(message);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('100 simple request in a series', function() {
|
||||
it('should work as expected', function(done) {
|
||||
var path = '/x';
|
||||
var message = 'Hello world';
|
||||
|
||||
var server = http2.createServer(serverOptions, function(request, response) {
|
||||
expect(request.url).to.equal(path);
|
||||
response.end(message);
|
||||
});
|
||||
|
||||
var n = 100;
|
||||
server.listen(1242, function() {
|
||||
doRequest();
|
||||
function doRequest() {
|
||||
http2.get('https://localhost:1242' + path, function(response) {
|
||||
response.on('data', function(data) {
|
||||
expect(data.toString()).to.equal(message);
|
||||
if (n) {
|
||||
n -= 1;
|
||||
doRequest();
|
||||
} else {
|
||||
server.close();
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('request with payload', function() {
|
||||
it('should work as expected', function(done) {
|
||||
var path = '/x';
|
||||
var message = 'Hello world';
|
||||
|
||||
var server = http2.createServer(serverOptions, function(request, response) {
|
||||
expect(request.url).to.equal(path);
|
||||
request.once('data', function(data) {
|
||||
expect(data.toString()).to.equal(message);
|
||||
response.end();
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(1240, function() {
|
||||
var request = http2.request({
|
||||
host: 'localhost',
|
||||
port: 1240,
|
||||
path: path
|
||||
});
|
||||
request.write(message);
|
||||
request.end();
|
||||
request.on('response', function() {
|
||||
server.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('request with custom status code and headers', function() {
|
||||
it('should work as expected', function(done) {
|
||||
var path = '/x';
|
||||
var message = 'Hello world';
|
||||
var headerName = 'name';
|
||||
var headerValue = 'value';
|
||||
|
||||
var server = http2.createServer(serverOptions, function(request, response) {
|
||||
// Request URL and headers
|
||||
expect(request.url).to.equal(path);
|
||||
expect(request.headers[headerName]).to.equal(headerValue);
|
||||
|
||||
// A header to be overwritten later
|
||||
response.setHeader(headerName, 'to be overwritten');
|
||||
expect(response.getHeader(headerName)).to.equal('to be overwritten');
|
||||
|
||||
// A header to be deleted
|
||||
response.setHeader('nonexistent', 'x');
|
||||
response.removeHeader('nonexistent');
|
||||
expect(response.getHeader('nonexistent')).to.equal(undefined);
|
||||
|
||||
// A set-cookie header which should always be an array
|
||||
response.setHeader('set-cookie', 'foo');
|
||||
|
||||
// Don't send date
|
||||
response.sendDate = false;
|
||||
|
||||
// Specifying more headers, the status code and a reason phrase with `writeHead`
|
||||
var moreHeaders = {};
|
||||
moreHeaders[headerName] = headerValue;
|
||||
response.writeHead(600, 'to be discarded', moreHeaders);
|
||||
expect(response.getHeader(headerName)).to.equal(headerValue);
|
||||
|
||||
// Empty response body
|
||||
response.end(message);
|
||||
});
|
||||
|
||||
server.listen(1239, function() {
|
||||
var headers = {};
|
||||
headers[headerName] = headerValue;
|
||||
var request = http2.request({
|
||||
host: 'localhost',
|
||||
port: 1239,
|
||||
path: path,
|
||||
headers: headers
|
||||
});
|
||||
request.end();
|
||||
request.on('response', function(response) {
|
||||
expect(response.headers[headerName]).to.equal(headerValue);
|
||||
expect(response.headers['nonexistent']).to.equal(undefined);
|
||||
expect(response.headers['set-cookie']).to.an.instanceof(Array)
|
||||
expect(response.headers['set-cookie']).to.deep.equal(['foo'])
|
||||
expect(response.headers['date']).to.equal(undefined);
|
||||
response.on('data', function(data) {
|
||||
expect(data.toString()).to.equal(message);
|
||||
server.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('request over plain TCP', function() {
|
||||
it('should work as expected', function(done) {
|
||||
var path = '/x';
|
||||
var message = 'Hello world';
|
||||
|
||||
var server = http2.raw.createServer({
|
||||
log: util.serverLog
|
||||
}, function(request, response) {
|
||||
expect(request.url).to.equal(path);
|
||||
response.end(message);
|
||||
});
|
||||
|
||||
server.listen(1237, function() {
|
||||
var request = http2.raw.request({
|
||||
plain: true,
|
||||
host: 'localhost',
|
||||
port: 1237,
|
||||
path: path
|
||||
}, function(response) {
|
||||
response.on('data', function(data) {
|
||||
expect(data.toString()).to.equal(message);
|
||||
server.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
request.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('get over plain TCP', function() {
|
||||
it('should work as expected', function(done) {
|
||||
var path = '/x';
|
||||
var message = 'Hello world';
|
||||
|
||||
var server = http2.raw.createServer({
|
||||
log: util.serverLog
|
||||
}, function(request, response) {
|
||||
expect(request.url).to.equal(path);
|
||||
response.end(message);
|
||||
});
|
||||
|
||||
server.listen(1237, function() {
|
||||
var request = http2.raw.get('http://localhost:1237/x', function(response) {
|
||||
response.on('data', function(data) {
|
||||
expect(data.toString()).to.equal(message);
|
||||
server.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
request.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('request to an HTTPS/1 server', function() {
|
||||
it('should fall back to HTTPS/1 successfully', function(done) {
|
||||
var path = '/x';
|
||||
var message = 'Hello world';
|
||||
|
||||
var server = https.createServer(serverOptions, function(request, response) {
|
||||
expect(request.url).to.equal(path);
|
||||
response.end(message);
|
||||
});
|
||||
|
||||
server.listen(5678, function() {
|
||||
http2.get('https://localhost:5678' + path, function(response) {
|
||||
response.on('data', function(data) {
|
||||
expect(data.toString()).to.equal(message);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('2 parallel request to an HTTPS/1 server', function() {
|
||||
it('should fall back to HTTPS/1 successfully', function(originalDone) {
|
||||
var path = '/x';
|
||||
var message = 'Hello world';
|
||||
var done = util.callNTimes(2, function() {
|
||||
server.close();
|
||||
originalDone();
|
||||
});
|
||||
|
||||
var server = https.createServer(serverOptions, function(request, response) {
|
||||
expect(request.url).to.equal(path);
|
||||
response.end(message);
|
||||
});
|
||||
|
||||
server.listen(6789, function() {
|
||||
http2.get('https://localhost:6789' + path, function(response) {
|
||||
response.on('data', function(data) {
|
||||
expect(data.toString()).to.equal(message);
|
||||
done();
|
||||
});
|
||||
});
|
||||
http2.get('https://localhost:6789' + path, function(response) {
|
||||
response.on('data', function(data) {
|
||||
expect(data.toString()).to.equal(message);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('HTTPS/1 request to a HTTP/2 server', function() {
|
||||
it('should fall back to HTTPS/1 successfully', function(done) {
|
||||
var path = '/x';
|
||||
var message = 'Hello world';
|
||||
|
||||
var server = http2.createServer(serverOptions, function(request, response) {
|
||||
expect(request.url).to.equal(path);
|
||||
response.end(message);
|
||||
});
|
||||
|
||||
server.listen(1236, function() {
|
||||
var options = url.parse('https://localhost:1236' + path);
|
||||
options.agent = new https.Agent(agentOptions);
|
||||
https.get(options, function(response) {
|
||||
response.on('data', function(data) {
|
||||
expect(data.toString()).to.equal(message);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('two parallel request', function() {
|
||||
it('should work as expected', function(done) {
|
||||
var path = '/x';
|
||||
var message = 'Hello world';
|
||||
|
||||
var server = http2.createServer(serverOptions, function(request, response) {
|
||||
expect(request.url).to.equal(path);
|
||||
response.end(message);
|
||||
});
|
||||
|
||||
server.listen(1237, function() {
|
||||
done = util.callNTimes(2, done);
|
||||
// 1. request
|
||||
http2.get('https://localhost:1237' + path, function(response) {
|
||||
response.on('data', function(data) {
|
||||
expect(data.toString()).to.equal(message);
|
||||
done();
|
||||
});
|
||||
});
|
||||
// 2. request
|
||||
http2.get('https://localhost:1237' + path, function(response) {
|
||||
response.on('data', function(data) {
|
||||
expect(data.toString()).to.equal(message);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('two subsequent request', function() {
|
||||
it('should use the same HTTP/2 connection', function(done) {
|
||||
var path = '/x';
|
||||
var message = 'Hello world';
|
||||
|
||||
var server = http2.createServer(serverOptions, function(request, response) {
|
||||
expect(request.url).to.equal(path);
|
||||
response.end(message);
|
||||
});
|
||||
|
||||
server.listen(1238, function() {
|
||||
// 1. request
|
||||
http2.get('https://localhost:1238' + path, function(response) {
|
||||
response.on('data', function(data) {
|
||||
expect(data.toString()).to.equal(message);
|
||||
|
||||
// 2. request
|
||||
http2.get('https://localhost:1238' + path, function(response) {
|
||||
response.on('data', function(data) {
|
||||
expect(data.toString()).to.equal(message);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('https server node module specification conformance', function() {
|
||||
it('should provide API for remote HTTP 1.1 client address', function(done) {
|
||||
var remoteAddress = null;
|
||||
var remotePort = null;
|
||||
|
||||
var server = http2.createServer(serverOptions, function(request, response) {
|
||||
// HTTPS 1.1 client with Node 0.10 server
|
||||
if (!request.remoteAddress) {
|
||||
if (request.socket.socket) {
|
||||
remoteAddress = request.socket.socket.remoteAddress;
|
||||
remotePort = request.socket.socket.remotePort;
|
||||
} else {
|
||||
remoteAddress = request.socket.remoteAddress;
|
||||
remotePort = request.socket.remotePort;
|
||||
}
|
||||
} else {
|
||||
// HTTPS 1.1/2.0 client with Node 0.12 server
|
||||
remoteAddress = request.remoteAddress;
|
||||
remotePort = request.remotePort;
|
||||
}
|
||||
response.write('Pong');
|
||||
response.end();
|
||||
});
|
||||
|
||||
server.listen(1259, 'localhost', function() {
|
||||
var request = https.request({
|
||||
host: 'localhost',
|
||||
port: 1259,
|
||||
path: '/',
|
||||
ca: serverOptions.cert
|
||||
});
|
||||
request.write('Ping');
|
||||
request.end();
|
||||
request.on('response', function(response) {
|
||||
response.on('data', function(data) {
|
||||
var localAddress = response.socket.address();
|
||||
expect(remoteAddress).to.equal(localAddress.address);
|
||||
expect(remotePort).to.equal(localAddress.port);
|
||||
server.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should provide API for remote HTTP 2.0 client address', function(done) {
|
||||
var remoteAddress = null;
|
||||
var remotePort = null;
|
||||
var localAddress = null;
|
||||
|
||||
var server = http2.createServer(serverOptions, function(request, response) {
|
||||
remoteAddress = request.remoteAddress;
|
||||
remotePort = request.remotePort;
|
||||
response.write('Pong');
|
||||
response.end();
|
||||
});
|
||||
|
||||
server.listen(1258, 'localhost', function() {
|
||||
var request = http2.request({
|
||||
host: 'localhost',
|
||||
port: 1258,
|
||||
path: '/'
|
||||
});
|
||||
request.write('Ping');
|
||||
globalAgent.on('false:localhost:1258', function(endpoint) {
|
||||
localAddress = endpoint.socket.address();
|
||||
});
|
||||
request.end();
|
||||
request.on('response', function(response) {
|
||||
response.on('data', function(data) {
|
||||
expect(remoteAddress).to.equal(localAddress.address);
|
||||
expect(remotePort).to.equal(localAddress.port);
|
||||
server.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should expose net.Socket as .socket and .connection', function(done) {
|
||||
var server = http2.createServer(serverOptions, function(request, response) {
|
||||
expect(request.socket).to.equal(request.connection);
|
||||
expect(request.socket).to.be.instanceof(net.Socket);
|
||||
response.write('Pong');
|
||||
response.end();
|
||||
done();
|
||||
});
|
||||
|
||||
server.listen(1248, 'localhost', function() {
|
||||
var request = https.request({
|
||||
host: 'localhost',
|
||||
port: 1248,
|
||||
path: '/',
|
||||
ca: serverOptions.cert
|
||||
});
|
||||
request.write('Ping');
|
||||
request.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('request and response with trailers', function() {
|
||||
it('should work as expected', function(done) {
|
||||
var path = '/x';
|
||||
var message = 'Hello world';
|
||||
var requestTrailers = { 'content-md5': 'x' };
|
||||
var responseTrailers = { 'content-md5': 'y' };
|
||||
|
||||
var server = http2.createServer(serverOptions, function(request, response) {
|
||||
expect(request.url).to.equal(path);
|
||||
request.on('data', util.noop);
|
||||
request.once('end', function() {
|
||||
expect(request.trailers).to.deep.equal(requestTrailers);
|
||||
response.write(message);
|
||||
response.addTrailers(responseTrailers);
|
||||
response.end();
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(1241, function() {
|
||||
var request = http2.request('https://localhost:1241' + path);
|
||||
request.addTrailers(requestTrailers);
|
||||
request.end();
|
||||
request.on('response', function(response) {
|
||||
response.on('data', util.noop);
|
||||
response.once('end', function() {
|
||||
expect(response.trailers).to.deep.equal(responseTrailers);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Handle socket error', function () {
|
||||
it('HTTPS on Connection Refused error', function (done) {
|
||||
var path = '/x';
|
||||
var request = http2.request('https://127.0.0.1:6666' + path);
|
||||
|
||||
request.on('error', function (err) {
|
||||
expect(err.errno).to.equal('ECONNREFUSED');
|
||||
done();
|
||||
});
|
||||
|
||||
request.on('response', function (response) {
|
||||
server._server._handle.destroy();
|
||||
|
||||
response.on('data', util.noop);
|
||||
|
||||
response.once('end', function () {
|
||||
done(new Error('Request should have failed'));
|
||||
});
|
||||
});
|
||||
|
||||
request.end();
|
||||
|
||||
});
|
||||
it('HTTP on Connection Refused error', function (done) {
|
||||
var path = '/x';
|
||||
|
||||
var request = http2.raw.request('http://127.0.0.1:6666' + path);
|
||||
|
||||
request.on('error', function (err) {
|
||||
expect(err.errno).to.equal('ECONNREFUSED');
|
||||
done();
|
||||
});
|
||||
|
||||
request.on('response', function (response) {
|
||||
server._server._handle.destroy();
|
||||
|
||||
response.on('data', util.noop);
|
||||
|
||||
response.once('end', function () {
|
||||
done(new Error('Request should have failed'));
|
||||
});
|
||||
});
|
||||
|
||||
request.end();
|
||||
});
|
||||
});
|
||||
describe('server push', function() {
|
||||
it('should work as expected', function(done) {
|
||||
var path = '/x';
|
||||
var message = 'Hello world';
|
||||
var pushedPath = '/y';
|
||||
var pushedMessage = 'Hello world 2';
|
||||
|
||||
var server = http2.createServer(serverOptions, function(request, response) {
|
||||
expect(request.url).to.equal(path);
|
||||
var push1 = response.push('/y');
|
||||
push1.end(pushedMessage);
|
||||
var push2 = response.push({ path: '/y', protocol: 'https:' });
|
||||
push2.end(pushedMessage);
|
||||
response.end(message);
|
||||
});
|
||||
|
||||
server.listen(1235, function() {
|
||||
var request = http2.get('https://localhost:1235' + path);
|
||||
done = util.callNTimes(5, done);
|
||||
|
||||
request.on('response', function(response) {
|
||||
response.on('data', function(data) {
|
||||
expect(data.toString()).to.equal(message);
|
||||
done();
|
||||
});
|
||||
response.on('end', done);
|
||||
});
|
||||
|
||||
request.on('push', function(promise) {
|
||||
expect(promise.url).to.be.equal(pushedPath);
|
||||
promise.on('response', function(pushStream) {
|
||||
pushStream.on('data', function(data) {
|
||||
expect(data.toString()).to.equal(pushedMessage);
|
||||
done();
|
||||
});
|
||||
pushStream.on('end', done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
413
testing/xpcshell/node-http2/test/stream.js
Normal file
413
testing/xpcshell/node-http2/test/stream.js
Normal file
|
|
@ -0,0 +1,413 @@
|
|||
var expect = require('chai').expect;
|
||||
var util = require('./util');
|
||||
|
||||
var stream = require('../lib/protocol/stream');
|
||||
var Stream = stream.Stream;
|
||||
|
||||
function createStream() {
|
||||
var stream = new Stream(util.log, null);
|
||||
stream.upstream._window = Infinity;
|
||||
return stream;
|
||||
}
|
||||
|
||||
// Execute a list of commands and assertions
|
||||
var recorded_events = ['state', 'error', 'window_update', 'headers', 'promise'];
|
||||
function execute_sequence(stream, sequence, done) {
|
||||
if (!done) {
|
||||
done = sequence;
|
||||
sequence = stream;
|
||||
stream = createStream();
|
||||
}
|
||||
|
||||
var outgoing_frames = [];
|
||||
|
||||
var emit = stream.emit, events = [];
|
||||
stream.emit = function(name) {
|
||||
if (recorded_events.indexOf(name) !== -1) {
|
||||
events.push({ name: name, data: Array.prototype.slice.call(arguments, 1) });
|
||||
}
|
||||
return emit.apply(this, arguments);
|
||||
};
|
||||
|
||||
var commands = [], checks = [];
|
||||
sequence.forEach(function(step) {
|
||||
if ('method' in step || 'incoming' in step || 'outgoing' in step || 'wait' in step || 'set_state' in step) {
|
||||
commands.push(step);
|
||||
}
|
||||
|
||||
if ('outgoing' in step || 'event' in step || 'active' in step) {
|
||||
checks.push(step);
|
||||
}
|
||||
});
|
||||
|
||||
var activeCount = 0;
|
||||
function count_change(change) {
|
||||
activeCount += change;
|
||||
}
|
||||
|
||||
function execute(callback) {
|
||||
var command = commands.shift();
|
||||
if (command) {
|
||||
if ('method' in command) {
|
||||
var value = stream[command.method.name].apply(stream, command.method.arguments);
|
||||
if (command.method.ret) {
|
||||
command.method.ret(value);
|
||||
}
|
||||
execute(callback);
|
||||
} else if ('incoming' in command) {
|
||||
command.incoming.count_change = count_change;
|
||||
stream.upstream.write(command.incoming);
|
||||
execute(callback);
|
||||
} else if ('outgoing' in command) {
|
||||
outgoing_frames.push(stream.upstream.read());
|
||||
execute(callback);
|
||||
} else if ('set_state' in command) {
|
||||
stream.state = command.set_state;
|
||||
execute(callback);
|
||||
} else if ('wait' in command) {
|
||||
setTimeout(execute.bind(null, callback), command.wait);
|
||||
} else {
|
||||
throw new Error('Invalid command', command);
|
||||
}
|
||||
} else {
|
||||
setTimeout(callback, 5);
|
||||
}
|
||||
}
|
||||
|
||||
function check() {
|
||||
checks.forEach(function(check) {
|
||||
if ('outgoing' in check) {
|
||||
var frame = outgoing_frames.shift();
|
||||
for (var key in check.outgoing) {
|
||||
expect(frame).to.have.property(key).that.deep.equals(check.outgoing[key]);
|
||||
}
|
||||
count_change(frame.count_change);
|
||||
} else if ('event' in check) {
|
||||
var event = events.shift();
|
||||
expect(event.name).to.be.equal(check.event.name);
|
||||
check.event.data.forEach(function(data, index) {
|
||||
expect(event.data[index]).to.deep.equal(data);
|
||||
});
|
||||
} else if ('active' in check) {
|
||||
expect(activeCount).to.be.equal(check.active);
|
||||
} else {
|
||||
throw new Error('Invalid check', check);
|
||||
}
|
||||
});
|
||||
done();
|
||||
}
|
||||
|
||||
setImmediate(execute.bind(null, check));
|
||||
}
|
||||
|
||||
var example_frames = [
|
||||
{ type: 'PRIORITY', flags: {}, priority: 1 },
|
||||
{ type: 'WINDOW_UPDATE', flags: {}, settings: {} },
|
||||
{ type: 'RST_STREAM', flags: {}, error: 'CANCEL' },
|
||||
{ type: 'HEADERS', flags: {}, headers: {}, priority: undefined },
|
||||
{ type: 'DATA', flags: {}, data: new Buffer(5) },
|
||||
{ type: 'PUSH_PROMISE', flags: {}, headers: {}, promised_stream: new Stream(util.log, null) }
|
||||
];
|
||||
|
||||
var invalid_incoming_frames = {
|
||||
IDLE: [
|
||||
{ type: 'DATA', flags: {}, data: new Buffer(5) },
|
||||
{ type: 'WINDOW_UPDATE', flags: {}, settings: {} },
|
||||
{ type: 'PUSH_PROMISE', flags: {}, headers: {} },
|
||||
{ type: 'RST_STREAM', flags: {}, error: 'CANCEL' }
|
||||
],
|
||||
RESERVED_LOCAL: [
|
||||
{ type: 'DATA', flags: {}, data: new Buffer(5) },
|
||||
{ type: 'HEADERS', flags: {}, headers: {}, priority: undefined },
|
||||
{ type: 'PUSH_PROMISE', flags: {}, headers: {} },
|
||||
{ type: 'WINDOW_UPDATE', flags: {}, settings: {} }
|
||||
],
|
||||
RESERVED_REMOTE: [
|
||||
{ type: 'DATA', flags: {}, data: new Buffer(5) },
|
||||
{ type: 'PUSH_PROMISE', flags: {}, headers: {} },
|
||||
{ type: 'WINDOW_UPDATE', flags: {}, settings: {} }
|
||||
],
|
||||
OPEN: [
|
||||
],
|
||||
HALF_CLOSED_LOCAL: [
|
||||
],
|
||||
HALF_CLOSED_REMOTE: [
|
||||
{ type: 'DATA', flags: {}, data: new Buffer(5) },
|
||||
{ type: 'HEADERS', flags: {}, headers: {}, priority: undefined },
|
||||
{ type: 'PUSH_PROMISE', flags: {}, headers: {} }
|
||||
]
|
||||
};
|
||||
|
||||
var invalid_outgoing_frames = {
|
||||
IDLE: [
|
||||
{ type: 'DATA', flags: {}, data: new Buffer(5) },
|
||||
{ type: 'WINDOW_UPDATE', flags: {}, settings: {} },
|
||||
{ type: 'PUSH_PROMISE', flags: {}, headers: {} }
|
||||
],
|
||||
RESERVED_LOCAL: [
|
||||
{ type: 'DATA', flags: {}, data: new Buffer(5) },
|
||||
{ type: 'PUSH_PROMISE', flags: {}, headers: {} },
|
||||
{ type: 'WINDOW_UPDATE', flags: {}, settings: {} }
|
||||
],
|
||||
RESERVED_REMOTE: [
|
||||
{ type: 'DATA', flags: {}, data: new Buffer(5) },
|
||||
{ type: 'HEADERS', flags: {}, headers: {}, priority: undefined },
|
||||
{ type: 'PUSH_PROMISE', flags: {}, headers: {} },
|
||||
{ type: 'WINDOW_UPDATE', flags: {}, settings: {} }
|
||||
],
|
||||
OPEN: [
|
||||
],
|
||||
HALF_CLOSED_LOCAL: [
|
||||
{ type: 'DATA', flags: {}, data: new Buffer(5) },
|
||||
{ type: 'HEADERS', flags: {}, headers: {}, priority: undefined },
|
||||
{ type: 'PUSH_PROMISE', flags: {}, headers: {} }
|
||||
],
|
||||
HALF_CLOSED_REMOTE: [
|
||||
],
|
||||
CLOSED: [
|
||||
{ type: 'WINDOW_UPDATE', flags: {}, settings: {} },
|
||||
{ type: 'HEADERS', flags: {}, headers: {}, priority: undefined },
|
||||
{ type: 'DATA', flags: {}, data: new Buffer(5) },
|
||||
{ type: 'PUSH_PROMISE', flags: {}, headers: {}, promised_stream: new Stream(util.log, null) }
|
||||
]
|
||||
};
|
||||
|
||||
describe('stream.js', function() {
|
||||
describe('Stream class', function() {
|
||||
describe('._transition(sending, frame) method', function() {
|
||||
it('should emit error, and answer RST_STREAM for invalid incoming frames', function() {
|
||||
Object.keys(invalid_incoming_frames).forEach(function(state) {
|
||||
invalid_incoming_frames[state].forEach(function(invalid_frame) {
|
||||
var stream = createStream();
|
||||
var connectionErrorHappened = false;
|
||||
stream.state = state;
|
||||
stream.once('connectionError', function() { connectionErrorHappened = true; });
|
||||
stream._transition(false, invalid_frame);
|
||||
expect(connectionErrorHappened);
|
||||
});
|
||||
});
|
||||
|
||||
// CLOSED state as a result of incoming END_STREAM (or RST_STREAM)
|
||||
var stream = createStream();
|
||||
stream.headers({});
|
||||
stream.end();
|
||||
stream.upstream.write({ type: 'HEADERS', headers:{}, flags: { END_STREAM: true }, count_change: util.noop });
|
||||
example_frames.slice(2).forEach(function(invalid_frame) {
|
||||
invalid_frame.count_change = util.noop;
|
||||
expect(stream._transition.bind(stream, false, invalid_frame)).to.throw('Uncaught, unspecified "error" event.');
|
||||
});
|
||||
|
||||
// CLOSED state as a result of outgoing END_STREAM
|
||||
stream = createStream();
|
||||
stream.upstream.write({ type: 'HEADERS', headers:{}, flags: { END_STREAM: true }, count_change: util.noop });
|
||||
stream.headers({});
|
||||
stream.end();
|
||||
example_frames.slice(3).forEach(function(invalid_frame) {
|
||||
invalid_frame.count_change = util.noop;
|
||||
expect(stream._transition.bind(stream, false, invalid_frame)).to.throw('Uncaught, unspecified "error" event.');
|
||||
});
|
||||
});
|
||||
it('should throw exception for invalid outgoing frames', function() {
|
||||
Object.keys(invalid_outgoing_frames).forEach(function(state) {
|
||||
invalid_outgoing_frames[state].forEach(function(invalid_frame) {
|
||||
var stream = createStream();
|
||||
stream.state = state;
|
||||
expect(stream._transition.bind(stream, true, invalid_frame)).to.throw(Error);
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should close the stream when there\'s an incoming or outgoing RST_STREAM', function() {
|
||||
[
|
||||
'RESERVED_LOCAL',
|
||||
'RESERVED_REMOTE',
|
||||
'OPEN',
|
||||
'HALF_CLOSED_LOCAL',
|
||||
'HALF_CLOSED_REMOTE'
|
||||
].forEach(function(state) {
|
||||
[true, false].forEach(function(sending) {
|
||||
var stream = createStream();
|
||||
stream.state = state;
|
||||
stream._transition(sending, { type: 'RST_STREAM', flags: {} });
|
||||
expect(stream.state).to.be.equal('CLOSED');
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should ignore any incoming frame after sending reset', function() {
|
||||
var stream = createStream();
|
||||
stream.reset();
|
||||
example_frames.forEach(stream._transition.bind(stream, false));
|
||||
});
|
||||
it('should ignore certain incoming frames after closing the stream with END_STREAM', function() {
|
||||
var stream = createStream();
|
||||
stream.upstream.write({ type: 'HEADERS', flags: { END_STREAM: true }, headers:{} });
|
||||
stream.headers({});
|
||||
stream.end();
|
||||
example_frames.slice(0,3).forEach(function(frame) {
|
||||
frame.count_change = util.noop;
|
||||
stream._transition(false, frame);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('test scenario', function() {
|
||||
describe('sending request', function() {
|
||||
it('should trigger the appropriate state transitions and outgoing frames', function(done) {
|
||||
execute_sequence([
|
||||
{ method : { name: 'headers', arguments: [{ ':path': '/' }] } },
|
||||
{ outgoing: { type: 'HEADERS', flags: { }, headers: { ':path': '/' } } },
|
||||
{ event : { name: 'state', data: ['OPEN'] } },
|
||||
|
||||
{ wait : 5 },
|
||||
{ method : { name: 'end', arguments: [] } },
|
||||
{ event : { name: 'state', data: ['HALF_CLOSED_LOCAL'] } },
|
||||
{ outgoing: { type: 'DATA', flags: { END_STREAM: true }, data: new Buffer(0) } },
|
||||
|
||||
{ wait : 10 },
|
||||
{ incoming: { type: 'HEADERS', flags: { }, headers: { ':status': 200 } } },
|
||||
{ incoming: { type: 'DATA' , flags: { END_STREAM: true }, data: new Buffer(5) } },
|
||||
{ event : { name: 'headers', data: [{ ':status': 200 }] } },
|
||||
{ event : { name: 'state', data: ['CLOSED'] } },
|
||||
|
||||
{ active : 0 }
|
||||
], done);
|
||||
});
|
||||
});
|
||||
describe('answering request', function() {
|
||||
it('should trigger the appropriate state transitions and outgoing frames', function(done) {
|
||||
var payload = new Buffer(5);
|
||||
execute_sequence([
|
||||
{ incoming: { type: 'HEADERS', flags: { }, headers: { ':path': '/' } } },
|
||||
{ event : { name: 'state', data: ['OPEN'] } },
|
||||
{ event : { name: 'headers', data: [{ ':path': '/' }] } },
|
||||
|
||||
{ wait : 5 },
|
||||
{ incoming: { type: 'DATA', flags: { }, data: new Buffer(5) } },
|
||||
{ incoming: { type: 'DATA', flags: { END_STREAM: true }, data: new Buffer(10) } },
|
||||
{ event : { name: 'state', data: ['HALF_CLOSED_REMOTE'] } },
|
||||
|
||||
{ wait : 5 },
|
||||
{ method : { name: 'headers', arguments: [{ ':status': 200 }] } },
|
||||
{ outgoing: { type: 'HEADERS', flags: { }, headers: { ':status': 200 } } },
|
||||
|
||||
{ wait : 5 },
|
||||
{ method : { name: 'end', arguments: [payload] } },
|
||||
{ outgoing: { type: 'DATA', flags: { END_STREAM: true }, data: payload } },
|
||||
{ event : { name: 'state', data: ['CLOSED'] } },
|
||||
|
||||
{ active : 0 }
|
||||
], done);
|
||||
});
|
||||
});
|
||||
describe('sending push stream', function() {
|
||||
it('should trigger the appropriate state transitions and outgoing frames', function(done) {
|
||||
var payload = new Buffer(5);
|
||||
var pushStream;
|
||||
|
||||
execute_sequence([
|
||||
// receiving request
|
||||
{ incoming: { type: 'HEADERS', flags: { END_STREAM: true }, headers: { ':path': '/' } } },
|
||||
{ event : { name: 'state', data: ['OPEN'] } },
|
||||
{ event : { name: 'state', data: ['HALF_CLOSED_REMOTE'] } },
|
||||
{ event : { name: 'headers', data: [{ ':path': '/' }] } },
|
||||
|
||||
// sending response headers
|
||||
{ wait : 5 },
|
||||
{ method : { name: 'headers', arguments: [{ ':status': '200' }] } },
|
||||
{ outgoing: { type: 'HEADERS', flags: { }, headers: { ':status': '200' } } },
|
||||
|
||||
// sending push promise
|
||||
{ method : { name: 'promise', arguments: [{ ':path': '/' }], ret: function(str) { pushStream = str; } } },
|
||||
{ outgoing: { type: 'PUSH_PROMISE', flags: { }, headers: { ':path': '/' } } },
|
||||
|
||||
// sending response data
|
||||
{ method : { name: 'end', arguments: [payload] } },
|
||||
{ outgoing: { type: 'DATA', flags: { END_STREAM: true }, data: payload } },
|
||||
{ event : { name: 'state', data: ['CLOSED'] } },
|
||||
|
||||
{ active : 0 }
|
||||
], function() {
|
||||
// initial state of the promised stream
|
||||
expect(pushStream.state).to.equal('RESERVED_LOCAL');
|
||||
|
||||
execute_sequence(pushStream, [
|
||||
// push headers
|
||||
{ wait : 5 },
|
||||
{ method : { name: 'headers', arguments: [{ ':status': '200' }] } },
|
||||
{ outgoing: { type: 'HEADERS', flags: { }, headers: { ':status': '200' } } },
|
||||
{ event : { name: 'state', data: ['HALF_CLOSED_REMOTE'] } },
|
||||
|
||||
// push data
|
||||
{ method : { name: 'end', arguments: [payload] } },
|
||||
{ outgoing: { type: 'DATA', flags: { END_STREAM: true }, data: payload } },
|
||||
{ event : { name: 'state', data: ['CLOSED'] } },
|
||||
|
||||
{ active : 1 }
|
||||
], done);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('receiving push stream', function() {
|
||||
it('should trigger the appropriate state transitions and outgoing frames', function(done) {
|
||||
var payload = new Buffer(5);
|
||||
var original_stream = createStream();
|
||||
var promised_stream = createStream();
|
||||
|
||||
done = util.callNTimes(2, done);
|
||||
|
||||
execute_sequence(original_stream, [
|
||||
// sending request headers
|
||||
{ method : { name: 'headers', arguments: [{ ':path': '/' }] } },
|
||||
{ method : { name: 'end', arguments: [] } },
|
||||
{ outgoing: { type: 'HEADERS', flags: { END_STREAM: true }, headers: { ':path': '/' } } },
|
||||
{ event : { name: 'state', data: ['OPEN'] } },
|
||||
{ event : { name: 'state', data: ['HALF_CLOSED_LOCAL'] } },
|
||||
|
||||
// receiving response headers
|
||||
{ wait : 10 },
|
||||
{ incoming: { type: 'HEADERS', flags: { }, headers: { ':status': 200 } } },
|
||||
{ event : { name: 'headers', data: [{ ':status': 200 }] } },
|
||||
|
||||
// receiving push promise
|
||||
{ incoming: { type: 'PUSH_PROMISE', flags: { }, headers: { ':path': '/2.html' }, promised_stream: promised_stream } },
|
||||
{ event : { name: 'promise', data: [promised_stream, { ':path': '/2.html' }] } },
|
||||
|
||||
// receiving response data
|
||||
{ incoming: { type: 'DATA' , flags: { END_STREAM: true }, data: payload } },
|
||||
{ event : { name: 'state', data: ['CLOSED'] } },
|
||||
|
||||
{ active : 0 }
|
||||
], done);
|
||||
|
||||
execute_sequence(promised_stream, [
|
||||
// initial state of the promised stream
|
||||
{ event : { name: 'state', data: ['RESERVED_REMOTE'] } },
|
||||
|
||||
// push headers
|
||||
{ wait : 10 },
|
||||
{ incoming: { type: 'HEADERS', flags: { END_STREAM: false }, headers: { ':status': 200 } } },
|
||||
{ event : { name: 'state', data: ['HALF_CLOSED_LOCAL'] } },
|
||||
{ event : { name: 'headers', data: [{ ':status': 200 }] } },
|
||||
|
||||
// push data
|
||||
{ incoming: { type: 'DATA', flags: { END_STREAM: true }, data: payload } },
|
||||
{ event : { name: 'state', data: ['CLOSED'] } },
|
||||
|
||||
{ active : 0 }
|
||||
], done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('bunyan formatter', function() {
|
||||
describe('`s`', function() {
|
||||
var format = stream.serializers.s;
|
||||
it('should assign a unique ID to each frame', function() {
|
||||
var stream1 = createStream();
|
||||
var stream2 = createStream();
|
||||
expect(format(stream1)).to.be.equal(format(stream1));
|
||||
expect(format(stream2)).to.be.equal(format(stream2));
|
||||
expect(format(stream1)).to.not.be.equal(format(stream2));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
89
testing/xpcshell/node-http2/test/util.js
Normal file
89
testing/xpcshell/node-http2/test/util.js
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var spawn = require('child_process').spawn;
|
||||
|
||||
function noop() {}
|
||||
exports.noop = noop;
|
||||
|
||||
if (process.env.HTTP2_LOG) {
|
||||
var logOutput = process.stderr;
|
||||
if (process.stderr.isTTY) {
|
||||
var bin = path.resolve(path.dirname(require.resolve('bunyan')), '..', 'bin', 'bunyan');
|
||||
if(bin && fs.existsSync(bin)) {
|
||||
logOutput = spawn(bin, ['-o', 'short'], {
|
||||
stdio: [null, process.stderr, process.stderr]
|
||||
}).stdin;
|
||||
}
|
||||
}
|
||||
exports.createLogger = function(name) {
|
||||
return require('bunyan').createLogger({
|
||||
name: name,
|
||||
stream: logOutput,
|
||||
level: process.env.HTTP2_LOG,
|
||||
serializers: require('../lib/http').serializers
|
||||
});
|
||||
};
|
||||
exports.log = exports.createLogger('test');
|
||||
exports.clientLog = exports.createLogger('client');
|
||||
exports.serverLog = exports.createLogger('server');
|
||||
} else {
|
||||
exports.createLogger = function() {
|
||||
return exports.log;
|
||||
};
|
||||
exports.log = exports.clientLog = exports.serverLog = {
|
||||
fatal: noop,
|
||||
error: noop,
|
||||
warn : noop,
|
||||
info : noop,
|
||||
debug: noop,
|
||||
trace: noop,
|
||||
|
||||
child: function() { return this; }
|
||||
};
|
||||
}
|
||||
|
||||
exports.callNTimes = function callNTimes(limit, done) {
|
||||
if (limit === 0) {
|
||||
done();
|
||||
} else {
|
||||
var i = 0;
|
||||
return function() {
|
||||
i += 1;
|
||||
if (i === limit) {
|
||||
done();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Concatenate an array of buffers into a new buffer
|
||||
exports.concat = function concat(buffers) {
|
||||
var size = 0;
|
||||
for (var i = 0; i < buffers.length; i++) {
|
||||
size += buffers[i].length;
|
||||
}
|
||||
|
||||
var concatenated = new Buffer(size);
|
||||
for (var cursor = 0, j = 0; j < buffers.length; cursor += buffers[j].length, j++) {
|
||||
buffers[j].copy(concatenated, cursor);
|
||||
}
|
||||
|
||||
return concatenated;
|
||||
};
|
||||
|
||||
exports.random = function random(min, max) {
|
||||
return min + Math.floor(Math.random() * (max - min + 1));
|
||||
};
|
||||
|
||||
// Concatenate an array of buffers and then cut them into random size buffers
|
||||
exports.shuffleBuffers = function shuffleBuffers(buffers) {
|
||||
var concatenated = exports.concat(buffers), output = [], written = 0;
|
||||
|
||||
while (written < concatenated.length) {
|
||||
var chunk_size = Math.min(concatenated.length - written, Math.ceil(Math.random()*20));
|
||||
output.push(concatenated.slice(written, written + chunk_size));
|
||||
written += chunk_size;
|
||||
}
|
||||
|
||||
return output;
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue