http2: address initial pr feedback

PR-URL: https://github.com/nodejs/node/pull/14239
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
James M Snell 2017-07-31 11:53:15 -07:00
parent 78cd5fedd5
commit 59e509808a
18 changed files with 142 additions and 132 deletions

View File

@ -410,9 +410,10 @@ added: REPLACEME
(No Error). (No Error).
* `lastStreamID` {number} The Stream ID of the last successfully processed * `lastStreamID` {number} The Stream ID of the last successfully processed
`Http2Stream` on this `Http2Session`. `Http2Stream` on this `Http2Session`.
* `opaqueData` {Buffer} A `Buffer` instance containing arbitrary additional * `opaqueData` {Buffer|Uint8Array} A `Buffer` or `Uint8Array` instance
data to send to the peer upon disconnection. This is used, typically, to containing arbitrary additional data to send to the peer upon disconnection.
provide additional data for debugging failures, if necessary. This is used, typically, to provide additional data for debugging failures,
if necessary.
* `callback` {Function} A callback that is invoked after the session shutdown * `callback` {Function} A callback that is invoked after the session shutdown
has been completed. has been completed.
* Returns: {undefined} * Returns: {undefined}
@ -965,7 +966,7 @@ added: REPLACEME
initiated. initiated.
* Returns: {undefined} * Returns: {undefined}
Initiates a push stream. The callback is invoked with the new `Htt2Stream` Initiates a push stream. The callback is invoked with the new `Http2Stream`
instance created for the push stream. instance created for the push stream.
```js ```js
@ -1619,7 +1620,7 @@ passed in. These will always be reported by a synchronous `throw`.
State Errors occur when an action is attempted at an incorrect time (for State Errors occur when an action is attempted at an incorrect time (for
instance, attempting to send data on a stream after it has closed). These will instance, attempting to send data on a stream after it has closed). These will
be repoorted using either a synchronous `throw` or via an `'error'` event on be reported using either a synchronous `throw` or via an `'error'` event on
the `Http2Stream`, `Http2Session` or HTTP/2 Server objects, depending on where the `Http2Stream`, `Http2Session` or HTTP/2 Server objects, depending on where
and when the error occurs. and when the error occurs.

View File

