diff options
author | 2023-09-09 13:56:29 -0300 | |
---|---|---|
committer | 2023-09-09 13:56:29 -0300 | |
commit | d2008abc465201a9c3feffbc328597c8a6fe2b67 (patch) | |
tree | a8401e641cb215e03ab8113267fb943802d27773 | |
parent | 603fcc423bfd17178a16f3eb945ede0bcd1e7d65 (diff) | |
download | bun-ciro/http2.tar.gz bun-ciro/http2.tar.zst bun-ciro/http2.zip |
more thingsciro/http2
-rw-r--r-- | src/js/node/http2.ts | 662 |
1 files changed, 395 insertions, 267 deletions
diff --git a/src/js/node/http2.ts b/src/js/node/http2.ts index 67900db2c..2bdb05170 100644 --- a/src/js/node/http2.ts +++ b/src/js/node/http2.ts @@ -9,6 +9,309 @@ type TLSSocket = typeof tls.TLSSocket; const EventEmitter = require("node:events"); const { Duplex } = require("node:stream"); const { H2FrameParser } = $lazy("internal/http2"); +const sensitiveHeaders = Symbol.for("nodejs.http2.sensitiveHeaders"); + +const constants = { + NGHTTP2_ERR_FRAME_SIZE_ERROR: -522, + NGHTTP2_SESSION_SERVER: 0, + NGHTTP2_SESSION_CLIENT: 1, + NGHTTP2_STREAM_STATE_IDLE: 1, + NGHTTP2_STREAM_STATE_OPEN: 2, + NGHTTP2_STREAM_STATE_RESERVED_LOCAL: 3, + NGHTTP2_STREAM_STATE_RESERVED_REMOTE: 4, + NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL: 5, + NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE: 6, + NGHTTP2_STREAM_STATE_CLOSED: 7, + NGHTTP2_FLAG_NONE: 0, + NGHTTP2_FLAG_END_STREAM: 1, + NGHTTP2_FLAG_END_HEADERS: 4, + NGHTTP2_FLAG_ACK: 1, + NGHTTP2_FLAG_PADDED: 8, + NGHTTP2_FLAG_PRIORITY: 32, + DEFAULT_SETTINGS_HEADER_TABLE_SIZE: 4096, + DEFAULT_SETTINGS_ENABLE_PUSH: 1, + DEFAULT_SETTINGS_MAX_CONCURRENT_STREAMS: 4294967295, + DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE: 65535, + DEFAULT_SETTINGS_MAX_FRAME_SIZE: 16384, + DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE: 65535, + DEFAULT_SETTINGS_ENABLE_CONNECT_PROTOCOL: 0, + MAX_MAX_FRAME_SIZE: 16777215, + MIN_MAX_FRAME_SIZE: 16384, + MAX_INITIAL_WINDOW_SIZE: 2147483647, + NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: 1, + NGHTTP2_SETTINGS_ENABLE_PUSH: 2, + NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: 3, + NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: 4, + NGHTTP2_SETTINGS_MAX_FRAME_SIZE: 5, + NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: 6, + NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: 8, + PADDING_STRATEGY_NONE: 0, + PADDING_STRATEGY_ALIGNED: 1, + PADDING_STRATEGY_MAX: 2, + PADDING_STRATEGY_CALLBACK: 1, + NGHTTP2_NO_ERROR: 0, + NGHTTP2_PROTOCOL_ERROR: 1, + NGHTTP2_INTERNAL_ERROR: 2, + NGHTTP2_FLOW_CONTROL_ERROR: 3, + NGHTTP2_SETTINGS_TIMEOUT: 4, + NGHTTP2_STREAM_CLOSED: 5, + NGHTTP2_FRAME_SIZE_ERROR: 6, + NGHTTP2_REFUSED_STREAM: 7, + NGHTTP2_CANCEL: 8, + NGHTTP2_COMPRESSION_ERROR: 9, + NGHTTP2_CONNECT_ERROR: 10, + NGHTTP2_ENHANCE_YOUR_CALM: 11, + NGHTTP2_INADEQUATE_SECURITY: 12, + NGHTTP2_HTTP_1_1_REQUIRED: 13, + NGHTTP2_DEFAULT_WEIGHT: 16, + HTTP2_HEADER_STATUS: ":status", + HTTP2_HEADER_METHOD: ":method", + HTTP2_HEADER_AUTHORITY: ":authority", + HTTP2_HEADER_SCHEME: ":scheme", + HTTP2_HEADER_PATH: ":path", + HTTP2_HEADER_PROTOCOL: ":protocol", + HTTP2_HEADER_ACCEPT_ENCODING: "accept-encoding", + HTTP2_HEADER_ACCEPT_LANGUAGE: "accept-language", + HTTP2_HEADER_ACCEPT_RANGES: "accept-ranges", + HTTP2_HEADER_ACCEPT: "accept", + HTTP2_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS: "access-control-allow-credentials", + HTTP2_HEADER_ACCESS_CONTROL_ALLOW_HEADERS: "access-control-allow-headers", + HTTP2_HEADER_ACCESS_CONTROL_ALLOW_METHODS: "access-control-allow-methods", + HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN: "access-control-allow-origin", + HTTP2_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS: "access-control-expose-headers", + HTTP2_HEADER_ACCESS_CONTROL_REQUEST_HEADERS: "access-control-request-headers", + HTTP2_HEADER_ACCESS_CONTROL_REQUEST_METHOD: "access-control-request-method", + HTTP2_HEADER_AGE: "age", + HTTP2_HEADER_AUTHORIZATION: "authorization", + HTTP2_HEADER_CACHE_CONTROL: "cache-control", + HTTP2_HEADER_CONNECTION: "connection", + HTTP2_HEADER_CONTENT_DISPOSITION: "content-disposition", + HTTP2_HEADER_CONTENT_ENCODING: "content-encoding", + HTTP2_HEADER_CONTENT_LENGTH: "content-length", + HTTP2_HEADER_CONTENT_TYPE: "content-type", + HTTP2_HEADER_COOKIE: "cookie", + HTTP2_HEADER_DATE: "date", + HTTP2_HEADER_ETAG: "etag", + HTTP2_HEADER_FORWARDED: "forwarded", + HTTP2_HEADER_HOST: "host", + HTTP2_HEADER_IF_MODIFIED_SINCE: "if-modified-since", + HTTP2_HEADER_IF_NONE_MATCH: "if-none-match", + HTTP2_HEADER_IF_RANGE: "if-range", + HTTP2_HEADER_LAST_MODIFIED: "last-modified", + HTTP2_HEADER_LINK: "link", + HTTP2_HEADER_LOCATION: "location", + HTTP2_HEADER_RANGE: "range", + HTTP2_HEADER_REFERER: "referer", + HTTP2_HEADER_SERVER: "server", + HTTP2_HEADER_SET_COOKIE: "set-cookie", + HTTP2_HEADER_STRICT_TRANSPORT_SECURITY: "strict-transport-security", + HTTP2_HEADER_TRANSFER_ENCODING: "transfer-encoding", + HTTP2_HEADER_TE: "te", + HTTP2_HEADER_UPGRADE_INSECURE_REQUESTS: "upgrade-insecure-requests", + HTTP2_HEADER_UPGRADE: "upgrade", + HTTP2_HEADER_USER_AGENT: "user-agent", + HTTP2_HEADER_VARY: "vary", + HTTP2_HEADER_X_CONTENT_TYPE_OPTIONS: "x-content-type-options", + HTTP2_HEADER_X_FRAME_OPTIONS: "x-frame-options", + HTTP2_HEADER_KEEP_ALIVE: "keep-alive", + HTTP2_HEADER_PROXY_CONNECTION: "proxy-connection", + HTTP2_HEADER_X_XSS_PROTECTION: "x-xss-protection", + HTTP2_HEADER_ALT_SVC: "alt-svc", + HTTP2_HEADER_CONTENT_SECURITY_POLICY: "content-security-policy", + HTTP2_HEADER_EARLY_DATA: "early-data", + HTTP2_HEADER_EXPECT_CT: "expect-ct", + HTTP2_HEADER_ORIGIN: "origin", + HTTP2_HEADER_PURPOSE: "purpose", + HTTP2_HEADER_TIMING_ALLOW_ORIGIN: "timing-allow-origin", + HTTP2_HEADER_X_FORWARDED_FOR: "x-forwarded-for", + HTTP2_HEADER_PRIORITY: "priority", + HTTP2_HEADER_ACCEPT_CHARSET: "accept-charset", + HTTP2_HEADER_ACCESS_CONTROL_MAX_AGE: "access-control-max-age", + HTTP2_HEADER_ALLOW: "allow", + HTTP2_HEADER_CONTENT_LANGUAGE: "content-language", + HTTP2_HEADER_CONTENT_LOCATION: "content-location", + HTTP2_HEADER_CONTENT_MD5: "content-md5", + HTTP2_HEADER_CONTENT_RANGE: "content-range", + HTTP2_HEADER_DNT: "dnt", + HTTP2_HEADER_EXPECT: "expect", + HTTP2_HEADER_EXPIRES: "expires", + HTTP2_HEADER_FROM: "from", + HTTP2_HEADER_IF_MATCH: "if-match", + HTTP2_HEADER_IF_UNMODIFIED_SINCE: "if-unmodified-since", + HTTP2_HEADER_MAX_FORWARDS: "max-forwards", + HTTP2_HEADER_PREFER: "prefer", + HTTP2_HEADER_PROXY_AUTHENTICATE: "proxy-authenticate", + HTTP2_HEADER_PROXY_AUTHORIZATION: "proxy-authorization", + HTTP2_HEADER_REFRESH: "refresh", + HTTP2_HEADER_RETRY_AFTER: "retry-after", + HTTP2_HEADER_TRAILER: "trailer", + HTTP2_HEADER_TK: "tk", + HTTP2_HEADER_VIA: "via", + HTTP2_HEADER_WARNING: "warning", + HTTP2_HEADER_WWW_AUTHENTICATE: "www-authenticate", + HTTP2_HEADER_HTTP2_SETTINGS: "http2-settings", + HTTP2_METHOD_ACL: "ACL", + HTTP2_METHOD_BASELINE_CONTROL: "BASELINE-CONTROL", + HTTP2_METHOD_BIND: "BIND", + HTTP2_METHOD_CHECKIN: "CHECKIN", + HTTP2_METHOD_CHECKOUT: "CHECKOUT", + HTTP2_METHOD_CONNECT: "CONNECT", + HTTP2_METHOD_COPY: "COPY", + HTTP2_METHOD_DELETE: "DELETE", + HTTP2_METHOD_GET: "GET", + HTTP2_METHOD_HEAD: "HEAD", + HTTP2_METHOD_LABEL: "LABEL", + HTTP2_METHOD_LINK: "LINK", + HTTP2_METHOD_LOCK: "LOCK", + HTTP2_METHOD_MERGE: "MERGE", + HTTP2_METHOD_MKACTIVITY: "MKACTIVITY", + HTTP2_METHOD_MKCALENDAR: "MKCALENDAR", + HTTP2_METHOD_MKCOL: "MKCOL", + HTTP2_METHOD_MKREDIRECTREF: "MKREDIRECTREF", + HTTP2_METHOD_MKWORKSPACE: "MKWORKSPACE", + HTTP2_METHOD_MOVE: "MOVE", + HTTP2_METHOD_OPTIONS: "OPTIONS", + HTTP2_METHOD_ORDERPATCH: "ORDERPATCH", + HTTP2_METHOD_PATCH: "PATCH", + HTTP2_METHOD_POST: "POST", + HTTP2_METHOD_PRI: "PRI", + HTTP2_METHOD_PROPFIND: "PROPFIND", + HTTP2_METHOD_PROPPATCH: "PROPPATCH", + HTTP2_METHOD_PUT: "PUT", + HTTP2_METHOD_REBIND: "REBIND", + HTTP2_METHOD_REPORT: "REPORT", + HTTP2_METHOD_SEARCH: "SEARCH", + HTTP2_METHOD_TRACE: "TRACE", + HTTP2_METHOD_UNBIND: "UNBIND", + HTTP2_METHOD_UNCHECKOUT: "UNCHECKOUT", + HTTP2_METHOD_UNLINK: "UNLINK", + HTTP2_METHOD_UNLOCK: "UNLOCK", + HTTP2_METHOD_UPDATE: "UPDATE", + HTTP2_METHOD_UPDATEREDIRECTREF: "UPDATEREDIRECTREF", + HTTP2_METHOD_VERSION_CONTROL: "VERSION-CONTROL", + HTTP_STATUS_CONTINUE: 100, + HTTP_STATUS_SWITCHING_PROTOCOLS: 101, + HTTP_STATUS_PROCESSING: 102, + HTTP_STATUS_EARLY_HINTS: 103, + HTTP_STATUS_OK: 200, + HTTP_STATUS_CREATED: 201, + HTTP_STATUS_ACCEPTED: 202, + HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION: 203, + HTTP_STATUS_NO_CONTENT: 204, + HTTP_STATUS_RESET_CONTENT: 205, + HTTP_STATUS_PARTIAL_CONTENT: 206, + HTTP_STATUS_MULTI_STATUS: 207, + HTTP_STATUS_ALREADY_REPORTED: 208, + HTTP_STATUS_IM_USED: 226, + HTTP_STATUS_MULTIPLE_CHOICES: 300, + HTTP_STATUS_MOVED_PERMANENTLY: 301, + HTTP_STATUS_FOUND: 302, + HTTP_STATUS_SEE_OTHER: 303, + HTTP_STATUS_NOT_MODIFIED: 304, + HTTP_STATUS_USE_PROXY: 305, + HTTP_STATUS_TEMPORARY_REDIRECT: 307, + HTTP_STATUS_PERMANENT_REDIRECT: 308, + HTTP_STATUS_BAD_REQUEST: 400, + HTTP_STATUS_UNAUTHORIZED: 401, + HTTP_STATUS_PAYMENT_REQUIRED: 402, + HTTP_STATUS_FORBIDDEN: 403, + HTTP_STATUS_NOT_FOUND: 404, + HTTP_STATUS_METHOD_NOT_ALLOWED: 405, + HTTP_STATUS_NOT_ACCEPTABLE: 406, + HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED: 407, + HTTP_STATUS_REQUEST_TIMEOUT: 408, + HTTP_STATUS_CONFLICT: 409, + HTTP_STATUS_GONE: 410, + HTTP_STATUS_LENGTH_REQUIRED: 411, + HTTP_STATUS_PRECONDITION_FAILED: 412, + HTTP_STATUS_PAYLOAD_TOO_LARGE: 413, + HTTP_STATUS_URI_TOO_LONG: 414, + HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE: 415, + HTTP_STATUS_RANGE_NOT_SATISFIABLE: 416, + HTTP_STATUS_EXPECTATION_FAILED: 417, + HTTP_STATUS_TEAPOT: 418, + HTTP_STATUS_MISDIRECTED_REQUEST: 421, + HTTP_STATUS_UNPROCESSABLE_ENTITY: 422, + HTTP_STATUS_LOCKED: 423, + HTTP_STATUS_FAILED_DEPENDENCY: 424, + HTTP_STATUS_TOO_EARLY: 425, + HTTP_STATUS_UPGRADE_REQUIRED: 426, + HTTP_STATUS_PRECONDITION_REQUIRED: 428, + HTTP_STATUS_TOO_MANY_REQUESTS: 429, + HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE: 431, + HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS: 451, + HTTP_STATUS_INTERNAL_SERVER_ERROR: 500, + HTTP_STATUS_NOT_IMPLEMENTED: 501, + HTTP_STATUS_BAD_GATEWAY: 502, + HTTP_STATUS_SERVICE_UNAVAILABLE: 503, + HTTP_STATUS_GATEWAY_TIMEOUT: 504, + HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED: 505, + HTTP_STATUS_VARIANT_ALSO_NEGOTIATES: 506, + HTTP_STATUS_INSUFFICIENT_STORAGE: 507, + HTTP_STATUS_LOOP_DETECTED: 508, + HTTP_STATUS_BANDWIDTH_LIMIT_EXCEEDED: 509, + HTTP_STATUS_NOT_EXTENDED: 510, + HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED: 511, +}; + + +const NoPayloadMethods = new Set([ + constants.HTTP2_METHOD_DELETE, + constants.HTTP2_METHOD_DELETE, + constants.HTTP2_METHOD_HEAD, +]); + +const ValidPseudoHeaders = new Set([ + constants.HTTP2_HEADER_STATUS, + constants.HTTP2_HEADER_METHOD, + constants.HTTP2_HEADER_AUTHORITY, + constants.HTTP2_HEADER_SCHEME, + constants.HTTP2_HEADER_PATH, + constants.HTTP2_HEADER_PROTOCOL, +]); + +const SingleValueHeaders = new Set([ + constants.HTTP2_HEADER_STATUS, + constants.HTTP2_HEADER_METHOD, + constants.HTTP2_HEADER_AUTHORITY, + constants.HTTP2_HEADER_SCHEME, + constants.HTTP2_HEADER_PATH, + constants.HTTP2_HEADER_PROTOCOL, + constants.HTTP2_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS, + constants.HTTP2_HEADER_ACCESS_CONTROL_MAX_AGE, + constants.HTTP2_HEADER_ACCESS_CONTROL_REQUEST_METHOD, + constants.HTTP2_HEADER_AGE, + constants.HTTP2_HEADER_AUTHORIZATION, + constants.HTTP2_HEADER_CONTENT_ENCODING, + constants.HTTP2_HEADER_CONTENT_LANGUAGE, + constants.HTTP2_HEADER_CONTENT_LENGTH, + constants.HTTP2_HEADER_CONTENT_LOCATION, + constants.HTTP2_HEADER_CONTENT_MD5, + constants.HTTP2_HEADER_CONTENT_RANGE, + constants.HTTP2_HEADER_CONTENT_TYPE, + constants.HTTP2_HEADER_DATE, + constants.HTTP2_HEADER_DNT, + constants.HTTP2_HEADER_ETAG, + constants.HTTP2_HEADER_EXPIRES, + constants.HTTP2_HEADER_FROM, + constants.HTTP2_HEADER_HOST, + constants.HTTP2_HEADER_IF_MATCH, + constants.HTTP2_HEADER_IF_MODIFIED_SINCE, + constants.HTTP2_HEADER_IF_NONE_MATCH, + constants.HTTP2_HEADER_IF_RANGE, + constants.HTTP2_HEADER_IF_UNMODIFIED_SINCE, + constants.HTTP2_HEADER_LAST_MODIFIED, + constants.HTTP2_HEADER_LOCATION, + constants.HTTP2_HEADER_MAX_FORWARDS, + constants.HTTP2_HEADER_PROXY_AUTHORIZATION, + constants.HTTP2_HEADER_RANGE, + constants.HTTP2_HEADER_REFERER, + constants.HTTP2_HEADER_RETRY_AFTER, + constants.HTTP2_HEADER_TK, + constants.HTTP2_HEADER_UPGRADE_INSECURE_REQUESTS, + constants.HTTP2_HEADER_USER_AGENT, + constants.HTTP2_HEADER_X_CONTENT_TYPE_OPTIONS, +]); type NativeHttp2HeaderValue = { @@ -31,10 +334,6 @@ class Http2Session extends EventEmitter { } -const http2 = { - sensitiveHeaders: Symbol("bun.http2.sensitiveHeaders"), -}; - class ClientStream extends EventEmitter { } @@ -42,13 +341,37 @@ class Http2Stream extends EventEmitter { } + +function streamErrorFromCode(code: number) { + const error = new Error(`Stream closed with error code ${code}`); + error.code = "ERR_HTTP2_STREAM_ERROR"; + error.errno = code; + return error; +} + +function assertPseudoHeader(name: string) { + if(ValidPseudoHeaders.has(name)) return; + + const error = new TypeError(`"${name}" is an invalid pseudoheader or is used incorrectly`); + error.code = "ERR_HTTP2_INVALID_PSEUDOHEADER"; + throw error;; +} + +function assertSingleValueHeader(name: string) { + if(SingleValueHeaders.has(name)) return; + + const error = new TypeError(`"${name}" is an invalid single value header`); + error.code = "ERR_HTTP2_INVALID_SINGLE_VALUE_HEADER"; + throw error; +} + const bunHTTP2Write = Symbol.for("::bunhttp2write::"); const bunHTTP2StreamResponded = Symbol.for("::bunhttp2hasResponded::"); const bunHTTP2StreamReadQueue = Symbol.for("::bunhttp2ReadQueue::"); function reduceToCompatibleHeaders(obj: any, currentValue: any) { let { name, value } = currentValue; - if(name === ":status") { + if(name === constants.HTTP2_HEADER_STATUS) { value = parseInt(value, 10); } const lastValue = obj[name]; @@ -82,12 +405,12 @@ class ClientHttp2Stream extends Duplex { } _destroy(err, callback) { - + //TODO: SEND RST_STREAM callback(err); } _final(callback) { - + //TODO: SEND RST_STREAM callback(); } @@ -139,11 +462,12 @@ class ClientHttp2Session extends Http2Session{ self.#connecions++; }, streamError(self: ClientHttp2Session, streamId: number, error: number){ - // var stream = self.#streams.get(streamId); - // if(stream) { - // stream.state = 2; - // stream.error = error; - // } + var stream = self.#streams.get(streamId); + const error_instance = streamErrorFromCode(error); + if(stream) { + stream.emit("error", error_instance); + } + self.emit("streamError", error_instance); }, streamEnd(self: ClientHttp2Session, streamId: number){ self.#connecions--; @@ -383,6 +707,39 @@ class ClientHttp2Session extends Http2Session{ } } + destroy(error: Error, code: number) { + if(!this.#socket) return; + // sent RST code ERR_HTTP2_STREAM_CANCEL to each stream here + // E('ERR_HTTP2_STREAM_CANCEL', function(error) { + // let msg = 'The pending stream has been canceled'; + // if (error) { + // this.cause = error; + // if (typeof error.message === 'string') + // msg += ` (caused by: ${error.message})`; + // } + // return msg; + // }, Error); + this.goaway(code || constants.NGHTTP2_INTERNAL_ERROR, 0, Buffer.alloc(0)); + // TODO: we can force end here but i think the best would be to wait for the goaway to be sent + this.#parser?.detach(); + this.#socket?.end(); + this.#parser = null; + this.#socket = null; + // this should not be needed since RST + GOAWAY should be sent + for(let [_,stream] of this.#streams){ + if(error) { + stream.emit("error", error); + } + stream.emit("close"); + } + + if(error){ + this.emit("error", error); + } + + this.emit("close"); + } + request(headers: any, options?: any) { if(!(headers instanceof Object)) { throw new Error("ERROR_HTTP2: Invalid headers"); @@ -394,41 +751,49 @@ class ClientHttp2Session extends Http2Session{ Object.keys(headers).forEach(key => { //@ts-ignore - if (key === http2.sensitiveHeaders) { + if (key === sensitiveHeaders) { const name = headers[0]; const values = headers[key]; - if(Array.isArray(values) === false) { - throw new Error("ERROR_HTTP2: Invalid sensitiveHeaders"); + if(!Array.isArray(values)) { + const error = new TypeError('headers[http2.neverIndex]'); + error.code = 'ERR_INVALID_ARG_VALUE'; + throw error; + } + if(name.startsWith(":")) { + assertPseudoHeader(name); } switch(name) { - case ":scheme": + case constants.HTTP2_HEADER_SCHEME: has_scheme = true; break; - case ":authority": + case constants.HTTP2_HEADER_AUTHORITY: has_authority = true; break; - case ":method": + case constants.HTTP2_HEADER_METHOD: method = values[1]?.toString(); break; }; - if(name === ":scheme") { + if(name === constants.HTTP2_HEADER_SCHEME) { has_scheme = true; - } else if(name === ":authority") { + } else if(name === constants.HTTP2_HEADER_AUTHORITY) { has_authority = true; } for(let i = 1; i < values.length; i++){ flat_headers.push({ name: name, value: values[i]?.toString(), neverIndex: true }); } } else { + if(key.startsWith(":")) { + assertPseudoHeader(key); + } switch(key) { - case ":scheme": + case constants.HTTP2_HEADER_SCHEME: has_scheme = true; break; - case ":authority": + case constants.HTTP2_HEADER_AUTHORITY: has_authority = true; break; - case ":method": + case constants.HTTP2_HEADER_METHOD: method = headers[key]?.toString() || "GET"; break; } @@ -439,6 +804,7 @@ class ClientHttp2Session extends Http2Session{ flat_headers.push({ name: key, value: value[i]?.toString(), neverIndex: true }); } } else { + assertSingleValueHeader(key); flat_headers.push({ name: key, value: value?.toString() }); } } @@ -466,8 +832,13 @@ class ClientHttp2Session extends Http2Session{ flat_headers.push({ name: ":method", value: method }); } + if(NoPayloadMethods.has(method.toUpperCase())) { + options = options || {}; + options.endStream = true; + } + let stream_id: number; - if(arguments.length === 1) { + if(typeof options === undefined) { stream_id = this.#parser.request(flat_headers); } else { stream_id = this.#parser.request(flat_headers, options); @@ -493,248 +864,6 @@ function connect(url: string | URL, options?: Settings) { } return ClientHttp2Session.connect(url); } -const constants = { - NGHTTP2_ERR_FRAME_SIZE_ERROR: -522, - NGHTTP2_SESSION_SERVER: 0, - NGHTTP2_SESSION_CLIENT: 1, - NGHTTP2_STREAM_STATE_IDLE: 1, - NGHTTP2_STREAM_STATE_OPEN: 2, - NGHTTP2_STREAM_STATE_RESERVED_LOCAL: 3, - NGHTTP2_STREAM_STATE_RESERVED_REMOTE: 4, - NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL: 5, - NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE: 6, - NGHTTP2_STREAM_STATE_CLOSED: 7, - NGHTTP2_FLAG_NONE: 0, - NGHTTP2_FLAG_END_STREAM: 1, - NGHTTP2_FLAG_END_HEADERS: 4, - NGHTTP2_FLAG_ACK: 1, - NGHTTP2_FLAG_PADDED: 8, - NGHTTP2_FLAG_PRIORITY: 32, - DEFAULT_SETTINGS_HEADER_TABLE_SIZE: 4096, - DEFAULT_SETTINGS_ENABLE_PUSH: 1, - DEFAULT_SETTINGS_MAX_CONCURRENT_STREAMS: 4294967295, - DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE: 65535, - DEFAULT_SETTINGS_MAX_FRAME_SIZE: 16384, - DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE: 65535, - DEFAULT_SETTINGS_ENABLE_CONNECT_PROTOCOL: 0, - MAX_MAX_FRAME_SIZE: 16777215, - MIN_MAX_FRAME_SIZE: 16384, - MAX_INITIAL_WINDOW_SIZE: 2147483647, - NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: 1, - NGHTTP2_SETTINGS_ENABLE_PUSH: 2, - NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: 3, - NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: 4, - NGHTTP2_SETTINGS_MAX_FRAME_SIZE: 5, - NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: 6, - NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: 8, - PADDING_STRATEGY_NONE: 0, - PADDING_STRATEGY_ALIGNED: 1, - PADDING_STRATEGY_MAX: 2, - PADDING_STRATEGY_CALLBACK: 1, - NGHTTP2_NO_ERROR: 0, - NGHTTP2_PROTOCOL_ERROR: 1, - NGHTTP2_INTERNAL_ERROR: 2, - NGHTTP2_FLOW_CONTROL_ERROR: 3, - NGHTTP2_SETTINGS_TIMEOUT: 4, - NGHTTP2_STREAM_CLOSED: 5, - NGHTTP2_FRAME_SIZE_ERROR: 6, - NGHTTP2_REFUSED_STREAM: 7, - NGHTTP2_CANCEL: 8, - NGHTTP2_COMPRESSION_ERROR: 9, - NGHTTP2_CONNECT_ERROR: 10, - NGHTTP2_ENHANCE_YOUR_CALM: 11, - NGHTTP2_INADEQUATE_SECURITY: 12, - NGHTTP2_HTTP_1_1_REQUIRED: 13, - NGHTTP2_DEFAULT_WEIGHT: 16, - HTTP2_HEADER_STATUS: ":status", - HTTP2_HEADER_METHOD: ":method", - HTTP2_HEADER_AUTHORITY: ":authority", - HTTP2_HEADER_SCHEME: ":scheme", - HTTP2_HEADER_PATH: ":path", - HTTP2_HEADER_PROTOCOL: ":protocol", - HTTP2_HEADER_ACCEPT_ENCODING: "accept-encoding", - HTTP2_HEADER_ACCEPT_LANGUAGE: "accept-language", - HTTP2_HEADER_ACCEPT_RANGES: "accept-ranges", - HTTP2_HEADER_ACCEPT: "accept", - HTTP2_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS: "access-control-allow-credentials", - HTTP2_HEADER_ACCESS_CONTROL_ALLOW_HEADERS: "access-control-allow-headers", - HTTP2_HEADER_ACCESS_CONTROL_ALLOW_METHODS: "access-control-allow-methods", - HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN: "access-control-allow-origin", - HTTP2_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS: "access-control-expose-headers", - HTTP2_HEADER_ACCESS_CONTROL_REQUEST_HEADERS: "access-control-request-headers", - HTTP2_HEADER_ACCESS_CONTROL_REQUEST_METHOD: "access-control-request-method", - HTTP2_HEADER_AGE: "age", - HTTP2_HEADER_AUTHORIZATION: "authorization", - HTTP2_HEADER_CACHE_CONTROL: "cache-control", - HTTP2_HEADER_CONNECTION: "connection", - HTTP2_HEADER_CONTENT_DISPOSITION: "content-disposition", - HTTP2_HEADER_CONTENT_ENCODING: "content-encoding", - HTTP2_HEADER_CONTENT_LENGTH: "content-length", - HTTP2_HEADER_CONTENT_TYPE: "content-type", - HTTP2_HEADER_COOKIE: "cookie", - HTTP2_HEADER_DATE: "date", - HTTP2_HEADER_ETAG: "etag", - HTTP2_HEADER_FORWARDED: "forwarded", - HTTP2_HEADER_HOST: "host", - HTTP2_HEADER_IF_MODIFIED_SINCE: "if-modified-since", - HTTP2_HEADER_IF_NONE_MATCH: "if-none-match", - HTTP2_HEADER_IF_RANGE: "if-range", - HTTP2_HEADER_LAST_MODIFIED: "last-modified", - HTTP2_HEADER_LINK: "link", - HTTP2_HEADER_LOCATION: "location", - HTTP2_HEADER_RANGE: "range", - HTTP2_HEADER_REFERER: "referer", - HTTP2_HEADER_SERVER: "server", - HTTP2_HEADER_SET_COOKIE: "set-cookie", - HTTP2_HEADER_STRICT_TRANSPORT_SECURITY: "strict-transport-security", - HTTP2_HEADER_TRANSFER_ENCODING: "transfer-encoding", - HTTP2_HEADER_TE: "te", - HTTP2_HEADER_UPGRADE_INSECURE_REQUESTS: "upgrade-insecure-requests", - HTTP2_HEADER_UPGRADE: "upgrade", - HTTP2_HEADER_USER_AGENT: "user-agent", - HTTP2_HEADER_VARY: "vary", - HTTP2_HEADER_X_CONTENT_TYPE_OPTIONS: "x-content-type-options", - HTTP2_HEADER_X_FRAME_OPTIONS: "x-frame-options", - HTTP2_HEADER_KEEP_ALIVE: "keep-alive", - HTTP2_HEADER_PROXY_CONNECTION: "proxy-connection", - HTTP2_HEADER_X_XSS_PROTECTION: "x-xss-protection", - HTTP2_HEADER_ALT_SVC: "alt-svc", - HTTP2_HEADER_CONTENT_SECURITY_POLICY: "content-security-policy", - HTTP2_HEADER_EARLY_DATA: "early-data", - HTTP2_HEADER_EXPECT_CT: "expect-ct", - HTTP2_HEADER_ORIGIN: "origin", - HTTP2_HEADER_PURPOSE: "purpose", - HTTP2_HEADER_TIMING_ALLOW_ORIGIN: "timing-allow-origin", - HTTP2_HEADER_X_FORWARDED_FOR: "x-forwarded-for", - HTTP2_HEADER_PRIORITY: "priority", - HTTP2_HEADER_ACCEPT_CHARSET: "accept-charset", - HTTP2_HEADER_ACCESS_CONTROL_MAX_AGE: "access-control-max-age", - HTTP2_HEADER_ALLOW: "allow", - HTTP2_HEADER_CONTENT_LANGUAGE: "content-language", - HTTP2_HEADER_CONTENT_LOCATION: "content-location", - HTTP2_HEADER_CONTENT_MD5: "content-md5", - HTTP2_HEADER_CONTENT_RANGE: "content-range", - HTTP2_HEADER_DNT: "dnt", - HTTP2_HEADER_EXPECT: "expect", - HTTP2_HEADER_EXPIRES: "expires", - HTTP2_HEADER_FROM: "from", - HTTP2_HEADER_IF_MATCH: "if-match", - HTTP2_HEADER_IF_UNMODIFIED_SINCE: "if-unmodified-since", - HTTP2_HEADER_MAX_FORWARDS: "max-forwards", - HTTP2_HEADER_PREFER: "prefer", - HTTP2_HEADER_PROXY_AUTHENTICATE: "proxy-authenticate", - HTTP2_HEADER_PROXY_AUTHORIZATION: "proxy-authorization", - HTTP2_HEADER_REFRESH: "refresh", - HTTP2_HEADER_RETRY_AFTER: "retry-after", - HTTP2_HEADER_TRAILER: "trailer", - HTTP2_HEADER_TK: "tk", - HTTP2_HEADER_VIA: "via", - HTTP2_HEADER_WARNING: "warning", - HTTP2_HEADER_WWW_AUTHENTICATE: "www-authenticate", - HTTP2_HEADER_HTTP2_SETTINGS: "http2-settings", - HTTP2_METHOD_ACL: "ACL", - HTTP2_METHOD_BASELINE_CONTROL: "BASELINE-CONTROL", - HTTP2_METHOD_BIND: "BIND", - HTTP2_METHOD_CHECKIN: "CHECKIN", - HTTP2_METHOD_CHECKOUT: "CHECKOUT", - HTTP2_METHOD_CONNECT: "CONNECT", - HTTP2_METHOD_COPY: "COPY", - HTTP2_METHOD_DELETE: "DELETE", - HTTP2_METHOD_GET: "GET", - HTTP2_METHOD_HEAD: "HEAD", - HTTP2_METHOD_LABEL: "LABEL", - HTTP2_METHOD_LINK: "LINK", - HTTP2_METHOD_LOCK: "LOCK", - HTTP2_METHOD_MERGE: "MERGE", - HTTP2_METHOD_MKACTIVITY: "MKACTIVITY", - HTTP2_METHOD_MKCALENDAR: "MKCALENDAR", - HTTP2_METHOD_MKCOL: "MKCOL", - HTTP2_METHOD_MKREDIRECTREF: "MKREDIRECTREF", - HTTP2_METHOD_MKWORKSPACE: "MKWORKSPACE", - HTTP2_METHOD_MOVE: "MOVE", - HTTP2_METHOD_OPTIONS: "OPTIONS", - HTTP2_METHOD_ORDERPATCH: "ORDERPATCH", - HTTP2_METHOD_PATCH: "PATCH", - HTTP2_METHOD_POST: "POST", - HTTP2_METHOD_PRI: "PRI", - HTTP2_METHOD_PROPFIND: "PROPFIND", - HTTP2_METHOD_PROPPATCH: "PROPPATCH", - HTTP2_METHOD_PUT: "PUT", - HTTP2_METHOD_REBIND: "REBIND", - HTTP2_METHOD_REPORT: "REPORT", - HTTP2_METHOD_SEARCH: "SEARCH", - HTTP2_METHOD_TRACE: "TRACE", - HTTP2_METHOD_UNBIND: "UNBIND", - HTTP2_METHOD_UNCHECKOUT: "UNCHECKOUT", - HTTP2_METHOD_UNLINK: "UNLINK", - HTTP2_METHOD_UNLOCK: "UNLOCK", - HTTP2_METHOD_UPDATE: "UPDATE", - HTTP2_METHOD_UPDATEREDIRECTREF: "UPDATEREDIRECTREF", - HTTP2_METHOD_VERSION_CONTROL: "VERSION-CONTROL", - HTTP_STATUS_CONTINUE: 100, - HTTP_STATUS_SWITCHING_PROTOCOLS: 101, - HTTP_STATUS_PROCESSING: 102, - HTTP_STATUS_EARLY_HINTS: 103, - HTTP_STATUS_OK: 200, - HTTP_STATUS_CREATED: 201, - HTTP_STATUS_ACCEPTED: 202, - HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION: 203, - HTTP_STATUS_NO_CONTENT: 204, - HTTP_STATUS_RESET_CONTENT: 205, - HTTP_STATUS_PARTIAL_CONTENT: 206, - HTTP_STATUS_MULTI_STATUS: 207, - HTTP_STATUS_ALREADY_REPORTED: 208, - HTTP_STATUS_IM_USED: 226, - HTTP_STATUS_MULTIPLE_CHOICES: 300, - HTTP_STATUS_MOVED_PERMANENTLY: 301, - HTTP_STATUS_FOUND: 302, - HTTP_STATUS_SEE_OTHER: 303, - HTTP_STATUS_NOT_MODIFIED: 304, - HTTP_STATUS_USE_PROXY: 305, - HTTP_STATUS_TEMPORARY_REDIRECT: 307, - HTTP_STATUS_PERMANENT_REDIRECT: 308, - HTTP_STATUS_BAD_REQUEST: 400, - HTTP_STATUS_UNAUTHORIZED: 401, - HTTP_STATUS_PAYMENT_REQUIRED: 402, - HTTP_STATUS_FORBIDDEN: 403, - HTTP_STATUS_NOT_FOUND: 404, - HTTP_STATUS_METHOD_NOT_ALLOWED: 405, - HTTP_STATUS_NOT_ACCEPTABLE: 406, - HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED: 407, - HTTP_STATUS_REQUEST_TIMEOUT: 408, - HTTP_STATUS_CONFLICT: 409, - HTTP_STATUS_GONE: 410, - HTTP_STATUS_LENGTH_REQUIRED: 411, - HTTP_STATUS_PRECONDITION_FAILED: 412, - HTTP_STATUS_PAYLOAD_TOO_LARGE: 413, - HTTP_STATUS_URI_TOO_LONG: 414, - HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE: 415, - HTTP_STATUS_RANGE_NOT_SATISFIABLE: 416, - HTTP_STATUS_EXPECTATION_FAILED: 417, - HTTP_STATUS_TEAPOT: 418, - HTTP_STATUS_MISDIRECTED_REQUEST: 421, - HTTP_STATUS_UNPROCESSABLE_ENTITY: 422, - HTTP_STATUS_LOCKED: 423, - HTTP_STATUS_FAILED_DEPENDENCY: 424, - HTTP_STATUS_TOO_EARLY: 425, - HTTP_STATUS_UPGRADE_REQUIRED: 426, - HTTP_STATUS_PRECONDITION_REQUIRED: 428, - HTTP_STATUS_TOO_MANY_REQUESTS: 429, - HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE: 431, - HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS: 451, - HTTP_STATUS_INTERNAL_SERVER_ERROR: 500, - HTTP_STATUS_NOT_IMPLEMENTED: 501, - HTTP_STATUS_BAD_GATEWAY: 502, - HTTP_STATUS_SERVICE_UNAVAILABLE: 503, - HTTP_STATUS_GATEWAY_TIMEOUT: 504, - HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED: 505, - HTTP_STATUS_VARIANT_ALSO_NEGOTIATES: 506, - HTTP_STATUS_INSUFFICIENT_STORAGE: 507, - HTTP_STATUS_LOOP_DETECTED: 508, - HTTP_STATUS_BANDWIDTH_LIMIT_EXCEEDED: 509, - HTTP_STATUS_NOT_EXTENDED: 510, - HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED: 511, -}; function createServer() { throwNotImplemented("node:http2 createServer", 887); @@ -760,7 +889,6 @@ function getPackedSettings() { function getUnpackedSettings() { return Buffer.alloc(0); } -const sensitiveHeaders = Symbol.for("nodejs.http2.sensitiveHeaders"); function Http2ServerRequest() { throwNotImplemented("node:http2 Http2ServerRequest", 887); } |