0
0
mirror of https://github.com/nodejs/node.git synced 2024-12-01 16:10:02 +01:00
nodejs/test/parallel/test-http2-compat-serverresponse-end.js
Robert Nagy 6f613d8abb http,stream: add writableEnded
This is work towards resolving the response.finished confusion and
future deprecation.

Note that implementation-wise, streams have both an ending and ended
state. However, in this case (in order to avoid confusion in user space)
writableEnded is equal to writable.ending. The ending vs ended situation
is internal state required for internal stream logic.

PR-URL: https://github.com/nodejs/node/pull/28934
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
2019-08-17 00:28:06 -07:00

356 lines
10 KiB
JavaScript

'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.close();
}));
request.end();
request.resume();
}));
}));
}
{
// Http2ServerResponse.end should return self after end
const server = createServer(mustCall((request, response) => {
strictEqual(response, response.end());
strictEqual(response, response.end());
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.setEncoding('utf8');
request.on('end', mustCall(() => {
client.close();
}));
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.close();
}));
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.close()));
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);
strictEqual(response.writableEnded, false);
response.writeHead(HTTP_STATUS_OK, { foo: 'bar' });
response.end('data', mustCall());
strictEqual(response.writableEnded, true);
}));
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.close();
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.close();
server.close();
}));
request.end();
request.resume();
}));
}));
}
{
// Should be able to call .end with cb from stream 'close'
const server = createServer(mustCall((request, response) => {
response.writeHead(HTTP_STATUS_OK, { foo: 'bar' });
response.stream.on('close', 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.close();
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.close();
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.close();
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.close();
server.close();
}));
request.end();
request.resume();
}));
}));
}