Skip to content
This repository has been archived by the owner on Oct 15, 2020. It is now read-only.

Commit

Permalink
meta: merge node/master into node-chakracore/master
Browse files Browse the repository at this point in the history
Merge 9f3d59e as of 2017-11-05
This commit was automatically generated. For any problems, please contact jackhorton

Reviewed-By: Jack Horton <jahorto@microsoft.com>
  • Loading branch information
chakrabot committed Nov 8, 2017
2 parents 20c2134 + 9f3d59e commit 6a20f8d
Show file tree
Hide file tree
Showing 12 changed files with 314 additions and 187 deletions.
33 changes: 30 additions & 3 deletions doc/api/http2.md
Original file line number Diff line number Diff line change
Expand Up @@ -1470,11 +1470,18 @@ not be emitted.
### http2.createServer(options[, onRequestHandler])
<!-- YAML
added: v8.4.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/16676
description: Added the `maxHeaderListPairs` option with a default limit of
128 header pairs.
-->

* `options` {Object}
* `maxDeflateDynamicTableSize` {number} Sets the maximum dynamic table size
for deflating header fields. **Default:** `4Kib`
* `maxHeaderListPairs` {number} Sets the maximum number of header entries.
**Default:** `128`. The minimum value is `4`.
* `maxSendHeaderBlockLength` {number} Sets the maximum allowed size for a
serialized, compressed block of headers. Attempts to send headers that
exceed this limit will result in a `'frameError'` event being emitted
Expand Down Expand Up @@ -1525,6 +1532,11 @@ server.listen(80);
### http2.createSecureServer(options[, onRequestHandler])
<!-- YAML
added: v8.4.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/16676
description: Added the `maxHeaderListPairs` option with a default limit of
128 header pairs.
-->

* `options` {Object}
Expand All @@ -1533,6 +1545,8 @@ added: v8.4.0
`false`. See the [`'unknownProtocol'`][] event. See [ALPN negotiation][].
* `maxDeflateDynamicTableSize` {number} Sets the maximum dynamic table size
for deflating header fields. **Default:** `4Kib`
* `maxHeaderListPairs` {number} Sets the maximum number of header entries.
**Default:** `128`. The minimum value is `4`.
* `maxSendHeaderBlockLength` {number} Sets the maximum allowed size for a
serialized, compressed block of headers. Attempts to send headers that
exceed this limit will result in a `'frameError'` event being emitted
Expand Down Expand Up @@ -1590,12 +1604,19 @@ server.listen(80);
### http2.connect(authority[, options][, listener])
<!-- YAML
added: v8.4.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/16676
description: Added the `maxHeaderListPairs` option with a default limit of
128 header pairs.
-->

