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