aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xdoc/api/http2.md22
-rw-r--r--lib/internal/http2/compat.js2
-rwxr-xr-xlib/internal/http2/core.js104
-rwxr-xr-xsrc/node_http2.cc12
-rwxr-xr-xsrc/node_http2_core-inl.h8
-rw-r--r--src/node_http2_core.cc22
-rwxr-xr-xsrc/node_http2_core.h8
-rw-r--r--test/parallel/test-http2-client-settings-before-connect.js22
-rw-r--r--test/parallel/test-http2-client-shutdown-before-connect.js2
-rw-r--r--test/parallel/test-http2-compat-serverresponse-flushheaders.js2
-rw-r--r--test/parallel/test-http2-compat-serverresponse-headers.js2
-rw-r--r--test/parallel/test-http2-compat-serverresponse-statusmessage.js4
-rw-r--r--test/parallel/test-http2-compat-serverresponse-writehead.js2
-rw-r--r--test/parallel/test-http2-create-client-connect.js12
-rw-r--r--test/parallel/test-http2-create-client-secure-session.js6
-rw-r--r--test/parallel/test-http2-getpackedsettings.js2
-rwxr-xr-xtest/parallel/test-http2-info-headers.js2
-rw-r--r--test/parallel/test-http2-max-concurrent-streams.js2
-rw-r--r--test/parallel/test-http2-misused-pseudoheaders.js2
-rw-r--r--test/parallel/test-http2-multi-content-length.js2
-rw-r--r--test/parallel/test-http2-multiplex.js2
-rw-r--r--test/parallel/test-http2-priority-event.js2
-rw-r--r--test/parallel/test-http2-respond-file-fd-range.js94
-rw-r--r--test/parallel/test-http2-respond-file-range.js52
-rw-r--r--test/parallel/test-http2-serve-file.js4
-rw-r--r--test/parallel/test-http2-server-push-disabled.js2
-rw-r--r--test/parallel/test-http2-server-shutdown-before-respond.js2
-rw-r--r--test/parallel/test-http2-session-settings.js2
-rw-r--r--test/parallel/test-http2-timeouts.js2
-rw-r--r--test/parallel/test-http2-trailers.js2
-rw-r--r--test/parallel/test-http2-util-headers-list.js6
-rw-r--r--test/parallel/test-http2-write-empty-string.js2
-rw-r--r--test/parallel/test-tls-disable-renegotiation.js10
33 files changed, 350 insertions, 72 deletions
diff --git a/doc/api/http2.md b/doc/api/http2.md
index 6bc1543c07..cc7f3ea3b5 100755
--- a/doc/api/http2.md
+++ b/doc/api/http2.md
@@ -998,13 +998,17 @@ server.on('stream', (stream) => {
});
```
-#### http2stream.respondWithFD(fd[, headers])
+#### http2stream.respondWithFD(fd[, headers[, options]])
<!-- YAML
added: REPLACEME
-->
* `fd` {number} A readable file descriptor
* `headers` {[Headers Object][]}
+* `options` {Object}
+ * `statCheck` {Function}
+ * `offset` {number} The offset position at which to begin reading
+ * `length` {number} The amount of data from the fd to send
Initiates a response whose data is read from the given file descriptor. No
validation is performed on the given file descriptor. If an error occurs while
@@ -1034,6 +1038,16 @@ server.on('stream', (stream) => {
server.on('close', () => fs.closeSync(fd));
```
+The optional `options.statCheck` function may be specified to give user code
+an opportunity to set additional content headers based on the `fs.Stat` details
+of the given fd. If the `statCheck` function is provided, the
+`http2stream.respondWithFD()` method will perform an `fs.fstat()` call to
+collect details on the provided file descriptor.
+
+The `offset` and `length` options may be used to limit the response to a
+specific range subset. This can be used, for instance, to support HTTP Range
+requests.
+
#### http2stream.respondWithFile(path[, headers[, options]])
<!-- YAML
added: REPLACEME
@@ -1043,6 +1057,8 @@ added: REPLACEME
* `headers` {[Headers Object][]}
* `options` {Object}
* `statCheck` {Function}
+ * `offset` {number} The offset position at which to begin reading
+ * `length` {number} The amount of data from the fd to send
Sends a regular file as the response. The `path` must specify a regular file
or an `'error'` event will be emitted on the `Http2Stream` object.
@@ -1096,6 +1112,10 @@ server.on('stream', (stream) => {
The `content-length` header field will be automatically set.
+The `offset` and `length` options may be used to limit the response to a
+specific range subset. This can be used, for instance, to support HTTP Range
+requests.
+
### Class: Http2Server
<!-- YAML
added: REPLACEME
diff --git a/lib/internal/http2/compat.js b/lib/internal/http2/compat.js
index cd9a1fa2b7..c258079356 100644
--- a/lib/internal/http2/compat.js
+++ b/lib/internal/http2/compat.js
@@ -452,7 +452,7 @@ class Http2ServerResponse extends Stream {
stream.once('finish', cb);
}
- this[kBeginSend]({endStream: true});
+ this[kBeginSend]({ endStream: true });
if (stream !== undefined) {
stream.end();
diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js
index 38cd316029..dc3033f2ed 100755
--- a/lib/internal/http2/core.js
+++ b/lib/internal/http2/core.js
@@ -1541,7 +1541,7 @@ function processHeaders(headers) {
return headers;
}
-function processRespondWithFD(fd, headers) {
+function processRespondWithFD(fd, headers, offset = 0, length = -1) {
const session = this[kSession];
const state = this[kState];
state.headersSent = true;
@@ -1551,7 +1551,7 @@ function processRespondWithFD(fd, headers) {
const handle = session[kHandle];
const ret =
- handle.submitFile(this[kID], fd, headers);
+ handle.submitFile(this[kID], fd, headers, offset, length);
let err;
switch (ret) {
case NGHTTP2_ERR_NOMEM:
@@ -1575,26 +1575,71 @@ function doSendFD(session, options, fd, headers, err, stat) {
process.nextTick(() => this.emit('error', err));
return;
}
+
+ const statOptions = {
+ offset: options.offset !== undefined ? options.offset : 0,
+ length: options.length !== undefined ? options.length : -1
+ };
+
+ if (typeof options.statCheck === 'function' &&
+ options.statCheck.call(this, stat, headers, statOptions) === false) {
+ return;
+ }
+
+ const headersList = mapToHeaders(headers,
+ assertValidPseudoHeaderResponse);
+ if (!Array.isArray(headersList)) {
+ process.nextTick(() => this.emit('error', headersList));
+ }
+
+ processRespondWithFD.call(this, fd, headersList,
+ statOptions.offset,
+ statOptions.length);
+}
+
+function doSendFileFD(session, options, fd, headers, err, stat) {
+ if (this.destroyed || session.destroyed) {
+ abort(this);
+ return;
+ }
+ if (err) {
+ process.nextTick(() => this.emit('error', err));
+ return;
+ }
if (!stat.isFile()) {
err = new errors.Error('ERR_HTTP2_SEND_FILE');
process.nextTick(() => this.emit('error', err));
return;
}
+ const statOptions = {
+ offset: options.offset !== undefined ? options.offset : 0,
+ length: options.length !== undefined ? options.length : -1
+ };
+
// Set the content-length by default
- headers[HTTP2_HEADER_CONTENT_LENGTH] = stat.size;
if (typeof options.statCheck === 'function' &&
options.statCheck.call(this, stat, headers) === false) {
return;
}
+ statOptions.length =
+ statOptions.length < 0 ? stat.size - (+statOptions.offset) :
+ Math.min(stat.size - (+statOptions.offset),
+ statOptions.length);
+
+ if (headers[HTTP2_HEADER_CONTENT_LENGTH] === undefined)
+ headers[HTTP2_HEADER_CONTENT_LENGTH] = statOptions.length;
+
const headersList = mapToHeaders(headers,
assertValidPseudoHeaderResponse);
if (!Array.isArray(headersList)) {
- throw headersList;
+ process.nextTick(() => this.emit('error', headersList));
}
- processRespondWithFD.call(this, fd, headersList);
+ processRespondWithFD.call(this, fd, headersList,
+ options.offset,
+ options.length);
}
function afterOpen(session, options, headers, err, fd) {
@@ -1609,7 +1654,7 @@ function afterOpen(session, options, headers, err, fd) {
}
state.fd = fd;
- fs.fstat(fd, doSendFD.bind(this, session, options, fd, headers));
+ fs.fstat(fd, doSendFileFD.bind(this, session, options, fd, headers));
}
@@ -1786,12 +1831,12 @@ class ServerHttp2Stream extends Http2Stream {
}
// Initiate a response using an open FD. Note that there are fewer
- // protections with this approach. For one, the fd is not validated.
- // In respondWithFile, the file is checked to make sure it is a
+ // protections with this approach. For one, the fd is not validated by
+ // default. In respondWithFile, the file is checked to make sure it is a
// regular file, here the fd is passed directly. If the underlying
// mechanism is not able to read from the fd, then the stream will be
// reset with an error code.
- respondWithFD(fd, headers) {
+ respondWithFD(fd, headers, options) {
const session = this[kSession];
if (this.destroyed)
throw new errors.Error('ERR_HTTP2_INVALID_STREAM');
@@ -1803,6 +1848,26 @@ class ServerHttp2Stream extends Http2Stream {
if (state.headersSent)
throw new errors.Error('ERR_HTTP2_HEADERS_SENT');
+ assertIsObject(options, 'options');
+ options = Object.assign(Object.create(null), options);
+
+ if (options.offset !== undefined && typeof options.offset !== 'number')
+ throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
+ 'offset',
+ options.offset);
+
+ if (options.length !== undefined && typeof options.length !== 'number')
+ throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
+ 'length',
+ options.length);
+
+ if (options.statCheck !== undefined &&
+ typeof options.statCheck !== 'function') {
+ throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
+ 'statCheck',
+ options.statCheck);
+ }
+
if (typeof fd !== 'number')
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
'fd', 'number');
@@ -1816,13 +1881,20 @@ class ServerHttp2Stream extends Http2Stream {
throw new errors.Error('ERR_HTTP2_PAYLOAD_FORBIDDEN', statusCode);
}
+ if (options.statCheck !== undefined) {
+ fs.fstat(fd, doSendFD.bind(this, session, options, fd, headers));
+ return;
+ }
+
const headersList = mapToHeaders(headers,
assertValidPseudoHeaderResponse);
if (!Array.isArray(headersList)) {
- throw headersList;
+ process.nextTick(() => this.emit('error', headersList));
}
- processRespondWithFD.call(this, fd, headersList);
+ processRespondWithFD.call(this, fd, headersList,
+ options.offset,
+ options.length);
}
// Initiate a file response on this Http2Stream. The path is passed to
@@ -1847,6 +1919,16 @@ class ServerHttp2Stream extends Http2Stream {
assertIsObject(options, 'options');
options = Object.assign(Object.create(null), options);
+ if (options.offset !== undefined && typeof options.offset !== 'number')
+ throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
+ 'offset',
+ options.offset);
+
+ if (options.length !== undefined && typeof options.length !== 'number')
+ throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
+ 'length',
+ options.length);
+
if (options.statCheck !== undefined &&
typeof options.statCheck !== 'function') {
throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
diff --git a/src/node_http2.cc b/src/node_http2.cc
index 61a98758b1..ee12072939 100755
--- a/src/node_http2.cc
+++ b/src/node_http2.cc
@@ -604,7 +604,9 @@ void Http2Session::SubmitResponse(const FunctionCallbackInfo<Value>& args) {
void Http2Session::SubmitFile(const FunctionCallbackInfo<Value>& args) {
CHECK(args[0]->IsNumber()); // Stream ID
CHECK(args[1]->IsNumber()); // File Descriptor
- CHECK(args[2]->IsArray()); // Headers
+ CHECK(args[2]->IsArray()); // Headers
+ CHECK(args[3]->IsNumber()); // Offset
+ CHECK(args[4]->IsNumber()); // Length
Http2Session* session;
Nghttp2Stream* stream;
@@ -618,6 +620,11 @@ void Http2Session::SubmitFile(const FunctionCallbackInfo<Value>& args) {
int fd = args[1]->Int32Value(context).ToChecked();
Local<Array> headers = args[2].As<Array>();
+ int64_t offset = args[3]->IntegerValue(context).ToChecked();
+ int64_t length = args[4]->IntegerValue(context).ToChecked();
+
+ CHECK_GE(offset, 0);
+
DEBUG_HTTP2("Http2Session: submitting file %d for stream %d: headers: %d, "
"end-stream: %d\n", fd, id, headers->Length());
@@ -627,7 +634,8 @@ void Http2Session::SubmitFile(const FunctionCallbackInfo<Value>& args) {
Headers list(isolate, context, headers);
- args.GetReturnValue().Set(stream->SubmitFile(fd, *list, list.length()));
+ args.GetReturnValue().Set(stream->SubmitFile(fd, *list, list.length(),
+ offset, length));
}
void Http2Session::SendHeaders(const FunctionCallbackInfo<Value>& args) {
diff --git a/src/node_http2_core-inl.h b/src/node_http2_core-inl.h
index c79d412e63..f20bdc3749 100755
--- a/src/node_http2_core-inl.h
+++ b/src/node_http2_core-inl.h
@@ -429,7 +429,10 @@ inline int Nghttp2Stream::SubmitResponse(nghttp2_nv* nva,
}
// Initiate a response that contains data read from a file descriptor.
-inline int Nghttp2Stream::SubmitFile(int fd, nghttp2_nv* nva, size_t len) {
+inline int Nghttp2Stream::SubmitFile(int fd,
+ nghttp2_nv* nva, size_t len,
+ int64_t offset,
+ int64_t length) {
CHECK_GT(len, 0);
CHECK_GT(fd, 0);
DEBUG_HTTP2("Nghttp2Stream %d: submitting file\n", id_);
@@ -438,6 +441,9 @@ inline int Nghttp2Stream::SubmitFile(int fd, nghttp2_nv* nva, size_t len) {
prov.source.fd = fd;
prov.read_callback = Nghttp2Session::OnStreamReadFD;
+ if (offset > 0) fd_offset_ = offset;
+ if (length > -1) fd_length_ = length;
+
return nghttp2_submit_response(session_->session(), id_,
nva, len, &prov);
}
diff --git a/src/node_http2_core.cc b/src/node_http2_core.cc
index 4d9ab4a4df..3273c8aaa2 100644
--- a/src/node_http2_core.cc
+++ b/src/node_http2_core.cc
@@ -180,18 +180,25 @@ ssize_t Nghttp2Session::OnStreamReadFD(nghttp2_session* session,
int fd = source->fd;
int64_t offset = stream->fd_offset_;
- ssize_t numchars;
+ ssize_t numchars = 0;
+
+ if (stream->fd_length_ >= 0 &&
+ stream->fd_length_ < static_cast<int64_t>(length))
+ length = stream->fd_length_;
uv_buf_t data;
data.base = reinterpret_cast<char*>(buf);
data.len = length;
uv_fs_t read_req;
- numchars = uv_fs_read(handle->loop_,
- &read_req,
- fd, &data, 1,
- offset, nullptr);
- uv_fs_req_cleanup(&read_req);
+
+ if (length > 0) {
+ numchars = uv_fs_read(handle->loop_,
+ &read_req,
+ fd, &data, 1,
+ offset, nullptr);
+ uv_fs_req_cleanup(&read_req);
+ }
// Close the stream with an error if reading fails
if (numchars < 0)
@@ -199,9 +206,10 @@ ssize_t Nghttp2Session::OnStreamReadFD(nghttp2_session* session,
// Update the read offset for the next read
stream->fd_offset_ += numchars;
+ stream->fd_length_ -= numchars;
// if numchars < length, assume that we are done.
- if (static_cast<size_t>(numchars) < length) {
+ if (static_cast<size_t>(numchars) < length || length <= 0) {
DEBUG_HTTP2("Nghttp2Session %d: no more data for stream %d\n",
handle->session_type_, id);
*flags |= NGHTTP2_DATA_FLAG_EOF;
diff --git a/src/node_http2_core.h b/src/node_http2_core.h
index 4c3fc5549c..80dd98e9ff 100755
--- a/src/node_http2_core.h
+++ b/src/node_http2_core.h
@@ -310,7 +310,10 @@ class Nghttp2Stream {
bool emptyPayload = false);
// Send data read from a file descriptor as the response on this stream.
- inline int SubmitFile(int fd, nghttp2_nv* nva, size_t len);
+ inline int SubmitFile(int fd,
+ nghttp2_nv* nva, size_t len,
+ int64_t offset,
+ int64_t length);
// Submit informational headers for this stream
inline int SubmitInfo(nghttp2_nv* nva, size_t len);
@@ -420,7 +423,8 @@ class Nghttp2Stream {
nghttp2_stream_write_queue* queue_tail_ = nullptr;
unsigned int queue_head_index_ = 0;
size_t queue_head_offset_ = 0;
- size_t fd_offset_ = 0;
+ int64_t fd_offset_ = 0;
+ int64_t fd_length_ = -1;
// The Current Headers block... As headers are received for this stream,
// they are temporarily stored here until the OnFrameReceived is called
diff --git a/test/parallel/test-http2-client-settings-before-connect.js b/test/parallel/test-http2-client-settings-before-connect.js
index 9391502479..f320aa97db 100644
--- a/test/parallel/test-http2-client-settings-before-connect.js
+++ b/test/parallel/test-http2-client-settings-before-connect.js
@@ -24,28 +24,28 @@ server.on('listening', common.mustCall(() => {
const client = h2.connect(`http://localhost:${server.address().port}`);
- assert.throws(() => client.settings({headerTableSize: -1}),
+ assert.throws(() => client.settings({ headerTableSize: -1 }),
RangeError);
- assert.throws(() => client.settings({headerTableSize: 2 ** 32}),
+ assert.throws(() => client.settings({ headerTableSize: 2 ** 32 }),
RangeError);
- assert.throws(() => client.settings({initialWindowSize: -1}),
+ assert.throws(() => client.settings({ initialWindowSize: -1 }),
RangeError);
- assert.throws(() => client.settings({initialWindowSize: 2 ** 32}),
+ assert.throws(() => client.settings({ initialWindowSize: 2 ** 32 }),
RangeError);
- assert.throws(() => client.settings({maxFrameSize: 1}),
+ assert.throws(() => client.settings({ maxFrameSize: 1 }),
RangeError);
- assert.throws(() => client.settings({maxFrameSize: 2 ** 24}),
+ assert.throws(() => client.settings({ maxFrameSize: 2 ** 24 }),
RangeError);
- assert.throws(() => client.settings({maxConcurrentStreams: -1}),
+ assert.throws(() => client.settings({ maxConcurrentStreams: -1 }),
RangeError);
- assert.throws(() => client.settings({maxConcurrentStreams: 2 ** 31}),
+ assert.throws(() => client.settings({ maxConcurrentStreams: 2 ** 31 }),
RangeError);
- assert.throws(() => client.settings({maxHeaderListSize: -1}),
+ assert.throws(() => client.settings({ maxHeaderListSize: -1 }),
RangeError);
- assert.throws(() => client.settings({maxHeaderListSize: 2 ** 32}),
+ assert.throws(() => client.settings({ maxHeaderListSize: 2 ** 32 }),
RangeError);
['a', 1, 0, null, {}].forEach((i) => {
- assert.throws(() => client.settings({enablePush: i}), TypeError);
+ assert.throws(() => client.settings({ enablePush: i }), TypeError);
});
client.settings({ maxFrameSize: 1234567 });
diff --git a/test/parallel/test-http2-client-shutdown-before-connect.js b/test/parallel/test-http2-client-shutdown-before-connect.js
index 203963bf57..eabfc6261c 100644
--- a/test/parallel/test-http2-client-shutdown-before-connect.js
+++ b/test/parallel/test-http2-client-shutdown-before-connect.js
@@ -15,7 +15,7 @@ server.on('listening', common.mustCall(() => {
const client = h2.connect(`http://localhost:${server.address().port}`);
- client.shutdown({graceful: true}, common.mustCall(() => {
+ client.shutdown({ graceful: true }, common.mustCall(() => {
server.close();
client.destroy();
}));
diff --git a/test/parallel/test-http2-compat-serverresponse-flushheaders.js b/test/parallel/test-http2-compat-serverresponse-flushheaders.js
index 4bfe490912..932f79b7c2 100644
--- a/test/parallel/test-http2-compat-serverresponse-flushheaders.js
+++ b/test/parallel/test-http2-compat-serverresponse-flushheaders.js
@@ -13,7 +13,7 @@ server.listen(0, common.mustCall(function() {
server.once('request', common.mustCall(function(request, response) {
response.flushHeaders();
response.flushHeaders(); // Idempotent
- response.writeHead(400, {'foo-bar': 'abc123'}); // Ignored
+ response.writeHead(400, { 'foo-bar': 'abc123' }); // Ignored
response.on('finish', common.mustCall(function() {
server.close();
diff --git a/test/parallel/test-http2-compat-serverresponse-headers.js b/test/parallel/test-http2-compat-serverresponse-headers.js
index 28bd36ce2e..8e11babddf 100644
--- a/test/parallel/test-http2-compat-serverresponse-headers.js
+++ b/test/parallel/test-http2-compat-serverresponse-headers.js
@@ -53,7 +53,7 @@ server.listen(0, common.mustCall(function() {
response.setHeader(real, expectedValue);
const expectedHeaderNames = [real];
assert.deepStrictEqual(response.getHeaderNames(), expectedHeaderNames);
- const expectedHeaders = {[real]: expectedValue};
+ const expectedHeaders = { [real]: expectedValue };
assert.deepStrictEqual(response.getHeaders(), expectedHeaders);
response.getHeaders()[fake] = fake;
diff --git a/test/parallel/test-http2-compat-serverresponse-statusmessage.js b/test/parallel/test-http2-compat-serverresponse-statusmessage.js
index 08822c9939..c9c5b5a013 100644
--- a/test/parallel/test-http2-compat-serverresponse-statusmessage.js
+++ b/test/parallel/test-http2-compat-serverresponse-statusmessage.js
@@ -8,7 +8,7 @@ const h2 = require('http2');
// Http2ServerResponse.writeHead should accept an optional status message
const unsupportedWarned = common.mustCall(1);
-process.on('warning', ({name, message}) => {
+process.on('warning', ({ name, message }) => {
const expectedMessage =
'Status message is not supported by HTTP/2 (RFC7540 8.1.2.4)';
if (name === 'UnsupportedWarning' && message === expectedMessage)
@@ -21,7 +21,7 @@ server.listen(0, common.mustCall(function() {
server.once('request', common.mustCall(function(request, response) {
const statusCode = 200;
const statusMessage = 'OK';
- const headers = {'foo-bar': 'abc123'};
+ const headers = { 'foo-bar': 'abc123' };
response.writeHead(statusCode, statusMessage, headers);
response.on('finish', common.mustCall(function() {
diff --git a/test/parallel/test-http2-compat-serverresponse-writehead.js b/test/parallel/test-http2-compat-serverresponse-writehead.js
index b4c531d339..721b84ca3e 100644
--- a/test/parallel/test-http2-compat-serverresponse-writehead.js
+++ b/test/parallel/test-http2-compat-serverresponse-writehead.js
@@ -13,7 +13,7 @@ server.listen(0, common.mustCall(function() {
server.once('request', common.mustCall(function(request, response) {
response.setHeader('foo-bar', 'def456');
response.writeHead(500);
- response.writeHead(418, {'foo-bar': 'abc123'}); // Override
+ response.writeHead(418, { 'foo-bar': 'abc123' }); // Override
response.on('finish', common.mustCall(function() {
assert.doesNotThrow(() => { response.writeHead(300); });
diff --git a/test/parallel/test-http2-create-client-connect.js b/test/parallel/test-http2-create-client-connect.js
index 8173dc3d08..376b0a208d 100644
--- a/test/parallel/test-http2-create-client-connect.js
+++ b/test/parallel/test-http2-create-client-connect.js
@@ -21,8 +21,8 @@ const URL = url.URL;
[`http://localhost:${port}`],
[new URL(`http://localhost:${port}`)],
[url.parse(`http://localhost:${port}`)],
- [{port: port}, {protocol: 'http:'}],
- [{port: port, hostname: '127.0.0.1'}, {protocol: 'http:'}]
+ [{ port: port }, { protocol: 'http:' }],
+ [{ port: port, hostname: '127.0.0.1' }, { protocol: 'http:' }]
];
let count = items.length;
@@ -41,7 +41,7 @@ const URL = url.URL;
});
// Will fail because protocol does not match the server.
- h2.connect({port: port, protocol: 'https:'})
+ h2.connect({ port: port, protocol: 'https:' })
.on('socketError', common.mustCall());
}));
}
@@ -60,14 +60,14 @@ const URL = url.URL;
server.on('listening', common.mustCall(function() {
const port = this.address().port;
- const opts = {rejectUnauthorized: false};
+ const opts = { rejectUnauthorized: false };
const items = [
[`https://localhost:${port}`, opts],
[new URL(`https://localhost:${port}`), opts],
[url.parse(`https://localhost:${port}`), opts],
- [{port: port, protocol: 'https:'}, opts],
- [{port: port, hostname: '127.0.0.1', protocol: 'https:'}, opts]
+ [{ port: port, protocol: 'https:' }, opts],
+ [{ port: port, hostname: '127.0.0.1', protocol: 'https:' }, opts]
];
let count = items.length;
diff --git a/test/parallel/test-http2-create-client-secure-session.js b/test/parallel/test-http2-create-client-secure-session.js
index 9b1cf4a0c9..a775ebd71e 100644
--- a/test/parallel/test-http2-create-client-secure-session.js
+++ b/test/parallel/test-http2-create-client-secure-session.js
@@ -27,7 +27,7 @@ function onStream(stream, headers) {
}
function verifySecureSession(key, cert, ca, opts) {
- const server = h2.createSecureServer({cert, key});
+ const server = h2.createSecureServer({ cert, key });
server.on('stream', common.mustCall(onStream));
server.listen(0);
server.on('listening', common.mustCall(function() {
@@ -35,7 +35,7 @@ function verifySecureSession(key, cert, ca, opts) {
if (!opts) {
opts = {};
}
- opts.secureContext = tls.createSecureContext({ca});
+ opts.secureContext = tls.createSecureContext({ ca });
const client = h2.connect(`https://localhost:${this.address().port}`, opts, function() {
const req = client.request(headers);
@@ -72,4 +72,4 @@ verifySecureSession(
loadKey('agent1-key.pem'),
loadKey('agent1-cert.pem'),
loadKey('ca1-cert.pem'),
- {servername: 'agent1'});
+ { servername: 'agent1' });
diff --git a/test/parallel/test-http2-getpackedsettings.js b/test/parallel/test-http2-getpackedsettings.js
index 0c1a1bccee..0e3744fc27 100644
--- a/test/parallel/test-http2-getpackedsettings.js
+++ b/test/parallel/test-http2-getpackedsettings.js
@@ -122,7 +122,7 @@ assert.doesNotThrow(() => http2.getPackedSettings({ enablePush: false }));
const packed = Buffer.from([0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF]);
assert.throws(() => {
- http2.getUnpackedSettings(packed, {validate: true});
+ http2.getUnpackedSettings(packed, { validate: true });
}, common.expectsError({
code: 'ERR_HTTP2_INVALID_SETTING_VALUE',
type: RangeError,
diff --git a/test/parallel/test-http2-info-headers.js b/test/parallel/test-http2-info-headers.js
index c5d93d514f..cb91b560cd 100755
--- a/test/parallel/test-http2-info-headers.js
+++ b/test/parallel/test-http2-info-headers.js
@@ -56,7 +56,7 @@ server.on('listening', common.mustCall(() => {
const client = h2.connect(`http://localhost:${server.address().port}`);
- const req = client.request({ ':path': '/'});
+ const req = client.request({ ':path': '/' });
// The additionalHeaders method does not exist on client stream
assert.strictEqual(req.additionalHeaders, undefined);
diff --git a/test/parallel/test-http2-max-concurrent-streams.js b/test/parallel/test-http2-max-concurrent-streams.js
index 6725a7c754..9b582eb68d 100644
--- a/test/parallel/test-http2-max-concurrent-streams.js
+++ b/test/parallel/test-http2-max-concurrent-streams.js
@@ -13,7 +13,7 @@ const {
} = h2.constants;
// Only allow one stream to be open at a time
-const server = h2.createServer({ settings: { maxConcurrentStreams: 1 }});
+const server = h2.createServer({ settings: { maxConcurrentStreams: 1 } });
// The stream handler must be called only once
server.on('stream', common.mustCall((stream) => {
diff --git a/test/parallel/test-http2-misused-pseudoheaders.js b/test/parallel/test-http2-misused-pseudoheaders.js
index e356169d26..94475519cb 100644
--- a/test/parallel/test-http2-misused-pseudoheaders.js
+++ b/test/parallel/test-http2-misused-pseudoheaders.js
@@ -18,7 +18,7 @@ function onStream(stream, headers, flags) {
':method',
':scheme'
].forEach((i) => {
- assert.throws(() => stream.respond({[i]: '/'}),
+ assert.throws(() => stream.respond({ [i]: '/' }),
common.expectsError({
code: 'ERR_HTTP2_INVALID_PSEUDOHEADER'
}));
diff --git a/test/parallel/test-http2-multi-content-length.js b/test/parallel/test-http2-multi-content-length.js
index 5dcd56990b..9ab1d48c21 100644
--- a/test/parallel/test-http2-multi-content-length.js
+++ b/test/parallel/test-http2-multi-content-length.js
@@ -47,7 +47,7 @@ server.listen(0, common.mustCall(() => {
// Request 3 will fail because nghttp2 does not allow the content-length
// header to be set for non-payload bearing requests...
- const req3 = client.request({ 'content-length': 1});
+ const req3 = client.request({ 'content-length': 1 });
req3.resume();
req3.on('end', common.mustCall(maybeClose));
req3.on('error', common.expectsError({
diff --git a/test/parallel/test-http2-multiplex.js b/test/parallel/test-http2-multiplex.js
index b6b81c73a6..7467b0fa60 100644
--- a/test/parallel/test-http2-multiplex.js
+++ b/test/parallel/test-http2-multiplex.js
@@ -30,7 +30,7 @@ server.listen(0, common.mustCall(() => {
}
function doRequest() {
- const req = client.request({ ':method': 'POST '});
+ const req = client.request({ ':method': 'POST ' });
let data = '';
req.setEncoding('utf8');
diff --git a/test/parallel/test-http2-priority-event.js b/test/parallel/test-http2-priority-event.js
index bbb248265e..505f0f1e9b 100644
--- a/test/parallel/test-http2-priority-event.js
+++ b/test/parallel/test-http2-priority-event.js
@@ -37,7 +37,7 @@ server.on('priority', common.mustCall(onPriority));
server.on('listening', common.mustCall(() => {
const client = h2.connect(`http://localhost:${server.address().port}`);
- const req = client.request({ ':path': '/'});
+ const req = client.request({ ':path': '/' });
client.on('connect', () => {
req.priority({
diff --git a/test/parallel/test-http2-respond-file-fd-range.js b/test/parallel/test-http2-respond-file-fd-range.js
new file mode 100644
index 0000000000..083dd45615
--- /dev/null
+++ b/test/parallel/test-http2-respond-file-fd-range.js
@@ -0,0 +1,94 @@
+// Flags: --expose-http2
+'use strict';
+
+// Tests the ability to minimally request a byte range with respondWithFD
+
+const common = require('../common');
+const http2 = require('http2');
+const assert = require('assert');
+const path = require('path');
+const fs = require('fs');
+
+const {
+ HTTP2_HEADER_CONTENT_TYPE,
+ HTTP2_HEADER_CONTENT_LENGTH
+} = http2.constants;
+
+const fname = path.resolve(common.fixturesDir, 'printA.js');
+const data = fs.readFileSync(fname);
+const fd = fs.openSync(fname, 'r');
+
+// Note: this is not anywhere close to a proper implementation of the range
+// header.
+function getOffsetLength(range) {
+ if (range === undefined)
+ return [0, -1];
+ const r = /bytes=(\d+)-(\d+)/.exec(range);
+ return [+r[1], +r[2] - +r[1]];
+}
+
+const server = http2.createServer();
+server.on('stream', (stream, headers) => {
+
+ const [ offset, length ] = getOffsetLength(headers.range);
+
+ stream.respondWithFD(fd, {
+ [HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
+ }, {
+ statCheck: common.mustCall((stat, headers, options) => {
+ assert.strictEqual(options.length, length);
+ assert.strictEqual(options.offset, offset);
+ headers[HTTP2_HEADER_CONTENT_LENGTH] =
+ Math.min(options.length, stat.size - offset);
+ }),
+ offset: offset,
+ length: length
+ });
+});
+server.on('close', common.mustCall(() => fs.closeSync(fd)));
+server.listen(0, () => {
+ const client = http2.connect(`http://localhost:${server.address().port}`);
+
+ let remaining = 2;
+ function maybeClose() {
+ if (--remaining === 0) {
+ client.destroy();
+ server.close();
+ }
+ }
+
+ {
+ const req = client.request({ range: 'bytes=8-11' });
+
+ req.on('response', common.mustCall((headers) => {
+ assert.strictEqual(headers[HTTP2_HEADER_CONTENT_TYPE], 'text/plain');
+ assert.strictEqual(+headers[HTTP2_HEADER_CONTENT_LENGTH], 3);
+ }));
+ req.setEncoding('utf8');
+ let check = '';
+ req.on('data', (chunk) => check += chunk);
+ req.on('end', common.mustCall(() => {
+ assert.strictEqual(check, data.toString('utf8', 8, 11));
+ }));
+ req.on('streamClosed', common.mustCall(maybeClose));
+ req.end();
+ }
+
+ {
+ const req = client.request({ range: 'bytes=8-28' });
+
+ req.on('response', common.mustCall((headers) => {
+ assert.strictEqual(headers[HTTP2_HEADER_CONTENT_TYPE], 'text/plain');
+ assert.strictEqual(+headers[HTTP2_HEADER_CONTENT_LENGTH], 9);
+ }));
+ req.setEncoding('utf8');
+ let check = '';
+ req.on('data', (chunk) => check += chunk);
+ req.on('end', common.mustCall(() => {
+ assert.strictEqual(check, data.toString('utf8', 8, 28));
+ }));
+ req.on('streamClosed', common.mustCall(maybeClose));
+ req.end();
+ }
+
+});
diff --git a/test/parallel/test-http2-respond-file-range.js b/test/parallel/test-http2-respond-file-range.js
new file mode 100644
index 0000000000..40c5526953
--- /dev/null
+++ b/test/parallel/test-http2-respond-file-range.js
@@ -0,0 +1,52 @@
+// Flags: --expose-http2
+'use strict';
+
+const common = require('../common');
+const http2 = require('http2');
+const assert = require('assert');
+const path = require('path');
+const fs = require('fs');
+
+const {
+ HTTP2_HEADER_CONTENT_TYPE,
+ HTTP2_HEADER_CONTENT_LENGTH,
+ HTTP2_HEADER_LAST_MODIFIED
+} = http2.constants;
+
+const fname = path.resolve(common.fixturesDir, 'printA.js');
+const data = fs.readFileSync(fname);
+const stat = fs.statSync(fname);
+
+const server = http2.createServer();
+server.on('stream', (stream) => {
+ stream.respondWithFile(fname, {
+ [HTTP2_HEADER_CONTENT_TYPE]: 'text/plain'
+ }, {
+ statCheck: common.mustCall((stat, headers) => {
+ headers[HTTP2_HEADER_LAST_MODIFIED] = stat.mtime.toUTCString();
+ }),
+ offset: 8,
+ length: 3
+ });
+});
+server.listen(0, () => {
+
+ const client = http2.connect(`http://localhost:${server.address().port}`);
+ const req = client.request();
+
+ req.on('response', common.mustCall((headers) => {
+ assert.strictEqual(headers[HTTP2_HEADER_CONTENT_TYPE], 'text/plain');
+ assert.strictEqual(+headers[HTTP2_HEADER_CONTENT_LENGTH], 3);
+ assert.strictEqual(headers[HTTP2_HEADER_LAST_MODIFIED],
+ stat.mtime.toUTCString());
+ }));
+ req.setEncoding('utf8');
+ let check = '';
+ req.on('data', (chunk) => check += chunk);
+ req.on('end', common.mustCall(() => {
+ assert.strictEqual(check, data.toString('utf8', 8, 11));
+ client.destroy();
+ server.close();
+ }));
+ req.end();
+});
diff --git a/test/parallel/test-http2-serve-file.js b/test/parallel/test-http2-serve-file.js
index 1092703274..6c88a42863 100644
--- a/test/parallel/test-http2-serve-file.js
+++ b/test/parallel/test-http2-serve-file.js
@@ -25,7 +25,7 @@ const key = loadKey('agent8-key.pem');
const cert = loadKey('agent8-cert.pem');
const ca = loadKey('fake-startcom-root-cert.pem');
-const server = http2.createSecureServer({key, cert});
+const server = http2.createSecureServer({ key, cert });
server.on('stream', (stream, headers) => {
const name = headers[HTTP2_HEADER_PATH].slice(1);
@@ -44,7 +44,7 @@ server.on('stream', (stream, headers) => {
server.listen(8000, () => {
- const secureContext = tls.createSecureContext({ca});
+ const secureContext = tls.createSecureContext({ ca });
const client = http2.connect(`https://localhost:${server.address().port}`,
{ secureContext });
diff --git a/test/parallel/test-http2-server-push-disabled.js b/test/parallel/test-http2-server-push-disabled.js
index 1fedf22293..33f8ed7c65 100644
--- a/test/parallel/test-http2-server-push-disabled.js
+++ b/test/parallel/test-http2-server-push-disabled.js
@@ -36,7 +36,7 @@ server.on('stream', common.mustCall((stream) => {
}));
server.listen(0, common.mustCall(() => {
- const options = {settings: { enablePush: false }};
+ const options = { settings: { enablePush: false } };
const client = http2.connect(`http://localhost:${server.address().port}`,
options);
const req = client.request({ ':path': '/' });
diff --git a/test/parallel/test-http2-server-shutdown-before-respond.js b/test/parallel/test-http2-server-shutdown-before-respond.js
index c7ffee7e31..ccdbbf5d01 100644
--- a/test/parallel/test-http2-server-shutdown-before-respond.js
+++ b/test/parallel/test-http2-server-shutdown-before-respond.js
@@ -11,7 +11,7 @@ server.on('stream', common.mustCall(onStream));
function onStream(stream, headers, flags) {
const session = stream.session;
- stream.session.shutdown({graceful: true}, common.mustCall(() => {
+ stream.session.shutdown({ graceful: true }, common.mustCall(() => {
session.destroy();
}));
stream.respond({});
diff --git a/test/parallel/test-http2-session-settings.js b/test/parallel/test-http2-session-settings.js
index bc9877e23e..1d358f2c96 100644
--- a/test/parallel/test-http2-session-settings.js
+++ b/test/parallel/test-http2-session-settings.js
@@ -90,7 +90,7 @@ server.on('listening', common.mustCall(() => {
}));
});
[1, {}, 'test', [], null, Infinity, NaN].forEach((i) => {
- assert.throws(() => client.settings({enablePush: i}),
+ assert.throws(() => client.settings({ enablePush: i }),
common.expectsError({
code: 'ERR_HTTP2_INVALID_SETTING_VALUE',
type: TypeError
diff --git a/test/parallel/test-http2-timeouts.js b/test/parallel/test-http2-timeouts.js
index 132496e1fc..093a1bc3d1 100644
--- a/test/parallel/test-http2-timeouts.js
+++ b/test/parallel/test-http2-timeouts.js
@@ -9,7 +9,7 @@ const server = h2.createServer();
// we use the lower-level API here
server.on('stream', common.mustCall((stream) => {
stream.setTimeout(1, common.mustCall(() => {
- stream.respond({':status': 200});
+ stream.respond({ ':status': 200 });
stream.end('hello world');
}));
}));
diff --git a/test/parallel/test-http2-trailers.js b/test/parallel/test-http2-trailers.js
index 35a5dbf28a..04ad3b0ff9 100644
--- a/test/parallel/test-http2-trailers.js
+++ b/test/parallel/test-http2-trailers.js
@@ -29,7 +29,7 @@ server.listen(0);
server.on('listening', common.mustCall(function() {
const client = h2.connect(`http://localhost:${this.address().port}`);
- const req = client.request({':path': '/'});
+ const req = client.request({ ':path': '/' });
req.on('data', common.mustCall());
req.on('trailers', common.mustCall((headers) => {
assert.strictEqual(headers[trailerKey], trailerValue);
diff --git a/test/parallel/test-http2-util-headers-list.js b/test/parallel/test-http2-util-headers-list.js
index d19c78a2b3..c895a4f7a0 100644
--- a/test/parallel/test-http2-util-headers-list.js
+++ b/test/parallel/test-http2-util-headers-list.js
@@ -191,7 +191,7 @@ const {
common.expectsError({
code: 'ERR_HTTP2_HEADER_SINGLE_VALUE',
message: msg
- })(mapToHeaders({[name]: [1, 2, 3]}));
+ })(mapToHeaders({ [name]: [1, 2, 3] }));
});
[
@@ -217,7 +217,7 @@ const {
HTTP2_HEADER_VIA,
HTTP2_HEADER_WWW_AUTHENTICATE
].forEach((name) => {
- assert(!(mapToHeaders({[name]: [1, 2, 3]}) instanceof Error), name);
+ assert(!(mapToHeaders({ [name]: [1, 2, 3] }) instanceof Error), name);
});
const regex =
@@ -242,7 +242,7 @@ const regex =
common.expectsError({
code: 'ERR_HTTP2_INVALID_CONNECTION_HEADERS',
message: regex
- })(mapToHeaders({[name]: 'abc'}));
+ })(mapToHeaders({ [name]: 'abc' }));
});
assert(!(mapToHeaders({ te: 'trailers' }) instanceof Error));
diff --git a/test/parallel/test-http2-write-empty-string.js b/test/parallel/test-http2-write-empty-string.js
index 74675fd67d..661777e39f 100644
--- a/test/parallel/test-http2-write-empty-string.js
+++ b/test/parallel/test-http2-write-empty-string.js
@@ -6,7 +6,7 @@ const assert = require('assert');
const http2 = require('http2');
const server = http2.createServer(function(request, response) {
- response.writeHead(200, {'Content-Type': 'text/plain'});
+ response.writeHead(200, { 'Content-Type': 'text/plain' });
response.write('1\n');
response.write('');
response.write('2\n');
diff --git a/test/parallel/test-tls-disable-renegotiation.js b/test/parallel/test-tls-disable-renegotiation.js
index 0efa935e43..616b08a7cb 100644
--- a/test/parallel/test-tls-disable-renegotiation.js
+++ b/test/parallel/test-tls-disable-renegotiation.js
@@ -39,13 +39,17 @@ const server = tls.Server(options, common.mustCall((socket) => {
server.listen(0, common.mustCall(() => {
const port = server.address().port;
+ const options = {
+ rejectUnauthorized: false,
+ port
+ };
const client =
- tls.connect({rejectUnauthorized: false, port: port}, common.mustCall(() => {
+ tls.connect(options, common.mustCall(() => {
client.write('');
// Negotiation is still permitted for this first
// attempt. This should succeed.
client.renegotiate(
- {rejectUnauthorized: false},
+ { rejectUnauthorized: false },
common.mustCall(() => {
// Once renegotiation completes, we write some
// data to the socket, which triggers the on
@@ -58,7 +62,7 @@ server.listen(0, common.mustCall(() => {
// server will simply drop the connection after
// emitting the error.
client.renegotiate(
- {rejectUnauthorized: false},
+ { rejectUnauthorized: false },
common.mustNotCall());
}));
}));