1'use strict'; 2 3const { 4 ArrayIsArray, 5 ArrayPrototypeIncludes, 6 ArrayPrototypeMap, 7 ArrayPrototypePush, 8 Error, 9 MathMax, 10 Number, 11 ObjectCreate, 12 ObjectDefineProperty, 13 ObjectKeys, 14 SafeSet, 15 String, 16 StringFromCharCode, 17 StringPrototypeIncludes, 18 StringPrototypeToLowerCase, 19 Symbol, 20} = primordials; 21 22const binding = internalBinding('http2'); 23const { 24 codes: { 25 ERR_HTTP2_HEADER_SINGLE_VALUE, 26 ERR_HTTP2_INVALID_CONNECTION_HEADERS, 27 ERR_HTTP2_INVALID_PSEUDOHEADER, 28 ERR_HTTP2_INVALID_SETTING_VALUE, 29 ERR_INVALID_ARG_TYPE, 30 ERR_INVALID_HTTP_TOKEN, 31 }, 32 captureLargerStackTrace, 33 getMessage, 34 hideStackFrames, 35 kIsNodeError, 36} = require('internal/errors'); 37 38const kSensitiveHeaders = Symbol('nodejs.http2.sensitiveHeaders'); 39const kSocket = Symbol('socket'); 40const kProxySocket = Symbol('proxySocket'); 41const kRequest = Symbol('request'); 42 43const { 44 NGHTTP2_NV_FLAG_NONE, 45 NGHTTP2_NV_FLAG_NO_INDEX, 46 NGHTTP2_SESSION_CLIENT, 47 NGHTTP2_SESSION_SERVER, 48 49 HTTP2_HEADER_STATUS, 50 HTTP2_HEADER_METHOD, 51 HTTP2_HEADER_AUTHORITY, 52 HTTP2_HEADER_SCHEME, 53 HTTP2_HEADER_PATH, 54 HTTP2_HEADER_PROTOCOL, 55 HTTP2_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS, 56 HTTP2_HEADER_ACCESS_CONTROL_MAX_AGE, 57 HTTP2_HEADER_ACCESS_CONTROL_REQUEST_METHOD, 58 HTTP2_HEADER_AGE, 59 HTTP2_HEADER_AUTHORIZATION, 60 HTTP2_HEADER_CONTENT_ENCODING, 61 HTTP2_HEADER_CONTENT_LANGUAGE, 62 HTTP2_HEADER_CONTENT_LENGTH, 63 HTTP2_HEADER_CONTENT_LOCATION, 64 HTTP2_HEADER_CONTENT_MD5, 65 HTTP2_HEADER_CONTENT_RANGE, 66 HTTP2_HEADER_CONTENT_TYPE, 67 HTTP2_HEADER_COOKIE, 68 HTTP2_HEADER_DATE, 69 HTTP2_HEADER_DNT, 70 HTTP2_HEADER_ETAG, 71 HTTP2_HEADER_EXPIRES, 72 HTTP2_HEADER_FROM, 73 HTTP2_HEADER_HOST, 74 HTTP2_HEADER_IF_MATCH, 75 HTTP2_HEADER_IF_NONE_MATCH, 76 HTTP2_HEADER_IF_MODIFIED_SINCE, 77 HTTP2_HEADER_IF_RANGE, 78 HTTP2_HEADER_IF_UNMODIFIED_SINCE, 79 HTTP2_HEADER_LAST_MODIFIED, 80 HTTP2_HEADER_LOCATION, 81 HTTP2_HEADER_MAX_FORWARDS, 82 HTTP2_HEADER_PROXY_AUTHORIZATION, 83 HTTP2_HEADER_RANGE, 84 HTTP2_HEADER_REFERER, 85 HTTP2_HEADER_RETRY_AFTER, 86 HTTP2_HEADER_SET_COOKIE, 87 HTTP2_HEADER_TK, 88 HTTP2_HEADER_UPGRADE_INSECURE_REQUESTS, 89 HTTP2_HEADER_USER_AGENT, 90 HTTP2_HEADER_X_CONTENT_TYPE_OPTIONS, 91 92 HTTP2_HEADER_CONNECTION, 93 HTTP2_HEADER_UPGRADE, 94 HTTP2_HEADER_HTTP2_SETTINGS, 95 HTTP2_HEADER_TE, 96 HTTP2_HEADER_TRANSFER_ENCODING, 97 HTTP2_HEADER_KEEP_ALIVE, 98 HTTP2_HEADER_PROXY_CONNECTION, 99 100 HTTP2_METHOD_DELETE, 101 HTTP2_METHOD_GET, 102 HTTP2_METHOD_HEAD, 103} = binding.constants; 104 105// This set is defined strictly by the HTTP/2 specification. Only 106// :-prefixed headers defined by that specification may be added to 107// this set. 108const kValidPseudoHeaders = new SafeSet([ 109 HTTP2_HEADER_STATUS, 110 HTTP2_HEADER_METHOD, 111 HTTP2_HEADER_AUTHORITY, 112 HTTP2_HEADER_SCHEME, 113 HTTP2_HEADER_PATH, 114 HTTP2_HEADER_PROTOCOL, 115]); 116 117// This set contains headers that are permitted to have only a single 118// value. Multiple instances must not be specified. 119const kSingleValueHeaders = new SafeSet([ 120 HTTP2_HEADER_STATUS, 121 HTTP2_HEADER_METHOD, 122 HTTP2_HEADER_AUTHORITY, 123 HTTP2_HEADER_SCHEME, 124 HTTP2_HEADER_PATH, 125 HTTP2_HEADER_PROTOCOL, 126 HTTP2_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS, 127 HTTP2_HEADER_ACCESS_CONTROL_MAX_AGE, 128 HTTP2_HEADER_ACCESS_CONTROL_REQUEST_METHOD, 129 HTTP2_HEADER_AGE, 130 HTTP2_HEADER_AUTHORIZATION, 131 HTTP2_HEADER_CONTENT_ENCODING, 132 HTTP2_HEADER_CONTENT_LANGUAGE, 133 HTTP2_HEADER_CONTENT_LENGTH, 134 HTTP2_HEADER_CONTENT_LOCATION, 135 HTTP2_HEADER_CONTENT_MD5, 136 HTTP2_HEADER_CONTENT_RANGE, 137 HTTP2_HEADER_CONTENT_TYPE, 138 HTTP2_HEADER_DATE, 139 HTTP2_HEADER_DNT, 140 HTTP2_HEADER_ETAG, 141 HTTP2_HEADER_EXPIRES, 142 HTTP2_HEADER_FROM, 143 HTTP2_HEADER_HOST, 144 HTTP2_HEADER_IF_MATCH, 145 HTTP2_HEADER_IF_MODIFIED_SINCE, 146 HTTP2_HEADER_IF_NONE_MATCH, 147 HTTP2_HEADER_IF_RANGE, 148 HTTP2_HEADER_IF_UNMODIFIED_SINCE, 149 HTTP2_HEADER_LAST_MODIFIED, 150 HTTP2_HEADER_LOCATION, 151 HTTP2_HEADER_MAX_FORWARDS, 152 HTTP2_HEADER_PROXY_AUTHORIZATION, 153 HTTP2_HEADER_RANGE, 154 HTTP2_HEADER_REFERER, 155 HTTP2_HEADER_RETRY_AFTER, 156 HTTP2_HEADER_TK, 157 HTTP2_HEADER_UPGRADE_INSECURE_REQUESTS, 158 HTTP2_HEADER_USER_AGENT, 159 HTTP2_HEADER_X_CONTENT_TYPE_OPTIONS, 160]); 161 162// The HTTP methods in this set are specifically defined as assigning no 163// meaning to the request payload. By default, unless the user explicitly 164// overrides the endStream option on the request method, the endStream 165// option will be defaulted to true when these methods are used. 166const kNoPayloadMethods = new SafeSet([ 167 HTTP2_METHOD_DELETE, 168 HTTP2_METHOD_GET, 169 HTTP2_METHOD_HEAD, 170]); 171 172// The following ArrayBuffer instances are used to share memory more efficiently 173// with the native binding side for a number of methods. These are not intended 174// to be used directly by users in any way. The ArrayBuffers are created on 175// the native side with values that are filled in on demand, the js code then 176// reads those values out. The set of IDX constants that follow identify the 177// relevant data positions within these buffers. 178const { settingsBuffer, optionsBuffer } = binding; 179 180// Note that Float64Array is used here because there is no Int64Array available 181// and these deal with numbers that can be beyond the range of Uint32 and Int32. 182// The values set on the native side will always be integers. This is not a 183// unique example of this, this pattern can be found in use in other parts of 184// Node.js core as a performance optimization. 185const { sessionState, streamState } = binding; 186 187const IDX_SETTINGS_HEADER_TABLE_SIZE = 0; 188const IDX_SETTINGS_ENABLE_PUSH = 1; 189const IDX_SETTINGS_INITIAL_WINDOW_SIZE = 2; 190const IDX_SETTINGS_MAX_FRAME_SIZE = 3; 191const IDX_SETTINGS_MAX_CONCURRENT_STREAMS = 4; 192const IDX_SETTINGS_MAX_HEADER_LIST_SIZE = 5; 193const IDX_SETTINGS_ENABLE_CONNECT_PROTOCOL = 6; 194const IDX_SETTINGS_FLAGS = 7; 195 196const IDX_SESSION_STATE_EFFECTIVE_LOCAL_WINDOW_SIZE = 0; 197const IDX_SESSION_STATE_EFFECTIVE_RECV_DATA_LENGTH = 1; 198const IDX_SESSION_STATE_NEXT_STREAM_ID = 2; 199const IDX_SESSION_STATE_LOCAL_WINDOW_SIZE = 3; 200const IDX_SESSION_STATE_LAST_PROC_STREAM_ID = 4; 201const IDX_SESSION_STATE_REMOTE_WINDOW_SIZE = 5; 202const IDX_SESSION_STATE_OUTBOUND_QUEUE_SIZE = 6; 203const IDX_SESSION_STATE_HD_DEFLATE_DYNAMIC_TABLE_SIZE = 7; 204const IDX_SESSION_STATE_HD_INFLATE_DYNAMIC_TABLE_SIZE = 8; 205const IDX_STREAM_STATE = 0; 206const IDX_STREAM_STATE_WEIGHT = 1; 207const IDX_STREAM_STATE_SUM_DEPENDENCY_WEIGHT = 2; 208const IDX_STREAM_STATE_LOCAL_CLOSE = 3; 209const IDX_STREAM_STATE_REMOTE_CLOSE = 4; 210const IDX_STREAM_STATE_LOCAL_WINDOW_SIZE = 5; 211 212const IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE = 0; 213const IDX_OPTIONS_MAX_RESERVED_REMOTE_STREAMS = 1; 214const IDX_OPTIONS_MAX_SEND_HEADER_BLOCK_LENGTH = 2; 215const IDX_OPTIONS_PEER_MAX_CONCURRENT_STREAMS = 3; 216const IDX_OPTIONS_PADDING_STRATEGY = 4; 217const IDX_OPTIONS_MAX_HEADER_LIST_PAIRS = 5; 218const IDX_OPTIONS_MAX_OUTSTANDING_PINGS = 6; 219const IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS = 7; 220const IDX_OPTIONS_MAX_SESSION_MEMORY = 8; 221const IDX_OPTIONS_MAX_SETTINGS = 9; 222const IDX_OPTIONS_FLAGS = 10; 223 224function updateOptionsBuffer(options) { 225 let flags = 0; 226 if (typeof options.maxDeflateDynamicTableSize === 'number') { 227 flags |= (1 << IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE); 228 optionsBuffer[IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE] = 229 options.maxDeflateDynamicTableSize; 230 } 231 if (typeof options.maxReservedRemoteStreams === 'number') { 232 flags |= (1 << IDX_OPTIONS_MAX_RESERVED_REMOTE_STREAMS); 233 optionsBuffer[IDX_OPTIONS_MAX_RESERVED_REMOTE_STREAMS] = 234 options.maxReservedRemoteStreams; 235 } 236 if (typeof options.maxSendHeaderBlockLength === 'number') { 237 flags |= (1 << IDX_OPTIONS_MAX_SEND_HEADER_BLOCK_LENGTH); 238 optionsBuffer[IDX_OPTIONS_MAX_SEND_HEADER_BLOCK_LENGTH] = 239 options.maxSendHeaderBlockLength; 240 } 241 if (typeof options.peerMaxConcurrentStreams === 'number') { 242 flags |= (1 << IDX_OPTIONS_PEER_MAX_CONCURRENT_STREAMS); 243 optionsBuffer[IDX_OPTIONS_PEER_MAX_CONCURRENT_STREAMS] = 244 options.peerMaxConcurrentStreams; 245 } 246 if (typeof options.paddingStrategy === 'number') { 247 flags |= (1 << IDX_OPTIONS_PADDING_STRATEGY); 248 optionsBuffer[IDX_OPTIONS_PADDING_STRATEGY] = 249 options.paddingStrategy; 250 } 251 if (typeof options.maxHeaderListPairs === 'number') { 252 flags |= (1 << IDX_OPTIONS_MAX_HEADER_LIST_PAIRS); 253 optionsBuffer[IDX_OPTIONS_MAX_HEADER_LIST_PAIRS] = 254 options.maxHeaderListPairs; 255 } 256 if (typeof options.maxOutstandingPings === 'number') { 257 flags |= (1 << IDX_OPTIONS_MAX_OUTSTANDING_PINGS); 258 optionsBuffer[IDX_OPTIONS_MAX_OUTSTANDING_PINGS] = 259 options.maxOutstandingPings; 260 } 261 if (typeof options.maxOutstandingSettings === 'number') { 262 flags |= (1 << IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS); 263 optionsBuffer[IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS] = 264 MathMax(1, options.maxOutstandingSettings); 265 } 266 if (typeof options.maxSessionMemory === 'number') { 267 flags |= (1 << IDX_OPTIONS_MAX_SESSION_MEMORY); 268 optionsBuffer[IDX_OPTIONS_MAX_SESSION_MEMORY] = 269 MathMax(1, options.maxSessionMemory); 270 } 271 if (typeof options.maxSettings === 'number') { 272 flags |= (1 << IDX_OPTIONS_MAX_SETTINGS); 273 optionsBuffer[IDX_OPTIONS_MAX_SETTINGS] = 274 MathMax(1, options.maxSettings); 275 } 276 optionsBuffer[IDX_OPTIONS_FLAGS] = flags; 277} 278 279function getDefaultSettings() { 280 settingsBuffer[IDX_SETTINGS_FLAGS] = 0; 281 binding.refreshDefaultSettings(); 282 const holder = ObjectCreate(null); 283 284 const flags = settingsBuffer[IDX_SETTINGS_FLAGS]; 285 286 if ((flags & (1 << IDX_SETTINGS_HEADER_TABLE_SIZE)) === 287 (1 << IDX_SETTINGS_HEADER_TABLE_SIZE)) { 288 holder.headerTableSize = 289 settingsBuffer[IDX_SETTINGS_HEADER_TABLE_SIZE]; 290 } 291 292 if ((flags & (1 << IDX_SETTINGS_ENABLE_PUSH)) === 293 (1 << IDX_SETTINGS_ENABLE_PUSH)) { 294 holder.enablePush = 295 settingsBuffer[IDX_SETTINGS_ENABLE_PUSH] === 1; 296 } 297 298 if ((flags & (1 << IDX_SETTINGS_INITIAL_WINDOW_SIZE)) === 299 (1 << IDX_SETTINGS_INITIAL_WINDOW_SIZE)) { 300 holder.initialWindowSize = 301 settingsBuffer[IDX_SETTINGS_INITIAL_WINDOW_SIZE]; 302 } 303 304 if ((flags & (1 << IDX_SETTINGS_MAX_FRAME_SIZE)) === 305 (1 << IDX_SETTINGS_MAX_FRAME_SIZE)) { 306 holder.maxFrameSize = 307 settingsBuffer[IDX_SETTINGS_MAX_FRAME_SIZE]; 308 } 309 310 if ((flags & (1 << IDX_SETTINGS_MAX_CONCURRENT_STREAMS)) === 311 (1 << IDX_SETTINGS_MAX_CONCURRENT_STREAMS)) { 312 holder.maxConcurrentStreams = 313 settingsBuffer[IDX_SETTINGS_MAX_CONCURRENT_STREAMS]; 314 } 315 316 if ((flags & (1 << IDX_SETTINGS_MAX_HEADER_LIST_SIZE)) === 317 (1 << IDX_SETTINGS_MAX_HEADER_LIST_SIZE)) { 318 holder.maxHeaderListSize = holder.maxHeaderSize = 319 settingsBuffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE]; 320 } 321 322 if ((flags & (1 << IDX_SETTINGS_ENABLE_CONNECT_PROTOCOL)) === 323 (1 << IDX_SETTINGS_ENABLE_CONNECT_PROTOCOL)) { 324 holder.enableConnectProtocol = 325 settingsBuffer[IDX_SETTINGS_ENABLE_CONNECT_PROTOCOL] === 1; 326 } 327 328 return holder; 329} 330 331// Remote is a boolean. true to fetch remote settings, false to fetch local. 332// this is only called internally 333function getSettings(session, remote) { 334 if (remote) 335 session.remoteSettings(); 336 else 337 session.localSettings(); 338 339 return { 340 headerTableSize: settingsBuffer[IDX_SETTINGS_HEADER_TABLE_SIZE], 341 enablePush: !!settingsBuffer[IDX_SETTINGS_ENABLE_PUSH], 342 initialWindowSize: settingsBuffer[IDX_SETTINGS_INITIAL_WINDOW_SIZE], 343 maxFrameSize: settingsBuffer[IDX_SETTINGS_MAX_FRAME_SIZE], 344 maxConcurrentStreams: settingsBuffer[IDX_SETTINGS_MAX_CONCURRENT_STREAMS], 345 maxHeaderListSize: settingsBuffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE], 346 maxHeaderSize: settingsBuffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE], 347 enableConnectProtocol: 348 !!settingsBuffer[IDX_SETTINGS_ENABLE_CONNECT_PROTOCOL], 349 }; 350} 351 352function updateSettingsBuffer(settings) { 353 let flags = 0; 354 if (typeof settings.headerTableSize === 'number') { 355 flags |= (1 << IDX_SETTINGS_HEADER_TABLE_SIZE); 356 settingsBuffer[IDX_SETTINGS_HEADER_TABLE_SIZE] = 357 settings.headerTableSize; 358 } 359 if (typeof settings.maxConcurrentStreams === 'number') { 360 flags |= (1 << IDX_SETTINGS_MAX_CONCURRENT_STREAMS); 361 settingsBuffer[IDX_SETTINGS_MAX_CONCURRENT_STREAMS] = 362 settings.maxConcurrentStreams; 363 } 364 if (typeof settings.initialWindowSize === 'number') { 365 flags |= (1 << IDX_SETTINGS_INITIAL_WINDOW_SIZE); 366 settingsBuffer[IDX_SETTINGS_INITIAL_WINDOW_SIZE] = 367 settings.initialWindowSize; 368 } 369 if (typeof settings.maxFrameSize === 'number') { 370 flags |= (1 << IDX_SETTINGS_MAX_FRAME_SIZE); 371 settingsBuffer[IDX_SETTINGS_MAX_FRAME_SIZE] = 372 settings.maxFrameSize; 373 } 374 if (typeof settings.maxHeaderListSize === 'number' || 375 typeof settings.maxHeaderSize === 'number') { 376 flags |= (1 << IDX_SETTINGS_MAX_HEADER_LIST_SIZE); 377 if (settings.maxHeaderSize !== undefined && 378 (settings.maxHeaderSize !== settings.maxHeaderListSize)) { 379 process.emitWarning( 380 'settings.maxHeaderSize overwrite settings.maxHeaderListSize', 381 ); 382 settingsBuffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE] = 383 settings.maxHeaderSize; 384 } else { 385 settingsBuffer[IDX_SETTINGS_MAX_HEADER_LIST_SIZE] = 386 settings.maxHeaderListSize; 387 } 388 } 389 if (typeof settings.enablePush === 'boolean') { 390 flags |= (1 << IDX_SETTINGS_ENABLE_PUSH); 391 settingsBuffer[IDX_SETTINGS_ENABLE_PUSH] = Number(settings.enablePush); 392 } 393 if (typeof settings.enableConnectProtocol === 'boolean') { 394 flags |= (1 << IDX_SETTINGS_ENABLE_CONNECT_PROTOCOL); 395 settingsBuffer[IDX_SETTINGS_ENABLE_CONNECT_PROTOCOL] = 396 Number(settings.enableConnectProtocol); 397 } 398 399 settingsBuffer[IDX_SETTINGS_FLAGS] = flags; 400} 401 402function getSessionState(session) { 403 session.refreshState(); 404 return { 405 effectiveLocalWindowSize: 406 sessionState[IDX_SESSION_STATE_EFFECTIVE_LOCAL_WINDOW_SIZE], 407 effectiveRecvDataLength: 408 sessionState[IDX_SESSION_STATE_EFFECTIVE_RECV_DATA_LENGTH], 409 nextStreamID: 410 sessionState[IDX_SESSION_STATE_NEXT_STREAM_ID], 411 localWindowSize: 412 sessionState[IDX_SESSION_STATE_LOCAL_WINDOW_SIZE], 413 lastProcStreamID: 414 sessionState[IDX_SESSION_STATE_LAST_PROC_STREAM_ID], 415 remoteWindowSize: 416 sessionState[IDX_SESSION_STATE_REMOTE_WINDOW_SIZE], 417 outboundQueueSize: 418 sessionState[IDX_SESSION_STATE_OUTBOUND_QUEUE_SIZE], 419 deflateDynamicTableSize: 420 sessionState[IDX_SESSION_STATE_HD_DEFLATE_DYNAMIC_TABLE_SIZE], 421 inflateDynamicTableSize: 422 sessionState[IDX_SESSION_STATE_HD_INFLATE_DYNAMIC_TABLE_SIZE], 423 }; 424} 425 426function getStreamState(stream) { 427 stream.refreshState(); 428 return { 429 state: streamState[IDX_STREAM_STATE], 430 weight: streamState[IDX_STREAM_STATE_WEIGHT], 431 sumDependencyWeight: streamState[IDX_STREAM_STATE_SUM_DEPENDENCY_WEIGHT], 432 localClose: streamState[IDX_STREAM_STATE_LOCAL_CLOSE], 433 remoteClose: streamState[IDX_STREAM_STATE_REMOTE_CLOSE], 434 localWindowSize: streamState[IDX_STREAM_STATE_LOCAL_WINDOW_SIZE], 435 }; 436} 437 438function isIllegalConnectionSpecificHeader(name, value) { 439 switch (name) { 440 case HTTP2_HEADER_CONNECTION: 441 case HTTP2_HEADER_UPGRADE: 442 case HTTP2_HEADER_HTTP2_SETTINGS: 443 case HTTP2_HEADER_KEEP_ALIVE: 444 case HTTP2_HEADER_PROXY_CONNECTION: 445 case HTTP2_HEADER_TRANSFER_ENCODING: 446 return true; 447 case HTTP2_HEADER_TE: 448 return value !== 'trailers'; 449 default: 450 return false; 451 } 452} 453 454const assertValidPseudoHeader = hideStackFrames((key) => { 455 if (!kValidPseudoHeaders.has(key)) { 456 throw new ERR_HTTP2_INVALID_PSEUDOHEADER(key); 457 } 458}); 459 460const assertValidPseudoHeaderResponse = hideStackFrames((key) => { 461 if (key !== ':status') { 462 throw new ERR_HTTP2_INVALID_PSEUDOHEADER(key); 463 } 464}); 465 466const assertValidPseudoHeaderTrailer = hideStackFrames((key) => { 467 throw new ERR_HTTP2_INVALID_PSEUDOHEADER(key); 468}); 469 470const emptyArray = []; 471const kNeverIndexFlag = StringFromCharCode(NGHTTP2_NV_FLAG_NO_INDEX); 472const kNoHeaderFlags = StringFromCharCode(NGHTTP2_NV_FLAG_NONE); 473function mapToHeaders(map, 474 assertValuePseudoHeader = assertValidPseudoHeader) { 475 let headers = ''; 476 let pseudoHeaders = ''; 477 let count = 0; 478 const keys = ObjectKeys(map); 479 const singles = new SafeSet(); 480 let i, j; 481 let isArray; 482 let key; 483 let value; 484 let isSingleValueHeader; 485 let err; 486 const neverIndex = 487 ArrayPrototypeMap(map[kSensitiveHeaders] || emptyArray, 488 StringPrototypeToLowerCase); 489 for (i = 0; i < keys.length; ++i) { 490 key = keys[i]; 491 value = map[key]; 492 if (value === undefined || key === '') 493 continue; 494 key = StringPrototypeToLowerCase(key); 495 isSingleValueHeader = kSingleValueHeaders.has(key); 496 isArray = ArrayIsArray(value); 497 if (isArray) { 498 switch (value.length) { 499 case 0: 500 continue; 501 case 1: 502 value = String(value[0]); 503 isArray = false; 504 break; 505 default: 506 if (isSingleValueHeader) 507 throw new ERR_HTTP2_HEADER_SINGLE_VALUE(key); 508 } 509 } else { 510 value = String(value); 511 } 512 if (isSingleValueHeader) { 513 if (singles.has(key)) 514 throw new ERR_HTTP2_HEADER_SINGLE_VALUE(key); 515 singles.add(key); 516 } 517 const flags = ArrayPrototypeIncludes(neverIndex, key) ? 518 kNeverIndexFlag : 519 kNoHeaderFlags; 520 if (key[0] === ':') { 521 err = assertValuePseudoHeader(key); 522 if (err !== undefined) 523 throw err; 524 pseudoHeaders += `${key}\0${value}\0${flags}`; 525 count++; 526 continue; 527 } 528 if (StringPrototypeIncludes(key, ' ')) { 529 throw new ERR_INVALID_HTTP_TOKEN('Header name', key); 530 } 531 if (isIllegalConnectionSpecificHeader(key, value)) { 532 throw new ERR_HTTP2_INVALID_CONNECTION_HEADERS(key); 533 } 534 if (isArray) { 535 for (j = 0; j < value.length; ++j) { 536 const val = String(value[j]); 537 headers += `${key}\0${val}\0${flags}`; 538 } 539 count += value.length; 540 continue; 541 } 542 headers += `${key}\0${value}\0${flags}`; 543 count++; 544 } 545 546 return [pseudoHeaders + headers, count]; 547} 548 549class NghttpError extends Error { 550 constructor(integerCode, customErrorCode) { 551 super(customErrorCode ? 552 getMessage(customErrorCode, [], null) : 553 binding.nghttp2ErrorString(integerCode)); 554 this.code = customErrorCode || 'ERR_HTTP2_ERROR'; 555 this.errno = integerCode; 556 captureLargerStackTrace(this); 557 ObjectDefineProperty(this, kIsNodeError, { 558 __proto__: null, 559 value: true, 560 enumerable: false, 561 writable: false, 562 configurable: true, 563 }); 564 } 565 566 toString() { 567 return `${this.name} [${this.code}]: ${this.message}`; 568 } 569} 570 571const assertIsObject = hideStackFrames((value, name, types) => { 572 if (value !== undefined && 573 (value === null || 574 typeof value !== 'object' || 575 ArrayIsArray(value))) { 576 throw new ERR_INVALID_ARG_TYPE(name, types || 'Object', value); 577 } 578}); 579 580const assertWithinRange = hideStackFrames( 581 (name, value, min = 0, max = Infinity) => { 582 if (value !== undefined && 583 (typeof value !== 'number' || value < min || value > max)) { 584 throw new ERR_HTTP2_INVALID_SETTING_VALUE.RangeError( 585 name, value, min, max); 586 } 587 }, 588); 589 590function toHeaderObject(headers, sensitiveHeaders) { 591 const obj = ObjectCreate(null); 592 for (let n = 0; n < headers.length; n += 2) { 593 const name = headers[n]; 594 let value = headers[n + 1]; 595 if (name === HTTP2_HEADER_STATUS) 596 value |= 0; 597 const existing = obj[name]; 598 if (existing === undefined) { 599 obj[name] = name === HTTP2_HEADER_SET_COOKIE ? [value] : value; 600 } else if (!kSingleValueHeaders.has(name)) { 601 switch (name) { 602 case HTTP2_HEADER_COOKIE: 603 // https://tools.ietf.org/html/rfc7540#section-8.1.2.5 604 // "...If there are multiple Cookie header fields after decompression, 605 // these MUST be concatenated into a single octet string using the 606 // two-octet delimiter of 0x3B, 0x20 (the ASCII string "; ") before 607 // being passed into a non-HTTP/2 context." 608 obj[name] = `${existing}; ${value}`; 609 break; 610 case HTTP2_HEADER_SET_COOKIE: 611 // https://tools.ietf.org/html/rfc7230#section-3.2.2 612 // "Note: In practice, the "Set-Cookie" header field ([RFC6265]) often 613 // appears multiple times in a response message and does not use the 614 // list syntax, violating the above requirements on multiple header 615 // fields with the same name. Since it cannot be combined into a 616 // single field-value, recipients ought to handle "Set-Cookie" as a 617 // special case while processing header fields." 618 ArrayPrototypePush(existing, value); 619 break; 620 default: 621 // https://tools.ietf.org/html/rfc7230#section-3.2.2 622 // "A recipient MAY combine multiple header fields with the same field 623 // name into one "field-name: field-value" pair, without changing the 624 // semantics of the message, by appending each subsequent field value 625 // to the combined field value in order, separated by a comma." 626 obj[name] = `${existing}, ${value}`; 627 break; 628 } 629 } 630 } 631 obj[kSensitiveHeaders] = sensitiveHeaders; 632 return obj; 633} 634 635function isPayloadMeaningless(method) { 636 return kNoPayloadMethods.has(method); 637} 638 639function sessionName(type) { 640 switch (type) { 641 case NGHTTP2_SESSION_CLIENT: 642 return 'client'; 643 case NGHTTP2_SESSION_SERVER: 644 return 'server'; 645 default: 646 return '<invalid>'; 647 } 648} 649 650function getAuthority(headers) { 651 // For non-CONNECT requests, HTTP/2 allows either :authority 652 // or Host to be used equivalently. The first is preferred 653 // when making HTTP/2 requests, and the latter is preferred 654 // when converting from an HTTP/1 message. 655 if (headers[HTTP2_HEADER_AUTHORITY] !== undefined) 656 return headers[HTTP2_HEADER_AUTHORITY]; 657 if (headers[HTTP2_HEADER_HOST] !== undefined) 658 return headers[HTTP2_HEADER_HOST]; 659} 660 661module.exports = { 662 assertIsObject, 663 assertValidPseudoHeader, 664 assertValidPseudoHeaderResponse, 665 assertValidPseudoHeaderTrailer, 666 assertWithinRange, 667 getAuthority, 668 getDefaultSettings, 669 getSessionState, 670 getSettings, 671 getStreamState, 672 isPayloadMeaningless, 673 kSensitiveHeaders, 674 kSocket, 675 kProxySocket, 676 kRequest, 677 mapToHeaders, 678 NghttpError, 679 sessionName, 680 toHeaderObject, 681 updateOptionsBuffer, 682 updateSettingsBuffer, 683}; 684