@ -2,8 +2,9 @@
/* eslint-disable no-use-before-define */ /* eslint-disable no-use-before-define */
require('internal/util').assertCrypto();
const binding = process.binding('http2'); const binding = process.binding('http2');
const debug = require('util').debuglog('http2');
const assert = require('assert'); const assert = require('assert');
const Buffer = require('buffer').Buffer; const Buffer = require('buffer').Buffer;
const EventEmitter = require('events'); const EventEmitter = require('events');
@ -18,6 +19,8 @@ const { onServerStream } = require('internal/http2/compat');
const { utcDate } = require('internal/http'); const { utcDate } = require('internal/http');
const { _connectionListener: httpConnectionListener } = require('http'); const { _connectionListener: httpConnectionListener } = require('http');
const { isUint8Array } = process.binding('util'); const { isUint8Array } = process.binding('util');
const debug = util.debuglog('http2');
const { const {
assertIsObject, assertIsObject,
@ -459,9 +462,9 @@ function requestOnConnect(headers, options) {
} }
function validatePriorityOptions(options) { function validatePriorityOptions(options) {
if (options.weight === undefined) if (options.weight === undefined) {
options.weight = NGHTTP2_DEFAULT_WEIGHT; options.weight = NGHTTP2_DEFAULT_WEIGHT;
else if (typeof options.weight !== 'number') { } else if (typeof options.weight !== 'number') {
const err = new errors.RangeError('ERR_INVALID_OPT_VALUE', const err = new errors.RangeError('ERR_INVALID_OPT_VALUE',
'weight', 'weight',
options.weight); options.weight);
@ -469,9 +472,9 @@ function validatePriorityOptions(options) {
throw err; throw err;
} }
if (options.parent === undefined) if (options.parent === undefined) {
options.parent = 0; options.parent = 0;
else if (typeof options.parent !== 'number' || options.parent < 0) { } else if (typeof options.parent !== 'number' || options.parent < 0) {
const err = new errors.RangeError('ERR_INVALID_OPT_VALUE', const err = new errors.RangeError('ERR_INVALID_OPT_VALUE',
'parent', 'parent',
options.parent); options.parent);
@ -479,9 +482,9 @@ function validatePriorityOptions(options) {
throw err; throw err;
} }
if (options.exclusive === undefined) if (options.exclusive === undefined) {
options.exclusive = false; options.exclusive = false;
else if (typeof options.exclusive !== 'boolean') { } else if (typeof options.exclusive !== 'boolean') {
const err = new errors.RangeError('ERR_INVALID_OPT_VALUE', const err = new errors.RangeError('ERR_INVALID_OPT_VALUE',
'exclusive', 'exclusive',
options.exclusive); options.exclusive);
@ -489,9 +492,9 @@ function validatePriorityOptions(options) {
throw err; throw err;
} }
if (options.silent === undefined) if (options.silent === undefined) {
options.silent = false; options.silent = false;
else if (typeof options.silent !== 'boolean') { } else if (typeof options.silent !== 'boolean') {
const err = new errors.RangeError('ERR_INVALID_OPT_VALUE', const err = new errors.RangeError('ERR_INVALID_OPT_VALUE',
'silent', 'silent',
options.silent); options.silent);
@ -982,7 +985,7 @@ class Http2Session extends EventEmitter {
options = Object.assign(Object.create(null), options); options = Object.assign(Object.create(null), options);
if (options.opaqueData !== undefined && if (options.opaqueData !== undefined &&
!Buffer.isBuffer(options.opaqueData)) { !isUint8Array(options.opaqueData)) {
throw new errors.TypeError('ERR_INVALID_OPT_VALUE', throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
'opaqueData', 'opaqueData',
options.opaqueData); options.opaqueData);
@ -1008,13 +1011,6 @@ class Http2Session extends EventEmitter {
options.lastStreamID); options.lastStreamID);
} }
if (options.opaqueData !== undefined &&
!Buffer.isBuffer(options.opaqueData)) {
throw new errors.TypeError('ERR_INVALID_OPT_VALUE',
'opaqueData',
options.opaqueData);
}
if (callback) { if (callback) {
this.on('shutdown', callback); this.on('shutdown', callback);
} }
@ -1233,7 +1229,6 @@ function streamOnceReady() {
function abort(stream) { function abort(stream) {
if (!stream[kState].aborted && if (!stream[kState].aborted &&
stream._writableState &&
!(stream._writableState.ended || stream._writableState.ending)) { !(stream._writableState.ended || stream._writableState.ending)) {
stream.emit('aborted'); stream.emit('aborted');
stream[kState].aborted = true; stream[kState].aborted = true;
@ -1351,7 +1346,6 @@ class Http2Stream extends Duplex {
if (err) if (err)
throw util._errnoException(err, 'write', req.error); throw util._errnoException(err, 'write', req.error);
this._bytesDispatched += req.bytes; this._bytesDispatched += req.bytes;
} }
_writev(data, cb) { _writev(data, cb) {

View File

@ -219,7 +219,6 @@ function getDefaultSettings() {
if ((flags & (1 << IDX_SETTINGS_MAX_CONCURRENT_STREAMS)) === if ((flags & (1 << IDX_SETTINGS_MAX_CONCURRENT_STREAMS)) ===
(1 << IDX_SETTINGS_MAX_CONCURRENT_STREAMS)) { (1 << IDX_SETTINGS_MAX_CONCURRENT_STREAMS)) {
console.log('setting it');
holder.maxConcurrentStreams = holder.maxConcurrentStreams =
settingsBuffer[IDX_SETTINGS_MAX_CONCURRENT_STREAMS]; settingsBuffer[IDX_SETTINGS_MAX_CONCURRENT_STREAMS];
} }

View File

@ -258,7 +258,9 @@ NODE_EXTERN void RunAtExit(Environment* env);
v8::Isolate* isolate = target->GetIsolate(); \ v8::Isolate* isolate = target->GetIsolate(); \
v8::Local<v8::Context> context = isolate->GetCurrentContext(); \ v8::Local<v8::Context> context = isolate->GetCurrentContext(); \
v8::Local<v8::String> constant_name = \ v8::Local<v8::String> constant_name = \
v8::String::NewFromUtf8(isolate, #constant); \ v8::String::NewFromUtf8(isolate, #constant, \
v8::NewStringType::kInternalized) \
.ToLocalChecked(); \
v8::Local<v8::Number> constant_value = \ v8::Local<v8::Number> constant_value = \
v8::Number::New(isolate, static_cast<double>(constant)); \ v8::Number::New(isolate, static_cast<double>(constant)); \
v8::PropertyAttribute constant_attributes = \ v8::PropertyAttribute constant_attributes = \

View File

@ -357,6 +357,7 @@ size_t NodeBIO::IndexOf(char delim, size_t limit) {
return max; return max;
} }
void NodeBIO::Write(const char* data, size_t size) { void NodeBIO::Write(const char* data, size_t size) {
size_t offset = 0; size_t offset = 0;
size_t left = size; size_t left = size;

View File

@ -61,33 +61,28 @@ Http2Options::Http2Options(Environment* env) {
uint32_t* buffer = env->http2_options_buffer(); uint32_t* buffer = env->http2_options_buffer();
uint32_t flags = buffer[IDX_OPTIONS_FLAGS]; uint32_t flags = buffer[IDX_OPTIONS_FLAGS];
if ((flags & (1 << IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE)) == if (flags & (1 << IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE)) {
(1 << IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE)) {
SetMaxDeflateDynamicTableSize( SetMaxDeflateDynamicTableSize(
buffer[IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE]); buffer[IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE]);
} }
if ((flags & (1 << IDX_OPTIONS_MAX_RESERVED_REMOTE_STREAMS)) == if (flags & (1 << IDX_OPTIONS_MAX_RESERVED_REMOTE_STREAMS)) {
(1 << IDX_OPTIONS_MAX_RESERVED_REMOTE_STREAMS)) {
SetMaxReservedRemoteStreams( SetMaxReservedRemoteStreams(
buffer[IDX_OPTIONS_MAX_RESERVED_REMOTE_STREAMS]); buffer[IDX_OPTIONS_MAX_RESERVED_REMOTE_STREAMS]);
} }
if ((flags & (1 << IDX_OPTIONS_MAX_SEND_HEADER_BLOCK_LENGTH)) == if (flags & (1 << IDX_OPTIONS_MAX_SEND_HEADER_BLOCK_LENGTH)) {
(1 << IDX_OPTIONS_MAX_SEND_HEADER_BLOCK_LENGTH)) {
SetMaxSendHeaderBlockLength( SetMaxSendHeaderBlockLength(
buffer[IDX_OPTIONS_MAX_SEND_HEADER_BLOCK_LENGTH]); buffer[IDX_OPTIONS_MAX_SEND_HEADER_BLOCK_LENGTH]);
} }
SetPeerMaxConcurrentStreams(100); // Recommended default SetPeerMaxConcurrentStreams(100); // Recommended default
if ((flags & (1 << IDX_OPTIONS_PEER_MAX_CONCURRENT_STREAMS)) == if (flags & (1 << IDX_OPTIONS_PEER_MAX_CONCURRENT_STREAMS)) {
(1 << IDX_OPTIONS_PEER_MAX_CONCURRENT_STREAMS)) {
SetPeerMaxConcurrentStreams( SetPeerMaxConcurrentStreams(
buffer[IDX_OPTIONS_PEER_MAX_CONCURRENT_STREAMS]); buffer[IDX_OPTIONS_PEER_MAX_CONCURRENT_STREAMS]);
} }
if ((flags & (1 << IDX_OPTIONS_PADDING_STRATEGY)) == if (flags & (1 << IDX_OPTIONS_PADDING_STRATEGY)) {
(1 << IDX_OPTIONS_PADDING_STRATEGY)) {
SetPaddingStrategy(buffer[IDX_OPTIONS_PADDING_STRATEGY]); SetPaddingStrategy(buffer[IDX_OPTIONS_PADDING_STRATEGY]);
} }
} }
@ -197,48 +192,42 @@ void PackSettings(const FunctionCallbackInfo<Value>& args) {
uint32_t* const buffer = env->http2_settings_buffer(); uint32_t* const buffer = env->http2_settings_buffer();
uint32_t flags = buffer[IDX_SETTINGS_COUNT]; uint32_t flags = buffer[IDX_SETTINGS_COUNT];
if ((flags & (1 << IDX_SETTINGS_HEADER_TABLE_SIZE)) == if (flags & (1 << IDX_SETTINGS_HEADER_TABLE_SIZE)) {
(1 << IDX_SETTINGS_HEADER_TABLE_SIZE)) {
DEBUG_HTTP2("Setting header table size: %d\n", DEBUG_HTTP2("Setting header table size: %d\n",
buffer[IDX_SETTINGS_HEADER_TABLE_SIZE]); buffer[IDX_SETTINGS_HEADER_TABLE_SIZE]);
entries.push_back({NGHTTP2_SETTINGS_HEADER_TABLE_SIZE, entries.push_back({NGHTTP2_SETTINGS_HEADER_TABLE_SIZE,
buffer[IDX_SETTINGS_HEADER_TABLE_SIZE]}); buffer[IDX_SETTINGS_HEADER_TABLE_SIZE]});
} }
if ((flags & (1 << IDX_SETTINGS_MAX_CONCURRENT_STREAMS)) == if (flags & (1 << IDX_SETTINGS_MAX_CONCURRENT_STREAMS)) {
(1 << IDX_SETTINGS_MAX_CONCURRENT_STREAMS)) {
DEBUG_HTTP2("Setting max concurrent streams: %d\n", DEBUG_HTTP2("Setting max concurrent streams: %d\n",
buffer[IDX_SETTINGS_MAX_CONCURRENT_STREAMS]); buffer[IDX_SETTINGS_MAX_CONCURRENT_STREAMS]);
entries.push_back({NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, entries.push_back({NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS,
buffer[IDX_SETTINGS_MAX_CONCURRENT_STREAMS]}); buffer[IDX_SETTINGS_MAX_CONCURRENT_STREAMS]});
} }
if ((flags & (1 << IDX_SETTINGS_MAX_FRAME_SIZE)) == if (flags & (1 << IDX_SETTINGS_MAX_FRAME_SIZE)) {
(1 << IDX_SETTINGS_MAX_FRAME_SIZE)) {
DEBUG_HTTP2("Setting max frame size: %d\n", DEBUG_HTTP2("Setting max frame size: %d\n",
buffer[IDX_SETTINGS_MAX_FRAME_SIZE]); buffer[IDX_SETTINGS_MAX_FRAME_SIZE]);
entries.push_back({NGHTTP2_SETTINGS_MAX_FRAME_SIZE, entries.push_back({NGHTTP2_SETTINGS_MAX_FRAME_SIZE,
buffer[IDX_SETTINGS_MAX_FRAME_SIZE]}); buffer[IDX_SETTINGS_MAX_FRAME_SIZE]});
} }
if ((flags & (1 << IDX_SETTINGS_INITIAL_WINDOW_SIZE)) == if (flags & (1 << IDX_SETTINGS_INITIAL_WINDOW_SIZE)) {
(1 << IDX_SETTINGS_INITIAL_WINDOW_SIZE)) {
DEBUG_HTTP2("Setting initial window size: %d\n", DEBUG_HTTP2("Setting initial window size: %d\n",
buffer[IDX_SETTINGS_INITIAL_WINDOW_SIZE]); buffer[IDX_SETTINGS_INITIAL_WINDOW_SIZE]);
entries.push_back({NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, entries.push_back({NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE,
buffer[IDX_SETTINGS_INITIAL_WINDOW_SIZE]}); buffer[IDX_SETTINGS_INITIAL_WINDOW_SIZE]});
} }
if ((flags & (1 << IDX_SETTINGS_MAX_HEADER_LIST_SIZE)) == if (flags & (1 << IDX_SETTINGS_MAX_HEADER_LIST_SIZE)) {
(1 << IDX_SETTINGS_MAX_HEADER_LIST_SIZE)) {
DEBUG_HTTP2("Setting max header list size: %d\n", DEBUG_HTTP2("Setting max header list size: %d\n",
buffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE]); buffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE]);
entries.push_back({NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, entries.push_back({NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE,
buffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE]}); buffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE]});
} }
if ((flags & (1 << IDX_SETTINGS_ENABLE_PUSH)) == if (flags & (1 << IDX_SETTINGS_ENABLE_PUSH)) {
(1 << IDX_SETTINGS_ENABLE_PUSH)) {
DEBUG_HTTP2("Setting enable push: %d\n", DEBUG_HTTP2("Setting enable push: %d\n",
buffer[IDX_SETTINGS_ENABLE_PUSH]); buffer[IDX_SETTINGS_ENABLE_PUSH]);
entries.push_back({NGHTTP2_SETTINGS_ENABLE_PUSH, entries.push_back({NGHTTP2_SETTINGS_ENABLE_PUSH,
@ -457,48 +446,42 @@ void Http2Session::SubmitSettings(const FunctionCallbackInfo<Value>& args) {
std::vector<nghttp2_settings_entry> entries; std::vector<nghttp2_settings_entry> entries;
entries.reserve(6); entries.reserve(6);
if ((flags & (1 << IDX_SETTINGS_HEADER_TABLE_SIZE)) == if (flags & (1 << IDX_SETTINGS_HEADER_TABLE_SIZE)) {
(1 << IDX_SETTINGS_HEADER_TABLE_SIZE)) {
DEBUG_HTTP2("Setting header table size: %d\n", DEBUG_HTTP2("Setting header table size: %d\n",
buffer[IDX_SETTINGS_HEADER_TABLE_SIZE]); buffer[IDX_SETTINGS_HEADER_TABLE_SIZE]);
entries.push_back({NGHTTP2_SETTINGS_HEADER_TABLE_SIZE, entries.push_back({NGHTTP2_SETTINGS_HEADER_TABLE_SIZE,
buffer[IDX_SETTINGS_HEADER_TABLE_SIZE]}); buffer[IDX_SETTINGS_HEADER_TABLE_SIZE]});
} }
if ((flags & (1 << IDX_SETTINGS_MAX_CONCURRENT_STREAMS)) == if (flags & (1 << IDX_SETTINGS_MAX_CONCURRENT_STREAMS)) {
(1 << IDX_SETTINGS_MAX_CONCURRENT_STREAMS)) {
DEBUG_HTTP2("Setting max concurrent streams: %d\n", DEBUG_HTTP2("Setting max concurrent streams: %d\n",
buffer[IDX_SETTINGS_MAX_CONCURRENT_STREAMS]); buffer[IDX_SETTINGS_MAX_CONCURRENT_STREAMS]);
entries.push_back({NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, entries.push_back({NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS,
buffer[IDX_SETTINGS_MAX_CONCURRENT_STREAMS]}); buffer[IDX_SETTINGS_MAX_CONCURRENT_STREAMS]});
} }
if ((flags & (1 << IDX_SETTINGS_MAX_FRAME_SIZE)) == if (flags & (1 << IDX_SETTINGS_MAX_FRAME_SIZE)) {
(1 << IDX_SETTINGS_MAX_FRAME_SIZE)) {
DEBUG_HTTP2("Setting max frame size: %d\n", DEBUG_HTTP2("Setting max frame size: %d\n",
buffer[IDX_SETTINGS_MAX_FRAME_SIZE]); buffer[IDX_SETTINGS_MAX_FRAME_SIZE]);
entries.push_back({NGHTTP2_SETTINGS_MAX_FRAME_SIZE, entries.push_back({NGHTTP2_SETTINGS_MAX_FRAME_SIZE,
buffer[IDX_SETTINGS_MAX_FRAME_SIZE]}); buffer[IDX_SETTINGS_MAX_FRAME_SIZE]});
} }
if ((flags & (1 << IDX_SETTINGS_INITIAL_WINDOW_SIZE)) == if (flags & (1 << IDX_SETTINGS_INITIAL_WINDOW_SIZE)) {
(1 << IDX_SETTINGS_INITIAL_WINDOW_SIZE)) {
DEBUG_HTTP2("Setting initial window size: %d\n", DEBUG_HTTP2("Setting initial window size: %d\n",
buffer[IDX_SETTINGS_INITIAL_WINDOW_SIZE]); buffer[IDX_SETTINGS_INITIAL_WINDOW_SIZE]);
entries.push_back({NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, entries.push_back({NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE,
buffer[IDX_SETTINGS_INITIAL_WINDOW_SIZE]}); buffer[IDX_SETTINGS_INITIAL_WINDOW_SIZE]});
} }
if ((flags & (1 << IDX_SETTINGS_MAX_HEADER_LIST_SIZE)) == if (flags & (1 << IDX_SETTINGS_MAX_HEADER_LIST_SIZE)) {
(1 << IDX_SETTINGS_MAX_HEADER_LIST_SIZE)) {
DEBUG_HTTP2("Setting max header list size: %d\n", DEBUG_HTTP2("Setting max header list size: %d\n",
buffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE]); buffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE]);
entries.push_back({NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, entries.push_back({NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE,
buffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE]}); buffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE]});
} }
if ((flags & (1 << IDX_SETTINGS_ENABLE_PUSH)) == if (flags & (1 << IDX_SETTINGS_ENABLE_PUSH)) {
(1 << IDX_SETTINGS_ENABLE_PUSH)) {
DEBUG_HTTP2("Setting enable push: %d\n", DEBUG_HTTP2("Setting enable push: %d\n",
buffer[IDX_SETTINGS_ENABLE_PUSH]); buffer[IDX_SETTINGS_ENABLE_PUSH]);
entries.push_back({NGHTTP2_SETTINGS_ENABLE_PUSH, entries.push_back({NGHTTP2_SETTINGS_ENABLE_PUSH,

View File

@ -143,8 +143,7 @@ class Nghttp2Session {
// Removes a stream instance from this session // Removes a stream instance from this session
inline void RemoveStream(int32_t id); inline void RemoveStream(int32_t id);
virtual void Send(uv_buf_t* buf, virtual void Send(uv_buf_t* buf, size_t length) {}
size_t length) {}
virtual void OnHeaders(Nghttp2Stream* stream, virtual void OnHeaders(Nghttp2Stream* stream,
nghttp2_header_list* headers, nghttp2_header_list* headers,
nghttp2_headers_category cat, nghttp2_headers_category cat,
@ -294,7 +293,7 @@ class Nghttp2Stream {
// Returns true if this stream has been destroyed // Returns true if this stream has been destroyed
inline bool IsDestroyed() const { inline bool IsDestroyed() const {
return (flags_ & NGHTTP2_STREAM_DESTROYED) == NGHTTP2_STREAM_DESTROYED; return flags_ & NGHTTP2_STREAM_DESTROYED;
} }
// Queue outbound chunks of data to be sent on this stream // Queue outbound chunks of data to be sent on this stream
@ -340,7 +339,7 @@ class Nghttp2Stream {
// Returns true if this stream is writable. // Returns true if this stream is writable.
inline bool IsWritable() const { inline bool IsWritable() const {
return (flags_ & NGHTTP2_STREAM_FLAG_SHUT) == 0; return !(flags_ & NGHTTP2_STREAM_FLAG_SHUT);
} }
// Start Reading. If there are queued data chunks, they are pushed into // Start Reading. If there are queued data chunks, they are pushed into
@ -352,15 +351,15 @@ class Nghttp2Stream {
// Returns true if reading is paused // Returns true if reading is paused
inline bool IsPaused() const { inline bool IsPaused() const {
return (flags_ & NGHTTP2_STREAM_READ_PAUSED) == NGHTTP2_STREAM_READ_PAUSED; return flags_ & NGHTTP2_STREAM_READ_PAUSED;
} }
// Returns true if this stream is in the reading state, which occurs when // Returns true if this stream is in the reading state, which occurs when
// the NGHTTP2_STREAM_READ_START flag has been set and the // the NGHTTP2_STREAM_READ_START flag has been set and the
// NGHTTP2_STREAM_READ_PAUSED flag is *not* set. // NGHTTP2_STREAM_READ_PAUSED flag is *not* set.
inline bool IsReading() const { inline bool IsReading() const {
return ((flags_ & NGHTTP2_STREAM_READ_START) == NGHTTP2_STREAM_READ_START) return flags_ & NGHTTP2_STREAM_READ_START &&
&& ((flags_ & NGHTTP2_STREAM_READ_PAUSED) == 0); !(flags_ & NGHTTP2_STREAM_READ_PAUSED);
} }
inline void Close(int32_t code) { inline void Close(int32_t code) {
@ -374,7 +373,7 @@ class Nghttp2Stream {
// Returns true if this stream has been closed either by receiving or // Returns true if this stream has been closed either by receiving or
// sending an RST_STREAM frame. // sending an RST_STREAM frame.
inline bool IsClosed() const { inline bool IsClosed() const {
return (flags_ & NGHTTP2_STREAM_CLOSED) == NGHTTP2_STREAM_CLOSED; return flags_ & NGHTTP2_STREAM_CLOSED;
} }
// Returns the RST_STREAM code used to close this stream // Returns the RST_STREAM code used to close this stream

View File

@ -408,7 +408,6 @@ void StreamBase::AfterWrite(WriteWrap* req_wrap, int status) {
// Unref handle property // Unref handle property
Local<Object> req_wrap_obj = req_wrap->object(); Local<Object> req_wrap_obj = req_wrap->object();
req_wrap_obj->Delete(env->context(), env->handle_string()).FromJust(); req_wrap_obj->Delete(env->context(), env->handle_string()).FromJust();
wrap->OnAfterWrite(req_wrap); wrap->OnAfterWrite(req_wrap);
Local<Value> argv[] = { Local<Value> argv[] = {

View File

@ -16,10 +16,8 @@ server.on('stream', (stream) => {
// must call). The error may or may not be reported depending on operating // must call). The error may or may not be reported depending on operating
// system specific timings. // system specific timings.
stream.on('error', (err) => { stream.on('error', (err) => {
if (err) {
assert.strictEqual(err.code, 'ERR_HTTP2_STREAM_ERROR'); assert.strictEqual(err.code, 'ERR_HTTP2_STREAM_ERROR');
assert.strictEqual(err.message, 'Stream closed with error code 2'); assert.strictEqual(err.message, 'Stream closed with error code 2');
}
}); });
stream.respond({}); stream.respond({});
stream.end(); stream.end();

View File

@ -1,8 +1,8 @@
// Flags: --expose-http2 // Flags: --expose-http2
'use strict'; 'use strict';
const { strictEqual } = require('assert');
const { mustCall, mustNotCall } = require('../common'); const { mustCall, mustNotCall } = require('../common');
const { strictEqual } = require('assert');
const { const {
createServer, createServer,
connect, connect,

View File

@ -42,13 +42,25 @@ server.listen(0, common.mustCall(function() {
assert.throws(function() { assert.throws(function() {
response.setHeader(':status', 'foobar'); response.setHeader(':status', 'foobar');
}, Error); }, common.expectsError({
code: 'ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED',
type: Error,
message: 'Cannot set HTTP/2 pseudo-headers'
}));
assert.throws(function() { assert.throws(function() {
response.setHeader(real, null); response.setHeader(real, null);
}, TypeError); }, common.expectsError({
code: 'ERR_HTTP2_INVALID_HEADER_VALUE',
type: TypeError,
message: 'Value must not be undefined or null'
}));
assert.throws(function() { assert.throws(function() {
response.setHeader(real, undefined); response.setHeader(real, undefined);
}, TypeError); }, common.expectsError({
code: 'ERR_HTTP2_INVALID_HEADER_VALUE',
type: TypeError,
message: 'Value must not be undefined or null'
}));
response.setHeader(real, expectedValue); response.setHeader(real, expectedValue);
const expectedHeaderNames = [real]; const expectedHeaderNames = [real];

View File

@ -1,8 +1,8 @@
// Flags: --expose-http2 // Flags: --expose-http2
'use strict'; 'use strict';
const { throws } = require('assert');
const { mustCall, mustNotCall, expectsError } = require('../common'); const { mustCall, mustNotCall, expectsError } = require('../common');
const { throws } = require('assert');
const { createServer, connect } = require('http2'); const { createServer, connect } = require('http2');
// Http2ServerResponse.write does not imply there is a callback // Http2ServerResponse.write does not imply there is a callback
@ -35,7 +35,11 @@ const expectedError = expectsError({
response.on('error', mustNotCall()); response.on('error', mustNotCall());
throws( throws(
() => { response.write('muahaha'); }, () => { response.write('muahaha'); },
/The stream is already closed/ expectsError({
code: 'ERR_HTTP2_STREAM_CLOSED',
type: Error,
message: 'The stream is already closed'
})
); );
server.close(); server.close();
})); }));

View File

@ -2,6 +2,10 @@
'use strict'; 'use strict';
const common = require('../common'); const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert'); const assert = require('assert');
const path = require('path'); const path = require('path');
const fs = require('fs'); const fs = require('fs');

View File

@ -1,11 +1,11 @@
// Flags: --expose-http2 // Flags: --expose-http2
'use strict'; 'use strict';
const { const common = require('../common');
fixturesDir,
mustCall, if (!common.hasCrypto)
mustNotCall common.skip('missing crypto');
} = require('../common');
const { strictEqual } = require('assert'); const { strictEqual } = require('assert');
const { join } = require('path'); const { join } = require('path');
const { readFileSync } = require('fs'); const { readFileSync } = require('fs');
@ -18,7 +18,7 @@ const { connect: tls } = require('tls');
const countdown = (count, done) => () => --count === 0 && done(); const countdown = (count, done) => () => --count === 0 && done();
function loadKey(keyname) { function loadKey(keyname) {
return readFileSync(join(fixturesDir, 'keys', keyname)); return readFileSync(join(common.fixturesDir, 'keys', keyname));
} }
const key = loadKey('agent8-key.pem'); const key = loadKey('agent8-key.pem');
@ -46,14 +46,14 @@ function onSession(session) {
}; };
const request = session.request(headers); const request = session.request(headers);
request.on('response', mustCall((headers) => { request.on('response', common.mustCall((headers) => {
strictEqual(headers[':status'], 200); strictEqual(headers[':status'], 200);
strictEqual(headers['content-type'], 'application/json'); strictEqual(headers['content-type'], 'application/json');
})); }));
request.setEncoding('utf8'); request.setEncoding('utf8');
let raw = ''; let raw = '';
request.on('data', (chunk) => { raw += chunk; }); request.on('data', (chunk) => { raw += chunk; });
request.on('end', mustCall(() => { request.on('end', common.mustCall(() => {
const { alpnProtocol, httpVersion } = JSON.parse(raw); const { alpnProtocol, httpVersion } = JSON.parse(raw);
strictEqual(alpnProtocol, 'h2'); strictEqual(alpnProtocol, 'h2');
strictEqual(httpVersion, '2.0'); strictEqual(httpVersion, '2.0');
@ -68,12 +68,12 @@ function onSession(session) {
{ {
const server = createSecureServer( const server = createSecureServer(
{ cert, key, allowHTTP1: true }, { cert, key, allowHTTP1: true },
mustCall(onRequest, 2) common.mustCall(onRequest, 2)
); );
server.listen(0); server.listen(0);
server.on('listening', mustCall(() => { server.on('listening', common.mustCall(() => {
const { port } = server.address(); const { port } = server.address();
const origin = `https://localhost:${port}`; const origin = `https://localhost:${port}`;
@ -83,13 +83,13 @@ function onSession(session) {
connect( connect(
origin, origin,
clientOptions, clientOptions,
mustCall(onSession.bind({ cleanup, server })) common.mustCall(onSession.bind({ cleanup, server }))
); );
// HTTP/1.1 client // HTTP/1.1 client
get( get(
Object.assign(parse(origin), clientOptions), Object.assign(parse(origin), clientOptions),
mustCall((response) => { common.mustCall((response) => {
strictEqual(response.statusCode, 200); strictEqual(response.statusCode, 200);
strictEqual(response.statusMessage, 'OK'); strictEqual(response.statusMessage, 'OK');
strictEqual(response.headers['content-type'], 'application/json'); strictEqual(response.headers['content-type'], 'application/json');
@ -97,7 +97,7 @@ function onSession(session) {
response.setEncoding('utf8'); response.setEncoding('utf8');
let raw = ''; let raw = '';
response.on('data', (chunk) => { raw += chunk; }); response.on('data', (chunk) => { raw += chunk; });
response.on('end', mustCall(() => { response.on('end', common.mustCall(() => {
const { alpnProtocol, httpVersion } = JSON.parse(raw); const { alpnProtocol, httpVersion } = JSON.parse(raw);
strictEqual(alpnProtocol, false); strictEqual(alpnProtocol, false);
strictEqual(httpVersion, '1.1'); strictEqual(httpVersion, '1.1');
@ -113,16 +113,16 @@ function onSession(session) {
{ {
const server = createSecureServer( const server = createSecureServer(
{ cert, key }, { cert, key },
mustCall(onRequest) common.mustCall(onRequest)
); );
server.on('unknownProtocol', mustCall((socket) => { server.on('unknownProtocol', common.mustCall((socket) => {
socket.destroy(); socket.destroy();
}, 2)); }, 2));
server.listen(0); server.listen(0);
server.on('listening', mustCall(() => { server.on('listening', common.mustCall(() => {
const { port } = server.address(); const { port } = server.address();
const origin = `https://localhost:${port}`; const origin = `https://localhost:${port}`;
@ -132,15 +132,15 @@ function onSession(session) {
connect( connect(
origin, origin,
clientOptions, clientOptions,
mustCall(onSession.bind({ cleanup, server })) common.mustCall(onSession.bind({ cleanup, server }))
); );
// HTTP/1.1 client // HTTP/1.1 client
get(Object.assign(parse(origin), clientOptions), mustNotCall()) get(Object.assign(parse(origin), clientOptions), common.mustNotCall())
.on('error', mustCall(cleanup)); .on('error', common.mustCall(cleanup));
// Incompatible ALPN TLS client // Incompatible ALPN TLS client
tls(Object.assign({ port, ALPNProtocols: ['fake'] }, clientOptions)) tls(Object.assign({ port, ALPNProtocols: ['fake'] }, clientOptions))
.on('error', mustCall(cleanup)); .on('error', common.mustCall(cleanup));
})); }));
} }

View File

@ -22,6 +22,7 @@ server.listen(0, common.mustCall(() => {
} }
} }
{
// Request 1 will fail because there are two content-length header values // Request 1 will fail because there are two content-length header values
const req = client.request({ const req = client.request({
':method': 'POST', ':method': 'POST',
@ -35,24 +36,29 @@ server.listen(0, common.mustCall(() => {
})); }));
req.on('error', common.mustCall(maybeClose)); req.on('error', common.mustCall(maybeClose));
req.end('a'); req.end('a');
}
{
// Request 2 will succeed // Request 2 will succeed
const req2 = client.request({ const req = client.request({
':method': 'POST', ':method': 'POST',
'content-length': 1 'content-length': 1
}); });
req2.resume(); req.resume();
req2.on('end', common.mustCall(maybeClose)); req.on('end', common.mustCall(maybeClose));
req2.end('a'); req.end('a');
}
{
// Request 3 will fail because nghttp2 does not allow the content-length // Request 3 will fail because nghttp2 does not allow the content-length
// header to be set for non-payload bearing requests... // header to be set for non-payload bearing requests...
const req3 = client.request({ 'content-length': 1 }); const req = client.request({ 'content-length': 1 });
req3.resume(); req.resume();
req3.on('end', common.mustCall(maybeClose)); req.on('end', common.mustCall(maybeClose));
req3.on('error', common.expectsError({ req.on('error', common.expectsError({
code: 'ERR_HTTP2_STREAM_ERROR', code: 'ERR_HTTP2_STREAM_ERROR',
type: Error, type: Error,
message: 'Stream closed with error code 1' message: 'Stream closed with error code 1'
})); }));
}
})); }));

View File

@ -2,6 +2,10 @@
'use strict'; 'use strict';
const common = require('../common'); const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert'); const assert = require('assert');
const http2 = require('http2'); const http2 = require('http2');
const fs = require('fs'); const fs = require('fs');
@ -42,7 +46,7 @@ server.on('stream', (stream, headers) => {
}); });
}); });
server.listen(8000, () => { server.listen(0, () => {
const secureContext = tls.createSecureContext({ ca }); const secureContext = tls.createSecureContext({ ca });
const client = http2.connect(`https://localhost:${server.address().port}`, const client = http2.connect(`https://localhost:${server.address().port}`,

View File

@ -6,6 +6,10 @@
// other than start listening. // other than start listening.
const common = require('../common'); const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert'); const assert = require('assert');
const http2 = require('http2'); const http2 = require('http2');
const path = require('path'); const path = require('path');

View File

@ -6,8 +6,8 @@
// TODO: This test makes large buffer allocations (128KiB) and should be tested // TODO: This test makes large buffer allocations (128KiB) and should be tested
// on smaller / IoT platforms in case this poses problems for these targets. // on smaller / IoT platforms in case this poses problems for these targets.
const assert = require('assert');
const common = require('../common'); const common = require('../common');
const assert = require('assert');
const h2 = require('http2'); const h2 = require('http2');
// Given a list of buffers and an initial window size, have a server write // Given a list of buffers and an initial window size, have a server write