| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 00f6846 commit dd60d35
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -108,6 +108,7 @@ const kInit = Symbol('init'); | |||
| 108 | 108 | const kInfoHeaders = Symbol('sent-info-headers'); | |
| 109 | 109 | const kMaybeDestroy = Symbol('maybe-destroy'); | |
| 110 | 110 | const kLocalSettings = Symbol('local-settings'); | |
| 111 | + const kNativeFields = Symbol('kNativeFields'); | ||
| 111 | 112 | const kOptions = Symbol('options'); | |
| 112 | 113 | const kOwner = Symbol('owner'); | |
| 113 | 114 | const kOrigin = Symbol('origin'); | |
@@ -130,7 +131,15 @@ const { | |||
| 130 | 131 | paddingBuffer, | |
| 131 | 132 | PADDING_BUF_FRAME_LENGTH, | |
| 132 | 133 | PADDING_BUF_MAX_PAYLOAD_LENGTH, | |
| 133 | - PADDING_BUF_RETURN_VALUE | ||
| 134 | + PADDING_BUF_RETURN_VALUE, | ||
| 135 | + kBitfield, | ||
| 136 | + kSessionPriorityListenerCount, | ||
| 137 | + kSessionFrameErrorListenerCount, | ||
| 138 | + kSessionUint8FieldCount, | ||
| 139 | + kSessionHasRemoteSettingsListeners, | ||
| 140 | + kSessionRemoteSettingsIsUpToDate, | ||
| 141 | + kSessionHasPingListeners, | ||
| 142 | + kSessionHasAltsvcListeners, | ||
| 134 | 143 | } = binding; | |
| 135 | 144 | ||
| 136 | 145 | const { | |
@@ -305,6 +314,76 @@ function submitRstStream(code) { | |||
| 305 | 314 | } | |
| 306 | 315 | } | |
| 307 | 316 | ||
| 317 | + // Keep track of the number/presence of JS event listeners. Knowing that there | ||
| 318 | + // are no listeners allows the C++ code to skip calling into JS for an event. | ||
| 319 | + function sessionListenerAdded(name) { | ||
| 320 | + switch (name) { | ||
| 321 | + case 'ping': | ||
| 322 | + this[kNativeFields][kBitfield] |= 1 << kSessionHasPingListeners; | ||
| 323 | + break; | ||
| 324 | + case 'altsvc': | ||
| 325 | + this[kNativeFields][kBitfield] |= 1 << kSessionHasAltsvcListeners; | ||
| 326 | + break; | ||
| 327 | + case 'remoteSettings': | ||
| 328 | + this[kNativeFields][kBitfield] |= 1 << kSessionHasRemoteSettingsListeners; | ||
| 329 | + break; | ||
| 330 | + case 'priority': | ||
| 331 | + this[kNativeFields][kSessionPriorityListenerCount]++; | ||
| 332 | + break; | ||
| 333 | + case 'frameError': | ||
| 334 | + this[kNativeFields][kSessionFrameErrorListenerCount]++; | ||
| 335 | + break; | ||
| 336 | + } | ||
| 337 | + } | ||
| 338 | + | ||
| 339 | + function sessionListenerRemoved(name) { | ||
| 340 | + switch (name) { | ||
| 341 | + case 'ping': | ||
| 342 | + if (this.listenerCount(name) > 0) return; | ||
| 343 | + this[kNativeFields][kBitfield] &= ~(1 << kSessionHasPingListeners); | ||
| 344 | + break; | ||
| 345 | + case 'altsvc': | ||
| 346 | + if (this.listenerCount(name) > 0) return; | ||
| 347 | + this[kNativeFields][kBitfield] &= ~(1 << kSessionHasAltsvcListeners); | ||
| 348 | + break; | ||
| 349 | + case 'remoteSettings': | ||
| 350 | + if (this.listenerCount(name) > 0) return; | ||
| 351 | + this[kNativeFields][kBitfield] &= | ||
| 352 | + ~(1 << kSessionHasRemoteSettingsListeners); | ||
| 353 | + break; | ||
| 354 | + case 'priority': | ||
| 355 | + this[kNativeFields][kSessionPriorityListenerCount]--; | ||
| 356 | + break; | ||
| 357 | + case 'frameError': | ||
| 358 | + this[kNativeFields][kSessionFrameErrorListenerCount]--; | ||
| 359 | + break; | ||
| 360 | + } | ||
| 361 | + } | ||
| 362 | + | ||
| 363 | + // Also keep track of listeners for the Http2Stream instances, as some events | ||
| 364 | + // are emitted on those objects. | ||
| 365 | + function streamListenerAdded(name) { | ||
| 366 | + switch (name) { | ||
| 367 | + case 'priority': | ||
| 368 | + this[kSession][kNativeFields][kSessionPriorityListenerCount]++; | ||
| 369 | + break; | ||
| 370 | + case 'frameError': | ||
| 371 | + this[kSession][kNativeFields][kSessionFrameErrorListenerCount]++; | ||
| 372 | + break; | ||
| 373 | + } | ||
| 374 | + } | ||
| 375 | + | ||
| 376 | + function streamListenerRemoved(name) { | ||
| 377 | + switch (name) { | ||
| 378 | + case 'priority': | ||
| 379 | + this[kSession][kNativeFields][kSessionPriorityListenerCount]--; | ||
| 380 | + break; | ||
| 381 | + case 'frameError': | ||
| 382 | + this[kSession][kNativeFields][kSessionFrameErrorListenerCount]--; | ||
| 383 | + break; | ||
| 384 | + } | ||
| 385 | + } | ||
| 386 | + | ||
| 308 | 387 | function onPing(payload) { | |
| 309 | 388 | const session = this[kOwner]; | |
| 310 | 389 | if (session.destroyed) | |
@@ -394,7 +473,6 @@ function onSettings() { | |||
| 394 | 473 | return; | |
| 395 | 474 | session[kUpdateTimer](); | |
| 396 | 475 | debugSessionObj(session, 'new settings received'); | |
| 397 | - session[kRemoteSettings] = undefined; | ||
| 398 | 476 | session.emit('remoteSettings', session.remoteSettings); | |
| 399 | 477 | } | |
| 400 | 478 | ||
@@ -845,6 +923,10 @@ function setupHandle(socket, type, options) { | |||
| 845 | 923 | handle.consume(socket._handle._externalStream); | |
| 846 | 924 | ||
| 847 | 925 | this[kHandle] = handle; | |
| 926 | + if (this[kNativeFields]) | ||
| 927 | + handle.fields.set(this[kNativeFields]); | ||
| 928 | + else | ||
| 929 | + this[kNativeFields] = handle.fields; | ||
| 848 | 930 | ||
| 849 | 931 | if (socket.encrypted) { | |
| 850 | 932 | this[kAlpnProtocol] = socket.alpnProtocol; | |
@@ -886,6 +968,7 @@ function finishSessionDestroy(session, error) { | |||
| 886 | 968 | session[kProxySocket] = undefined; | |
| 887 | 969 | session[kSocket] = undefined; | |
| 888 | 970 | session[kHandle] = undefined; | |
| 971 | + session[kNativeFields] = new Uint8Array(kSessionUint8FieldCount); | ||
| 889 | 972 | socket[kSession] = undefined; | |
| 890 | 973 | socket[kServer] = undefined; | |
| 891 | 974 | ||
@@ -963,6 +1046,7 @@ class Http2Session extends EventEmitter { | |||
| 963 | 1046 | this[kType] = type; | |
| 964 | 1047 | this[kProxySocket] = null; | |
| 965 | 1048 | this[kSocket] = socket; | |
| 1049 | + this[kHandle] = undefined; | ||
| 966 | 1050 | ||
| 967 | 1051 | // Do not use nagle's algorithm | |
| 968 | 1052 | if (typeof socket.setNoDelay === 'function') | |
@@ -981,6 +1065,11 @@ class Http2Session extends EventEmitter { | |||
| 981 | 1065 | setupFn(); | |
| 982 | 1066 | } | |
| 983 | 1067 | ||
| 1068 | + if (!this[kNativeFields]) | ||
| 1069 | + this[kNativeFields] = new Uint8Array(kSessionUint8FieldCount); | ||
| 1070 | + this.on('newListener', sessionListenerAdded); | ||
| 1071 | + this.on('removeListener', sessionListenerRemoved); | ||
| 1072 | + | ||
| 984 | 1073 | debugSession(type, 'created'); | |
| 985 | 1074 | } | |
| 986 | 1075 | ||
@@ -1136,13 +1225,18 @@ class Http2Session extends EventEmitter { | |||
| 1136 | 1225 | ||
| 1137 | 1226 | // The settings currently in effect for the remote peer. | |
| 1138 | 1227 | get remoteSettings() { | |
| 1139 | - const settings = this[kRemoteSettings]; | ||
| 1140 | - if (settings !== undefined) | ||
| 1141 | - return settings; | ||
| 1228 | + if (this[kNativeFields][kBitfield] & | ||
| 1229 | + (1 << kSessionRemoteSettingsIsUpToDate)) { | ||
| 1230 | + const settings = this[kRemoteSettings]; | ||
| 1231 | + if (settings !== undefined) { | ||
| 1232 | + return settings; | ||
| 1233 | + } | ||
| 1234 | + } | ||
| 1142 | 1235 | ||
| 1143 | 1236 | if (this.destroyed || this.connecting) | |
| 1144 | 1237 | return {}; | |
| 1145 | 1238 | ||
| 1239 | + this[kNativeFields][kBitfield] |= (1 << kSessionRemoteSettingsIsUpToDate); | ||
| 1146 | 1240 | return this[kRemoteSettings] = getSettings(this[kHandle], true); // Remote | |
| 1147 | 1241 | } | |
| 1148 | 1242 | ||
@@ -1330,6 +1424,12 @@ class ServerHttp2Session extends Http2Session { | |||
| 1330 | 1424 | constructor(options, socket, server) { | |
| 1331 | 1425 | super(NGHTTP2_SESSION_SERVER, options, socket); | |
| 1332 | 1426 | this[kServer] = server; | |
| 1427 | + // This is a bit inaccurate because it does not reflect changes to | ||
| 1428 | + // number of listeners made after the session was created. This should | ||
| 1429 | + // not be an issue in practice. Additionally, the 'priority' event on | ||
| 1430 | + // server instances (or any other object) is fully undocumented. | ||
| 1431 | + this[kNativeFields][kSessionPriorityListenerCount] = | ||
| 1432 | + server.listenerCount('priority'); | ||
| 1333 | 1433 | } | |
| 1334 | 1434 | ||
| 1335 | 1435 | get server() { | |
@@ -1668,6 +1768,9 @@ class Http2Stream extends Duplex { | |||
| 1668 | 1768 | }; | |
| 1669 | 1769 | ||
| 1670 | 1770 | this.on('pause', streamOnPause); | |
| 1771 | + | ||
| 1772 | + this.on('newListener', streamListenerAdded); | ||
| 1773 | + this.on('removeListener', streamListenerRemoved); | ||
| 1671 | 1774 | } | |
| 1672 | 1775 | ||
| 1673 | 1776 | [kUpdateTimer]() { | |
@@ -2620,7 +2723,7 @@ function sessionOnPriority(stream, parent, weight, exclusive) { | |||
| 2620 | 2723 | } | |
| 2621 | 2724 | ||
| 2622 | 2725 | function sessionOnError(error) { | |
| 2623 | - if (this[kServer]) | ||
| 2726 | + if (this[kServer] !== undefined) | ||
| 2624 | 2727 | this[kServer].emit('sessionError', error, this); | |
| 2625 | 2728 | } | |
| 2626 | 2729 | ||
@@ -2669,8 +2772,10 @@ function connectionListener(socket) { | |||
| 2669 | 2772 | const session = new ServerHttp2Session(options, socket, this); | |
| 2670 | 2773 | ||
| 2671 | 2774 | session.on('stream', sessionOnStream); | |
| 2672 | - session.on('priority', sessionOnPriority); | ||
| 2673 | 2775 | session.on('error', sessionOnError); | |
| 2776 | + // Don't count our own internal listener. | ||
| 2777 | + session.on('priority', sessionOnPriority); | ||
| 2778 | + session[kNativeFields][kSessionPriorityListenerCount]--; | ||
| 2674 | 2779 | ||
| 2675 | 2780 | if (this.timeout) | |
| 2676 | 2781 | session.setTimeout(this.timeout, sessionOnTimeout); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -161,6 +161,7 @@ class ModuleWrap; | |||
| 161 | 161 | V(family_string, "family") \ | |
| 162 | 162 | V(fatal_exception_string, "_fatalException") \ | |
| 163 | 163 | V(fd_string, "fd") \ | |
| 164 | + V(fields_string, "fields") \ | ||
| 164 | 165 | V(file_string, "file") \ | |
| 165 | 166 | V(fingerprint_string, "fingerprint") \ | |
| 166 | 167 | V(flags_string, "flags") \ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -20,6 +20,7 @@ using v8::ObjectTemplate; | |||
| 20 | 20 | using v8::String; | |
| 21 | 21 | using v8::Uint32; | |
| 22 | 22 | using v8::Uint32Array; | |
| 23 | + using v8::Uint8Array; | ||
| 23 | 24 | using v8::Undefined; | |
| 24 | 25 | ||
| 25 | 26 | using node::performance::PerformanceEntry; | |
@@ -667,6 +668,15 @@ Http2Session::Http2Session(Environment* env, | |||
| 667 | 668 | ||
| 668 | 669 | outgoing_storage_.reserve(4096); | |
| 669 | 670 | outgoing_buffers_.reserve(32); | |
| 671 | + | ||
| 672 | + { | ||
| 673 | + // Make the js_fields_ property accessible to JS land. | ||
| 674 | + Local<ArrayBuffer> ab = | ||
| 675 | + ArrayBuffer::New(env->isolate(), js_fields_, kSessionUint8FieldCount); | ||
| 676 | + Local<Uint8Array> uint8_arr = | ||
| 677 | + Uint8Array::New(ab, 0, kSessionUint8FieldCount); | ||
| 678 | + /*USE*/(wrap->Set(env->context(), env->fields_string(), uint8_arr)); | ||
| 679 | + } | ||
| 670 | 680 | } | |
| 671 | 681 | ||
| 672 | 682 | void Http2Session::Unconsume() { | |
@@ -1090,7 +1100,8 @@ inline int Http2Session::OnFrameNotSent(nghttp2_session* handle, | |||
| 1090 | 1100 | // Do not report if the frame was not sent due to the session closing | |
| 1091 | 1101 | if (error_code == NGHTTP2_ERR_SESSION_CLOSING || | |
| 1092 | 1102 | error_code == NGHTTP2_ERR_STREAM_CLOSED || | |
| 1093 | - error_code == NGHTTP2_ERR_STREAM_CLOSING) { | ||
| 1103 | + error_code == NGHTTP2_ERR_STREAM_CLOSING || | ||
| 1104 | + session->js_fields_[kSessionFrameErrorListenerCount] == 0) { | ||
| 1094 | 1105 | return 0; | |
| 1095 | 1106 | } | |
| 1096 | 1107 | ||
@@ -1346,7 +1357,8 @@ inline void Http2Session::HandleHeadersFrame(const nghttp2_frame* frame) { | |||
| 1346 | 1357 | // received. Notifies JS land about the priority change. Note that priorities | |
| 1347 | 1358 | // are considered advisory only, so this has no real effect other than to | |
| 1348 | 1359 | // simply let user code know that the priority has changed. | |
| 1349 | - inline void Http2Session::HandlePriorityFrame(const nghttp2_frame* frame) { | ||
| 1360 | + void Http2Session::HandlePriorityFrame(const nghttp2_frame* frame) { | ||
| 1361 | + if (js_fields_[kSessionPriorityListenerCount] == 0) return; | ||
| 1350 | 1362 | Isolate* isolate = env()->isolate(); | |
| 1351 | 1363 | HandleScope scope(isolate); | |
| 1352 | 1364 | Local<Context> context = env()->context(); | |
@@ -1413,7 +1425,8 @@ inline void Http2Session::HandleGoawayFrame(const nghttp2_frame* frame) { | |||
| 1413 | 1425 | } | |
| 1414 | 1426 | ||
| 1415 | 1427 | // Called by OnFrameReceived when a complete ALTSVC frame has been received. | |
| 1416 | - inline void Http2Session::HandleAltSvcFrame(const nghttp2_frame* frame) { | ||
| 1428 | + void Http2Session::HandleAltSvcFrame(const nghttp2_frame* frame) { | ||
| 1429 | + if (!(js_fields_[kBitfield] & (1 << kSessionHasAltsvcListeners))) return; | ||
| 1417 | 1430 | Isolate* isolate = env()->isolate(); | |
| 1418 | 1431 | HandleScope scope(isolate); | |
| 1419 | 1432 | Local<Context> context = env()->context(); | |
@@ -1499,6 +1512,7 @@ inline void Http2Session::HandlePingFrame(const nghttp2_frame* frame) { | |||
| 1499 | 1512 | return; | |
| 1500 | 1513 | } | |
| 1501 | 1514 | ||
| 1515 | + if (!(js_fields_[kBitfield] & (1 << kSessionHasPingListeners))) return; | ||
| 1502 | 1516 | // Notify the session that a ping occurred | |
| 1503 | 1517 | arg = Buffer::Copy(env(), | |
| 1504 | 1518 | reinterpret_cast<const char*>(frame->ping.opaque_data), | |
@@ -1510,6 +1524,9 @@ inline void Http2Session::HandlePingFrame(const nghttp2_frame* frame) { | |||
| 1510 | 1524 | inline void Http2Session::HandleSettingsFrame(const nghttp2_frame* frame) { | |
| 1511 | 1525 | bool ack = frame->hd.flags & NGHTTP2_FLAG_ACK; | |
| 1512 | 1526 | if (!ack) { | |
| 1527 | + js_fields_[kBitfield] &= ~(1 << kSessionRemoteSettingsIsUpToDate); | ||
| 1528 | + if (!(js_fields_[kBitfield] & (1 << kSessionHasRemoteSettingsListeners))) | ||
| 1529 | + return; | ||
| 1513 | 1530 | // This is not a SETTINGS acknowledgement, notify and return | |
| 1514 | 1531 | MakeCallback(env()->onsettings_string(), 0, nullptr); | |
| 1515 | 1532 | return; | |
@@ -3135,6 +3152,16 @@ void Initialize(Local<Object> target, | |||
| 3135 | 3152 | NODE_DEFINE_CONSTANT(target, PADDING_BUF_MAX_PAYLOAD_LENGTH); | |
| 3136 | 3153 | NODE_DEFINE_CONSTANT(target, PADDING_BUF_RETURN_VALUE); | |
| 3137 | 3154 | ||
| 3155 | + NODE_DEFINE_CONSTANT(target, kBitfield); | ||
| 3156 | + NODE_DEFINE_CONSTANT(target, kSessionPriorityListenerCount); | ||
| 3157 | + NODE_DEFINE_CONSTANT(target, kSessionFrameErrorListenerCount); | ||
| 3158 | + NODE_DEFINE_CONSTANT(target, kSessionUint8FieldCount); | ||
| 3159 | + | ||
| 3160 | + NODE_DEFINE_CONSTANT(target, kSessionHasRemoteSettingsListeners); | ||
| 3161 | + NODE_DEFINE_CONSTANT(target, kSessionRemoteSettingsIsUpToDate); | ||
| 3162 | + NODE_DEFINE_CONSTANT(target, kSessionHasPingListeners); | ||
| 3163 | + NODE_DEFINE_CONSTANT(target, kSessionHasAltsvcListeners); | ||
| 3164 | + | ||
| 3138 | 3165 | // Method to fetch the nghttp2 string description of an nghttp2 error code | |
| 3139 | 3166 | env->SetMethod(target, "nghttp2ErrorString", HttpErrorString); | |
| 3140 | 3167 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -735,6 +735,23 @@ class Http2Stream::Provider::Stream : public Http2Stream::Provider { | |||
| 735 | 735 | void* user_data); | |
| 736 | 736 | }; | |
| 737 | 737 | ||
| 738 | + // Indices for js_fields_, which serves as a way to communicate data with JS | ||
| 739 | + // land fast. In particular, we store information about the number/presence | ||
| 740 | + // of certain event listeners in JS, and skip calls from C++ into JS if they | ||
| 741 | + // are missing. | ||
| 742 | + enum SessionUint8Fields { | ||
| 743 | + kBitfield, // See below | ||
| 744 | + kSessionPriorityListenerCount, | ||
| 745 | + kSessionFrameErrorListenerCount, | ||
| 746 | + kSessionUint8FieldCount | ||
| 747 | + }; | ||
| 748 | + | ||
| 749 | + enum SessionBitfieldFlags { | ||
| 750 | + kSessionHasRemoteSettingsListeners, | ||
| 751 | + kSessionRemoteSettingsIsUpToDate, | ||
| 752 | + kSessionHasPingListeners, | ||
| 753 | + kSessionHasAltsvcListeners | ||
| 754 | + }; | ||
| 738 | 755 | ||
| 739 | 756 | class Http2Session : public AsyncWrap { | |
| 740 | 757 | public: | |
@@ -1016,6 +1033,9 @@ class Http2Session : public AsyncWrap { | |||
| 1016 | 1033 | // The underlying nghttp2_session handle | |
| 1017 | 1034 | nghttp2_session* session_; | |
| 1018 | 1035 | ||
| 1036 | + // JS-accessible numeric fields, as indexed by SessionUint8Fields. | ||
| 1037 | + uint8_t js_fields_[kSessionUint8FieldCount] = {}; | ||
| 1038 | + | ||
| 1019 | 1039 | // The session type: client or server | |
| 1020 | 1040 | nghttp2_session_type session_type_; | |
| 1021 | 1041 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments