aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/bun.js')
-rw-r--r--src/bun.js/api/bun/h2_frame_parser.zig1483
-rw-r--r--src/bun.js/api/bun/lshpack.translated.zig442
-rw-r--r--src/bun.js/api/h2.classes.ts20
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h3
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h3
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h6
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h7
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses.cpp130
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses.h50
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.cpp9
-rw-r--r--src/bun.js/bindings/generated_classes.zig12
11 files changed, 2092 insertions, 73 deletions
diff --git a/src/bun.js/api/bun/h2_frame_parser.zig b/src/bun.js/api/bun/h2_frame_parser.zig
index 2041aad69..23e4d7eb2 100644
--- a/src/bun.js/api/bun/h2_frame_parser.zig
+++ b/src/bun.js/api/bun/h2_frame_parser.zig
@@ -6,6 +6,7 @@ const Allocator = std.mem.Allocator;
const JSC = bun.JSC;
const MutableString = bun.MutableString;
const native_endian = @import("builtin").target.cpu.arch.endian();
+const lshpack = @import("./lshpack.translated.zig");
const JSValue = JSC.JSValue;
@@ -24,6 +25,20 @@ const FrameType = enum(u8) {
HTTP_FRAME_CONTINUATION = 0x09,
};
+const PingFrameFlags = enum(u8) {
+ ACK = 0x1,
+};
+const DataFrameFlags = enum(u8) {
+ END_STREAM = 0x1,
+ PADDED = 0x8,
+};
+const HeadersFrameFlags = enum(u8) {
+ END_STREAM = 0x1,
+ END_HEADERS = 0x4,
+ PADDED = 0x8,
+ PRIORITY = 0x20,
+};
+
const ErrorCode = enum(u32) {
NO_ERROR = 0x0,
PROTOCOL_ERROR = 0x1,
@@ -57,6 +72,50 @@ const UInt31WithReserved = packed struct(u32) {
pub fn from(value: u32) UInt31WithReserved {
return @bitCast(value);
}
+
+ pub fn toUInt32(value: UInt31WithReserved) u32 {
+ return @bitCast(value);
+ }
+
+ pub inline fn fromBytes(src: []const u8) UInt31WithReserved {
+ var dst: u32 = 0;
+ @memcpy(@as(*[4]u8, @ptrCast(&dst)), src);
+ if (native_endian != .Big) {
+ dst = @byteSwap(dst);
+ }
+ return @bitCast(dst);
+ }
+
+ pub inline fn write(this: UInt31WithReserved, comptime Writer: type, writer: Writer) void {
+ var value: u32 = @bitCast(this);
+ if (native_endian != .Big) {
+ value = @byteSwap(value);
+ }
+
+ _ = writer.write(std.mem.asBytes(&value)) catch 0;
+ }
+};
+
+const StreamPriority = packed struct(u40) {
+ streamIdentifier: u32 = 0,
+ weight: u8 = 0,
+
+ pub const byteSize: usize = 5;
+ pub inline fn write(this: *StreamPriority, comptime Writer: type, writer: Writer) void {
+ var swap = this.*;
+ if (native_endian != .Big) {
+ std.mem.byteSwapAllFields(StreamPriority, &swap);
+ }
+
+ _ = writer.write(std.mem.asBytes(&swap)[0..StreamPriority.byteSize]) catch 0;
+ }
+
+ pub inline fn from(dst: *StreamPriority, src: []const u8) void {
+ @memcpy(@as(*[StreamPriority.byteSize]u8, @ptrCast(dst)), src);
+ if (native_endian != .Big) {
+ std.mem.byteSwapAllFields(StreamPriority, dst);
+ }
+ }
};
const FrameHeader = packed struct(u72) {
@@ -127,7 +186,7 @@ const FullSettingsPayload = packed struct(u288) {
}
pub fn updateWith(this: *FullSettingsPayload, option: SettingsPayloadUnit) void {
- switch (option.type) {
+ switch (@as(SettingsType, @enumFromInt(option.type))) {
.SETTINGS_HEADER_TABLE_SIZE => this.headerTableSize = option.value,
.SETTINGS_ENABLE_PUSH => this.enablePush = option.value,
.SETTINGS_MAX_CONCURRENT_STREAMS => this.maxConcurrentStreams = option.value,
@@ -156,13 +215,19 @@ const Handlers = struct {
onStreamData: JSC.JSValue = .zero,
onRemoteSettings: JSC.JSValue = .zero,
onLocalSettings: JSC.JSValue = .zero,
+ onWantTrailers: JSC.JSValue = .zero,
+ onPing: JSC.JSValue = .zero,
+ onEnd: JSC.JSValue = .zero,
+ onGoAway: JSC.JSValue = .zero,
+ onFrameError: JSC.JSValue = .zero,
+
binary_type: BinaryType = .Buffer,
vm: *JSC.VirtualMachine,
globalObject: *JSC.JSGlobalObject,
- pub fn callEventHandler(this: *Handlers, comptime event: []const u8, thisValue: JSValue, data: []const JSValue) bool {
- const callback = @field(this, event);
+ pub fn callEventHandler(this: *Handlers, comptime event: @Type(.EnumLiteral), thisValue: JSValue, data: []const JSValue) bool {
+ const callback = @field(this, @tagName(event));
if (callback == .zero) {
return false;
}
@@ -211,9 +276,15 @@ const Handlers = struct {
.{ "onStreamError", "streamError" },
.{ "onRemoteSettings", "remoteSettings" },
.{ "onLocalSettings", "localSettings" },
+ .{ "onWantTrailers", "wantTrailers" },
+ .{ "onPing", "ping" },
+ .{ "onEnd", "end" },
.{ "onError", "error" },
+ .{ "onGoAway", "goaway" },
+ .{ "onFrameError", "frameError" },
.{ "onWrite", "write" },
};
+
inline for (pairs) |pair| {
if (opts.getTruthy(globalObject, pair.@"1")) |callback_value| {
if (!callback_value.isCell() or !callback_value.isCallable(globalObject.vm())) {
@@ -247,6 +318,7 @@ const Handlers = struct {
pub fn unprotect(this: *Handlers) void {
this.onError.unprotect();
+ this.onGoAway.unprotect();
this.onWrite.unprotect();
this.onStreamError.unprotect();
this.onStreamStart.unprotect();
@@ -256,6 +328,10 @@ const Handlers = struct {
this.onStreamError.unprotect();
this.onLocalSettings.unprotect();
this.onRemoteSettings.unprotect();
+ this.onWantTrailers.unprotect();
+ this.onPing.unprotect();
+ this.onEnd.unprotect();
+ this.onFrameError.unprotect();
}
pub fn clear(this: *Handlers) void {
@@ -269,6 +345,11 @@ const Handlers = struct {
this.onStreamError = .zero;
this.onLocalSettings = .zero;
this.onRemoteSettings = .zero;
+ this.onWantTrailers = .zero;
+ this.onPing = .zero;
+ this.onEnd = .zero;
+ this.onGoAway = .zero;
+ this.onFrameError = .zero;
}
pub fn protect(this: *Handlers) void {
@@ -282,12 +363,22 @@ const Handlers = struct {
this.onStreamError.protect();
this.onLocalSettings.protect();
this.onRemoteSettings.protect();
+ this.onWantTrailers.protect();
+ this.onEnd.protect();
+ this.onGoAway.protect();
+ this.onFrameError.protect();
}
};
pub const H2FrameParser = struct {
pub const log = Output.scoped(.H2FrameParser, false);
pub usingnamespace JSC.Codegen.JSH2FrameParser;
+ const MAX_WINDOW_SIZE = 2147483647;
+ const MAX_HEADER_TABLE_SIZE = 4294967295;
+ const MAX_STREAM_ID = 2147483647;
+ const WINDOW_INCREMENT_SIZE = 65536;
+ const MAX_HPACK_HEADER_SIZE = 65536;
+ const MAX_FRAME_SIZE = 16777215;
strong_ctx: JSC.Strong = .{},
allocator: Allocator,
@@ -301,12 +392,336 @@ pub const H2FrameParser = struct {
remainingLength: i32 = 0,
// buffer if more data is needed for the current frame
readBuffer: MutableString,
+ // current window size for the connection
+ windowSize: u32 = 65535,
+ // used window size for the connection
+ usedWindowSize: u32 = 0,
+ lastStreamID: u32 = 0,
+ streams: bun.U32HashMap(Stream),
+ const Stream = struct {
+ id: u32 = 0,
+ state: enum(u8) {
+ IDLE = 0,
+ RESERVED_LOCAL = 1,
+ RESERVED_REMOTE = 2,
+ OPEN = 3,
+ HALF_CLOSED_LOCAL = 4,
+ HALF_CLOSED_REMOTE = 5,
+ CLOSED = 6,
+ } = .IDLE,
+ waitForTrailers: bool = false,
+ endAfterHeaders: bool = false,
+ isWaitingMoreHeaders: bool = false,
+ padding: ?u8 = 0,
+ rstCode: u32 = 0,
+ sentHeaders: JSC.JSValue = .zero,
+ streamDependency: u32 = 0,
+ exclusive: bool = false,
+ weight: u16 = 36,
+ // current window size for the stream
+ windowSize: u32 = 65535,
+ // used window size for the stream
+ usedWindowSize: u32 = 0,
+
+ decoder: lshpack.lshpack_dec = undefined,
+ encoder: lshpack.lshpack_enc = undefined,
+ signal: ?*JSC.WebCore.AbortSignal = null,
+
+ pub fn init(streamIdentifier: u32, initialWindowSize: u32, headerTableSize: u32) Stream {
+ var stream = Stream{
+ .id = streamIdentifier,
+ .state = .OPEN,
+ .windowSize = initialWindowSize,
+ .usedWindowSize = 0,
+ .weight = 36,
+ };
+
+ if (lshpack.lshpack_enc_init(&stream.encoder) != 0) {
+ @panic("OOM");
+ }
+ lshpack.lshpack_dec_init(&stream.decoder);
+ lshpack.lshpack_enc_set_max_capacity(&stream.encoder, headerTableSize);
+ lshpack.lshpack_dec_set_max_capacity(&stream.decoder, headerTableSize);
+
+ return stream;
+ }
+
+ pub fn canReceiveData(this: *Stream) bool {
+ return switch (this.state) {
+ .IDLE, .RESERVED_LOCAL, .RESERVED_REMOTE, .OPEN, .HALF_CLOSED_LOCAL => false,
+ .HALF_CLOSED_REMOTE, .CLOSED => true,
+ };
+ }
- pub fn dispatch(this: *H2FrameParser, comptime event: []const u8, value: JSC.JSValue) void {
+ pub fn canSendData(this: *Stream) bool {
+ return switch (this.state) {
+ .IDLE, .RESERVED_LOCAL, .RESERVED_REMOTE, .OPEN, .HALF_CLOSED_REMOTE => false,
+ .HALF_CLOSED_LOCAL, .CLOSED => true,
+ };
+ }
+
+ pub fn attachSignal(this: *Stream, signal: *JSC.WebCore.AbortSignal) void {
+ _ = signal.ref();
+ this.signal = signal.listen(Stream, this, Stream.abortListener);
+ }
+
+ pub fn abortListener(this: *Stream, reason: JSValue) void {
+ log("abortListener", .{});
+ reason.ensureStillAlive();
+ _ = this;
+ //TODO: send RST_STREAM
+ }
+
+ const HeaderValue = struct {
+ name: []const u8,
+ value: []const u8,
+ next: usize,
+ };
+
+ pub fn decode(this: *Stream, header_buffer: *[MAX_HPACK_HEADER_SIZE]u8, src_buffer: []const u8) !HeaderValue {
+ var xhdr: lshpack.lsxpack_header = .{};
+
+ lshpack.lsxpack_header_prepare_decode(&xhdr, header_buffer.ptr, 0, header_buffer.len);
+ var start = @intFromPtr(src_buffer.ptr);
+ var src = src_buffer.ptr;
+ if (lshpack.lshpack_dec_decode(&this.decoder, &src, @ptrFromInt(start + src_buffer.len), &xhdr) != 0) {
+ return error.UnableToDecode;
+ }
+ const name = lshpack.lsxpack_header_get_name(&xhdr);
+ if (name.len == 0) {
+ return error.EmptyHeaderName;
+ }
+ return .{
+ .name = name,
+ .value = lshpack.lsxpack_header_get_value(&xhdr),
+ .next = @intFromPtr(src) - start,
+ };
+ }
+
+ pub fn encode(this: *Stream, header_buffer: *[MAX_HPACK_HEADER_SIZE]u8, dst_buffer: []const u8, name: []const u8, value: []const u8, never_index: bool) !usize {
+ var xhdr: lshpack.lsxpack_header = .{ .indexed_type = if (never_index) 2 else 0 };
+ const size = name.len + value.len;
+ if (size > MAX_HPACK_HEADER_SIZE) {
+ return error.HeaderTooLarge;
+ }
+
+ @memcpy(header_buffer[0..name.len], name);
+ @memcpy(header_buffer[name.len..size], value);
+ lshpack.lsxpack_header_set_offset2(&xhdr, header_buffer.ptr, 0, name.len, name.len, value.len);
+ if (never_index) {
+ xhdr.indexed_type = 2;
+ }
+
+ var start = @intFromPtr(dst_buffer.ptr);
+ const ptr = lshpack.lshpack_enc_encode(&this.encoder, dst_buffer.ptr, @ptrFromInt(start + dst_buffer.len), &xhdr);
+ const end = @intFromPtr(ptr) - start;
+ if (end > 0) {
+ return end;
+ }
+ return error.UnableToEncode;
+ }
+
+ pub fn deinit(this: *Stream) void {
+ lshpack.lshpack_dec_cleanup(&this.decoder);
+ lshpack.lshpack_enc_cleanup(&this.encoder);
+ if (this.signal) |signal| {
+ this.signal = null;
+ signal.detach(this);
+ }
+ this.sentHeaders.unprotect();
+ this.sentHeaders = .zero;
+ }
+ };
+
+ /// Calculate the new window size for the connection and the stream
+ /// https://datatracker.ietf.org/doc/html/rfc7540#section-6.9.1
+ fn ajustWindowSize(this: *H2FrameParser, stream: ?*Stream, payloadSize: u32) void {
+ this.usedWindowSize += payloadSize;
+ if (this.usedWindowSize >= this.windowSize) {
+ var increment_size: u32 = WINDOW_INCREMENT_SIZE;
+ var new_size = this.windowSize + increment_size;
+ if (new_size > MAX_WINDOW_SIZE) {
+ new_size = MAX_WINDOW_SIZE;
+ increment_size = this.windowSize - MAX_WINDOW_SIZE;
+ }
+ if (new_size == this.windowSize) {
+ this.sendGoAway(0, .FLOW_CONTROL_ERROR, "Window size overflow", this.lastStreamID);
+ return;
+ }
+ this.windowSize = new_size;
+ this.sendWindowUpdate(0, UInt31WithReserved.from(increment_size));
+ }
+
+ if (stream) |s| {
+ s.usedWindowSize += payloadSize;
+ if (s.usedWindowSize >= s.windowSize) {
+ var increment_size: u32 = WINDOW_INCREMENT_SIZE;
+ var new_size = s.windowSize + increment_size;
+ if (new_size > MAX_WINDOW_SIZE) {
+ new_size = MAX_WINDOW_SIZE;
+ increment_size = s.windowSize - MAX_WINDOW_SIZE;
+ }
+ s.windowSize = new_size;
+ this.sendWindowUpdate(s.id, UInt31WithReserved.from(increment_size));
+ }
+ }
+ }
+
+ pub fn setSettings(this: *H2FrameParser, settings: FullSettingsPayload) void {
+ var buffer: [FrameHeader.byteSize + FullSettingsPayload.byteSize]u8 = undefined;
+ @memset(&buffer, 0);
+ var stream = std.io.fixedBufferStream(&buffer);
+ const writer = stream.writer();
+ var settingsHeader: FrameHeader = .{
+ .type = @intFromEnum(FrameType.HTTP_FRAME_SETTINGS),
+ .flags = 0,
+ .streamIdentifier = 0,
+ .length = 36,
+ };
+ settingsHeader.write(@TypeOf(writer), writer);
+ this.localSettings = settings;
+ this.localSettings.write(@TypeOf(writer), writer);
+ this.write(&buffer);
+ this.ajustWindowSize(null, @intCast(buffer.len));
+ }
+
+ pub fn endStream(this: *H2FrameParser, stream: *Stream, rstCode: ErrorCode) void {
+ var buffer: [FrameHeader.byteSize + 4]u8 = undefined;
+ @memset(&buffer, 0);
+ var writerStream = std.io.fixedBufferStream(&buffer);
+ const writer = writerStream.writer();
+
+ var frame: FrameHeader = .{
+ .type = @intFromEnum(FrameType.HTTP_FRAME_RST_STREAM),
+ .flags = 0,
+ .streamIdentifier = stream.id,
+ .length = 4,
+ };
+ frame.write(@TypeOf(writer), writer);
+ var value: u32 = @intFromEnum(rstCode);
+ stream.rstCode = value;
+ if (native_endian != .Big) {
+ value = @byteSwap(value);
+ }
+ _ = writer.write(std.mem.asBytes(&value)) catch 0;
+
+ stream.state = .CLOSED;
+ if (rstCode == .NO_ERROR) {
+ this.dispatchWithExtra(.onStreamEnd, JSC.JSValue.jsNumber(stream.id), JSC.JSValue.jsUndefined());
+ } else {
+ this.dispatchWithExtra(.onStreamError, JSC.JSValue.jsNumber(stream.id), JSC.JSValue.jsNumber(value));
+ }
+
+ this.write(&buffer);
+ }
+
+ pub fn sendGoAway(this: *H2FrameParser, streamIdentifier: u32, rstCode: ErrorCode, debug_data: []const u8, lastStreamID: u32) void {
+ var buffer: [FrameHeader.byteSize + 8]u8 = undefined;
+ @memset(&buffer, 0);
+ var stream = std.io.fixedBufferStream(&buffer);
+ const writer = stream.writer();
+
+ var frame: FrameHeader = .{
+ .type = @intFromEnum(FrameType.HTTP_FRAME_GOAWAY),
+ .flags = 0,
+ .streamIdentifier = streamIdentifier,
+ .length = @intCast(8 + debug_data.len),
+ };
+ frame.write(@TypeOf(writer), writer);
+ var last_id = UInt31WithReserved.from(lastStreamID);
+ last_id.write(@TypeOf(writer), writer);
+ var value: u32 = @intFromEnum(rstCode);
+ if (native_endian != .Big) {
+ value = @byteSwap(value);
+ }
+ _ = writer.write(std.mem.asBytes(&value)) catch 0;
+
+ this.write(&buffer);
+ if (debug_data.len > 0) {
+ this.write(debug_data);
+ }
+ const chunk = this.handlers.binary_type.toJS(debug_data, this.handlers.globalObject);
+ if (rstCode != .NO_ERROR) {
+ this.dispatchWith2Extra(.onError, JSC.JSValue.jsNumber(value), JSC.JSValue.jsNumber(this.lastStreamID), chunk);
+ }
+ this.dispatchWithExtra(.onEnd, JSC.JSValue.jsNumber(this.lastStreamID), chunk);
+ }
+
+ pub fn sendPing(this: *H2FrameParser, ack: bool, payload: []const u8) void {
+ var buffer: [FrameHeader.byteSize + 8]u8 = undefined;
+ @memset(&buffer, 0);
+ var stream = std.io.fixedBufferStream(&buffer);
+ const writer = stream.writer();
+ var frame = FrameHeader{
+ .type = @intFromEnum(FrameType.HTTP_FRAME_PING),
+ .flags = if (ack) @intFromEnum(PingFrameFlags.ACK) else 0,
+ .streamIdentifier = 0,
+ .length = 8,
+ };
+ frame.write(@TypeOf(writer), writer);
+ _ = writer.write(payload) catch 0;
+ this.write(&buffer);
+ }
+
+ pub fn sendPrefaceAndSettings(this: *H2FrameParser) void {
+ // PREFACE + Settings Frame
+ var preface_buffer: [24 + FrameHeader.byteSize + FullSettingsPayload.byteSize]u8 = undefined;
+ @memset(&preface_buffer, 0);
+ var preface_stream = std.io.fixedBufferStream(&preface_buffer);
+ const writer = preface_stream.writer();
+ _ = writer.write("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") catch 0;
+ var settingsHeader: FrameHeader = .{
+ .type = @intFromEnum(FrameType.HTTP_FRAME_SETTINGS),
+ .flags = 0,
+ .streamIdentifier = 0,
+ .length = 36,
+ };
+ settingsHeader.write(@TypeOf(writer), writer);
+ this.localSettings.write(@TypeOf(writer), writer);
+ this.write(&preface_buffer);
+ this.ajustWindowSize(null, @intCast(preface_buffer.len));
+ }
+
+ pub fn sendWindowUpdate(this: *H2FrameParser, streamIdentifier: u32, windowSize: UInt31WithReserved) void {
+ var buffer: [FrameHeader.byteSize + 4]u8 = undefined;
+ @memset(&buffer, 0);
+ var stream = std.io.fixedBufferStream(&buffer);
+ const writer = stream.writer();
+ var settingsHeader: FrameHeader = .{
+ .type = @intFromEnum(FrameType.HTTP_FRAME_WINDOW_UPDATE),
+ .flags = 0,
+ .streamIdentifier = streamIdentifier,
+ .length = 4,
+ };
+ settingsHeader.write(@TypeOf(writer), writer);
+ // always clear reserved bit
+ const cleanWindowSize: UInt31WithReserved = .{
+ .reserved = false,
+ .uint31 = windowSize.uint31,
+ };
+ cleanWindowSize.write(@TypeOf(writer), writer);
+ this.write(&buffer);
+ }
+
+ pub fn dispatch(this: *H2FrameParser, comptime event: @Type(.EnumLiteral), value: JSC.JSValue) void {
JSC.markBinding(@src());
const ctx_value = this.strong_ctx.get() orelse JSC.JSValue.jsUndefined();
value.ensureStillAlive();
- _ = this.handlers.callEvent(event, ctx_value, &[_]JSC.JSValue{ ctx_value, value });
+ _ = this.handlers.callEventHandler(event, ctx_value, &[_]JSC.JSValue{ ctx_value, value });
+ }
+
+ pub fn dispatchWithExtra(this: *H2FrameParser, comptime event: @Type(.EnumLiteral), value: JSC.JSValue, extra: JSC.JSValue) void {
+ JSC.markBinding(@src());
+ const ctx_value = this.strong_ctx.get() orelse JSC.JSValue.jsUndefined();
+ value.ensureStillAlive();
+ _ = this.handlers.callEventHandler(event, ctx_value, &[_]JSC.JSValue{ ctx_value, value, extra });
+ }
+
+ pub fn dispatchWith2Extra(this: *H2FrameParser, comptime event: @Type(.EnumLiteral), value: JSC.JSValue, extra: JSC.JSValue, extra2: JSC.JSValue) void {
+ JSC.markBinding(@src());
+ const ctx_value = this.strong_ctx.get() orelse JSC.JSValue.jsUndefined();
+ value.ensureStillAlive();
+ _ = this.handlers.callEventHandler(event, ctx_value, &[_]JSC.JSValue{ ctx_value, value, extra, extra2 });
}
pub fn write(this: *H2FrameParser, bytes: []const u8) void {
@@ -326,71 +741,422 @@ pub const H2FrameParser = struct {
return JSC.JSValue.jsUndefined();
}
+
+ const Payload = struct {
+ data: []const u8,
+ end: usize,
+ };
+
+ // Default handling for payload is buffering it
+ // for data frames we use another strategy
+ pub fn handleIncommingPayload(this: *H2FrameParser, data: []const u8, streamIdentifier: u32) ?Payload {
+ const end: usize = @min(@as(usize, @intCast(this.remainingLength)), data.len);
+ const payload = data[0..end];
+ this.remainingLength -= @intCast(end);
+ if (this.remainingLength > 0) {
+ // buffer more data
+ _ = this.readBuffer.appendSlice(payload) catch @panic("OOM");
+ return null;
+ } else if (this.remainingLength < 0) {
+ this.sendGoAway(streamIdentifier, ErrorCode.FRAME_SIZE_ERROR, "Invalid frame size", this.lastStreamID);
+ return null;
+ }
+
+ this.currentFrame = null;
+ return .{
+ .data = payload,
+ .end = end,
+ };
+ }
+
+ pub fn handleWindowUpdateFrame(this: *H2FrameParser, frame: FrameHeader, data: []const u8, stream: ?*Stream) usize {
+ // must be always 4 bytes (https://datatracker.ietf.org/doc/html/rfc7540#section-6.9)
+ if (frame.length != 4) {
+ this.sendGoAway(frame.streamIdentifier, ErrorCode.FRAME_SIZE_ERROR, "Invalid dataframe frame size", this.lastStreamID);
+ return data.len;
+ }
+
+ if (handleIncommingPayload(this, data, frame.streamIdentifier)) |content| {
+ const payload = content.data;
+ const windowSizeIncrement = UInt31WithReserved.fromBytes(payload);
+ this.readBuffer.reset();
+ // we automatically send a window update when receiving one
+ this.sendWindowUpdate(frame.streamIdentifier, windowSizeIncrement);
+ if (stream) |s| {
+ s.windowSize += windowSizeIncrement.uint31;
+ } else {
+ this.windowSize += windowSizeIncrement.uint31;
+ }
+ log("windowSizeIncrement stream {} value {}", .{ frame.streamIdentifier, windowSizeIncrement.uint31 });
+ return content.end;
+ }
+ // needs more data
+ return data.len;
+ }
+
+ pub fn decodeHeaderBlock(this: *H2FrameParser, payload: []const u8, stream: *Stream, flags: u8) void {
+ log("decodeHeaderBlock", .{});
+
+ var header_buffer: [MAX_HPACK_HEADER_SIZE]u8 = undefined;
+ var offset: usize = 0;
+
+ const globalObject = this.handlers.globalObject;
+
+ const headers = JSC.JSValue.createEmptyArray(globalObject, 0);
+
+ while (true) {
+ const header = stream.decode(&header_buffer, payload[offset..]) catch break;
+ offset += header.next;
+ var result = JSValue.createEmptyObject(globalObject, 2);
+ const name = JSC.ZigString.fromUTF8(header.name).toValueGC(globalObject);
+ const value = JSC.ZigString.fromUTF8(header.value).toValueGC(globalObject);
+ result.put(globalObject, JSC.ZigString.static("name"), name);
+ result.put(globalObject, JSC.ZigString.static("value"), value);
+ headers.push(globalObject, result);
+ if (offset >= payload.len) {
+ break;
+ }
+ }
+
+ this.dispatchWith2Extra(.onStreamHeaders, JSC.JSValue.jsNumber(stream.id), headers, JSC.JSValue.jsNumber(flags));
+ }
+
+ pub fn handleDataFrame(this: *H2FrameParser, frame: FrameHeader, data: []const u8, stream_: ?*Stream) usize {
+ if (stream_ == null) {
+ this.sendGoAway(frame.streamIdentifier, ErrorCode.PROTOCOL_ERROR, "Data frame on connection stream", this.lastStreamID);
+ return data.len;
+ }
+ var settings = this.remoteSettings orelse this.localSettings;
+
+ if (frame.length > settings.maxFrameSize) {
+ this.sendGoAway(frame.streamIdentifier, ErrorCode.FRAME_SIZE_ERROR, "Invalid dataframe frame size", this.lastStreamID);
+ return data.len;
+ }
+
+ var stream = stream_.?;
+ this.readBuffer.reset();
+
+ const end: usize = @min(@as(usize, @intCast(this.remainingLength)), data.len);
+ var payload = data[0..end];
+
+ var data_needed: isize = this.remainingLength;
+
+ this.remainingLength -= @intCast(end);
+ var padding: u8 = 0;
+ if (frame.flags & @intFromEnum(DataFrameFlags.PADDED) != 0) {
+ if (stream.padding) |p| {
+ padding = p;
+ } else {
+ if (payload.len == 0) {
+ // await more data because we need to know the padding length
+ return data.len;
+ }
+ padding = payload[0];
+ stream.padding = payload[0];
+ payload = payload[1..];
+ }
+ }
+
+ if (this.remainingLength < 0) {
+ this.sendGoAway(frame.streamIdentifier, ErrorCode.FRAME_SIZE_ERROR, "Invalid data frame size", this.lastStreamID);
+ return data.len;
+ }
+
+ // ignore padding
+ if (data_needed > padding) {
+ data_needed -= padding;
+ payload = payload[0..@min(@as(usize, @intCast(data_needed)), payload.len)];
+ const chunk = this.handlers.binary_type.toJS(payload, this.handlers.globalObject);
+ this.dispatchWithExtra(.onStreamData, JSC.JSValue.jsNumber(frame.streamIdentifier), chunk);
+ } else {
+ data_needed = 0;
+ }
+
+ if (this.remainingLength == 0) {
+ this.currentFrame = null;
+ if (frame.flags & @intFromEnum(DataFrameFlags.END_STREAM) != 0) {
+ stream.state = .HALF_CLOSED_REMOTE;
+ this.dispatch(.onStreamEnd, JSC.JSValue.jsNumber(frame.streamIdentifier));
+ }
+ }
+
+ return end;
+ }
+ pub fn handleGoAwayFrame(this: *H2FrameParser, frame: FrameHeader, data: []const u8, stream_: ?*Stream) usize {
+ if (stream_ != null) {
+ this.sendGoAway(frame.streamIdentifier, ErrorCode.PROTOCOL_ERROR, "GoAway frame on stream", this.lastStreamID);
+ return data.len;
+ }
+ var settings = this.remoteSettings orelse this.localSettings;
+
+ if (frame.length < 8 or frame.length > settings.maxFrameSize) {
+ this.sendGoAway(frame.streamIdentifier, ErrorCode.FRAME_SIZE_ERROR, "invalid GoAway frame size", this.lastStreamID);
+ return data.len;
+ }
+
+ if (handleIncommingPayload(this, data, frame.streamIdentifier)) |content| {
+ const payload = content.data;
+ const last_stream_id: u32 = @intCast(UInt31WithReserved.fromBytes(payload[0..4]).uint31);
+ const error_code = UInt31WithReserved.fromBytes(payload[4..8]).toUInt32();
+ const chunk = this.handlers.binary_type.toJS(payload[8..], this.handlers.globalObject);
+ if (error_code != @intFromEnum(ErrorCode.NO_ERROR)) {
+ this.dispatchWith2Extra(.onGoAway, JSC.JSValue.jsNumber(error_code), JSC.JSValue.jsNumber(last_stream_id), chunk);
+ } else {
+ this.dispatchWithExtra(.onGoAway, JSC.JSValue.jsNumber(last_stream_id), chunk);
+ }
+ this.readBuffer.reset();
+ return content.end;
+ }
+ return data.len;
+ }
+ pub fn handleRSTStreamFrame(this: *H2FrameParser, frame: FrameHeader, data: []const u8, stream_: ?*Stream) usize {
+ if (stream_ == null) {
+ this.sendGoAway(frame.streamIdentifier, ErrorCode.PROTOCOL_ERROR, "RST_STREAM frame on connection stream", this.lastStreamID);
+ return data.len;
+ }
+ if (frame.length != 4) {
+ this.sendGoAway(frame.streamIdentifier, ErrorCode.FRAME_SIZE_ERROR, "invalid RST_STREAM frame size", this.lastStreamID);
+ return data.len;
+ }
+
+ var stream = stream_.?;
+
+ if (stream.isWaitingMoreHeaders) {
+ this.sendGoAway(frame.streamIdentifier, ErrorCode.PROTOCOL_ERROR, "Headers frame without continuation", this.lastStreamID);
+ return data.len;
+ }
+
+ if (handleIncommingPayload(this, data, frame.streamIdentifier)) |content| {
+ const payload = content.data;
+ const rst_code = UInt31WithReserved.fromBytes(payload).toUInt32();
+ stream.rstCode = rst_code;
+ this.readBuffer.reset();
+ if (rst_code != @intFromEnum(ErrorCode.NO_ERROR)) {
+ this.dispatchWithExtra(.onStreamError, JSC.JSValue.jsNumber(stream.id), JSC.JSValue.jsNumber(rst_code));
+ }
+ this.endStream(stream, ErrorCode.NO_ERROR);
+
+ return content.end;
+ }
+ return data.len;
+ }
+ pub fn handlePingFrame(this: *H2FrameParser, frame: FrameHeader, data: []const u8, stream_: ?*Stream) usize {
+ if (stream_ != null) {
+ this.sendGoAway(frame.streamIdentifier, ErrorCode.PROTOCOL_ERROR, "Ping frame on stream", this.lastStreamID);
+ return data.len;
+ }
+
+ if (frame.length != 8) {
+ this.sendGoAway(frame.streamIdentifier, ErrorCode.FRAME_SIZE_ERROR, "Invalid ping frame size", this.lastStreamID);
+ return data.len;
+ }
+
+ if (handleIncommingPayload(this, data, frame.streamIdentifier)) |content| {
+ const payload = content.data;
+ this.dispatch(.onPing, this.handlers.binary_type.toJS(payload, this.handlers.globalObject));
+ // if is not ACK send response
+ if (frame.flags & @intFromEnum(PingFrameFlags.ACK) == 0) {
+ this.sendPing(true, payload);
+ }
+ this.readBuffer.reset();
+ return content.end;
+ }
+ return data.len;
+ }
+ pub fn handlePriorityFrame(this: *H2FrameParser, frame: FrameHeader, data: []const u8, stream_: ?*Stream) usize {
+ if (stream_ == null) {
+ this.sendGoAway(frame.streamIdentifier, ErrorCode.PROTOCOL_ERROR, "Priority frame on connection stream", this.lastStreamID);
+ return data.len;
+ }
+
+ if (frame.length != StreamPriority.byteSize) {
+ this.sendGoAway(frame.streamIdentifier, ErrorCode.FRAME_SIZE_ERROR, "invalid Priority frame size", this.lastStreamID);
+ return data.len;
+ }
+
+ var stream = stream_.?;
+
+ if (handleIncommingPayload(this, data, frame.streamIdentifier)) |content| {
+ const payload = content.data;
+
+ var priority: StreamPriority = undefined;
+ priority.from(payload);
+
+ const stream_identifier = UInt31WithReserved.from(priority.streamIdentifier);
+ stream.streamDependency = stream_identifier.uint31;
+ stream.exclusive = stream_identifier.reserved;
+ stream.weight = priority.weight;
+
+ this.readBuffer.reset();
+ return content.end;
+ }
+ return data.len;
+ }
+ pub fn handleContinuationFrame(this: *H2FrameParser, frame: FrameHeader, data: []const u8, stream_: ?*Stream) usize {
+ if (stream_ == null) {
+ this.sendGoAway(frame.streamIdentifier, ErrorCode.PROTOCOL_ERROR, "Continuation on connection stream", this.lastStreamID);
+ return data.len;
+ }
+ var stream = stream_.?;
+ if (!stream.isWaitingMoreHeaders) {
+ this.sendGoAway(frame.streamIdentifier, ErrorCode.PROTOCOL_ERROR, "Continuation without headers", this.lastStreamID);
+ return data.len;
+ }
+ if (handleIncommingPayload(this, data, frame.streamIdentifier)) |content| {
+ const payload = content.data;
+ this.decodeHeaderBlock(payload[0..payload.len], stream, frame.flags);
+ this.readBuffer.reset();
+ if (frame.flags & @intFromEnum(HeadersFrameFlags.END_HEADERS) != 0) {
+ if (stream.state == .HALF_CLOSED_REMOTE) {
+ // no more continuation headers we can call it closed
+ stream.state = .CLOSED;
+ this.dispatch(.onStreamEnd, JSC.JSValue.jsNumber(frame.streamIdentifier));
+ }
+ stream.isWaitingMoreHeaders = false;
+ }
+
+ this.readBuffer.reset();
+ return content.end;
+ }
+
+ // needs more data
+ return data.len;
+ }
+
+ pub fn handleHeadersFrame(this: *H2FrameParser, frame: FrameHeader, data: []const u8, stream_: ?*Stream) usize {
+ if (stream_ == null) {
+ this.sendGoAway(frame.streamIdentifier, ErrorCode.PROTOCOL_ERROR, "Headers frame on connection stream", this.lastStreamID);
+ return data.len;
+ }
+ var settings = this.remoteSettings orelse this.localSettings;
+ if (frame.length > settings.maxFrameSize) {
+ this.sendGoAway(frame.streamIdentifier, ErrorCode.FRAME_SIZE_ERROR, "invalid Headers frame size", this.lastStreamID);
+ return data.len;
+ }
+ var stream = stream_.?;
+
+ if (stream.isWaitingMoreHeaders) {
+ this.sendGoAway(frame.streamIdentifier, ErrorCode.PROTOCOL_ERROR, "Headers frame without continuation", this.lastStreamID);
+ return data.len;
+ }
+
+ if (handleIncommingPayload(this, data, frame.streamIdentifier)) |content| {
+ const payload = content.data;
+ var offset: usize = 0;
+ var padding: usize = 0;
+ if (frame.flags & @intFromEnum(HeadersFrameFlags.PADDED) != 0) {
+ // padding length
+ padding = payload[0];
+ offset += 1;
+ }
+ if (frame.flags & @intFromEnum(HeadersFrameFlags.PRIORITY) != 0) {
+ // skip priority (client dont need to care about it)
+ offset += 5;
+ }
+ this.decodeHeaderBlock(payload[offset .. payload.len - padding], stream, frame.flags);
+ stream.isWaitingMoreHeaders = frame.flags & @intFromEnum(HeadersFrameFlags.END_HEADERS) != 0;
+ if (frame.flags & @intFromEnum(HeadersFrameFlags.END_STREAM) != 0) {
+ if (stream.isWaitingMoreHeaders) {
+ stream.state = .HALF_CLOSED_REMOTE;
+ } else {
+ // no more continuation headers we can call it closed
+ stream.state = .CLOSED;
+ this.dispatch(.onStreamEnd, JSC.JSValue.jsNumber(frame.streamIdentifier));
+ }
+ }
+ this.readBuffer.reset();
+ return content.end;
+ }
+
+ // needs more data
+ return data.len;
+ }
pub fn handleSettingsFrame(this: *H2FrameParser, frame: FrameHeader, data: []const u8) usize {
if (frame.streamIdentifier != 0) {
- // this.#connection.write(createGoAwayFrameBuffer(this.#lastStreamID, ErrorCode.PROTOCOL_ERROR, Buffer.alloc(0)));
-
- // throw new Error("PROTOCOL_ERROR");
- log("PROTOCOL_ERROR", .{});
+ this.sendGoAway(frame.streamIdentifier, ErrorCode.PROTOCOL_ERROR, "Settings frame on connection stream", this.lastStreamID);
return data.len;
}
+
const settingByteSize = SettingsPayloadUnit.byteSize;
if (frame.length > 0) {
- if (frame.flags & 0x1 or frame.length % settingByteSize != 0) {
- // this.#connection.write(
- // createGoAwayFrameBuffer(this.#lastStreamID, ErrorCode.FRAME_SIZE_ERROR, Buffer.alloc(0)),
- // );
- log("FRAME_SIZE_ERROR", .{});
+ if (frame.flags & 0x1 != 0 or frame.length % settingByteSize != 0) {
+ this.sendGoAway(frame.streamIdentifier, ErrorCode.FRAME_SIZE_ERROR, "Invalid settings frame size", this.lastStreamID);
return data.len;
}
} else {
- if (frame.flags & 0x1) {
+ if (frame.flags & 0x1 != 0) {
// we received an ACK
- this.remoteSettings = &this.localSettings;
+ this.remoteSettings = this.localSettings;
this.dispatch(.onLocalSettings, this.localSettings.toJS(this.handlers.globalObject));
}
+ this.currentFrame = null;
return 0;
}
- const end: i32 = @min(frame.remainingLength, data.len);
- const payload = data[0..end];
- this.remainingLength -= end;
- if (this.remainingLength > 0) {
- // buffer more data
- _ = this.readBuffer.appendSlice(payload) catch @panic("OOM");
- return data.len;
- } else if (this.remainingLength < 0) {
- // this.#connection.write(
- // createGoAwayFrameBuffer(this.#lastStreamID, ErrorCode.FRAME_SIZE_ERROR, Buffer.alloc(0)),
- // );
- log("FRAME_SIZE_ERROR", .{});
- return data.len;
+ if (handleIncommingPayload(this, data, frame.streamIdentifier)) |content| {
+ var remoteSettings = this.remoteSettings orelse this.localSettings;
+ var i: usize = 0;
+ const payload = content.data;
+ while (i < payload.len) {
+ defer i += settingByteSize;
+ var unit: SettingsPayloadUnit = undefined;
+ SettingsPayloadUnit.from(&unit, payload[i .. i + settingByteSize], 0, true);
+ remoteSettings.updateWith(unit);
+ }
+ this.readBuffer.reset();
+ this.remoteSettings = remoteSettings;
+ this.dispatch(.onRemoteSettings, remoteSettings.toJS(this.handlers.globalObject));
+ return content.end;
}
+ // needs more data
+ return data.len;
+ }
- this.currentFrame = null;
- var remoteSettings = this.remoteSettings orelse this.localSettings;
- var i: usize = 0;
- while (i < payload.len) {
- defer i += settingByteSize;
- var unit: SettingsPayloadUnit = undefined;
- SettingsPayloadUnit.from(&unit, payload[i .. i + settingByteSize], 0, true);
- remoteSettings.updateWith(unit);
+ fn handleReceivedStreamID(this: *H2FrameParser, streamIdentifier: u32) ?*Stream {
+ // connection stream
+ if (streamIdentifier == 0) {
+ return null;
}
- this.remoteSettings = remoteSettings;
- this.dispatch(.onRemoteSettings, remoteSettings.toJS(this.handlers.globalObject));
- // console.log(this.#settings);
- // this.#compressor = hpack.compressor.create({ table: { size: this.#settings.headerTableSize } });
- // this.#decompressor = hpack.decompressor.create({ table: { size: this.#settings.headerTableSize } });
+ // already exists
+ if (this.streams.getEntry(streamIdentifier)) |entry| {
+ return entry.value_ptr;
+ }
- return end;
+ if (streamIdentifier > this.lastStreamID) {
+ this.lastStreamID = streamIdentifier;
+ }
+
+ // new stream open
+ const settings = this.remoteSettings orelse this.localSettings;
+ var entry = this.streams.getOrPut(streamIdentifier) catch @panic("OOM");
+ entry.value_ptr.* = Stream.init(streamIdentifier, settings.initialWindowSize, settings.headerTableSize);
+
+ this.dispatch(.onStreamStart, JSC.JSValue.jsNumber(streamIdentifier));
+ return entry.value_ptr;
}
pub fn readBytes(this: *H2FrameParser, bytes: []u8) usize {
log("read", .{});
if (this.currentFrame) |header| {
- log("header {} {} {} {}", .{ header.type, header.length, header.flags, header.streamIdentifier });
- return bytes.len;
+ log("current frame {} {} {} {}", .{ header.type, header.length, header.flags, header.streamIdentifier });
+
+ const stream = this.handleReceivedStreamID(header.streamIdentifier);
+ return switch (@as(FrameType, @enumFromInt(header.type))) {
+ FrameType.HTTP_FRAME_SETTINGS => this.handleSettingsFrame(header, bytes),
+ FrameType.HTTP_FRAME_WINDOW_UPDATE => this.handleWindowUpdateFrame(header, bytes, stream),
+ FrameType.HTTP_FRAME_HEADERS => this.handleHeadersFrame(header, bytes, stream),
+ FrameType.HTTP_FRAME_DATA => this.handleDataFrame(header, bytes, stream),
+ FrameType.HTTP_FRAME_CONTINUATION => this.handleContinuationFrame(header, bytes, stream),
+ FrameType.HTTP_FRAME_PRIORITY => this.handlePriorityFrame(header, bytes, stream),
+ FrameType.HTTP_FRAME_PING => this.handlePingFrame(header, bytes, stream),
+ FrameType.HTTP_FRAME_GOAWAY => this.handleGoAwayFrame(header, bytes, stream),
+ FrameType.HTTP_FRAME_RST_STREAM => this.handleRSTStreamFrame(header, bytes, stream),
+ else => {
+ this.sendGoAway(header.streamIdentifier, ErrorCode.PROTOCOL_ERROR, "Unknown frame type", this.lastStreamID);
+ return bytes.len;
+ },
+ };
}
// nothing to do
@@ -418,12 +1184,21 @@ pub const H2FrameParser = struct {
this.currentFrame = header;
this.remainingLength = header.length;
- log("header {} {} {} {}", .{ header.type, header.length, header.flags, header.streamIdentifier });
-
+ log("new frame {} {} {} {}", .{ header.type, header.length, header.flags, header.streamIdentifier });
+ const stream = this.handleReceivedStreamID(header.streamIdentifier);
+ this.ajustWindowSize(stream, header.length);
return switch (@as(FrameType, @enumFromInt(header.type))) {
FrameType.HTTP_FRAME_SETTINGS => this.handleSettingsFrame(header, bytes[needed..]) + needed,
+ FrameType.HTTP_FRAME_WINDOW_UPDATE => this.handleWindowUpdateFrame(header, bytes[needed..], stream) + needed,
+ FrameType.HTTP_FRAME_HEADERS => this.handleHeadersFrame(header, bytes[needed..], stream) + needed,
+ FrameType.HTTP_FRAME_DATA => this.handleDataFrame(header, bytes[needed..], stream) + needed,
+ FrameType.HTTP_FRAME_CONTINUATION => this.handleContinuationFrame(header, bytes[needed..], stream) + needed,
+ FrameType.HTTP_FRAME_PRIORITY => this.handlePriorityFrame(header, bytes[needed..], stream) + needed,
+ FrameType.HTTP_FRAME_PING => this.handlePingFrame(header, bytes[needed..], stream) + needed,
+ FrameType.HTTP_FRAME_GOAWAY => this.handleGoAwayFrame(header, bytes[needed..], stream) + needed,
+ FrameType.HTTP_FRAME_RST_STREAM => this.handleRSTStreamFrame(header, bytes[needed..], stream) + needed,
else => {
- log("not implemented {}", .{header.type});
+ this.sendGoAway(header.streamIdentifier, ErrorCode.PROTOCOL_ERROR, "Unknown frame type", this.lastStreamID);
return bytes.len;
},
};
@@ -437,18 +1212,573 @@ pub const H2FrameParser = struct {
FrameHeader.from(&header, bytes[0..FrameHeader.byteSize], 0, true);
- log("header {} {} {} {}", .{ header.type, header.length, header.flags, header.streamIdentifier });
+ log("new frame {} {} {} {}", .{ header.type, header.length, header.flags, header.streamIdentifier });
this.currentFrame = header;
this.remainingLength = header.length;
+ const stream = this.handleReceivedStreamID(header.streamIdentifier);
+ this.ajustWindowSize(stream, header.length);
return switch (@as(FrameType, @enumFromInt(header.type))) {
FrameType.HTTP_FRAME_SETTINGS => this.handleSettingsFrame(header, bytes[FrameHeader.byteSize..]) + FrameHeader.byteSize,
+ FrameType.HTTP_FRAME_WINDOW_UPDATE => this.handleWindowUpdateFrame(header, bytes[FrameHeader.byteSize..], stream) + FrameHeader.byteSize,
+ FrameType.HTTP_FRAME_HEADERS => this.handleHeadersFrame(header, bytes[FrameHeader.byteSize..], stream) + FrameHeader.byteSize,
+ FrameType.HTTP_FRAME_DATA => this.handleDataFrame(header, bytes[FrameHeader.byteSize..], stream) + FrameHeader.byteSize,
+ FrameType.HTTP_FRAME_CONTINUATION => this.handleContinuationFrame(header, bytes[FrameHeader.byteSize..], stream) + FrameHeader.byteSize,
+ FrameType.HTTP_FRAME_PRIORITY => this.handlePriorityFrame(header, bytes[FrameHeader.byteSize..], stream) + FrameHeader.byteSize,
+ FrameType.HTTP_FRAME_PING => this.handlePingFrame(header, bytes[FrameHeader.byteSize..], stream) + FrameHeader.byteSize,
+ FrameType.HTTP_FRAME_GOAWAY => this.handleGoAwayFrame(header, bytes[FrameHeader.byteSize..], stream) + FrameHeader.byteSize,
+ FrameType.HTTP_FRAME_RST_STREAM => this.handleRSTStreamFrame(header, bytes[FrameHeader.byteSize..], stream) + FrameHeader.byteSize,
else => {
- log("not implemented {}", .{header.type});
+ this.sendGoAway(header.streamIdentifier, ErrorCode.PROTOCOL_ERROR, "Unknown frame type", this.lastStreamID);
return bytes.len;
},
};
}
+ const DirectWriterStruct = struct {
+ writer: *H2FrameParser,
+ pub fn write(this: *const DirectWriterStruct, data: []const u8) !bool {
+ this.writer.write(data);
+ return true;
+ }
+ };
+
+ fn toWriter(this: *H2FrameParser) DirectWriterStruct {
+ return DirectWriterStruct{ .writer = this };
+ }
+ pub fn setEncoding(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue {
+ JSC.markBinding(@src());
+ const args_list = callframe.arguments(1);
+ if (args_list.len < 1) {
+ globalObject.throw("Expected encoding argument", .{});
+ return .zero;
+ }
+ this.handlers.binary_type = BinaryType.fromJSValue(globalObject, args_list.ptr[0]) orelse {
+ const err = JSC.toInvalidArguments("Expected 'binaryType' to be 'arraybuffer', 'uint8array', 'buffer'", .{}, globalObject).asObjectRef();
+ globalObject.throwValue(err);
+ return .zero;
+ };
+
+ return JSC.JSValue.jsUndefined();
+ }
+
+ pub fn setSettingsFromJSValue(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, options: JSC.JSValue) bool {
+
+ if(options.isEmptyOrUndefinedOrNull() or !options.isObject()) {
+ globalObject.throw("Expected settings to be a object", .{});
+ return false;
+ }
+
+ if(options.get(globalObject, "headerTableSize")) |headerTableSize| {
+ if(headerTableSize.isNumber()) {
+ const headerTableSizeValue = headerTableSize.toInt32();
+ if(headerTableSizeValue > MAX_HEADER_TABLE_SIZE or headerTableSizeValue < 0) {
+ globalObject.throw("Expected headerTableSize to be a number between 0 and 2^32-1", .{});
+ return false;
+ }
+ this.localSettings.headerTableSize = @intCast(headerTableSizeValue);
+ } else if(!headerTableSize.isEmptyOrUndefinedOrNull()) {
+ globalObject.throw("Expected headerTableSize to be a number", .{});
+ return false;
+ }
+ }
+
+ if(options.get(globalObject, "enablePush")) |enablePush| {
+ if(enablePush.isBoolean()) {
+ this.localSettings.enablePush = if(enablePush.asBoolean()) 1 else 0;
+ } else if(!enablePush.isEmptyOrUndefinedOrNull()) {
+ globalObject.throw("Expected enablePush to be a boolean", .{});
+ return false;
+ }
+ }
+
+ if(options.get(globalObject, "initialWindowSize")) |initialWindowSize| {
+ if(initialWindowSize.isNumber()) {
+ const initialWindowSizeValue = initialWindowSize.toInt32();
+ if(initialWindowSizeValue > MAX_HEADER_TABLE_SIZE or initialWindowSizeValue < 0) {
+ globalObject.throw("Expected initialWindowSize to be a number between 0 and 2^32-1", .{});
+ return false;
+ }
+ } else if(!initialWindowSize.isEmptyOrUndefinedOrNull()) {
+ globalObject.throw("Expected initialWindowSize to be a number", .{});
+ return false;
+ }
+ }
+
+ if(options.get(globalObject, "maxFrameSize")) |maxFrameSize| {
+ if(maxFrameSize.isNumber()) {
+ const maxFrameSizeValue = maxFrameSize.toInt32();
+ if(maxFrameSizeValue > MAX_FRAME_SIZE or maxFrameSizeValue < 16384) {
+ globalObject.throw("Expected maxFrameSize to be a number between 16,384 and 2^24-1", .{});
+ return false;
+ }
+ this.localSettings.maxFrameSize = @intCast(maxFrameSizeValue);
+ } else if(!maxFrameSize.isEmptyOrUndefinedOrNull()) {
+ globalObject.throw("Expected maxFrameSize to be a number", .{});
+ return false;
+ }
+ }
+
+ if(options.get(globalObject, "maxConcurrentStreams")) |maxConcurrentStreams| {
+ if(maxConcurrentStreams.isNumber()) {
+ const maxConcurrentStreamsValue = maxConcurrentStreams.toInt32();
+ if(maxConcurrentStreamsValue > MAX_HEADER_TABLE_SIZE or maxConcurrentStreamsValue < 0) {
+ globalObject.throw("Expected maxConcurrentStreams to be a number between 0 and 2^32-1", .{});
+ return false;
+ }
+ this.localSettings.maxConcurrentStreams = @intCast(maxConcurrentStreamsValue);
+ } else if(!maxConcurrentStreams.isEmptyOrUndefinedOrNull()) {
+ globalObject.throw("Expected maxConcurrentStreams to be a number", .{});
+ return false;
+ }
+ }
+
+ if(options.get(globalObject, "maxHeaderListSize")) |maxHeaderListSize| {
+ if(maxHeaderListSize.isNumber()) {
+ const maxHeaderListSizeValue = maxHeaderListSize.toInt32();
+ if(maxHeaderListSizeValue > MAX_HEADER_TABLE_SIZE or maxHeaderListSizeValue < 0) {
+ globalObject.throw("Expected maxHeaderListSize to be a number between 0 and 2^32-1", .{});
+ return false;
+ }
+ this.localSettings.maxHeaderListSize = @intCast(maxHeaderListSizeValue);
+ } else if(!maxHeaderListSize.isEmptyOrUndefinedOrNull()) {
+ globalObject.throw("Expected maxHeaderListSize to be a number", .{});
+ return false;
+ }
+ }
+
+ if(options.get(globalObject, "maxHeaderSize")) |maxHeaderSize| {
+ if(maxHeaderSize.isNumber()) {
+ const maxHeaderSizeValue = maxHeaderSize.toInt32();
+ if(maxHeaderSizeValue > MAX_HEADER_TABLE_SIZE or maxHeaderSizeValue < 0) {
+ globalObject.throw("Expected maxHeaderSize to be a number between 0 and 2^32-1", .{});
+ return false;
+ }
+ this.localSettings.maxHeaderListSize = @intCast(maxHeaderSizeValue);
+ } else if(!maxHeaderSize.isEmptyOrUndefinedOrNull()) {
+ globalObject.throw("Expected maxHeaderSize to be a number", .{});
+ return false;
+ }
+ }
+ return true;
+ }
+
+ pub fn updateSettings(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue {
+ JSC.markBinding(@src());
+ const args_list = callframe.arguments(1);
+ if (args_list.len < 1) {
+ globalObject.throw("Expected settings argument", .{});
+ return .zero;
+ }
+
+ const options = args_list.ptr[0];
+
+ if(this.setSettingsFromJSValue(globalObject, options)) {
+ this.setSettings(this.localSettings);
+ return JSC.JSValue.jsUndefined();
+ }
+
+ return .zero;
+
+
+ }
+
+ pub fn getCurrentState(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue {
+ JSC.markBinding(@src());
+
+ // effectiveLocalWindowSize <number> The current local (receive) flow control window size for the Http2Session.
+ // effectiveRecvDataLength <number> The current number of bytes that have been received since the last flow control WINDOW_UPDATE.
+ // nextStreamID <number> The numeric identifier to be used the next time a new Http2Stream is created by this Http2Session.
+ // localWindowSize <number> The number of bytes that the remote peer can send without receiving a WINDOW_UPDATE.
+ // lastProcStreamID <number> The numeric id of the Http2Stream for which a HEADERS or DATA frame was most recently received.
+ // remoteWindowSize <number> The number of bytes that this Http2Session may send without receiving a WINDOW_UPDATE.
+ // outboundQueueSize <number> The number of frames currently within the outbound queue for this Http2Session.
+ // deflateDynamicTableSize <number> The current size in bytes of the outbound header compression state table.
+ // inflateDynamicTableSize <number> The current size in bytes of the inbound header compression state table.
+
+ var result = JSValue.createEmptyObject(globalObject, 9);
+ result.put(globalObject, JSC.ZigString.static("effectiveLocalWindowSize"), JSC.JSValue.jsNumber(this.localSettings.localWindowSize));
+ result.put(globalObject, JSC.ZigString.static("effectiveRecvDataLength"), JSC.JSValue.jsNumber(this.localSettings.localWindowSize - this.localSettings.usedWindowSize));
+ result.put(globalObject, JSC.ZigString.static("nextStreamID"), JSC.JSValue.jsNumber(this.getNextStreamID()));
+ var settings = this.remoteSettings orelse this.localWindowSize;
+ result.put(globalObject, JSC.ZigString.static("remoteWindowSize"), JSC.JSValue.jsNumber(settings.localWindowSize));
+ result.put(globalObject, JSC.ZigString.static("localWindowSize"), JSC.JSValue.jsNumber(this.localSettings.localWindowSize));
+ // TODO: make this real?
+ result.put(globalObject, JSC.ZigString.static("outboundQueueSize"), JSC.JSValue.jsNumber(0));
+ result.put(globalObject, JSC.ZigString.static("deflateDynamicTableSize"), JSC.JSValue.jsNumber(0));
+ result.put(globalObject, JSC.ZigString.static("inflateDynamicTableSize"), JSC.JSValue.jsNumber(0));
+
+ }
+ pub fn goaway(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue {
+ JSC.markBinding(@src());
+ const args_list = callframe.arguments(3);
+ if (args_list.len < 1) {
+ globalObject.throw("Expected errorCode argument", .{});
+ return .zero;
+ }
+
+ const error_code_arg = args_list.ptr[0];
+
+ if (!error_code_arg.isNumber()) {
+ globalObject.throw("Expected errorCode to be a number", .{});
+ return .zero;
+ }
+ const errorCode = error_code_arg.toInt32();
+ if (errorCode < 1 and errorCode > 13) {
+ globalObject.throw("invalid errorCode", .{});
+ }
+
+ var lastStreamID = this.lastStreamID;
+ if(args_list.len >= 2) {
+ const last_stream_arg = args_list.ptr[1];
+ if(!last_stream_arg.isEmptyOrUndefinedOrNull()) {
+ if (!last_stream_arg.isNumber()) {
+ globalObject.throw("Expected lastStreamId to be a number", .{});
+ return .zero;
+ }
+ const id = last_stream_arg.toInt32();
+ if(id < 0 and id > MAX_STREAM_ID) {
+ globalObject.throw("Expected lastStreamId to be a number between 1 and 2147483647", .{});
+ return .zero;
+ }
+ lastStreamID = @intCast(id);
+ }
+ if(args_list.len >= 3) {
+ const opaque_data_arg = args_list.ptr[2];
+ if(!opaque_data_arg.isEmptyOrUndefinedOrNull()) {
+ if (opaque_data_arg.asArrayBuffer(globalObject)) |array_buffer| {
+ var slice = array_buffer.slice();
+ this.sendGoAway(0, @enumFromInt(errorCode), slice, lastStreamID);
+ globalObject.throw("Expected lastStreamId to be a number", .{});
+ return .zero;
+ }
+ const id = last_stream_arg.toInt32();
+ if(id < 0 and id > MAX_STREAM_ID) {
+ globalObject.throw("Expected lastStreamId to be a number between 1 and 2147483647", .{});
+ return .zero;
+ }
+ }
+ }
+ }
+
+ this.sendGoAway(0, @enumFromInt(errorCode), "", lastStreamID);
+ return JSC.JSValue.jsUndefined();
+ }
+
+ pub fn ping(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue {
+ JSC.markBinding(@src());
+ const args_list = callframe.arguments(1);
+ if (args_list.len < 1) {
+ globalObject.throw("Expected payload argument", .{});
+ return .zero;
+ }
+
+ if (args_list.ptr[0].asArrayBuffer(globalObject)) |array_buffer| {
+ var slice = array_buffer.slice();
+ this.sendPing(false, slice);
+ return JSC.JSValue.jsUndefined();
+ }
+
+ globalObject.throw("Expected payload to be a Buffer", .{});
+ return .zero;
+ }
+ // pub fn writeStream(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue {
+ // JSC.markBinding(@src());
+ // const args_list = callframe.arguments(2);
+ // if (args_list.len < 2) {
+ // globalObject.throw("Expected stream and data arguments", .{});
+ // return .zero;
+ // }
+ // var close_stream = false;
+ // if(args_list > 2) {
+ // const close_arg = args_list.ptr[2];
+ // if (!close_arg.jsType().isBoolean()) {
+ // globalObject.throw("Expected close to be a boolean", .{});
+ // return .zero;
+ // }
+ // close_stream = close_arg.asBoolean();
+ // }
+
+ // const stream_arg = args_list.ptr[0];
+ // const data_arg = args_list.ptr[1];
+
+ // if (!stream_arg.isNumber()) {
+ // globalObject.throw("Expected stream to be a number", .{});
+ // return .zero;
+ // }
+
+ // const stream_id = stream_arg.asNumber().asUInt32(globalObject);
+ // if (stream_id == 0) {
+ // globalObject.throw("Invalid stream id", .{});
+ // return .zero;
+ // }
+
+ // const stream = this.streams.get(stream_id) orelse {
+ // globalObject.throw("Invalid stream id", .{});
+ // return .zero;
+ // };
+
+ // if (stream.state == .CLOSED) {
+ // globalObject.throw("Stream is closed", .{});
+ // return .zero;
+ // }
+
+ // if (stream.state == .HALF_CLOSED_LOCAL or stream.state == .HALF_CLOSED_REMOTE) {
+ // globalObject.throw("Stream is half closed", .{});
+ // return .zero;
+ // }
+
+ // if (data_arg.asArrayBuffer(globalObject)) |array_buffer| {
+ // var slice = array_buffer.slice();
+ // } else if (bun.String.tryFromJS(data_arg, globalObject)) |bun_str| {
+ // var zig_str = bun_str.toUTF8(bun.default_allocator);
+ // defer zig_str.deinit();
+ // var slice = zig_str.slice();
+
+ // } else {
+ // globalObject.throw("Expected data to be an ArrayBuffer or a string", .{});
+ // return .zero;
+ // }
+
+ // return JSC.JSValue.jsBoolean(true);
+ // }
+
+ fn getNextStreamID(this: *H2FrameParser) u32 {
+ var stream_id: u32 = this.lastStreamID;
+ if (stream_id % 2 == 0) {
+ stream_id += 1;
+ } else if (stream_id == 0) {
+ stream_id = 1;
+ } else {
+ stream_id += 2;
+ }
+
+ return stream_id;
+ }
+
+ pub fn request(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue {
+ JSC.markBinding(@src());
+ // we use PADDING_STRATEGY_NONE with is default
+ // TODO: PADDING_STRATEGY_MAX AND PADDING_STRATEGY_ALIGNED
+
+ const args_list = callframe.arguments(2);
+ if (args_list.len < 1) {
+ globalObject.throw("Expected headers argument", .{});
+ return .zero;
+ }
+
+ const headers_arg = args_list.ptr[0];
+
+ if (!headers_arg.jsType().isArray()) {
+ globalObject.throw("Expected headers to be an array", .{});
+ return .zero;
+ }
+
+ // max frame size will be always at least 16384
+ var buffer: [16384 - FrameHeader.byteSize - 5]u8 = undefined;
+ var header_buffer: [MAX_HPACK_HEADER_SIZE]u8 = undefined;
+ @memset(&buffer, 0);
+
+ var iter = headers_arg.arrayIterator(globalObject);
+ var encoded_size: usize = 0;
+
+ var stream_id: u32 = this.getNextStreamID();
+ if (stream_id > MAX_STREAM_ID) {
+ globalObject.throw("Failed to create stream", .{});
+ return .zero;
+ }
+
+ const stream = this.handleReceivedStreamID(stream_id) orelse {
+ globalObject.throw("Failed to create stream", .{});
+ return .zero;
+ };
+ // TODO: support CONTINUE for more headers if headers are too big
+ while (iter.next()) |header| {
+ if (!header.isObject()) {
+ stream.state = .CLOSED;
+ stream.rstCode = @intFromEnum(ErrorCode.INTERNAL_ERROR);
+ this.dispatchWithExtra(.onStreamError, JSC.JSValue.jsNumber(stream_id), JSC.JSValue.jsNumber(stream.rstCode));
+ globalObject.throwInvalidArguments("Expected header to be an Array of headers", .{});
+ return .zero;
+ }
+ var name = header.get(globalObject, "name") orelse JSC.JSValue.jsUndefined();
+ if (!name.isString()) {
+ stream.state = .CLOSED;
+ stream.rstCode = @intFromEnum(ErrorCode.INTERNAL_ERROR);
+ this.dispatchWithExtra(.onStreamError, JSC.JSValue.jsNumber(stream_id), JSC.JSValue.jsNumber(stream.rstCode));
+ globalObject.throwInvalidArguments("Expected header name to be a string", .{});
+ return .zero;
+ }
+
+ var value = header.get(globalObject, "value") orelse JSC.JSValue.jsUndefined();
+ if (!value.isString()) {
+ stream.state = .CLOSED;
+ stream.rstCode = @intFromEnum(ErrorCode.INTERNAL_ERROR);
+ this.dispatchWithExtra(.onStreamError, JSC.JSValue.jsNumber(stream_id), JSC.JSValue.jsNumber(stream.rstCode));
+ globalObject.throwInvalidArguments("Expected header value to be a string", .{});
+ return .zero;
+ }
+
+ var never_index = false;
+ var never_index_arg = header.get(globalObject, "neverIndex") orelse JSC.JSValue.jsUndefined();
+ if (never_index_arg.isBoolean()) {
+ never_index = never_index_arg.asBoolean();
+ } else {
+ never_index = false;
+ }
+
+ const name_slice = name.toSlice(globalObject, bun.default_allocator);
+ defer name_slice.deinit();
+ const value_slice = value.toSlice(globalObject, bun.default_allocator);
+ defer value_slice.deinit();
+
+ encoded_size += stream.encode(&header_buffer, buffer[encoded_size..], name_slice.slice(), value_slice.slice(), never_index) catch {
+ stream.state = .CLOSED;
+ stream.rstCode = @intFromEnum(ErrorCode.INTERNAL_ERROR);
+ this.dispatchWithExtra(.onStreamError, JSC.JSValue.jsNumber(stream_id), JSC.JSValue.jsNumber(stream.rstCode));
+ globalObject.throw("Failed to encode header", .{});
+ return .zero;
+ };
+ }
+ headers_arg.protect();
+ stream.sentHeaders = headers_arg;
+ var flags: u8 = @intFromEnum(HeadersFrameFlags.END_HEADERS);
+ var exclusive: bool = false;
+ var has_priority: bool = false;
+ var weight: i32 = 0;
+ var parent: i32 = 0;
+ var waitForTrailers: bool = false;
+ var signal: ?*JSC.WebCore.AbortSignal = null;
+ var end_stream: bool = false;
+ if (args_list.len > 1) {
+ const options = args_list.ptr[1];
+ if (!options.isObject()) {
+ stream.state = .CLOSED;
+ stream.rstCode = @intFromEnum(ErrorCode.INTERNAL_ERROR);
+ this.dispatchWithExtra(.onStreamError, JSC.JSValue.jsNumber(stream_id), JSC.JSValue.jsNumber(stream.rstCode));
+ globalObject.throw("Expected options to be an object", .{});
+ return .zero;
+ }
+
+ if (options.get(globalObject, "endStream")) |end_stream_js| {
+ if (end_stream_js.isBoolean()) {
+ if (end_stream_js.asBoolean()) {
+ end_stream = true;
+ flags |= @intFromEnum(HeadersFrameFlags.END_STREAM);
+ stream.endAfterHeaders = true;
+ }
+ }
+ }
+
+ if (options.get(globalObject, "exclusive")) |exclusive_js| {
+ if (exclusive_js.isBoolean()) {
+ if (exclusive_js.asBoolean()) {
+ exclusive = true;
+ stream.exclusive = true;
+ has_priority = true;
+ }
+ }
+ }
+
+ if (options.get(globalObject, "parent")) |parent_js| {
+ if (parent_js.isNumber() or parent_js.isInt32()) {
+ has_priority = true;
+ parent = parent_js.toInt32();
+ if (parent <= 0 or parent > MAX_STREAM_ID) {
+ stream.state = .CLOSED;
+ stream.rstCode = @intFromEnum(ErrorCode.INTERNAL_ERROR);
+ this.dispatchWithExtra(.onStreamError, JSC.JSValue.jsNumber(stream_id), JSC.JSValue.jsNumber(stream.rstCode));
+ globalObject.throw("Expected parent to be a number between 1 and 2147483647", .{});
+ return .zero;
+ }
+ stream.streamDependency = @intCast(parent);
+ }
+ }
+
+ if (options.get(globalObject, "weight")) |weight_js| {
+ if (weight_js.isNumber() or weight_js.isInt32()) {
+ has_priority = true;
+ weight = weight_js.toInt32();
+ if (weight < 1 or weight > 256) {
+ stream.state = .CLOSED;
+ stream.rstCode = @intFromEnum(ErrorCode.INTERNAL_ERROR);
+ this.dispatchWithExtra(.onStreamError, JSC.JSValue.jsNumber(stream_id), JSC.JSValue.jsNumber(stream.rstCode));
+ globalObject.throw("Expected weight to be a number between 1 and 256", .{});
+ return .zero;
+ }
+ stream.weight = @intCast(weight);
+ }
+
+ if (weight < 1 or weight > 256) {
+ stream.state = .CLOSED;
+ stream.rstCode = @intFromEnum(ErrorCode.INTERNAL_ERROR);
+ this.dispatchWithExtra(.onStreamError, JSC.JSValue.jsNumber(stream_id), JSC.JSValue.jsNumber(stream.rstCode));
+ globalObject.throw("Expected weight to be a number between 1 and 256", .{});
+ return .zero;
+ }
+ stream.weight = @intCast(weight);
+ }
+
+ if (options.get(globalObject, "waitForTrailers")) |trailes_js| {
+ if (trailes_js.isBoolean()) {
+ waitForTrailers = trailes_js.asBoolean();
+ stream.waitForTrailers = waitForTrailers;
+ }
+ }
+
+ if (options.get(globalObject, "signal")) |signal_arg| {
+ if (signal_arg.as(JSC.WebCore.AbortSignal)) |signal_| {
+ signal = signal_;
+ }
+ }
+ }
+
+ if (signal) |signal_| {
+ stream.attachSignal(signal_);
+ }
+
+ var length: usize = encoded_size;
+ if (has_priority) {
+ length += 5;
+ flags |= @intFromEnum(HeadersFrameFlags.PRIORITY);
+ }
+
+ log("request encoded_size {}", .{encoded_size});
+ var frame: FrameHeader = .{
+ .type = @intFromEnum(FrameType.HTTP_FRAME_HEADERS),
+ .flags = flags,
+ .streamIdentifier = stream.id,
+ .length = @intCast(encoded_size),
+ };
+ var writer = this.toWriter();
+ frame.write(@TypeOf(writer), writer);
+ //https://datatracker.ietf.org/doc/html/rfc7540#section-6.2
+ if (has_priority) {
+ var stream_identifier: UInt31WithReserved = .{
+ .reserved = exclusive,
+ .uint31 = @intCast(parent),
+ };
+
+ var priority: StreamPriority = .{
+ .streamIdentifier = stream_identifier.toUInt32(),
+ .weight = @intCast(weight),
+ };
+
+ priority.write(@TypeOf(writer), writer);
+ }
+
+ this.write(buffer[0..encoded_size]);
+
+ if (end_stream) {
+ stream.state = .HALF_CLOSED_LOCAL;
+
+ if (waitForTrailers) {
+ this.dispatch(.onWantTrailers, JSC.JSValue.jsNumber(stream.id));
+ }
+ }
+
+ return JSC.JSValue.jsNumber(stream.id);
+ }
+
pub fn read(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue {
JSC.markBinding(@src());
const args_list = callframe.arguments(1);
@@ -492,10 +1822,16 @@ pub const H2FrameParser = struct {
globalObject.throw("Expected \"context\" option", .{});
return null;
};
- const handlers = Handlers.fromJS(globalObject, options, &exception) orelse {
+ var handler_js = JSC.JSValue.zero;
+ if(options.get(globalObject, "handlers")) |handlers_| {
+ handler_js = handlers_;
+ }
+ const handlers = Handlers.fromJS(globalObject, handler_js, &exception) orelse {
globalObject.throwValue(exception.?.value());
return null;
};
+
+
const allocator = getAllocator(globalObject);
var this = allocator.create(H2FrameParser) catch unreachable;
@@ -509,36 +1845,43 @@ pub const H2FrameParser = struct {
.capacity = 0,
},
},
+ .streams = bun.U32HashMap(Stream).init(bun.default_allocator),
};
+ if(options.get(globalObject, "settings")) |settings_js| {
+ if(settings_js.isEmptyOrUndefinedOrNull()) {
+ if(!this.setSettingsFromJSValue(globalObject, settings_js)) {
+ this.deinit();
+ return null;
+ }
+ }
+ }
this.handlers.protect();
this.strong_ctx.set(globalObject, context_obj);
- // PREFACE + Settings Frame
- var preface_buffer: [24 + FrameHeader.byteSize + FullSettingsPayload.byteSize]u8 = undefined;
- @memset(&preface_buffer, 0);
- var preface_stream = std.io.fixedBufferStream(&preface_buffer);
- const writer = preface_stream.writer();
- _ = writer.write("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") catch 0;
- var settingsHeader: FrameHeader = .{
- .type = @intFromEnum(FrameType.HTTP_FRAME_SETTINGS),
- .flags = 0,
- .streamIdentifier = 0,
- .length = 36,
- };
- settingsHeader.write(@TypeOf(writer), writer);
- this.localSettings.write(@TypeOf(writer), writer);
- this.write(&preface_buffer);
+ this.sendPrefaceAndSettings();
return this;
}
- pub fn finalize(
- this: *H2FrameParser,
- ) callconv(.C) void {
+ pub fn deinit(this: *H2FrameParser) void {
var allocator = this.allocator;
+ defer allocator.destroy(this);
this.strong_ctx.deinit();
this.handlers.unprotect();
this.readBuffer.deinit();
- allocator.destroy(this);
+
+ var it = this.streams.iterator();
+ while (it.next()) |*entry| {
+ var stream = entry.value_ptr.*;
+ stream.deinit();
+ }
+
+ this.streams.deinit();
+ }
+
+ pub fn finalize(
+ this: *H2FrameParser,
+ ) callconv(.C) void {
+ this.deinit();
}
};
diff --git a/src/bun.js/api/bun/lshpack.translated.zig b/src/bun.js/api/bun/lshpack.translated.zig
new file mode 100644
index 000000000..725384d6c
--- /dev/null
+++ b/src/bun.js/api/bun/lshpack.translated.zig
@@ -0,0 +1,442 @@
+pub const __builtin_bswap16 = @import("std").zig.c_builtins.__builtin_bswap16;
+pub const __builtin_bswap32 = @import("std").zig.c_builtins.__builtin_bswap32;
+pub const __builtin_bswap64 = @import("std").zig.c_builtins.__builtin_bswap64;
+pub const __builtin_signbit = @import("std").zig.c_builtins.__builtin_signbit;
+pub const __builtin_signbitf = @import("std").zig.c_builtins.__builtin_signbitf;
+pub const __builtin_popcount = @import("std").zig.c_builtins.__builtin_popcount;
+pub const __builtin_ctz = @import("std").zig.c_builtins.__builtin_ctz;
+pub const __builtin_clz = @import("std").zig.c_builtins.__builtin_clz;
+pub const __builtin_sqrt = @import("std").zig.c_builtins.__builtin_sqrt;
+pub const __builtin_sqrtf = @import("std").zig.c_builtins.__builtin_sqrtf;
+pub const __builtin_sin = @import("std").zig.c_builtins.__builtin_sin;
+pub const __builtin_sinf = @import("std").zig.c_builtins.__builtin_sinf;
+pub const __builtin_cos = @import("std").zig.c_builtins.__builtin_cos;
+pub const __builtin_cosf = @import("std").zig.c_builtins.__builtin_cosf;
+pub const __builtin_exp = @import("std").zig.c_builtins.__builtin_exp;
+pub const __builtin_expf = @import("std").zig.c_builtins.__builtin_expf;
+pub const __builtin_exp2 = @import("std").zig.c_builtins.__builtin_exp2;
+pub const __builtin_exp2f = @import("std").zig.c_builtins.__builtin_exp2f;
+pub const __builtin_log = @import("std").zig.c_builtins.__builtin_log;
+pub const __builtin_logf = @import("std").zig.c_builtins.__builtin_logf;
+pub const __builtin_log2 = @import("std").zig.c_builtins.__builtin_log2;
+pub const __builtin_log2f = @import("std").zig.c_builtins.__builtin_log2f;
+pub const __builtin_log10 = @import("std").zig.c_builtins.__builtin_log10;
+pub const __builtin_log10f = @import("std").zig.c_builtins.__builtin_log10f;
+pub const __builtin_abs = @import("std").zig.c_builtins.__builtin_abs;
+pub const __builtin_fabs = @import("std").zig.c_builtins.__builtin_fabs;
+pub const __builtin_fabsf = @import("std").zig.c_builtins.__builtin_fabsf;
+pub const __builtin_floor = @import("std").zig.c_builtins.__builtin_floor;
+pub const __builtin_floorf = @import("std").zig.c_builtins.__builtin_floorf;
+pub const __builtin_ceil = @import("std").zig.c_builtins.__builtin_ceil;
+pub const __builtin_ceilf = @import("std").zig.c_builtins.__builtin_ceilf;
+pub const __builtin_trunc = @import("std").zig.c_builtins.__builtin_trunc;
+pub const __builtin_truncf = @import("std").zig.c_builtins.__builtin_truncf;
+pub const __builtin_round = @import("std").zig.c_builtins.__builtin_round;
+pub const __builtin_roundf = @import("std").zig.c_builtins.__builtin_roundf;
+pub const __builtin_strlen = @import("std").zig.c_builtins.__builtin_strlen;
+pub const __builtin_strcmp = @import("std").zig.c_builtins.__builtin_strcmp;
+pub const __builtin_object_size = @import("std").zig.c_builtins.__builtin_object_size;
+pub const __builtin___memset_chk = @import("std").zig.c_builtins.__builtin___memset_chk;
+pub const __builtin_memset = @import("std").zig.c_builtins.__builtin_memset;
+pub const __builtin___memcpy_chk = @import("std").zig.c_builtins.__builtin___memcpy_chk;
+pub const __builtin_memcpy = @import("std").zig.c_builtins.__builtin_memcpy;
+pub const __builtin_expect = @import("std").zig.c_builtins.__builtin_expect;
+pub const __builtin_nanf = @import("std").zig.c_builtins.__builtin_nanf;
+pub const __builtin_huge_valf = @import("std").zig.c_builtins.__builtin_huge_valf;
+pub const __builtin_inff = @import("std").zig.c_builtins.__builtin_inff;
+pub const __builtin_isnan = @import("std").zig.c_builtins.__builtin_isnan;
+pub const __builtin_isinf = @import("std").zig.c_builtins.__builtin_isinf;
+pub const __builtin_isinf_sign = @import("std").zig.c_builtins.__builtin_isinf_sign;
+pub const __has_builtin = @import("std").zig.c_builtins.__has_builtin;
+pub const __builtin_assume = @import("std").zig.c_builtins.__builtin_assume;
+pub const __builtin_unreachable = @import("std").zig.c_builtins.__builtin_unreachable;
+pub const __builtin_constant_p = @import("std").zig.c_builtins.__builtin_constant_p;
+pub const __builtin_mul_overflow = @import("std").zig.c_builtins.__builtin_mul_overflow;
+pub const __u_char = u8;
+pub const __u_short = c_ushort;
+pub const __u_int = c_uint;
+pub const __u_long = c_ulong;
+pub const __int8_t = i8;
+pub const __uint8_t = u8;
+pub const __int16_t = c_short;
+pub const __uint16_t = c_ushort;
+pub const __int32_t = c_int;
+pub const __uint32_t = c_uint;
+pub const __int64_t = c_long;
+pub const __uint64_t = c_ulong;
+pub const __int_least8_t = __int8_t;
+pub const __uint_least8_t = __uint8_t;
+pub const __int_least16_t = __int16_t;
+pub const __uint_least16_t = __uint16_t;
+pub const __int_least32_t = __int32_t;
+pub const __uint_least32_t = __uint32_t;
+pub const __int_least64_t = __int64_t;
+pub const __uint_least64_t = __uint64_t;
+pub const __quad_t = c_long;
+pub const __u_quad_t = c_ulong;
+pub const __intmax_t = c_long;
+pub const __uintmax_t = c_ulong;
+pub const __dev_t = c_ulong;
+pub const __uid_t = c_uint;
+pub const __gid_t = c_uint;
+pub const __ino_t = c_ulong;
+pub const __ino64_t = c_ulong;
+pub const __mode_t = c_uint;
+pub const __nlink_t = c_ulong;
+pub const __off_t = c_long;
+pub const __off64_t = c_long;
+pub const __pid_t = c_int;
+pub const __fsid_t = extern struct {
+ __val: [2]c_int,
+};
+pub const __clock_t = c_long;
+pub const __rlim_t = c_ulong;
+pub const __rlim64_t = c_ulong;
+pub const __id_t = c_uint;
+pub const __time_t = c_long;
+pub const __useconds_t = c_uint;
+pub const __suseconds_t = c_long;
+pub const __suseconds64_t = c_long;
+pub const __daddr_t = c_int;
+pub const __key_t = c_int;
+pub const __clockid_t = c_int;
+pub const __timer_t = ?*anyopaque;
+pub const __blksize_t = c_long;
+pub const __blkcnt_t = c_long;
+pub const __blkcnt64_t = c_long;
+pub const __fsblkcnt_t = c_ulong;
+pub const __fsblkcnt64_t = c_ulong;
+pub const __fsfilcnt_t = c_ulong;
+pub const __fsfilcnt64_t = c_ulong;
+pub const __fsword_t = c_long;
+pub const __ssize_t = c_long;
+pub const __syscall_slong_t = c_long;
+pub const __syscall_ulong_t = c_ulong;
+pub const __loff_t = __off64_t;
+pub const __caddr_t = [*c]u8;
+pub const __intptr_t = c_long;
+pub const __socklen_t = c_uint;
+pub const __sig_atomic_t = c_int;
+pub const int_least8_t = __int_least8_t;
+pub const int_least16_t = __int_least16_t;
+pub const int_least32_t = __int_least32_t;
+pub const int_least64_t = __int_least64_t;
+pub const uint_least8_t = __uint_least8_t;
+pub const uint_least16_t = __uint_least16_t;
+pub const uint_least32_t = __uint_least32_t;
+pub const uint_least64_t = __uint_least64_t;
+pub const int_fast8_t = i8;
+pub const int_fast16_t = c_long;
+pub const int_fast32_t = c_long;
+pub const int_fast64_t = c_long;
+pub const uint_fast8_t = u8;
+pub const uint_fast16_t = c_ulong;
+pub const uint_fast32_t = c_ulong;
+pub const uint_fast64_t = c_ulong;
+pub const intmax_t = __intmax_t;
+pub const uintmax_t = __uintmax_t;
+pub const lsxpack_strlen_t = u16;
+pub const LSXPACK_HPACK_VAL_MATCHED: c_int = 1;
+pub const LSXPACK_QPACK_IDX: c_int = 2;
+pub const LSXPACK_APP_IDX: c_int = 4;
+pub const LSXPACK_NAME_HASH: c_int = 8;
+pub const LSXPACK_NAMEVAL_HASH: c_int = 16;
+pub const LSXPACK_VAL_MATCHED: c_int = 32;
+pub const LSXPACK_NEVER_INDEX: c_int = 64;
+pub const enum_lsxpack_flag = c_uint;
+
+// /// When header are decoded, it should be stored to @buf starting from @name_offset,
+// /// <name>: <value>\r\n
+// /// So, it can be used directly as HTTP/1.1 header. there are 4 extra characters
+// /// added.
+// ///
+// /// limitation: we currently does not support total header size > 64KB.
+pub const struct_lsxpack_header = extern struct {
+ /// the buffer for headers
+ buf: [*]u8 = undefined,
+ /// hash value for name
+ name_hash: __uint32_t = 0,
+ /// hash value for name + value
+ nameval_hash: __uint32_t = 0,
+ /// the offset for name in the buffer
+ name_offset: lsxpack_strlen_t = 0,
+ /// the length of name
+ name_len: lsxpack_strlen_t = 0,
+ /// the offset for value in the buffer
+ val_offset: lsxpack_strlen_t = 0,
+ /// the length of value
+ val_len: lsxpack_strlen_t = 0,
+ /// mainly for cookie value chain
+ chain_next_idx: __uint16_t = 0,
+ /// HPACK static table index
+ hpack_index: __uint8_t = 0,
+ /// QPACK static table index
+ qpack_index: __uint8_t = 0,
+ /// APP header index
+ app_index: __uint8_t = 0,
+ /// combination of lsxpack_flag
+ flags: u8 = 0,
+ /// control to disable index or not
+ indexed_type: __uint8_t = 0,
+ /// num of extra bytes written to decoded buffer
+ dec_overhead: __uint8_t = 0,
+};
+pub const lsxpack_header_t = struct_lsxpack_header;
+
+pub fn lsxpack_header_set_offset2(arg_hdr: *lsxpack_header_t, arg_buf: [*c]u8, arg_name_offset: usize, arg_name_len: usize, arg_val_offset: usize, arg_val_len: usize) callconv(.C) void {
+ arg_hdr.* = .{};
+ arg_hdr.buf = arg_buf;
+ arg_hdr.name_offset = @truncate(arg_name_offset);
+ arg_hdr.val_offset = @truncate(arg_val_offset);
+ arg_hdr.name_len = @truncate(arg_name_len);
+ arg_hdr.val_len = @truncate(arg_val_len);
+}
+
+pub fn lsxpack_header_prepare_decode(arg_hdr: *lsxpack_header_t, arg_out: [*c]u8, arg_offset: usize, arg_len: usize) callconv(.C) void {
+ arg_hdr.* = .{};
+ arg_hdr.buf = arg_out;
+ arg_hdr.name_offset = @truncate(arg_offset);
+ if (arg_len > LSXPACK_MAX_STRLEN) {
+ arg_hdr.val_len = LSXPACK_MAX_STRLEN;
+ } else {
+ arg_hdr.val_len = @truncate(arg_len);
+ }
+}
+
+pub fn lsxpack_header_get_name(hdr: *lsxpack_header_t) []const u8 {
+ if (hdr.name_len != 0) return hdr.buf[hdr.name_offset .. hdr.name_offset + hdr.name_len];
+ return "";
+}
+pub fn lsxpack_header_get_value(hdr: *lsxpack_header_t) []const u8 {
+ if (hdr.val_len != 0) return hdr.buf[hdr.val_offset .. hdr.val_offset + hdr.val_len];
+ return "";
+}
+pub fn lsxpack_header_get_dec_size(arg_hdr: ?*const lsxpack_header_t) callconv(.C) usize {
+ var hdr = arg_hdr;
+ return @as(usize, @bitCast(@as(c_long, (@as(c_int, @bitCast(@as(c_uint, hdr.*.name_len))) + @as(c_int, @bitCast(@as(c_uint, hdr.*.val_len)))) + @as(c_int, @bitCast(@as(c_uint, hdr.*.dec_overhead))))));
+}
+pub fn lsxpack_header_mark_val_changed(arg_hdr: ?*lsxpack_header_t) callconv(.C) void {
+ var hdr = arg_hdr;
+ hdr.*.flags = @as(c_uint, @bitCast(@as(c_int, @bitCast(hdr.*.flags)) & ~((LSXPACK_HPACK_VAL_MATCHED | LSXPACK_VAL_MATCHED) | LSXPACK_NAMEVAL_HASH)));
+}
+pub const struct_lshpack_enc_table_entry = opaque {};
+pub const struct_lshpack_enc_head = extern struct {
+ stqh_first: ?*struct_lshpack_enc_table_entry,
+ stqh_last: [*c]?*struct_lshpack_enc_table_entry,
+};
+pub const struct_lshpack_double_enc_head = opaque {};
+pub const LSHPACK_ENC_USE_HIST: c_int = 1;
+const enum_unnamed_1 = c_uint;
+pub const struct_lshpack_enc = extern struct {
+ hpe_cur_capacity: c_uint,
+ hpe_max_capacity: c_uint,
+ hpe_next_id: c_uint,
+ hpe_nelem: c_uint,
+ hpe_nbits: c_uint,
+ hpe_all_entries: struct_lshpack_enc_head,
+ hpe_buckets: ?*struct_lshpack_double_enc_head,
+ hpe_hist_buf: [*c]u32,
+ hpe_hist_size: c_uint,
+ hpe_hist_idx: c_uint,
+ hpe_hist_wrapped: c_int,
+ hpe_flags: enum_unnamed_1,
+};
+pub const struct_lshpack_arr = extern struct {
+ nalloc: c_uint,
+ nelem: c_uint,
+ off: c_uint,
+ els: [*c]usize,
+};
+pub const struct_lshpack_dec = extern struct {
+ hpd_dyn_table: struct_lshpack_arr,
+ hpd_max_capacity: c_uint,
+ hpd_cur_max_capacity: c_uint,
+ hpd_cur_capacity: c_uint,
+ hpd_state: c_uint,
+};
+pub const LSHPACK_HDR_UNKNOWN: c_int = 0;
+pub const LSHPACK_HDR_AUTHORITY: c_int = 1;
+pub const LSHPACK_HDR_METHOD_GET: c_int = 2;
+pub const LSHPACK_HDR_METHOD_POST: c_int = 3;
+pub const LSHPACK_HDR_PATH: c_int = 4;
+pub const LSHPACK_HDR_PATH_INDEX_HTML: c_int = 5;
+pub const LSHPACK_HDR_SCHEME_HTTP: c_int = 6;
+pub const LSHPACK_HDR_SCHEME_HTTPS: c_int = 7;
+pub const LSHPACK_HDR_STATUS_200: c_int = 8;
+pub const LSHPACK_HDR_STATUS_204: c_int = 9;
+pub const LSHPACK_HDR_STATUS_206: c_int = 10;
+pub const LSHPACK_HDR_STATUS_304: c_int = 11;
+pub const LSHPACK_HDR_STATUS_400: c_int = 12;
+pub const LSHPACK_HDR_STATUS_404: c_int = 13;
+pub const LSHPACK_HDR_STATUS_500: c_int = 14;
+pub const LSHPACK_HDR_ACCEPT_CHARSET: c_int = 15;
+pub const LSHPACK_HDR_ACCEPT_ENCODING: c_int = 16;
+pub const LSHPACK_HDR_ACCEPT_LANGUAGE: c_int = 17;
+pub const LSHPACK_HDR_ACCEPT_RANGES: c_int = 18;
+pub const LSHPACK_HDR_ACCEPT: c_int = 19;
+pub const LSHPACK_HDR_ACCESS_CONTROL_ALLOW_ORIGIN: c_int = 20;
+pub const LSHPACK_HDR_AGE: c_int = 21;
+pub const LSHPACK_HDR_ALLOW: c_int = 22;
+pub const LSHPACK_HDR_AUTHORIZATION: c_int = 23;
+pub const LSHPACK_HDR_CACHE_CONTROL: c_int = 24;
+pub const LSHPACK_HDR_CONTENT_DISPOSITION: c_int = 25;
+pub const LSHPACK_HDR_CONTENT_ENCODING: c_int = 26;
+pub const LSHPACK_HDR_CONTENT_LANGUAGE: c_int = 27;
+pub const LSHPACK_HDR_CONTENT_LENGTH: c_int = 28;
+pub const LSHPACK_HDR_CONTENT_LOCATION: c_int = 29;
+pub const LSHPACK_HDR_CONTENT_RANGE: c_int = 30;
+pub const LSHPACK_HDR_CONTENT_TYPE: c_int = 31;
+pub const LSHPACK_HDR_COOKIE: c_int = 32;
+pub const LSHPACK_HDR_DATE: c_int = 33;
+pub const LSHPACK_HDR_ETAG: c_int = 34;
+pub const LSHPACK_HDR_EXPECT: c_int = 35;
+pub const LSHPACK_HDR_EXPIRES: c_int = 36;
+pub const LSHPACK_HDR_FROM: c_int = 37;
+pub const LSHPACK_HDR_HOST: c_int = 38;
+pub const LSHPACK_HDR_IF_MATCH: c_int = 39;
+pub const LSHPACK_HDR_IF_MODIFIED_SINCE: c_int = 40;
+pub const LSHPACK_HDR_IF_NONE_MATCH: c_int = 41;
+pub const LSHPACK_HDR_IF_RANGE: c_int = 42;
+pub const LSHPACK_HDR_IF_UNMODIFIED_SINCE: c_int = 43;
+pub const LSHPACK_HDR_LAST_MODIFIED: c_int = 44;
+pub const LSHPACK_HDR_LINK: c_int = 45;
+pub const LSHPACK_HDR_LOCATION: c_int = 46;
+pub const LSHPACK_HDR_MAX_FORWARDS: c_int = 47;
+pub const LSHPACK_HDR_PROXY_AUTHENTICATE: c_int = 48;
+pub const LSHPACK_HDR_PROXY_AUTHORIZATION: c_int = 49;
+pub const LSHPACK_HDR_RANGE: c_int = 50;
+pub const LSHPACK_HDR_REFERER: c_int = 51;
+pub const LSHPACK_HDR_REFRESH: c_int = 52;
+pub const LSHPACK_HDR_RETRY_AFTER: c_int = 53;
+pub const LSHPACK_HDR_SERVER: c_int = 54;
+pub const LSHPACK_HDR_SET_COOKIE: c_int = 55;
+pub const LSHPACK_HDR_STRICT_TRANSPORT_SECURITY: c_int = 56;
+pub const LSHPACK_HDR_TRANSFER_ENCODING: c_int = 57;
+pub const LSHPACK_HDR_USER_AGENT: c_int = 58;
+pub const LSHPACK_HDR_VARY: c_int = 59;
+pub const LSHPACK_HDR_VIA: c_int = 60;
+pub const LSHPACK_HDR_WWW_AUTHENTICATE: c_int = 61;
+pub const LSHPACK_HDR_TOBE_INDEXED: c_int = 255;
+pub const enum_lshpack_static_hdr_idx = c_uint;
+pub extern fn lshpack_enc_init([*c]struct_lshpack_enc) c_int;
+pub extern fn lshpack_enc_cleanup([*c]struct_lshpack_enc) void;
+pub extern fn lshpack_enc_encode(henc: [*c]struct_lshpack_enc, dst: [*c]const u8, dst_end: [*c]u8, input: ?*struct_lsxpack_header) [*c]u8;
+pub extern fn lshpack_enc_set_max_capacity([*c]struct_lshpack_enc, c_uint) void;
+pub extern fn lshpack_enc_use_hist([*c]struct_lshpack_enc, on: c_int) c_int;
+pub extern fn lshpack_enc_hist_used([*c]const struct_lshpack_enc) c_int;
+pub extern fn lshpack_dec_init([*c]struct_lshpack_dec) void;
+pub extern fn lshpack_dec_cleanup([*c]struct_lshpack_dec) void;
+pub extern fn lshpack_dec_decode(dec: [*c]struct_lshpack_dec, src: *[*]const u8, src_end: [*c]const u8, output: ?*struct_lsxpack_header) c_int;
+pub extern fn lshpack_dec_set_max_capacity([*c]struct_lshpack_dec, c_uint) void;
+pub extern fn lshpack_enc_get_stx_tab_id(?*struct_lsxpack_header) c_uint;
+
+pub const __INT64_C = @import("std").zig.c_translation.Macros.L_SUFFIX;
+pub const __UINT64_C = @import("std").zig.c_translation.Macros.UL_SUFFIX;
+pub const INT8_MIN = -@as(c_int, 128);
+pub const INT16_MIN = -@as(c_int, 32767) - @as(c_int, 1);
+pub const INT32_MIN = -@import("std").zig.c_translation.promoteIntLiteral(c_int, 2147483647, .decimal) - @as(c_int, 1);
+pub const INT64_MIN = -__INT64_C(@import("std").zig.c_translation.promoteIntLiteral(c_int, 9223372036854775807, .decimal)) - @as(c_int, 1);
+pub const INT8_MAX = @as(c_int, 127);
+pub const INT16_MAX = @as(c_int, 32767);
+pub const INT32_MAX = @import("std").zig.c_translation.promoteIntLiteral(c_int, 2147483647, .decimal);
+pub const INT64_MAX = __INT64_C(@import("std").zig.c_translation.promoteIntLiteral(c_int, 9223372036854775807, .decimal));
+pub const UINT8_MAX = @as(c_int, 255);
+pub const UINT16_MAX = @import("std").zig.c_translation.promoteIntLiteral(c_int, 65535, .decimal);
+pub const UINT32_MAX = @import("std").zig.c_translation.promoteIntLiteral(c_uint, 4294967295, .decimal);
+pub const UINT64_MAX = __UINT64_C(@import("std").zig.c_translation.promoteIntLiteral(c_int, 18446744073709551615, .decimal));
+pub const INT_LEAST8_MIN = -@as(c_int, 128);
+pub const INT_LEAST16_MIN = -@as(c_int, 32767) - @as(c_int, 1);
+pub const INT_LEAST32_MIN = -@import("std").zig.c_translation.promoteIntLiteral(c_int, 2147483647, .decimal) - @as(c_int, 1);
+pub const INT_LEAST64_MIN = -__INT64_C(@import("std").zig.c_translation.promoteIntLiteral(c_int, 9223372036854775807, .decimal)) - @as(c_int, 1);
+pub const INT_LEAST8_MAX = @as(c_int, 127);
+pub const INT_LEAST16_MAX = @as(c_int, 32767);
+pub const INT_LEAST32_MAX = @import("std").zig.c_translation.promoteIntLiteral(c_int, 2147483647, .decimal);
+pub const INT_LEAST64_MAX = __INT64_C(@import("std").zig.c_translation.promoteIntLiteral(c_int, 9223372036854775807, .decimal));
+pub const UINT_LEAST8_MAX = @as(c_int, 255);
+pub const UINT_LEAST16_MAX = @import("std").zig.c_translation.promoteIntLiteral(c_int, 65535, .decimal);
+pub const UINT_LEAST32_MAX = @import("std").zig.c_translation.promoteIntLiteral(c_uint, 4294967295, .decimal);
+pub const UINT_LEAST64_MAX = __UINT64_C(@import("std").zig.c_translation.promoteIntLiteral(c_int, 18446744073709551615, .decimal));
+pub const INT_FAST8_MIN = -@as(c_int, 128);
+pub const INT_FAST16_MIN = -@import("std").zig.c_translation.promoteIntLiteral(c_long, 9223372036854775807, .decimal) - @as(c_int, 1);
+pub const INT_FAST32_MIN = -@import("std").zig.c_translation.promoteIntLiteral(c_long, 9223372036854775807, .decimal) - @as(c_int, 1);
+pub const INT_FAST64_MIN = -__INT64_C(@import("std").zig.c_translation.promoteIntLiteral(c_int, 9223372036854775807, .decimal)) - @as(c_int, 1);
+pub const INT_FAST8_MAX = @as(c_int, 127);
+pub const INT_FAST16_MAX = @import("std").zig.c_translation.promoteIntLiteral(c_long, 9223372036854775807, .decimal);
+pub const INT_FAST32_MAX = @import("std").zig.c_translation.promoteIntLiteral(c_long, 9223372036854775807, .decimal);
+pub const INT_FAST64_MAX = __INT64_C(@import("std").zig.c_translation.promoteIntLiteral(c_int, 9223372036854775807, .decimal));
+pub const UINT_FAST8_MAX = @as(c_int, 255);
+pub const UINT_FAST16_MAX = @import("std").zig.c_translation.promoteIntLiteral(c_ulong, 18446744073709551615, .decimal);
+pub const UINT_FAST32_MAX = @import("std").zig.c_translation.promoteIntLiteral(c_ulong, 18446744073709551615, .decimal);
+pub const UINT_FAST64_MAX = __UINT64_C(@import("std").zig.c_translation.promoteIntLiteral(c_int, 18446744073709551615, .decimal));
+pub const INTPTR_MIN = -@import("std").zig.c_translation.promoteIntLiteral(c_long, 9223372036854775807, .decimal) - @as(c_int, 1);
+pub const INTPTR_MAX = @import("std").zig.c_translation.promoteIntLiteral(c_long, 9223372036854775807, .decimal);
+pub const UINTPTR_MAX = @import("std").zig.c_translation.promoteIntLiteral(c_ulong, 18446744073709551615, .decimal);
+pub const INTMAX_MIN = -__INT64_C(@import("std").zig.c_translation.promoteIntLiteral(c_int, 9223372036854775807, .decimal)) - @as(c_int, 1);
+pub const INTMAX_MAX = __INT64_C(@import("std").zig.c_translation.promoteIntLiteral(c_int, 9223372036854775807, .decimal));
+pub const UINTMAX_MAX = __UINT64_C(@import("std").zig.c_translation.promoteIntLiteral(c_int, 18446744073709551615, .decimal));
+pub const PTRDIFF_MIN = -@import("std").zig.c_translation.promoteIntLiteral(c_long, 9223372036854775807, .decimal) - @as(c_int, 1);
+pub const PTRDIFF_MAX = @import("std").zig.c_translation.promoteIntLiteral(c_long, 9223372036854775807, .decimal);
+pub const SIG_ATOMIC_MIN = -@import("std").zig.c_translation.promoteIntLiteral(c_int, 2147483647, .decimal) - @as(c_int, 1);
+pub const SIG_ATOMIC_MAX = @import("std").zig.c_translation.promoteIntLiteral(c_int, 2147483647, .decimal);
+pub const SIZE_MAX = @import("std").zig.c_translation.promoteIntLiteral(c_ulong, 18446744073709551615, .decimal);
+pub const WINT_MIN = @as(c_uint, 0);
+pub const WINT_MAX = @import("std").zig.c_translation.promoteIntLiteral(c_uint, 4294967295, .decimal);
+pub inline fn INT8_C(c: anytype) @TypeOf(c) {
+ return c;
+}
+pub inline fn INT16_C(c: anytype) @TypeOf(c) {
+ return c;
+}
+pub inline fn INT32_C(c: anytype) @TypeOf(c) {
+ return c;
+}
+pub const INT64_C = @import("std").zig.c_translation.Macros.L_SUFFIX;
+pub inline fn UINT8_C(c: anytype) @TypeOf(c) {
+ return c;
+}
+pub inline fn UINT16_C(c: anytype) @TypeOf(c) {
+ return c;
+}
+pub const UINT32_C = @import("std").zig.c_translation.Macros.U_SUFFIX;
+pub const UINT64_C = @import("std").zig.c_translation.Macros.UL_SUFFIX;
+pub const INTMAX_C = @import("std").zig.c_translation.Macros.L_SUFFIX;
+pub const UINTMAX_C = @import("std").zig.c_translation.Macros.UL_SUFFIX;
+pub const LSXPACK_HEADER_H_v206 = "";
+pub const _ASSERT_H = @as(c_int, 1);
+pub const _ASSERT_H_DECLS = "";
+pub const _STRING_H = @as(c_int, 1);
+pub const __need_size_t = "";
+pub const __need_NULL = "";
+pub const _SIZE_T = "";
+pub const NULL = @import("std").zig.c_translation.cast(?*anyopaque, @as(c_int, 0));
+pub const _BITS_TYPES_LOCALE_T_H = @as(c_int, 1);
+pub const _BITS_TYPES___LOCALE_T_H = @as(c_int, 1);
+pub const _STRINGS_H = @as(c_int, 1);
+pub const LSXPACK_MAX_STRLEN = UINT16_MAX;
+pub const LSXPACK_DEL = @import("std").zig.c_translation.cast([*c]u8, NULL);
+pub const LSHPACK_MAJOR_VERSION = @as(c_int, 2);
+pub const LSHPACK_MINOR_VERSION = @as(c_int, 3);
+pub const LSHPACK_PATCH_VERSION = @as(c_int, 0);
+pub const lshpack_strlen_t = lsxpack_strlen_t;
+pub const LSHPACK_MAX_STRLEN = LSXPACK_MAX_STRLEN;
+pub const LSHPACK_DEC_HTTP1X_OUTPUT = @as(c_int, 1);
+pub const LSHPACK_DEC_CALC_HASH = @as(c_int, 1);
+pub const LSHPACK_MAX_INDEX = @as(c_int, 61);
+pub const LSHPACK_ERR_MORE_BUF = -@as(c_int, 3);
+pub const LSHPACK_ERR_TOO_LARGE = -@as(c_int, 2);
+pub const LSHPACK_ERR_BAD_DATA = -@as(c_int, 1);
+pub const LSHPACK_OK = @as(c_int, 0);
+pub const LSHPACK_DEC_HTTP1X_EXTRA = @as(c_int, 2);
+pub inline fn lshpack_dec_extra_bytes(dec_: anytype) @TypeOf(@as(c_int, 4)) {
+ _ = @TypeOf(dec_);
+ return @as(c_int, 4);
+}
+pub const lsxpack_flag = enum_lsxpack_flag;
+pub const lsxpack_header = struct_lsxpack_header;
+pub const lshpack_enc_table_entry = struct_lshpack_enc_table_entry;
+pub const lshpack_enc_head = struct_lshpack_enc_head;
+pub const lshpack_double_enc_head = struct_lshpack_double_enc_head;
+pub const lshpack_enc = struct_lshpack_enc;
+pub const lshpack_arr = struct_lshpack_arr;
+pub const lshpack_dec = struct_lshpack_dec;
+pub const lshpack_static_hdr_idx = enum_lshpack_static_hdr_idx;
diff --git a/src/bun.js/api/h2.classes.ts b/src/bun.js/api/h2.classes.ts
index caadb5c40..3b53f3261 100644
--- a/src/bun.js/api/h2.classes.ts
+++ b/src/bun.js/api/h2.classes.ts
@@ -5,6 +5,26 @@ export default [
name: "H2FrameParser",
JSType: "0b11101110",
proto: {
+ request: {
+ fn: "request",
+ length: 2,
+ },
+ ping: {
+ fn: "ping",
+ length: 0,
+ },
+ goaway: {
+ fn: "goaway",
+ length: 3,
+ },
+ getCurrentState: {
+ fn: "getCurrentState",
+ length: 0,
+ },
+ settings: {
+ fn: "updateSettings",
+ length: 1,
+ },
read: {
fn: "read",
length: 1,
diff --git a/src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h b/src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h
index 3c3aa5669..5347e037b 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h
+++ b/src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h
@@ -21,7 +21,8 @@ std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForExpectStringMatching;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForFFI;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForFSWatcher;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForFileSystemRouter;
-std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForFileSystemRouterConstructor;std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForHTMLRewriter;
+std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForFileSystemRouterConstructor;std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForH2FrameParser;
+std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForH2FrameParserConstructor;std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForHTMLRewriter;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForHTMLRewriterConstructor;std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForHTTPSServer;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForHTTPServer;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForListener;
diff --git a/src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h b/src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h
index a521090c0..db9036b98 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h
+++ b/src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h
@@ -21,7 +21,8 @@ std::unique_ptr<IsoSubspace> m_subspaceForExpectStringMatching;
std::unique_ptr<IsoSubspace> m_subspaceForFFI;
std::unique_ptr<IsoSubspace> m_subspaceForFSWatcher;
std::unique_ptr<IsoSubspace> m_subspaceForFileSystemRouter;
-std::unique_ptr<IsoSubspace> m_subspaceForFileSystemRouterConstructor;std::unique_ptr<IsoSubspace> m_subspaceForHTMLRewriter;
+std::unique_ptr<IsoSubspace> m_subspaceForFileSystemRouterConstructor;std::unique_ptr<IsoSubspace> m_subspaceForH2FrameParser;
+std::unique_ptr<IsoSubspace> m_subspaceForH2FrameParserConstructor;std::unique_ptr<IsoSubspace> m_subspaceForHTMLRewriter;
std::unique_ptr<IsoSubspace> m_subspaceForHTMLRewriterConstructor;std::unique_ptr<IsoSubspace> m_subspaceForHTTPSServer;
std::unique_ptr<IsoSubspace> m_subspaceForHTTPServer;
std::unique_ptr<IsoSubspace> m_subspaceForListener;
diff --git a/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h b/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h
index 0596372b4..2dc660f5b 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h
+++ b/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h
@@ -136,6 +136,12 @@ JSC::Structure* JSFileSystemRouterStructure() { return m_JSFileSystemRouter.getI
JSC::LazyClassStructure m_JSFileSystemRouter;
bool hasJSFileSystemRouterSetterValue { false };
mutable JSC::WriteBarrier<JSC::Unknown> m_JSFileSystemRouterSetterValue;
+JSC::Structure* JSH2FrameParserStructure() { return m_JSH2FrameParser.getInitializedOnMainThread(this); }
+ JSC::JSObject* JSH2FrameParserConstructor() { return m_JSH2FrameParser.constructorInitializedOnMainThread(this); }
+ JSC::JSValue JSH2FrameParserPrototype() { return m_JSH2FrameParser.prototypeInitializedOnMainThread(this); }
+ JSC::LazyClassStructure m_JSH2FrameParser;
+ bool hasJSH2FrameParserSetterValue { false };
+ mutable JSC::WriteBarrier<JSC::Unknown> m_JSH2FrameParserSetterValue;
JSC::Structure* JSHTMLRewriterStructure() { return m_JSHTMLRewriter.getInitializedOnMainThread(this); }
JSC::JSObject* JSHTMLRewriterConstructor() { return m_JSHTMLRewriter.constructorInitializedOnMainThread(this); }
JSC::JSValue JSHTMLRewriterPrototype() { return m_JSHTMLRewriter.prototypeInitializedOnMainThread(this); }
diff --git a/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h b/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h
index 40a473727..1ff623c44 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h
+++ b/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h
@@ -137,6 +137,12 @@ void GlobalObject::initGeneratedLazyClasses() {
init.setStructure(WebCore::JSFileSystemRouter::createStructure(init.vm, init.global, init.prototype));
init.setConstructor(WebCore::JSFileSystemRouter::createConstructor(init.vm, init.global, init.prototype));
});
+ m_JSH2FrameParser.initLater(
+ [](LazyClassStructure::Initializer& init) {
+ init.setPrototype(WebCore::JSH2FrameParser::createPrototype(init.vm, reinterpret_cast<Zig::GlobalObject*>(init.global)));
+ init.setStructure(WebCore::JSH2FrameParser::createStructure(init.vm, init.global, init.prototype));
+ init.setConstructor(WebCore::JSH2FrameParser::createConstructor(init.vm, init.global, init.prototype));
+ });
m_JSHTMLRewriter.initLater(
[](LazyClassStructure::Initializer& init) {
init.setPrototype(WebCore::JSHTMLRewriter::createPrototype(init.vm, reinterpret_cast<Zig::GlobalObject*>(init.global)));
@@ -320,6 +326,7 @@ void GlobalObject::visitGeneratedLazyClasses(GlobalObject *thisObject, Visitor&
thisObject->m_JSFFI.visit(visitor); visitor.append(thisObject->m_JSFFISetterValue);
thisObject->m_JSFSWatcher.visit(visitor); visitor.append(thisObject->m_JSFSWatcherSetterValue);
thisObject->m_JSFileSystemRouter.visit(visitor); visitor.append(thisObject->m_JSFileSystemRouterSetterValue);
+ thisObject->m_JSH2FrameParser.visit(visitor); visitor.append(thisObject->m_JSH2FrameParserSetterValue);
thisObject->m_JSHTMLRewriter.visit(visitor); visitor.append(thisObject->m_JSHTMLRewriterSetterValue);
thisObject->m_JSHTTPSServer.visit(visitor); visitor.append(thisObject->m_JSHTTPSServerSetterValue);
thisObject->m_JSHTTPServer.visit(visitor); visitor.append(thisObject->m_JSHTTPServerSetterValue);
diff --git a/src/bun.js/bindings/ZigGeneratedClasses.cpp b/src/bun.js/bindings/ZigGeneratedClasses.cpp
index 376260383..86a1b3366 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses.cpp
+++ b/src/bun.js/bindings/ZigGeneratedClasses.cpp
@@ -11052,14 +11052,30 @@ extern "C" void H2FrameParserClass__finalize(void*);
extern "C" EncodedJSValue H2FrameParserPrototype__detach(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
JSC_DECLARE_HOST_FUNCTION(H2FrameParserPrototype__detachCallback);
+extern "C" EncodedJSValue H2FrameParserPrototype__goaway(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
+JSC_DECLARE_HOST_FUNCTION(H2FrameParserPrototype__goawayCallback);
+
+extern "C" EncodedJSValue H2FrameParserPrototype__ping(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
+JSC_DECLARE_HOST_FUNCTION(H2FrameParserPrototype__pingCallback);
+
extern "C" EncodedJSValue H2FrameParserPrototype__read(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
JSC_DECLARE_HOST_FUNCTION(H2FrameParserPrototype__readCallback);
+extern "C" EncodedJSValue H2FrameParserPrototype__request(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
+JSC_DECLARE_HOST_FUNCTION(H2FrameParserPrototype__requestCallback);
+
+extern "C" EncodedJSValue H2FrameParserPrototype__updateSettings(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
+JSC_DECLARE_HOST_FUNCTION(H2FrameParserPrototype__settingsCallback);
+
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSH2FrameParserPrototype, JSH2FrameParserPrototype::Base);
static const HashTableValue JSH2FrameParserPrototypeTableValues[] = {
{ "detach"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, H2FrameParserPrototype__detachCallback, 0 } },
- { "read"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, H2FrameParserPrototype__readCallback, 1 } }
+ { "goaway"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, H2FrameParserPrototype__goawayCallback, 3 } },
+ { "ping"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, H2FrameParserPrototype__pingCallback, 0 } },
+ { "read"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, H2FrameParserPrototype__readCallback, 1 } },
+ { "request"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, H2FrameParserPrototype__requestCallback, 2 } },
+ { "settings"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, H2FrameParserPrototype__settingsCallback, 1 } }
};
const ClassInfo JSH2FrameParserPrototype::s_info = { "H2FrameParser"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSH2FrameParserPrototype) };
@@ -11104,6 +11120,62 @@ JSC_DEFINE_HOST_FUNCTION(H2FrameParserPrototype__detachCallback, (JSGlobalObject
return H2FrameParserPrototype__detach(thisObject->wrapped(), lexicalGlobalObject, callFrame);
}
+JSC_DEFINE_HOST_FUNCTION(H2FrameParserPrototype__goawayCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ auto& vm = lexicalGlobalObject->vm();
+
+ JSH2FrameParser* thisObject = jsDynamicCast<JSH2FrameParser*>(callFrame->thisValue());
+
+ if (UNLIKELY(!thisObject)) {
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ throwVMTypeError(lexicalGlobalObject, throwScope, "Expected 'this' to be instanceof H2FrameParser"_s);
+ return JSValue::encode({});
+ }
+
+ JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject);
+
+#ifdef BUN_DEBUG
+ /** View the file name of the JS file that called this function
+ * from a debugger */
+ SourceOrigin sourceOrigin = callFrame->callerSourceOrigin(vm);
+ const char* fileName = sourceOrigin.string().utf8().data();
+ static const char* lastFileName = nullptr;
+ if (lastFileName != fileName) {
+ lastFileName = fileName;
+ }
+#endif
+
+ return H2FrameParserPrototype__goaway(thisObject->wrapped(), lexicalGlobalObject, callFrame);
+}
+
+JSC_DEFINE_HOST_FUNCTION(H2FrameParserPrototype__pingCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ auto& vm = lexicalGlobalObject->vm();
+
+ JSH2FrameParser* thisObject = jsDynamicCast<JSH2FrameParser*>(callFrame->thisValue());
+
+ if (UNLIKELY(!thisObject)) {
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ throwVMTypeError(lexicalGlobalObject, throwScope, "Expected 'this' to be instanceof H2FrameParser"_s);
+ return JSValue::encode({});
+ }
+
+ JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject);
+
+#ifdef BUN_DEBUG
+ /** View the file name of the JS file that called this function
+ * from a debugger */
+ SourceOrigin sourceOrigin = callFrame->callerSourceOrigin(vm);
+ const char* fileName = sourceOrigin.string().utf8().data();
+ static const char* lastFileName = nullptr;
+ if (lastFileName != fileName) {
+ lastFileName = fileName;
+ }
+#endif
+
+ return H2FrameParserPrototype__ping(thisObject->wrapped(), lexicalGlobalObject, callFrame);
+}
+
JSC_DEFINE_HOST_FUNCTION(H2FrameParserPrototype__readCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
{
auto& vm = lexicalGlobalObject->vm();
@@ -11132,6 +11204,62 @@ JSC_DEFINE_HOST_FUNCTION(H2FrameParserPrototype__readCallback, (JSGlobalObject *
return H2FrameParserPrototype__read(thisObject->wrapped(), lexicalGlobalObject, callFrame);
}
+JSC_DEFINE_HOST_FUNCTION(H2FrameParserPrototype__requestCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ auto& vm = lexicalGlobalObject->vm();
+
+ JSH2FrameParser* thisObject = jsDynamicCast<JSH2FrameParser*>(callFrame->thisValue());
+
+ if (UNLIKELY(!thisObject)) {
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ throwVMTypeError(lexicalGlobalObject, throwScope, "Expected 'this' to be instanceof H2FrameParser"_s);
+ return JSValue::encode({});
+ }
+
+ JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject);
+
+#ifdef BUN_DEBUG
+ /** View the file name of the JS file that called this function
+ * from a debugger */
+ SourceOrigin sourceOrigin = callFrame->callerSourceOrigin(vm);
+ const char* fileName = sourceOrigin.string().utf8().data();
+ static const char* lastFileName = nullptr;
+ if (lastFileName != fileName) {
+ lastFileName = fileName;
+ }
+#endif
+
+ return H2FrameParserPrototype__request(thisObject->wrapped(), lexicalGlobalObject, callFrame);
+}
+
+JSC_DEFINE_HOST_FUNCTION(H2FrameParserPrototype__settingsCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
+{
+ auto& vm = lexicalGlobalObject->vm();
+
+ JSH2FrameParser* thisObject = jsDynamicCast<JSH2FrameParser*>(callFrame->thisValue());
+
+ if (UNLIKELY(!thisObject)) {
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ throwVMTypeError(lexicalGlobalObject, throwScope, "Expected 'this' to be instanceof H2FrameParser"_s);
+ return JSValue::encode({});
+ }
+
+ JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject);
+
+#ifdef BUN_DEBUG
+ /** View the file name of the JS file that called this function
+ * from a debugger */
+ SourceOrigin sourceOrigin = callFrame->callerSourceOrigin(vm);
+ const char* fileName = sourceOrigin.string().utf8().data();
+ static const char* lastFileName = nullptr;
+ if (lastFileName != fileName) {
+ lastFileName = fileName;
+ }
+#endif
+
+ return H2FrameParserPrototype__updateSettings(thisObject->wrapped(), lexicalGlobalObject, callFrame);
+}
+
void JSH2FrameParserPrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
{
Base::finishCreation(vm);
diff --git a/src/bun.js/bindings/ZigGeneratedClasses.h b/src/bun.js/bindings/ZigGeneratedClasses.h
index b8b694068..6bfd65a8a 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses.h
+++ b/src/bun.js/bindings/ZigGeneratedClasses.h
@@ -1311,6 +1311,56 @@ public:
mutable JSC::WriteBarrier<JSC::Unknown> m_style;
};
+class JSH2FrameParser final : public JSC::JSDestructibleObject {
+public:
+ using Base = JSC::JSDestructibleObject;
+ static JSH2FrameParser* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* ctx);
+
+ DECLARE_EXPORT_INFO;
+ template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
+ {
+ if constexpr (mode == JSC::SubspaceAccess::Concurrently)
+ return nullptr;
+ return WebCore::subspaceForImpl<JSH2FrameParser, WebCore::UseCustomHeapCellType::No>(
+ vm,
+ [](auto& spaces) { return spaces.m_clientSubspaceForH2FrameParser.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForH2FrameParser = std::forward<decltype(space)>(space); },
+ [](auto& spaces) { return spaces.m_subspaceForH2FrameParser.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_subspaceForH2FrameParser = std::forward<decltype(space)>(space); });
+ }
+
+ static void destroy(JSC::JSCell*);
+ static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
+ {
+ return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(static_cast<JSC::JSType>(0b11101110), StructureFlags), info());
+ }
+
+ static JSObject* createPrototype(VM& vm, JSDOMGlobalObject* globalObject);
+ static JSObject* createConstructor(VM& vm, JSGlobalObject* globalObject, JSValue prototype);
+
+ ~JSH2FrameParser();
+
+ void* wrapped() const { return m_ctx; }
+
+ void detach()
+ {
+ m_ctx = nullptr;
+ }
+
+ static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&);
+ static ptrdiff_t offsetOfWrapped() { return OBJECT_OFFSETOF(JSH2FrameParser, m_ctx); }
+
+ void* m_ctx { nullptr };
+
+ JSH2FrameParser(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr)
+ : Base(vm, structure)
+ {
+ m_ctx = sinkPtr;
+ }
+
+ void finishCreation(JSC::VM&);
+};
+
class JSHTMLRewriter final : public JSC::JSDestructibleObject {
public:
using Base = JSC::JSDestructibleObject;
diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp
index 467667953..9af138b9c 100644
--- a/src/bun.js/bindings/ZigGlobalObject.cpp
+++ b/src/bun.js/bindings/ZigGlobalObject.cpp
@@ -135,6 +135,7 @@ using namespace Bun;
extern "C" JSC::EncodedJSValue Bun__fetch(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
extern "C" JSC::EncodedJSValue Bun__canonicalizeIP(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
+extern "C" EncodedJSValue H2FrameParser__getConstructor(Zig::GlobalObject* globalObject);
using JSGlobalObject
= JSC::JSGlobalObject;
@@ -1640,6 +1641,14 @@ static JSC_DEFINE_HOST_FUNCTION(functionLazyLoad,
if (string == "events"_s) {
return JSValue::encode(WebCore::JSEventEmitter::getConstructor(vm, globalObject));
}
+ if(string == "internal/http2"_s) {
+ auto* obj = constructEmptyObject(globalObject);
+
+ obj->putDirect(
+ vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "H2FrameParser"_s)), JSValue::decode(H2FrameParser__getConstructor(globalObject)), 0);
+
+ return JSValue::encode(obj);
+ }
if (string == "internal/tls"_s) {
auto* obj = constructEmptyObject(globalObject);
diff --git a/src/bun.js/bindings/generated_classes.zig b/src/bun.js/bindings/generated_classes.zig
index 7d5571ff3..6f106a1dd 100644
--- a/src/bun.js/bindings/generated_classes.zig
+++ b/src/bun.js/bindings/generated_classes.zig
@@ -3009,13 +3009,25 @@ pub const JSH2FrameParser = struct {
if (@TypeOf(H2FrameParser.detach) != CallbackType)
@compileLog("Expected H2FrameParser.detach to be a callback but received " ++ @typeName(@TypeOf(H2FrameParser.detach)));
+ if (@TypeOf(H2FrameParser.goaway) != CallbackType)
+ @compileLog("Expected H2FrameParser.goaway to be a callback but received " ++ @typeName(@TypeOf(H2FrameParser.goaway)));
+ if (@TypeOf(H2FrameParser.ping) != CallbackType)
+ @compileLog("Expected H2FrameParser.ping to be a callback but received " ++ @typeName(@TypeOf(H2FrameParser.ping)));
if (@TypeOf(H2FrameParser.read) != CallbackType)
@compileLog("Expected H2FrameParser.read to be a callback but received " ++ @typeName(@TypeOf(H2FrameParser.read)));
+ if (@TypeOf(H2FrameParser.request) != CallbackType)
+ @compileLog("Expected H2FrameParser.request to be a callback but received " ++ @typeName(@TypeOf(H2FrameParser.request)));
+ if (@TypeOf(H2FrameParser.updateSettings) != CallbackType)
+ @compileLog("Expected H2FrameParser.updateSettings to be a callback but received " ++ @typeName(@TypeOf(H2FrameParser.updateSettings)));
if (!JSC.is_bindgen) {
@export(H2FrameParser.constructor, .{ .name = "H2FrameParserClass__construct" });
@export(H2FrameParser.detach, .{ .name = "H2FrameParserPrototype__detach" });
@export(H2FrameParser.finalize, .{ .name = "H2FrameParserClass__finalize" });
+ @export(H2FrameParser.goaway, .{ .name = "H2FrameParserPrototype__goaway" });
+ @export(H2FrameParser.ping, .{ .name = "H2FrameParserPrototype__ping" });
@export(H2FrameParser.read, .{ .name = "H2FrameParserPrototype__read" });
+ @export(H2FrameParser.request, .{ .name = "H2FrameParserPrototype__request" });
+ @export(H2FrameParser.updateSettings, .{ .name = "H2FrameParserPrototype__updateSettings" });
}
}
};