Skip to content
This repository has been archived by the owner on Jul 6, 2018. It is now read-only.

Commit

Permalink
Use Set instead of switch... much much faster
Browse files Browse the repository at this point in the history
  • Loading branch information
jasnell committed May 17, 2017
1 parent 71657ba commit 14f31a6
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 70 deletions.
126 changes: 59 additions & 67 deletions lib/internal/http2/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,47 @@ const {
HTTP2_HEADER_HOST
} = binding.constants;

const kValidPseudoHeaders = new Set([
HTTP2_HEADER_STATUS,
HTTP2_HEADER_METHOD,
HTTP2_HEADER_AUTHORITY,
HTTP2_HEADER_SCHEME,
HTTP2_HEADER_PATH
]);

const kSingleValueHeaders = new Set([
HTTP2_HEADER_STATUS,
HTTP2_HEADER_METHOD,
HTTP2_HEADER_AUTHORITY,
HTTP2_HEADER_SCHEME,
HTTP2_HEADER_PATH,
HTTP2_HEADER_AGE,
HTTP2_HEADER_AUTHORIZATION,
HTTP2_HEADER_CONTENT_ENCODING,
HTTP2_HEADER_CONTENT_LANGUAGE,
HTTP2_HEADER_CONTENT_LENGTH,
HTTP2_HEADER_CONTENT_LOCATION,
HTTP2_HEADER_CONTENT_MD5,
HTTP2_HEADER_CONTENT_RANGE,
HTTP2_HEADER_CONTENT_TYPE,
HTTP2_HEADER_DATE,
HTTP2_HEADER_ETAG,
HTTP2_HEADER_EXPIRES,
HTTP2_HEADER_FROM,
HTTP2_HEADER_IF_MATCH,
HTTP2_HEADER_IF_MODIFIED_SINCE,
HTTP2_HEADER_IF_NONE_MATCH,
HTTP2_HEADER_IF_RANGE,
HTTP2_HEADER_IF_UNMODIFIED_SINCE,
HTTP2_HEADER_LAST_MODIFIED,
HTTP2_HEADER_MAX_FORWARDS,
HTTP2_HEADER_PROXY_AUTHORIZATION,
HTTP2_HEADER_RANGE,
HTTP2_HEADER_REFERER,
HTTP2_HEADER_RETRY_AFTER,
HTTP2_HEADER_USER_AGENT
]);

// The following ArrayBuffer instances are used to share memory more efficiently
// with the native binding side for a number of methods. These are not intended
// to be used directly by users in any way. The ArrayBuffers are created on
Expand Down Expand Up @@ -180,61 +221,6 @@ function isIllegalConnectionSpecificHeader(name, value) {
}
}

function assertIllegalConnectionSpecificHeader(name, value, ctor) {
if (isIllegalConnectionSpecificHeader(name, value)) {
var err = new errors.Error('ERR_HTTP2_INVALID_CONNECTION_HEADERS');
Error.captureStackTrace(err, ctor);
throw err;
}
}

function assertValidPseudoHeader(name) {
switch (name) {
case HTTP2_HEADER_STATUS:
case HTTP2_HEADER_METHOD:
case HTTP2_HEADER_AUTHORITY:
case HTTP2_HEADER_SCHEME:
case HTTP2_HEADER_PATH:
return;
default:
throw new errors.Error('ERR_HTTP2_INVALID_PSEUDOHEADER', name);
}
}

