Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Http2 test coverage for ServerHttp2Stream.respondWithFD #15323

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions lib/internal/http2/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ const {
HTTP2_METHOD_CONNECT,

HTTP_STATUS_CONTINUE,
HTTP_STATUS_CONTENT_RESET,
HTTP_STATUS_RESET_CONTENT,
HTTP_STATUS_OK,
HTTP_STATUS_NO_CONTENT,
HTTP_STATUS_NOT_MODIFIED,
Expand Down Expand Up @@ -1879,7 +1879,7 @@ class ServerHttp2Stream extends Http2Stream {
// the options.endStream option to true so that the underlying
// bits do not attempt to send any.
if (statusCode === HTTP_STATUS_NO_CONTENT ||
statusCode === HTTP_STATUS_CONTENT_RESET ||
statusCode === HTTP_STATUS_RESET_CONTENT ||
statusCode === HTTP_STATUS_NOT_MODIFIED ||
state.headRequest === true) {
options.endStream = true;
Expand Down Expand Up @@ -1973,7 +1973,7 @@ class ServerHttp2Stream extends Http2Stream {
const statusCode = headers[HTTP2_HEADER_STATUS] |= 0;
// Payload/DATA frames are not permitted in these cases
if (statusCode === HTTP_STATUS_NO_CONTENT ||
statusCode === HTTP_STATUS_CONTENT_RESET ||
statusCode === HTTP_STATUS_RESET_CONTENT ||
statusCode === HTTP_STATUS_NOT_MODIFIED) {
throw new errors.Error('ERR_HTTP2_PAYLOAD_FORBIDDEN', statusCode);
}
Expand Down Expand Up @@ -2050,7 +2050,7 @@ class ServerHttp2Stream extends Http2Stream {
const statusCode = headers[HTTP2_HEADER_STATUS] |= 0;
// Payload/DATA frames are not permitted in these cases
if (statusCode === HTTP_STATUS_NO_CONTENT ||
statusCode === HTTP_STATUS_CONTENT_RESET ||
statusCode === HTTP_STATUS_RESET_CONTENT ||
statusCode === HTTP_STATUS_NOT_MODIFIED) {
throw new errors.Error('ERR_HTTP2_PAYLOAD_FORBIDDEN', statusCode);
}
Expand Down
49 changes: 49 additions & 0 deletions test/parallel/test-http2-fd-nan-respondfd.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Flags: --expose-http2
'use strict';

const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const http2 = require('http2');

// Test that ERR_INVALID_ARG_TYPE is thrown when ServerHttp2Stream.respondWithFD
// is called with an fd value that is not of type number

const types = {
function: () => {},
object: {},
array: [],
null: null,
symbol: Symbol('test')
};

const server = http2.createServer();

server.on('stream', common.mustCall(onStream));

function onStream(stream, headers, flags) {
Object.keys(types).forEach((type) => {

common.expectsError(() => stream.respondWithFD(type), {
type: TypeError,
code: 'ERR_INVALID_ARG_TYPE',
message: 'The "fd" argument must be of type number'
});

});
stream.end('hello world');
}

server.listen(0, common.mustCall(() => {
const port = server.address().port;
const client = http2.connect(`http://localhost:${port}`);
const req = client.request();

req.resume();

req.on('end', common.mustCall(() => {
client.destroy();
server.close();
}));
req.end();
}));
57 changes: 57 additions & 0 deletions test/parallel/test-http2-forbidden-headers-respondfd.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Flags: --expose-http2
'use strict';

const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const http2 = require('http2');

// Test that ERR_HTTP2_PAYLOAD_FORBIDDEN is thrown when
// ServerHttp2Stream.respondWithFD is called with a forbidden header status

const server = http2.createServer();

const {
HTTP2_HEADER_STATUS
} = http2.constants;

const {
constants
} = process.binding('http2');

const statuses = [
constants.HTTP_STATUS_NO_CONTENT,
constants.HTTP_STATUS_RESET_CONTENT,
constants.HTTP_STATUS_NOT_MODIFIED
];

server.on('stream', common.mustCall(onStream));

function onStream(stream, headers, flags) {
statuses.forEach((headerStatus) => {

common.expectsError(() => stream.respondWithFD(0, {
[HTTP2_HEADER_STATUS]: headerStatus
}), {
type: Error,
code: 'ERR_HTTP2_PAYLOAD_FORBIDDEN',
message: `Responses with ${headerStatus} status must not have a payload`
});

});
stream.end('hello world');
}

server.listen(0, common.mustCall(() => {
const port = server.address().port;
const client = http2.connect(`http://localhost:${port}`);
const req = client.request();

req.resume();

req.on('end', common.mustCall(() => {
client.destroy();
server.close();
}));
req.end();
}));
49 changes: 49 additions & 0 deletions test/parallel/test-http2-headers-sent-respondfd.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Flags: --expose-http2
'use strict';

const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const http2 = require('http2');

// Test that ERR_HTTP2_HEADERS_SENT error is thrown when headers are sent before
// calling ServerHttp2Stream.respondWithFD

const server = http2.createServer();

const {
HTTP2_HEADER_CONTENT_TYPE
} = http2.constants;

server.on('stream', common.mustCall(onStream));

function onStream(stream, headers, flags) {
stream.respond({
'content-type': 'text/html',
':status': 200
});

common.expectsError(() => stream.respondWithFD(0, {
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
}), {
type: Error,
code: 'ERR_HTTP2_HEADERS_SENT',
message: 'Response has already been initiated.'
});

stream.end('hello world');
}

server.listen(0, common.mustCall(() => {
const port = server.address().port;
const client = http2.connect(`http://localhost:${port}`);
const req = client.request();

req.resume();

req.on('end', common.mustCall(() => {
client.destroy();
server.close();
}));
req.end();
}));
71 changes: 71 additions & 0 deletions test/parallel/test-http2-options-errors-respondfd.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Flags: --expose-http2
'use strict';

const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const http2 = require('http2');

// Test that ERR_INVALID_OPT_VALUE TypeErrors are thrown when passing invalid
// options to ServerHttp2Stream.respondWithFD method

const optionsToTest = {
offset: 'number',
length: 'number',
statCheck: 'function',
getTrailers: 'function'
};

const types = {
function: () => {},
number: 1,
object: {},
array: [],
null: null,
symbol: Symbol('test')
};

const server = http2.createServer();

const {
HTTP2_HEADER_CONTENT_TYPE
} = http2.constants;

server.on('stream', common.mustCall(onStream));

function onStream(stream, headers, flags) {
Object.keys(optionsToTest).forEach((option) => {
Object.keys(types).forEach((type) => {
if (type === optionsToTest[option]) {
return;
}

common.expectsError(() => stream.respondWithFD(0, {
[HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
}, {
[option]: types[type]
}), {
type: TypeError,
code: 'ERR_INVALID_OPT_VALUE',
message: `The value "${String(types[type])}" is invalid ` +
`for option "${option}"`
});

});
});
stream.end('hello world');
}

server.listen(0, common.mustCall(() => {
const port = server.address().port;
const client = http2.connect(`http://localhost:${port}`);
const req = client.request();

req.resume();

req.on('end', common.mustCall(() => {
client.destroy();
server.close();
}));
req.end();
}));
38 changes: 38 additions & 0 deletions test/parallel/test-http2-server-destroy-before-respond-fd.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Flags: --expose-http2
'use strict';

const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const http2 = require('http2');

// Test that ERR_HTTP2_INVALID_STREAM is thrown when the stream session
// is destroyed

const server = http2.createServer();

server.on('stream', common.mustCall(onStream));

function onStream(stream, headers, flags) {
stream.session.destroy();

common.expectsError(
() => stream.respondWithFD(0), {
code: 'ERR_HTTP2_INVALID_STREAM',
message: /^The stream has been destroyed$/
});
}

server.listen(0, common.mustCall(() => {
const port = server.address().port;
const client = http2.connect(`http://localhost:${port}`);
const req = client.request({ ':path': '/' });

req.on('response', common.mustNotCall());
req.resume();
req.on('end', common.mustCall(() => {
server.close();
client.destroy();
}));
req.end();
}));