aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar cirospaciari <ciro.spaciari@gmail.com> 2023-09-09 13:56:29 -0300
committerGravatar cirospaciari <ciro.spaciari@gmail.com> 2023-09-09 13:56:29 -0300
commitd2008abc465201a9c3feffbc328597c8a6fe2b67 (patch)
treea8401e641cb215e03ab8113267fb943802d27773
parent603fcc423bfd17178a16f3eb945ede0bcd1e7d65 (diff)
downloadbun-ciro/http2.tar.gz
bun-ciro/http2.tar.zst
bun-ciro/http2.zip
more thingsciro/http2
-rw-r--r--src/js/node/http2.ts662
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);
}