http2: fix refs to status 205, add tests
Fix references within http2 core to HTTP_STATUS_CONTENT_RESET to point to the correct HTTP_STATUS_RESET_CONTENT. Add tests for status 204, 205 & 304 in respond, respondWithFD & respondWithFile. Add general error tests for respondWithFD & respondWithFile. PR-URL: https://github.com/nodejs/node/pull/15153 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Claudio Rodriguez <cjrodr@yahoo.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
This commit is contained in:
parent
c20901a7f5
commit
45357d0556
@ -121,7 +121,7 @@ const {
|
|||||||
HTTP2_METHOD_CONNECT,
|
HTTP2_METHOD_CONNECT,
|
||||||
|
|
||||||
HTTP_STATUS_CONTINUE,
|
HTTP_STATUS_CONTINUE,
|
||||||
HTTP_STATUS_CONTENT_RESET,
|
HTTP_STATUS_RESET_CONTENT,
|
||||||
HTTP_STATUS_OK,
|
HTTP_STATUS_OK,
|
||||||
HTTP_STATUS_NO_CONTENT,
|
HTTP_STATUS_NO_CONTENT,
|
||||||
HTTP_STATUS_NOT_MODIFIED,
|
HTTP_STATUS_NOT_MODIFIED,
|
||||||
@ -1879,7 +1879,7 @@ class ServerHttp2Stream extends Http2Stream {
|
|||||||
// the options.endStream option to true so that the underlying
|
// the options.endStream option to true so that the underlying
|
||||||
// bits do not attempt to send any.
|
// bits do not attempt to send any.
|
||||||
if (statusCode === HTTP_STATUS_NO_CONTENT ||
|
if (statusCode === HTTP_STATUS_NO_CONTENT ||
|
||||||
statusCode === HTTP_STATUS_CONTENT_RESET ||
|
statusCode === HTTP_STATUS_RESET_CONTENT ||
|
||||||
statusCode === HTTP_STATUS_NOT_MODIFIED ||
|
statusCode === HTTP_STATUS_NOT_MODIFIED ||
|
||||||
state.headRequest === true) {
|
state.headRequest === true) {
|
||||||
options.endStream = true;
|
options.endStream = true;
|
||||||
@ -1973,7 +1973,7 @@ class ServerHttp2Stream extends Http2Stream {
|
|||||||
const statusCode = headers[HTTP2_HEADER_STATUS] |= 0;
|
const statusCode = headers[HTTP2_HEADER_STATUS] |= 0;
|
||||||
// Payload/DATA frames are not permitted in these cases
|
// Payload/DATA frames are not permitted in these cases
|
||||||
if (statusCode === HTTP_STATUS_NO_CONTENT ||
|
if (statusCode === HTTP_STATUS_NO_CONTENT ||
|
||||||
statusCode === HTTP_STATUS_CONTENT_RESET ||
|
statusCode === HTTP_STATUS_RESET_CONTENT ||
|
||||||
statusCode === HTTP_STATUS_NOT_MODIFIED) {
|
statusCode === HTTP_STATUS_NOT_MODIFIED) {
|
||||||
throw new errors.Error('ERR_HTTP2_PAYLOAD_FORBIDDEN', statusCode);
|
throw new errors.Error('ERR_HTTP2_PAYLOAD_FORBIDDEN', statusCode);
|
||||||
}
|
}
|
||||||
@ -2050,7 +2050,7 @@ class ServerHttp2Stream extends Http2Stream {
|
|||||||
const statusCode = headers[HTTP2_HEADER_STATUS] |= 0;
|
const statusCode = headers[HTTP2_HEADER_STATUS] |= 0;
|
||||||
// Payload/DATA frames are not permitted in these cases
|
// Payload/DATA frames are not permitted in these cases
|
||||||
if (statusCode === HTTP_STATUS_NO_CONTENT ||
|
if (statusCode === HTTP_STATUS_NO_CONTENT ||
|
||||||
statusCode === HTTP_STATUS_CONTENT_RESET ||
|
statusCode === HTTP_STATUS_RESET_CONTENT ||
|
||||||
statusCode === HTTP_STATUS_NOT_MODIFIED) {
|
statusCode === HTTP_STATUS_NOT_MODIFIED) {
|
||||||
throw new errors.Error('ERR_HTTP2_PAYLOAD_FORBIDDEN', statusCode);
|
throw new errors.Error('ERR_HTTP2_PAYLOAD_FORBIDDEN', statusCode);
|
||||||
}
|
}
|
||||||
|
103
test/parallel/test-http2-respond-file-errors.js
Normal file
103
test/parallel/test-http2-respond-file-errors.js
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// Flags: --expose-http2
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
if (!common.hasCrypto)
|
||||||
|
common.skip('missing crypto');
|
||||||
|
const http2 = require('http2');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const optionsWithTypeError = {
|
||||||
|
offset: 'number',
|
||||||
|
length: 'number',
|
||||||
|
statCheck: 'function',
|
||||||
|
getTrailers: 'function'
|
||||||
|
};
|
||||||
|
|
||||||
|
const types = {
|
||||||
|
boolean: true,
|
||||||
|
function: () => {},
|
||||||
|
number: 1,
|
||||||
|
object: {},
|
||||||
|
array: [],
|
||||||
|
null: null,
|
||||||
|
symbol: Symbol('test')
|
||||||
|
};
|
||||||
|
|
||||||
|
const fname = path.resolve(common.fixturesDir, 'elipses.txt');
|
||||||
|
|
||||||
|
const server = http2.createServer();
|
||||||
|
|
||||||
|
server.on('stream', common.mustCall((stream) => {
|
||||||
|
// Check for all possible TypeError triggers on options
|
||||||
|
Object.keys(optionsWithTypeError).forEach((option) => {
|
||||||
|
Object.keys(types).forEach((type) => {
|
||||||
|
if (type === optionsWithTypeError[option]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
common.expectsError(
|
||||||
|
() => stream.respondWithFile(fname, {
|
||||||
|
[http2.constants.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}"`
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Should throw if :status 204, 205 or 304
|
||||||
|
[204, 205, 304].forEach((status) => common.expectsError(
|
||||||
|
() => stream.respondWithFile(fname, {
|
||||||
|
[http2.constants.HTTP2_HEADER_CONTENT_TYPE]: 'text/plain',
|
||||||
|
':status': status,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
code: 'ERR_HTTP2_PAYLOAD_FORBIDDEN',
|
||||||
|
message: `Responses with ${status} status must not have a payload`
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
// Should throw if headers already sent
|
||||||
|
stream.respond({
|
||||||
|
':status': 200,
|
||||||
|
});
|
||||||
|
common.expectsError(
|
||||||
|
() => stream.respondWithFile(fname, {
|
||||||
|
[http2.constants.HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
code: 'ERR_HTTP2_HEADERS_SENT',
|
||||||
|
message: 'Response has already been initiated.'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Should throw if stream already destroyed
|
||||||
|
stream.destroy();
|
||||||
|
common.expectsError(
|
||||||
|
() => stream.respondWithFile(fname, {
|
||||||
|
[http2.constants.HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
code: 'ERR_HTTP2_INVALID_STREAM',
|
||||||
|
message: 'The stream has been destroyed'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
|
||||||
|
server.listen(0, common.mustCall(() => {
|
||||||
|
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||||
|
const req = client.request();
|
||||||
|
|
||||||
|
req.on('streamClosed', common.mustCall(() => {
|
||||||
|
client.destroy();
|
||||||
|
server.close();
|
||||||
|
}));
|
||||||
|
req.end();
|
||||||
|
}));
|
123
test/parallel/test-http2-respond-file-fd-errors.js
Normal file
123
test/parallel/test-http2-respond-file-fd-errors.js
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
// Flags: --expose-http2
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
if (!common.hasCrypto)
|
||||||
|
common.skip('missing crypto');
|
||||||
|
const http2 = require('http2');
|
||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const optionsWithTypeError = {
|
||||||
|
offset: 'number',
|
||||||
|
length: 'number',
|
||||||
|
statCheck: 'function',
|
||||||
|
getTrailers: 'function'
|
||||||
|
};
|
||||||
|
|
||||||
|
const types = {
|
||||||
|
boolean: true,
|
||||||
|
function: () => {},
|
||||||
|
number: 1,
|
||||||
|
object: {},
|
||||||
|
array: [],
|
||||||
|
null: null,
|
||||||
|
symbol: Symbol('test')
|
||||||
|
};
|
||||||
|
|
||||||
|
const fname = path.resolve(common.fixturesDir, 'elipses.txt');
|
||||||
|
const fd = fs.openSync(fname, 'r');
|
||||||
|
|
||||||
|
const server = http2.createServer();
|
||||||
|
|
||||||
|
server.on('stream', common.mustCall((stream) => {
|
||||||
|
// should throw if fd isn't a number
|
||||||
|
Object.keys(types).forEach((type) => {
|
||||||
|
if (type === 'number') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
common.expectsError(
|
||||||
|
() => stream.respondWithFD(types[type], {
|
||||||
|
[http2.constants.HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
type: TypeError,
|
||||||
|
code: 'ERR_INVALID_ARG_TYPE',
|
||||||
|
message: 'The "fd" argument must be of type number'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check for all possible TypeError triggers on options
|
||||||
|
Object.keys(optionsWithTypeError).forEach((option) => {
|
||||||
|
Object.keys(types).forEach((type) => {
|
||||||
|
if (type === optionsWithTypeError[option]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
common.expectsError(
|
||||||
|
() => stream.respondWithFD(fd, {
|
||||||
|
[http2.constants.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}"`
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Should throw if :status 204, 205 or 304
|
||||||
|
[204, 205, 304].forEach((status) => common.expectsError(
|
||||||
|
() => stream.respondWithFD(fd, {
|
||||||
|
[http2.constants.HTTP2_HEADER_CONTENT_TYPE]: 'text/plain',
|
||||||
|
':status': status,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
code: 'ERR_HTTP2_PAYLOAD_FORBIDDEN',
|
||||||
|
message: `Responses with ${status} status must not have a payload`
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
// Should throw if headers already sent
|
||||||
|
stream.respond({
|
||||||
|
':status': 200,
|
||||||
|
});
|
||||||
|
common.expectsError(
|
||||||
|
() => stream.respondWithFD(fd, {
|
||||||
|
[http2.constants.HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
code: 'ERR_HTTP2_HEADERS_SENT',
|
||||||
|
message: 'Response has already been initiated.'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Should throw if stream already destroyed
|
||||||
|
stream.destroy();
|
||||||
|
common.expectsError(
|
||||||
|
() => stream.respondWithFD(fd, {
|
||||||
|
[http2.constants.HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
code: 'ERR_HTTP2_INVALID_STREAM',
|
||||||
|
message: 'The stream has been destroyed'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
|
||||||
|
server.listen(0, common.mustCall(() => {
|
||||||
|
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||||
|
const req = client.request();
|
||||||
|
|
||||||
|
req.on('streamClosed', common.mustCall(() => {
|
||||||
|
client.destroy();
|
||||||
|
server.close();
|
||||||
|
}));
|
||||||
|
req.end();
|
||||||
|
}));
|
40
test/parallel/test-http2-respond-no-data.js
Normal file
40
test/parallel/test-http2-respond-no-data.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Flags: --expose-http2
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
if (!common.hasCrypto)
|
||||||
|
common.skip('missing crypto');
|
||||||
|
const http2 = require('http2');
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
const server = http2.createServer();
|
||||||
|
|
||||||
|
// Check that stream ends immediately after respond on :status 204, 205 & 304
|
||||||
|
|
||||||
|
const status = [204, 205, 304];
|
||||||
|
|
||||||
|
server.on('stream', common.mustCall((stream) => {
|
||||||
|
stream.on('streamClosed', common.mustCall(() => {
|
||||||
|
assert.strictEqual(stream.destroyed, true);
|
||||||
|
}));
|
||||||
|
stream.respond({ ':status': status.shift() });
|
||||||
|
}, 3));
|
||||||
|
|
||||||
|
server.listen(0, common.mustCall(makeRequest));
|
||||||
|
|
||||||
|
function makeRequest() {
|
||||||
|
const client = http2.connect(`http://localhost:${server.address().port}`);
|
||||||
|
const req = client.request();
|
||||||
|
req.resume();
|
||||||
|
|
||||||
|
req.on('end', common.mustCall(() => {
|
||||||
|
client.destroy();
|
||||||
|
|
||||||
|
if (!status.length) {
|
||||||
|
server.close();
|
||||||
|
} else {
|
||||||
|
makeRequest();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
req.end();
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user