// TODO(jasnell): Find a faster way to do this
function assertSingleValueHeader(name) {
switch (name) {
case HTTP2_HEADER_AGE:
case HTTP2_HEADER_AUTHORIZATION:
case HTTP2_HEADER_CONTENT_ENCODING:
case HTTP2_HEADER_CONTENT_LANGUAGE:
case HTTP2_HEADER_CONTENT_LENGTH:
case HTTP2_HEADER_CONTENT_LOCATION:
case HTTP2_HEADER_CONTENT_MD5:
case HTTP2_HEADER_CONTENT_RANGE:
case HTTP2_HEADER_CONTENT_TYPE:
case HTTP2_HEADER_DATE:
case HTTP2_HEADER_ETAG:
case HTTP2_HEADER_EXPIRES:
case HTTP2_HEADER_FROM:
case HTTP2_HEADER_IF_MATCH:
case HTTP2_HEADER_IF_MODIFIED_SINCE:
case HTTP2_HEADER_IF_NONE_MATCH:
case HTTP2_HEADER_IF_RANGE:
case HTTP2_HEADER_IF_UNMODIFIED_SINCE:
case HTTP2_HEADER_LAST_MODIFIED:
case HTTP2_HEADER_MAX_FORWARDS:
case HTTP2_HEADER_PROXY_AUTHORIZATION:
case HTTP2_HEADER_RANGE:
case HTTP2_HEADER_REFERER:
case HTTP2_HEADER_RETRY_AFTER:
case HTTP2_HEADER_USER_AGENT:
throw new errors.Error('ERR_HTTP2_HEADER_SINGLE_VALUE', name);
default:
break;
}
}

function mapToHeaders(map) {
var ret = [];
var keys = Object.keys(map);
Expand All @@ -244,21 +230,28 @@ function mapToHeaders(map) {
var val;
if (typeof key === 'symbol' || value === undefined || !key)
continue;
var isArray = Array.isArray(value);
if (key[0] === ':') {
assertValidPseudoHeader(key);
if (isArray) {
if (value.length > 1)
throw new errors.Error('ERR_HTTP2_HEADER_SINGLE_VALUE', key);
value = value[0];
key = String(key).toLowerCase();
const isArray = Array.isArray(value);
if (isArray) {
switch (value.length) {
case 0:
continue;
case 1:
value = String(value[0]);
break;
default:
if (kSingleValueHeaders.has(key))
throw new errors.Error('ERR_HTTP2_HEADER_SINGLE_VALUE', key);
}
}
if (key[0] === ':') {
if (!kValidPseudoHeaders.has(key))
throw new errors.Error('ERR_HTTP2_INVALID_PSEUDOHEADER', key);
ret.unshift([key, String(value)]);
} else {
key = String(key).toLowerCase();
assertIllegalConnectionSpecificHeader(key, value, mapToHeaders);
if (isIllegalConnectionSpecificHeader(key, value))
throw new errors.Error('ERR_HTTP2_INVALID_CONNECTION_HEADERS');
if (isArray) {
if (value.length > 1)
assertSingleValueHeader(key);
for (var k = 0; k < value.length; k++) {
val = String(value[k]);
ret.push([key, val]);
Expand Down Expand Up @@ -301,7 +294,6 @@ function assertIsObject(value, name, types) {

module.exports = {
assertIsObject,
isIllegalConnectionSpecificHeader,
emitErrorIfNecessary,
getDefaultSettings,
getSessionState,
Expand Down
10 changes: 7 additions & 3 deletions test/parallel/test-http2-util-headers-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ const {
'abc': 1,
':status': 200,
':path': 'abc',
'xyz': [1, '2', { toString() { return '3'; } }, 4]
'xyz': [1, '2', { toString() { return '3'; } }, 4],
'foo': [],
'BAR': [1]
};

assert.deepStrictEqual(mapToHeaders(headers), [
Expand All @@ -86,15 +88,17 @@ const {
[ 'xyz', '1' ],
[ 'xyz', '2' ],
[ 'xyz', '3' ],
[ 'xyz', '4' ]
[ 'xyz', '4' ],
[ 'bar', '1' ]
]);
}

{
const headers = {
'abc': 1,
':path': 'abc',
':status': 200,
':status': [200],
':authority': [],
'xyz': [1, 2, 3, 4]
};

Expand Down

0 comments on commit 14f31a6

Please sign in to comment.