| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 66788fc commit 92c37fe
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,47 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const common = require('../common.js'); | ||
| 4 | + const PORT = common.PORT; | ||
| 5 | + | ||
| 6 | + var bench = common.createBenchmark(main, { | ||
| 7 | + n: [1e3], | ||
| 8 | + nheaders: [100, 1000], | ||
| 9 | + }, { flags: ['--expose-http2', '--no-warnings'] }); | ||
| 10 | + | ||
| 11 | + function main(conf) { | ||
| 12 | + const n = +conf.n; | ||
| 13 | + const nheaders = +conf.nheaders; | ||
| 14 | + const http2 = require('http2'); | ||
| 15 | + const server = http2.createServer(); | ||
| 16 | + | ||
| 17 | + const headersObject = { ':path': '/' }; | ||
| 18 | + for (var i = 0; i < nheaders; i++) { | ||
| 19 | + headersObject[`foo${i}`] = `some header value ${i}`; | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + server.on('stream', (stream) => { | ||
| 23 | + stream.respond(); | ||
| 24 | + stream.end('Hi!'); | ||
| 25 | + }); | ||
| 26 | + server.listen(PORT, () => { | ||
| 27 | + const client = http2.connect(`http://localhost:${PORT}/`); | ||
| 28 | + | ||
| 29 | + function doRequest(remaining) { | ||
| 30 | + const req = client.request(headersObject); | ||
| 31 | + req.end(); | ||
| 32 | + req.on('data', () => {}); | ||
| 33 | + req.on('end', () => { | ||
| 34 | + if (remaining > 0) { | ||
| 35 | + doRequest(remaining - 1); | ||
| 36 | + } else { | ||
| 37 | + bench.end(n); | ||
| 38 | + server.close(); | ||
| 39 | + client.destroy(); | ||
| 40 | + } | ||
| 41 | + }); | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + bench.start(); | ||
| 45 | + doRequest(n); | ||
| 46 | + }); | ||
| 47 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -375,7 +375,8 @@ function assertValidPseudoHeaderTrailer(key) { | |||
| 375 | 375 | ||
| 376 | 376 | function mapToHeaders(map, | |
| 377 | 377 | assertValuePseudoHeader = assertValidPseudoHeader) { | |
| 378 | - const ret = []; | ||
| 378 | + let ret = ''; | ||
| 379 | + let count = 0; | ||
| 379 | 380 | const keys = Object.keys(map); | |
| 380 | 381 | const singles = new Set(); | |
| 381 | 382 | for (var i = 0; i < keys.length; i++) { | |
@@ -402,7 +403,8 @@ function mapToHeaders(map, | |||
| 402 | 403 | const err = assertValuePseudoHeader(key); | |
| 403 | 404 | if (err !== undefined) | |
| 404 | 405 | return err; | |
| 405 | - ret.unshift([key, String(value)]); | ||
| 406 | + ret = `${key}\0${String(value)}\0${ret}`; | ||
| 407 | + count++; | ||
| 406 | 408 | } else { | |
| 407 | 409 | if (kSingleValueHeaders.has(key)) { | |
| 408 | 410 | if (singles.has(key)) | |
@@ -415,16 +417,18 @@ function mapToHeaders(map, | |||
| 415 | 417 | if (isArray) { | |
| 416 | 418 | for (var k = 0; k < value.length; k++) { | |
| 417 | 419 | val = String(value[k]); | |
| 418 | - ret.push([key, val]); | ||
| 420 | + ret += `${key}\0${val}\0`; | ||
| 419 | 421 | } | |
| 422 | + count += value.length; | ||
| 420 | 423 | } else { | |
| 421 | 424 | val = String(value); | |
| 422 | - ret.push([key, val]); | ||
| 425 | + ret += `${key}\0${val}\0`; | ||
| 426 | + count++; | ||
| 423 | 427 | } | |
| 424 | 428 | } | |
| 425 | 429 | } | |
| 426 | 430 | ||
| 427 | - return ret; | ||
| 431 | + return [ret, count]; | ||
| 428 | 432 | } | |
| 429 | 433 | ||
| 430 | 434 | class NghttpError extends Error { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -9,6 +9,8 @@ using v8::Boolean; | |||
| 9 | 9 | using v8::Context; | |
| 10 | 10 | using v8::Function; | |
| 11 | 11 | using v8::Integer; | |
| 12 | + using v8::String; | ||
| 13 | + using v8::Uint32; | ||
| 12 | 14 | using v8::Undefined; | |
| 13 | 15 | ||
| 14 | 16 | namespace http2 { | |
@@ -1075,6 +1077,69 @@ void Http2Session::Unconsume() { | |||
| 1075 | 1077 | } | |
| 1076 | 1078 | ||
| 1077 | 1079 | ||
| 1080 | + Headers::Headers(Isolate* isolate, | ||
| 1081 | + Local<Context> context, | ||
| 1082 | + Local<Array> headers) { | ||
| 1083 | + CHECK_EQ(headers->Length(), 2); | ||
| 1084 | + Local<Value> header_string = headers->Get(context, 0).ToLocalChecked(); | ||
| 1085 | + Local<Value> header_count = headers->Get(context, 1).ToLocalChecked(); | ||
| 1086 | + CHECK(header_string->IsString()); | ||
| 1087 | + CHECK(header_count->IsUint32()); | ||
| 1088 | + count_ = header_count.As<Uint32>()->Value(); | ||
| 1089 | + int header_string_len = header_string.As<String>()->Length(); | ||
| 1090 | + | ||
| 1091 | + if (count_ == 0) { | ||
| 1092 | + CHECK_EQ(header_string_len, 0); | ||
| 1093 | + return; | ||
| 1094 | + } | ||
| 1095 | + | ||
| 1096 | + // Allocate a single buffer with count_ nghttp2_nv structs, followed | ||
| 1097 | + // by the raw header data as passed from JS. This looks like: | ||
| 1098 | + // | possible padding | nghttp2_nv | nghttp2_nv | ... | header contents | | ||
| 1099 | + buf_.AllocateSufficientStorage((alignof(nghttp2_nv) - 1) + | ||
| 1100 | + count_ * sizeof(nghttp2_nv) + | ||
| 1101 | + header_string_len); | ||
| 1102 | + // Make sure the start address is aligned appropriately for an nghttp2_nv*. | ||
| 1103 | + char* start = reinterpret_cast<char*>( | ||
| 1104 | + ROUND_UP(reinterpret_cast<uintptr_t>(*buf_), alignof(nghttp2_nv))); | ||
| 1105 | + char* header_contents = start + (count_ * sizeof(nghttp2_nv)); | ||
| 1106 | + nghttp2_nv* const nva = reinterpret_cast<nghttp2_nv*>(start); | ||
| 1107 | + | ||
| 1108 | + CHECK_LE(header_contents + header_string_len, *buf_ + buf_.length()); | ||
| 1109 | + CHECK_EQ(header_string.As<String>() | ||
| 1110 | + ->WriteOneByte(reinterpret_cast<uint8_t*>(header_contents), | ||
| 1111 | + 0, header_string_len, | ||
| 1112 | + String::NO_NULL_TERMINATION), | ||
| 1113 | + header_string_len); | ||
| 1114 | + | ||
| 1115 | + size_t n = 0; | ||
| 1116 | + char* p; | ||
| 1117 | + for (p = header_contents; p < header_contents + header_string_len; n++) { | ||
| 1118 | + if (n >= count_) { | ||
| 1119 | + // This can happen if a passed header contained a null byte. In that | ||
| 1120 | + // case, just provide nghttp2 with an invalid header to make it reject | ||
| 1121 | + // the headers list. | ||
| 1122 | + static uint8_t zero = '\0'; | ||
| 1123 | + nva[0].name = nva[0].value = &zero; | ||
| 1124 | + nva[0].namelen = nva[0].valuelen = 1; | ||
| 1125 | + count_ = 1; | ||
| 1126 | + return; | ||
| 1127 | + } | ||
| 1128 | + | ||
| 1129 | + nva[n].flags = NGHTTP2_NV_FLAG_NONE; | ||
| 1130 | + nva[n].name = reinterpret_cast<uint8_t*>(p); | ||
| 1131 | + nva[n].namelen = strlen(p); | ||
| 1132 | + p += nva[n].namelen + 1; | ||
| 1133 | + nva[n].value = reinterpret_cast<uint8_t*>(p); | ||
| 1134 | + nva[n].valuelen = strlen(p); | ||
| 1135 | + p += nva[n].valuelen + 1; | ||
| 1136 | + } | ||
| 1137 | + | ||
| 1138 | + CHECK_EQ(p, header_contents + header_string_len); | ||
| 1139 | + CHECK_EQ(n, count_); | ||
| 1140 | + } | ||
| 1141 | + | ||
| 1142 | + | ||
| 1078 | 1143 | void Initialize(Local<Object> target, | |
| 1079 | 1144 | Local<Value> unused, | |
| 1080 | 1145 | Local<Context> context, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -515,54 +515,20 @@ class ExternalHeader : | |||
| 515 | 515 | ||
| 516 | 516 | class Headers { | |
| 517 | 517 | public: | |
| 518 | - Headers(Isolate* isolate, Local<Context> context, Local<Array> headers) { | ||
| 519 | - headers_.AllocateSufficientStorage(headers->Length()); | ||
| 520 | - Local<Value> item; | ||
| 521 | - Local<Array> header; | ||
| 522 | - | ||
| 523 | - for (size_t n = 0; n < headers->Length(); n++) { | ||
| 524 | - item = headers->Get(context, n).ToLocalChecked(); | ||
| 525 | - CHECK(item->IsArray()); | ||
| 526 | - header = item.As<Array>(); | ||
| 527 | - Local<Value> key = header->Get(context, 0).ToLocalChecked(); | ||
| 528 | - Local<Value> value = header->Get(context, 1).ToLocalChecked(); | ||
| 529 | - CHECK(key->IsString()); | ||
| 530 | - CHECK(value->IsString()); | ||
| 531 | - size_t keylen = StringBytes::StorageSize(isolate, key, ASCII); | ||
| 532 | - size_t valuelen = StringBytes::StorageSize(isolate, value, ASCII); | ||
| 533 | - headers_[n].flags = NGHTTP2_NV_FLAG_NONE; | ||
| 534 | - Local<Value> flag = header->Get(context, 2).ToLocalChecked(); | ||
| 535 | - if (flag->BooleanValue(context).ToChecked()) | ||
| 536 | - headers_[n].flags |= NGHTTP2_NV_FLAG_NO_INDEX; | ||
| 537 | - uint8_t* buf = Malloc<uint8_t>(keylen + valuelen); | ||
| 538 | - headers_[n].name = buf; | ||
| 539 | - headers_[n].value = buf + keylen; | ||
| 540 | - headers_[n].namelen = | ||
| 541 | - StringBytes::Write(isolate, | ||
| 542 | - reinterpret_cast<char*>(headers_[n].name), | ||
| 543 | - keylen, key, ASCII); | ||
| 544 | - headers_[n].valuelen = | ||
| 545 | - StringBytes::Write(isolate, | ||
| 546 | - reinterpret_cast<char*>(headers_[n].value), | ||
| 547 | - valuelen, value, ASCII); | ||
| 548 | - } | ||
| 549 | - } | ||
| 550 | - | ||
| 551 | - ~Headers() { | ||
| 552 | - for (size_t n = 0; n < headers_.length(); n++) | ||
| 553 | - free(headers_[n].name); | ||
| 554 | - } | ||
| 518 | + Headers(Isolate* isolate, Local<Context> context, Local<Array> headers); | ||
| 519 | + ~Headers() {} | ||
| 555 | 520 | ||
| 556 | 521 | nghttp2_nv* operator*() { | |
| 557 | - return *headers_; | ||
| 522 | + return reinterpret_cast<nghttp2_nv*>(*buf_); | ||
| 558 | 523 | } | |
| 559 | 524 | ||
| 560 | 525 | size_t length() const { | |
| 561 | - return headers_.length(); | ||
| 526 | + return count_; | ||
| 562 | 527 | } | |
| 563 | 528 | ||
| 564 | 529 | private: | |
| 565 | - MaybeStackBuffer<nghttp2_nv> headers_; | ||
| 530 | + size_t count_; | ||
| 531 | + MaybeStackBuffer<char, 3000> buf_; | ||
| 566 | 532 | }; | |
| 567 | 533 | ||
| 568 | 534 | } // namespace http2 | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -82,16 +82,11 @@ const { | |||
| 82 | 82 | 'BAR': [1] | |
| 83 | 83 | }; | |
| 84 | 84 | ||
| 85 | - assert.deepStrictEqual(mapToHeaders(headers), [ | ||
| 86 | - [ ':path', 'abc' ], | ||
| 87 | - [ ':status', '200' ], | ||
| 88 | - [ 'abc', '1' ], | ||
| 89 | - [ 'xyz', '1' ], | ||
| 90 | - [ 'xyz', '2' ], | ||
| 91 | - [ 'xyz', '3' ], | ||
| 92 | - [ 'xyz', '4' ], | ||
| 93 | - [ 'bar', '1' ] | ||
| 94 | - ]); | ||
| 85 | + assert.deepStrictEqual( | ||
| 86 | + mapToHeaders(headers), | ||
| 87 | + [ [ ':path', 'abc', ':status', '200', 'abc', '1', 'xyz', '1', 'xyz', '2', | ||
| 88 | + 'xyz', '3', 'xyz', '4', 'bar', '1', '' ].join('\0'), 8 ] | ||
| 89 | + ); | ||
| 95 | 90 | } | |
| 96 | 91 | ||
| 97 | 92 | { | |
@@ -103,15 +98,11 @@ const { | |||
| 103 | 98 | 'xyz': [1, 2, 3, 4] | |
| 104 | 99 | }; | |
| 105 | 100 | ||
| 106 | - assert.deepStrictEqual(mapToHeaders(headers), [ | ||
| 107 | - [ ':status', '200' ], | ||
| 108 | - [ ':path', 'abc' ], | ||
| 109 | - [ 'abc', '1' ], | ||
| 110 | - [ 'xyz', '1' ], | ||
| 111 | - [ 'xyz', '2' ], | ||
| 112 | - [ 'xyz', '3' ], | ||
| 113 | - [ 'xyz', '4' ] | ||
| 114 | - ]); | ||
| 101 | + assert.deepStrictEqual( | ||
| 102 | + mapToHeaders(headers), | ||
| 103 | + [ [ ':status', '200', ':path', 'abc', 'abc', '1', 'xyz', '1', 'xyz', '2', | ||
| 104 | + 'xyz', '3', 'xyz', '4', '' ].join('\0'), 7 ] | ||
| 105 | + ); | ||
| 115 | 106 | } | |
| 116 | 107 | ||
| 117 | 108 | { | |
@@ -124,15 +115,11 @@ const { | |||
| 124 | 115 | [Symbol('test')]: 1 // Symbol keys are ignored | |
| 125 | 116 | }; | |
| 126 | 117 | ||
| 127 | - assert.deepStrictEqual(mapToHeaders(headers), [ | ||
| 128 | - [ ':status', '200' ], | ||
| 129 | - [ ':path', 'abc' ], | ||
| 130 | - [ 'abc', '1' ], | ||
| 131 | - [ 'xyz', '1' ], | ||
| 132 | - [ 'xyz', '2' ], | ||
| 133 | - [ 'xyz', '3' ], | ||
| 134 | - [ 'xyz', '4' ] | ||
| 135 | - ]); | ||
| 118 | + assert.deepStrictEqual( | ||
| 119 | + mapToHeaders(headers), | ||
| 120 | + [ [ ':status', '200', ':path', 'abc', 'abc', '1', 'xyz', '1', 'xyz', '2', | ||
| 121 | + 'xyz', '3', 'xyz', '4', '' ].join('\0'), 7 ] | ||
| 122 | + ); | ||
| 136 | 123 | } | |
| 137 | 124 | ||
| 138 | 125 | { | |
@@ -144,14 +131,11 @@ const { | |||
| 144 | 131 | headers.foo = []; | |
| 145 | 132 | headers[':status'] = 200; | |
| 146 | 133 | ||
| 147 | - assert.deepStrictEqual(mapToHeaders(headers), [ | ||
| 148 | - [ ':status', '200' ], | ||
| 149 | - [ ':path', 'abc' ], | ||
| 150 | - [ 'xyz', '1' ], | ||
| 151 | - [ 'xyz', '2' ], | ||
| 152 | - [ 'xyz', '3' ], | ||
| 153 | - [ 'xyz', '4' ] | ||
| 154 | - ]); | ||
| 134 | + assert.deepStrictEqual( | ||
| 135 | + mapToHeaders(headers), | ||
| 136 | + [ [ ':status', '200', ':path', 'abc', 'xyz', '1', 'xyz', '2', 'xyz', '3', | ||
| 137 | + 'xyz', '4', '' ].join('\0'), 6 ] | ||
| 138 | + ); | ||
| 155 | 139 | } | |
| 156 | 140 | ||
| 157 | 141 | // The following are not allowed to have multiple values | |
| Back | FazBrowse Home | New Git URL |
0 commit comments