You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

325 lines
9.3 KiB

'use strict';
const {
mustCall,
mustNotCall,
hasCrypto,
platformTimeout,
skip
} = require('../common');
if (!hasCrypto)
skip('missing crypto');
const { strictEqual } = require('assert');
const {
createServer,
connect,
constants: {
HTTP2_HEADER_STATUS,
HTTP_STATUS_OK
}
} = require('http2');
{
// Http2ServerResponse.end accepts chunk, encoding, cb as args
// It may be invoked repeatedly without throwing errors
// but callback will only be called once
const server = createServer(mustCall((request, response) => {
response.end('end', 'utf8', mustCall(() => {
response.end(mustNotCall());
process.nextTick(() => {
response.end(mustNotCall());
server.close();
});
}));
response.on('finish', mustCall(() => {
response.end(mustNotCall());
}));
response.end(mustNotCall());
}));
server.listen(0, mustCall(() => {
let data = '';
const { port } = server.address();
const url = `http://localhost:${port}`;
const client = connect(url, mustCall(() => {
const headers = {
':path': '/',
':method': 'GET',
':scheme': 'http',
':authority': `localhost:${port}`
};
const request = client.request(headers);
request.setEncoding('utf8');
request.on('data', (chunk) => (data += chunk));
request.on('end', mustCall(() => {
strictEqual(data, 'end');
client.destroy();
}));
request.end();
request.resume();
}));
}));
}
{
// Http2ServerResponse.end can omit encoding arg, sets it to utf-8
const server = createServer(mustCall((request, response) => {
response.end('test\uD83D\uDE00', mustCall(() => {
server.close();
}));
}));
server.listen(0, mustCall(() => {
let data = '';
const { port } = server.address();
const url = `http://localhost:${port}`;
const client = connect(url, mustCall(() => {
const headers = {
':path': '/',
':method': 'GET',
':scheme': 'http',
':authority': `localhost:${port}`
};
const request = client.request(headers);
request.setEncoding('utf8');
request.on('data', (chunk) => (data += chunk));
request.on('end', mustCall(() => {
strictEqual(data, 'test\uD83D\uDE00');
client.destroy();
}));
request.end();
request.resume();
}));
}));
}
{
// Http2ServerResponse.end can omit chunk & encoding args
const server = createServer(mustCall((request, response) => {
response.end(mustCall(() => {
server.close();
}));
}));
server.listen(0, mustCall(() => {
const { port } = server.address();
const url = `http://localhost:${port}`;
const client = connect(url, mustCall(() => {
const headers = {
':path': '/',
':method': 'GET',
':scheme': 'http',
':authority': `localhost:${port}`
};
const request = client.request(headers);
request.on('data', mustNotCall());
request.on('end', mustCall(() => client.destroy()));
request.end();
request.resume();
}));
}));
}
{
// Http2ServerResponse.end is necessary on HEAD requests in compat
// for http1 compatibility
const server = createServer(mustCall((request, response) => {
strictEqual(response.finished, true);
response.writeHead(HTTP_STATUS_OK, { foo: 'bar' });
response.end('data', mustCall());
}));
server.listen(0, mustCall(() => {
const { port } = server.address();
const url = `http://localhost:${port}`;
const client = connect(url, mustCall(() => {
const headers = {
':path': '/',
':method': 'HEAD',
':scheme': 'http',
':authority': `localhost:${port}`
};
const request = client.request(headers);
request.on('response', mustCall((headers, flags) => {
strictEqual(headers[HTTP2_HEADER_STATUS], HTTP_STATUS_OK);
strictEqual(flags, 5); // the end of stream flag is set
strictEqual(headers.foo, 'bar');
}));
request.on('data', mustNotCall());
request.on('end', mustCall(() => {
client.destroy();
server.close();
}));
request.end();
request.resume();
}));
}));
}
{
// .end should trigger 'end' event on request if user did not attempt
// to read from the request
const server = createServer(mustCall((request, response) => {
request.on('end', mustCall());
response.end();
}));
server.listen(0, mustCall(() => {
const { port } = server.address();
const url = `http://localhost:${port}`;
const client = connect(url, mustCall(() => {
const headers = {
':path': '/',
':method': 'HEAD',
':scheme': 'http',
':authority': `localhost:${port}`
};
const request = client.request(headers);
request.on('data', mustNotCall());
request.on('end', mustCall(() => {
client.destroy();
server.close();
}));
request.end();
request.resume();
}));
}));
}
{
// Should be able to call .end with cb from stream 'streamClosed'
const server = createServer(mustCall((request, response) => {
response.writeHead(HTTP_STATUS_OK, { foo: 'bar' });
response.stream.on('streamClosed', mustCall(() => {
response.end(mustCall());
}));
}));
server.listen(0, mustCall(() => {
const { port } = server.address();
const url = `http://localhost:${port}`;
const client = connect(url, mustCall(() => {
const headers = {
':path': '/',
':method': 'HEAD',
':scheme': 'http',
':authority': `localhost:${port}`
};
const request = client.request(headers);
request.on('response', mustCall((headers, flags) => {
strictEqual(headers[HTTP2_HEADER_STATUS], HTTP_STATUS_OK);
strictEqual(flags, 5); // the end of stream flag is set
strictEqual(headers.foo, 'bar');
}));
request.on('data', mustNotCall());
request.on('end', mustCall(() => {
client.destroy();
server.close();
}));
request.end();
request.resume();
}));
}));
}
{
// Should be able to respond to HEAD request after timeout
const server = createServer(mustCall((request, response) => {
setTimeout(mustCall(() => {
response.writeHead(HTTP_STATUS_OK, { foo: 'bar' });
response.end('data', mustCall());
}), platformTimeout(10));
}));
server.listen(0, mustCall(() => {
const { port } = server.address();
const url = `http://localhost:${port}`;
const client = connect(url, mustCall(() => {
const headers = {
':path': '/',
':method': 'HEAD',
':scheme': 'http',
':authority': `localhost:${port}`
};
const request = client.request(headers);
request.on('response', mustCall((headers, flags) => {
strictEqual(headers[HTTP2_HEADER_STATUS], HTTP_STATUS_OK);
strictEqual(flags, 5); // the end of stream flag is set
strictEqual(headers.foo, 'bar');
}));
request.on('data', mustNotCall());
request.on('end', mustCall(() => {
client.destroy();
server.close();
}));
request.end();
request.resume();
}));
}));
}
{
// finish should only trigger after 'end' is called
const server = createServer(mustCall((request, response) => {
let finished = false;
response.writeHead(HTTP_STATUS_OK, { foo: 'bar' });
response.on('finish', mustCall(() => {
finished = false;
}));
response.end('data', mustCall(() => {
strictEqual(finished, false);
response.end('data', mustNotCall());
}));
}));
server.listen(0, mustCall(() => {
const { port } = server.address();
const url = `http://localhost:${port}`;
const client = connect(url, mustCall(() => {
const headers = {
':path': '/',
':method': 'HEAD',
':scheme': 'http',
':authority': `localhost:${port}`
};
const request = client.request(headers);
request.on('response', mustCall((headers, flags) => {
strictEqual(headers[HTTP2_HEADER_STATUS], HTTP_STATUS_OK);
strictEqual(flags, 5); // the end of stream flag is set
strictEqual(headers.foo, 'bar');
}));
request.on('data', mustNotCall());
request.on('end', mustCall(() => {
client.destroy();
server.close();
}));
request.end();
request.resume();
}));
}));
}
{
// Should be able to respond to HEAD with just .end
const server = createServer(mustCall((request, response) => {
response.end('data', mustCall());
response.end(mustNotCall());
}));
server.listen(0, mustCall(() => {
const { port } = server.address();
const url = `http://localhost:${port}`;
const client = connect(url, mustCall(() => {
const headers = {
':path': '/',
':method': 'HEAD',
':scheme': 'http',
':authority': `localhost:${port}`
};
const request = client.request(headers);
request.on('response', mustCall((headers, flags) => {
strictEqual(headers[HTTP2_HEADER_STATUS], HTTP_STATUS_OK);
strictEqual(flags, 5); // the end of stream flag is set
}));
request.on('data', mustNotCall());
request.on('end', mustCall(() => {
client.destroy();
server.close();
}));
request.end();
request.resume();
}));
}));
}