* `authority` {string|URL}
* `options` {Object}
* `maxDeflateDynamicTableSize` {number} Sets the maximum dynamic table size
for deflating header fields. **Default:** `4Kib`
* `maxHeaderListPairs` {number} Sets the maximum number of header entries.
**Default:** `128`. The minimum value is `1`.
* `maxReservedRemoteStreams` {number} Sets the maximum number of reserved push
streams the client will accept at any given time. Once the current number of
currently reserved push streams exceeds reaches this limit, new push streams
Expand Down Expand Up @@ -1747,7 +1768,13 @@ server.on('stream', (stream, headers) => {
```

### Settings Object

<!-- YAML
added: v8.4.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/16676
description: The `maxHeaderListSize` setting is now strictly enforced.
-->
The `http2.getDefaultSettings()`, `http2.getPackedSettings()`,
`http2.createServer()`, `http2.createSecureServer()`,
`http2session.settings()`, `http2session.localSettings`, and
Expand All @@ -1773,8 +1800,8 @@ properties.
concurrently at any given time in an `Http2Session`. The minimum value is
0. The maximum allowed value is 2<sup>31</sup>-1.
* `maxHeaderListSize` {number} Specifies the maximum size (uncompressed octets)
of header list that will be accepted. There is no default value. The minimum
allowed value is 0. The maximum allowed value is 2<sup>32</sup>-1.
of header list that will be accepted. The minimum allowed value is 0. The
maximum allowed value is 2<sup>32</sup>-1. **Default:** 65535.

All additional properties on the settings object are ignored.

Expand Down
80 changes: 39 additions & 41 deletions lib/_http_agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,33 +46,31 @@ function Agent(options) {

EventEmitter.call(this);

var self = this;
this.defaultPort = 80;
this.protocol = 'http:';

self.defaultPort = 80;
self.protocol = 'http:';

self.options = util._extend({}, options);
this.options = util._extend({}, options);

// don't confuse net and make it think that we're connecting to a pipe
self.options.path = null;
self.requests = {};
self.sockets = {};
self.freeSockets = {};
self.keepAliveMsecs = self.options.keepAliveMsecs || 1000;
self.keepAlive = self.options.keepAlive || false;
self.maxSockets = self.options.maxSockets || Agent.defaultMaxSockets;
self.maxFreeSockets = self.options.maxFreeSockets || 256;

self.on('free', function(socket, options) {
var name = self.getName(options);
this.options.path = null;
this.requests = {};
this.sockets = {};
this.freeSockets = {};
this.keepAliveMsecs = this.options.keepAliveMsecs || 1000;
this.keepAlive = this.options.keepAlive || false;
this.maxSockets = this.options.maxSockets || Agent.defaultMaxSockets;
this.maxFreeSockets = this.options.maxFreeSockets || 256;

this.on('free', (socket, options) => {
var name = this.getName(options);
debug('agent.on(free)', name);

if (socket.writable &&
self.requests[name] && self.requests[name].length) {
self.requests[name].shift().onSocket(socket);
if (self.requests[name].length === 0) {
this.requests[name] && this.requests[name].length) {
this.requests[name].shift().onSocket(socket);
if (this.requests[name].length === 0) {
// don't leak
delete self.requests[name];
delete this.requests[name];
}
} else {
// If there are no pending requests, then put it in
Expand All @@ -81,21 +79,21 @@ function Agent(options) {
if (req &&
req.shouldKeepAlive &&
socket.writable &&
self.keepAlive) {
var freeSockets = self.freeSockets[name];
this.keepAlive) {
var freeSockets = this.freeSockets[name];
var freeLen = freeSockets ? freeSockets.length : 0;
var count = freeLen;
if (self.sockets[name])
count += self.sockets[name].length;
if (this.sockets[name])
count += this.sockets[name].length;

if (count > self.maxSockets || freeLen >= self.maxFreeSockets) {
if (count > this.maxSockets || freeLen >= this.maxFreeSockets) {
socket.destroy();
} else if (self.keepSocketAlive(socket)) {
} else if (this.keepSocketAlive(socket)) {
freeSockets = freeSockets || [];
self.freeSockets[name] = freeSockets;
this.freeSockets[name] = freeSockets;
socket[async_id_symbol] = -1;
socket._httpMessage = null;
self.removeSocket(socket, options);
this.removeSocket(socket, options);
freeSockets.push(socket);
} else {
// Implementation doesn't want to keep socket alive
Expand Down Expand Up @@ -196,39 +194,39 @@ Agent.prototype.addRequest = function addRequest(req, options, port/*legacy*/,
};

Agent.prototype.createSocket = function createSocket(req, options, cb) {
var self = this;
options = util._extend({}, options);
util._extend(options, self.options);
util._extend(options, this.options);
if (options.socketPath)
options.path = options.socketPath;

if (!options.servername)
options.servername = calculateServerName(options, req);

var name = self.getName(options);
var name = this.getName(options);
options._agentKey = name;

debug('createConnection', name, options);
options.encoding = null;
var called = false;
const newSocket = self.createConnection(options, oncreate);
if (newSocket)
oncreate(null, newSocket);

function oncreate(err, s) {
const oncreate = (err, s) => {
if (called)
return;
called = true;
if (err)
return cb(err);
if (!self.sockets[name]) {
self.sockets[name] = [];
if (!this.sockets[name]) {
this.sockets[name] = [];
}
self.sockets[name].push(s);
debug('sockets', name, self.sockets[name].length);
installListeners(self, s, options);
this.sockets[name].push(s);
debug('sockets', name, this.sockets[name].length);
installListeners(this, s, options);
cb(null, s);
}
};

const newSocket = this.createConnection(options, oncreate);
if (newSocket)
oncreate(null, newSocket);
};

function calculateServerName(options, req) {
Expand Down
8 changes: 7 additions & 1 deletion lib/internal/http2/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ const IDX_OPTIONS_MAX_RESERVED_REMOTE_STREAMS = 1;
const IDX_OPTIONS_MAX_SEND_HEADER_BLOCK_LENGTH = 2;
const IDX_OPTIONS_PEER_MAX_CONCURRENT_STREAMS = 3;
const IDX_OPTIONS_PADDING_STRATEGY = 4;
const IDX_OPTIONS_FLAGS = 5;
const IDX_OPTIONS_MAX_HEADER_LIST_PAIRS = 5;
const IDX_OPTIONS_FLAGS = 6;

function updateOptionsBuffer(options) {
var flags = 0;
Expand Down Expand Up @@ -201,6 +202,11 @@ function updateOptionsBuffer(options) {
optionsBuffer[IDX_OPTIONS_PADDING_STRATEGY] =
options.paddingStrategy;
}
if (typeof options.maxHeaderListPairs === 'number') {
flags |= (1 << IDX_OPTIONS_MAX_HEADER_LIST_PAIRS);
optionsBuffer[IDX_OPTIONS_MAX_HEADER_LIST_PAIRS] =
options.maxHeaderListPairs;
}
optionsBuffer[IDX_OPTIONS_FLAGS] = flags;
}

Expand Down
31 changes: 21 additions & 10 deletions src/node_http2.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "node_http2_state.h"

#include <queue>
#include <algorithm>

namespace node {

Expand All @@ -20,8 +21,6 @@ using v8::Undefined;

namespace http2 {

Freelist<Nghttp2Stream, FREELIST_MAX> stream_free_list;

Nghttp2Session::Callbacks Nghttp2Session::callback_struct_saved[2] = {
Callbacks(false),
Callbacks(true)};
Expand Down Expand Up @@ -67,6 +66,10 @@ Http2Options::Http2Options(Environment* env) {
buffer.GetValue(IDX_OPTIONS_PADDING_STRATEGY));
SetPaddingStrategy(strategy);
}

if (flags & (1 << IDX_OPTIONS_MAX_HEADER_LIST_PAIRS)) {
SetMaxHeaderPairs(buffer[IDX_OPTIONS_MAX_HEADER_LIST_PAIRS]);
}
}

Http2Settings::Http2Settings(Environment* env) : env_(env) {
Expand Down Expand Up @@ -173,11 +176,14 @@ inline void Http2Settings::RefreshDefaults(Environment* env) {
DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE;
buffer[IDX_SETTINGS_MAX_FRAME_SIZE] =
DEFAULT_SETTINGS_MAX_FRAME_SIZE;
buffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE] =
DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE;
buffer[IDX_SETTINGS_COUNT] =
(1 << IDX_SETTINGS_HEADER_TABLE_SIZE) |
(1 << IDX_SETTINGS_ENABLE_PUSH) |
(1 << IDX_SETTINGS_INITIAL_WINDOW_SIZE) |
(1 << IDX_SETTINGS_MAX_FRAME_SIZE);
(1 << IDX_SETTINGS_MAX_FRAME_SIZE) |
(1 << IDX_SETTINGS_MAX_HEADER_LIST_SIZE);
}


Expand All @@ -192,7 +198,10 @@ Http2Session::Http2Session(Environment* env,

padding_strategy_ = opts.GetPaddingStrategy();

Init(type, *opts);
int32_t maxHeaderPairs = opts.GetMaxHeaderPairs();
maxHeaderPairs = type == NGHTTP2_SESSION_SERVER ?
std::max(maxHeaderPairs, 4) : std::max(maxHeaderPairs, 1);
Init(type, *opts, nullptr, maxHeaderPairs);

// For every node::Http2Session instance, there is a uv_prepare_t handle
// whose callback is triggered on every tick of the event loop. When
Expand Down Expand Up @@ -911,7 +920,8 @@ void Http2Session::OnTrailers(Nghttp2Stream* stream,

void Http2Session::OnHeaders(
Nghttp2Stream* stream,
std::queue<nghttp2_header>* headers,
nghttp2_header* headers,
size_t count,
nghttp2_headers_category cat,
uint8_t flags) {
Local<Context> context = env()->context();
Expand All @@ -936,18 +946,19 @@ void Http2Session::OnHeaders(
// like {name1: value1, name2: value2, name3: [value3, value4]}. We do it
// this way for performance reasons (it's faster to generate and pass an
// array than it is to generate and pass the object).
do {
size_t n = 0;
while (count > 0) {
size_t j = 0;
while (!headers->empty() && j < arraysize(argv) / 2) {
nghttp2_header item = headers->front();
while (count > 0 && j < arraysize(argv) / 2) {
nghttp2_header item = headers[n++];
// The header name and value are passed as external one-byte strings
name_str =
ExternalHeader::New<true>(env(), item.name).ToLocalChecked();
value_str =
ExternalHeader::New<false>(env(), item.value).ToLocalChecked();
argv[j * 2] = name_str;
argv[j * 2 + 1] = value_str;
headers->pop();
count--;
j++;
}
// For performance, we pass name and value pairs to array.protototype.push
Expand All @@ -956,7 +967,7 @@ void Http2Session::OnHeaders(
if (j > 0) {
fn->Call(env()->context(), holder, j * 2, argv).ToLocalChecked();
}
} while (!headers->empty());
}

Local<Value> args[4] = {
Integer::New(isolate, stream->id()),
Expand Down
25 changes: 12 additions & 13 deletions src/node_http2.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,14 +292,6 @@ const char* nghttp2_errname(int rv) {
}
}

#define DEFAULT_SETTINGS_HEADER_TABLE_SIZE 4096
#define DEFAULT_SETTINGS_ENABLE_PUSH 1
#define DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE 65535
#define DEFAULT_SETTINGS_MAX_FRAME_SIZE 16384
#define MAX_MAX_FRAME_SIZE 16777215
#define MIN_MAX_FRAME_SIZE DEFAULT_SETTINGS_MAX_FRAME_SIZE
#define MAX_INITIAL_WINDOW_SIZE 2147483647

// This allows for 4 default-sized frames with their frame headers
static const size_t kAllocBufferSize = 4 * (16384 + 9);

Expand All @@ -324,19 +316,25 @@ class Http2Options {
return options_;
}

void SetMaxHeaderPairs(uint32_t max) {
max_header_pairs_ = max;
}

uint32_t GetMaxHeaderPairs() const {
return max_header_pairs_;
}

void SetPaddingStrategy(padding_strategy_type val) {
#if DEBUG
CHECK_LE(val, PADDING_STRATEGY_CALLBACK);
#endif
padding_strategy_ = static_cast<padding_strategy_type>(val);
}

padding_strategy_type GetPaddingStrategy() {
padding_strategy_type GetPaddingStrategy() const {
return padding_strategy_;
}

private:
nghttp2_option* options_;
uint32_t max_header_pairs_ = DEFAULT_MAX_HEADER_LIST_PAIRS;
padding_strategy_type padding_strategy_ = PADDING_STRATEGY_NONE;
};

Expand Down Expand Up @@ -413,7 +411,8 @@ class Http2Session : public AsyncWrap,

void OnHeaders(
Nghttp2Stream* stream,
std::queue<nghttp2_header>* headers,
nghttp2_header* headers,
size_t count,
nghttp2_headers_category cat,
uint8_t flags) override;
void OnStreamClose(int32_t id, uint32_t code) override;
Expand Down
Loading

0 comments on commit 6a20f8d

Please sign in to comment.