aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar cirospaciari <ciro.spaciari@gmail.com> 2023-08-29 22:13:50 -0300
committerGravatar cirospaciari <ciro.spaciari@gmail.com> 2023-08-29 22:13:50 -0300
commitd134a261d5f710efd040122074fcfb9ed5dea23e (patch)
tree0639dc44b0f9f4a258ed8c13324a4c1d2cec7699
parent557e912d9a9914bd1962ef632d146f760f5ffa39 (diff)
downloadbun-d134a261d5f710efd040122074fcfb9ed5dea23e.tar.gz
bun-d134a261d5f710efd040122074fcfb9ed5dea23e.tar.zst
bun-d134a261d5f710efd040122074fcfb9ed5dea23e.zip
init
-rw-r--r--src/bun.js/api/bun/h2_frame_parser.zig544
-rw-r--r--src/bun.js/api/h2.classes.ts21
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses.cpp306
-rw-r--r--src/bun.js/bindings/generated_classes.zig75
-rw-r--r--src/bun.js/bindings/generated_classes_list.zig1
-rw-r--r--src/jsc.zig1
6 files changed, 948 insertions, 0 deletions
diff --git a/src/bun.js/api/bun/h2_frame_parser.zig b/src/bun.js/api/bun/h2_frame_parser.zig
new file mode 100644
index 000000000..2041aad69
--- /dev/null
+++ b/src/bun.js/api/bun/h2_frame_parser.zig
@@ -0,0 +1,544 @@
+const getAllocator = @import("../../base.zig").getAllocator;
+const bun = @import("root").bun;
+const Output = bun.Output;
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const JSC = bun.JSC;
+const MutableString = bun.MutableString;
+const native_endian = @import("builtin").target.cpu.arch.endian();
+
+const JSValue = JSC.JSValue;
+
+const BinaryType = JSC.BinaryType;
+
+const FrameType = enum(u8) {
+ HTTP_FRAME_DATA = 0x00,
+ HTTP_FRAME_HEADERS = 0x01,
+ HTTP_FRAME_PRIORITY = 0x02,
+ HTTP_FRAME_RST_STREAM = 0x03,
+ HTTP_FRAME_SETTINGS = 0x04,
+ HTTP_FRAME_PUSH_PROMISE = 0x05,
+ HTTP_FRAME_PING = 0x06,
+ HTTP_FRAME_GOAWAY = 0x07,
+ HTTP_FRAME_WINDOW_UPDATE = 0x08,
+ HTTP_FRAME_CONTINUATION = 0x09,
+};
+
+const ErrorCode = enum(u32) {
+ NO_ERROR = 0x0,
+ PROTOCOL_ERROR = 0x1,
+ INTERNAL_ERROR = 0x2,
+ FLOW_CONTROL_ERROR = 0x3,
+ SETTINGS_TIMEOUT = 0x4,
+ STREAM_CLOSED = 0x5,
+ FRAME_SIZE_ERROR = 0x6,
+ REFUSED_STREAM = 0x7,
+ CANCEL = 0x8,
+ COMPRESSION_ERROR = 0x9,
+ CONNECT_ERROR = 0xa,
+ ENHANCE_YOUR_CALM = 0xb,
+ INADEQUATE_SECURITY = 0xc,
+ HTTP_1_1_REQUIRED = 0xd,
+};
+
+const SettingsType = enum(u16) {
+ SETTINGS_HEADER_TABLE_SIZE = 0x1,
+ SETTINGS_ENABLE_PUSH = 0x2,
+ SETTINGS_MAX_CONCURRENT_STREAMS = 0x3,
+ SETTINGS_INITIAL_WINDOW_SIZE = 0x4,
+ SETTINGS_MAX_FRAME_SIZE = 0x5,
+ SETTINGS_MAX_HEADER_LIST_SIZE = 0x6,
+};
+
+const UInt31WithReserved = packed struct(u32) {
+ reserved: bool = false,
+ uint31: u31 = 0,
+
+ pub fn from(value: u32) UInt31WithReserved {
+ return @bitCast(value);
+ }
+};
+
+const FrameHeader = packed struct(u72) {
+ length: u24 = 0,
+ type: u8 = @intFromEnum(FrameType.HTTP_FRAME_SETTINGS),
+ flags: u8 = 0,
+ streamIdentifier: u32 = 0,
+
+ pub const byteSize: usize = 9;
+ pub inline fn write(this: *FrameHeader, comptime Writer: type, writer: Writer) void {
+ var swap = this.*;
+ if (native_endian != .Big) {
+ std.mem.byteSwapAllFields(FrameHeader, &swap);
+ }
+
+ _ = writer.write(std.mem.asBytes(&swap)[0..FrameHeader.byteSize]) catch 0;
+ }
+
+ pub inline fn from(dst: *FrameHeader, src: []const u8, offset: usize, comptime end: bool) void {
+ @memcpy(@as(*[FrameHeader.byteSize]u8, @ptrCast(dst))[offset .. src.len + offset], src);
+ if (comptime end) {
+ if (native_endian != .Big) {
+ std.mem.byteSwapAllFields(FrameHeader, dst);
+ }
+ }
+ }
+};
+
+const SettingsPayloadUnit = packed struct(u48) {
+ type: u16,
+ value: u32,
+ pub const byteSize: usize = 6;
+ pub inline fn from(dst: *SettingsPayloadUnit, src: []const u8, offset: usize, comptime end: bool) void {
+ @memcpy(@as(*[SettingsPayloadUnit.byteSize]u8, @ptrCast(dst))[offset .. src.len + offset], src);
+ if (comptime end) {
+ if (native_endian != .Big) {
+ std.mem.byteSwapAllFields(SettingsPayloadUnit, dst);
+ }
+ }
+ }
+};
+
+const FullSettingsPayload = packed struct(u288) {
+ _headerTableSizeType: u16 = @intFromEnum(SettingsType.SETTINGS_HEADER_TABLE_SIZE),
+ headerTableSize: u32 = 4096,
+ _enablePushType: u16 = @intFromEnum(SettingsType.SETTINGS_ENABLE_PUSH),
+ enablePush: u32 = 1,
+ _maxConcurrentStreamsType: u16 = @intFromEnum(SettingsType.SETTINGS_MAX_CONCURRENT_STREAMS),
+ maxConcurrentStreams: u32 = 100,
+ _initialWindowSizeType: u16 = @intFromEnum(SettingsType.SETTINGS_INITIAL_WINDOW_SIZE),
+ initialWindowSize: u32 = 65535,
+ _maxFrameSizeType: u16 = @intFromEnum(SettingsType.SETTINGS_MAX_FRAME_SIZE),
+ maxFrameSize: u32 = 16384,
+ _maxHeaderListSizeType: u16 = @intFromEnum(SettingsType.SETTINGS_MAX_HEADER_LIST_SIZE),
+ maxHeaderListSize: u32 = 65535,
+
+ pub const byteSize: usize = 36;
+ pub fn toJS(this: *FullSettingsPayload, globalObject: *JSC.JSGlobalObject) JSC.JSValue {
+ var result = JSValue.createEmptyObject(globalObject, 6);
+ result.put(globalObject, JSC.ZigString.static("headerTableSize"), JSC.JSValue.jsNumber(this.headerTableSize));
+ result.put(globalObject, JSC.ZigString.static("enablePush"), JSC.JSValue.jsNumber(this.enablePush));
+ result.put(globalObject, JSC.ZigString.static("maxConcurrentStreams"), JSC.JSValue.jsNumber(this.maxConcurrentStreams));
+ result.put(globalObject, JSC.ZigString.static("initialWindowSize"), JSC.JSValue.jsNumber(this.initialWindowSize));
+ result.put(globalObject, JSC.ZigString.static("maxFrameSize"), JSC.JSValue.jsNumber(this.maxFrameSize));
+ result.put(globalObject, JSC.ZigString.static("maxHeaderListSize"), JSC.JSValue.jsNumber(this.maxHeaderListSize));
+
+ return result;
+ }
+
+ pub fn updateWith(this: *FullSettingsPayload, option: SettingsPayloadUnit) void {
+ switch (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,
+ .SETTINGS_INITIAL_WINDOW_SIZE => this.initialWindowSize = option.value,
+ .SETTINGS_MAX_FRAME_SIZE => this.maxFrameSize = option.value,
+ .SETTINGS_MAX_HEADER_LIST_SIZE => this.maxHeaderListSize = option.value,
+ }
+ }
+ pub fn write(this: *FullSettingsPayload, comptime Writer: type, writer: Writer) void {
+ var swap = this.*;
+
+ if (native_endian != .Big) {
+ std.mem.byteSwapAllFields(FullSettingsPayload, &swap);
+ }
+ _ = writer.write(std.mem.asBytes(&swap)[0..FullSettingsPayload.byteSize]) catch 0;
+ }
+};
+
+const Handlers = struct {
+ onError: JSC.JSValue = .zero,
+ onWrite: JSC.JSValue = .zero,
+ onStreamError: JSC.JSValue = .zero,
+ onStreamStart: JSC.JSValue = .zero,
+ onStreamHeaders: JSC.JSValue = .zero,
+ onStreamEnd: JSC.JSValue = .zero,
+ onStreamData: JSC.JSValue = .zero,
+ onRemoteSettings: JSC.JSValue = .zero,
+ onLocalSettings: 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);
+ if (callback == .zero) {
+ return false;
+ }
+
+ const result = callback.callWithThis(this.globalObject, thisValue, data);
+ if (result.isAnyError()) {
+ this.vm.onUnhandledError(this.globalObject, result);
+ }
+
+ return true;
+ }
+
+ pub fn callErrorHandler(this: *Handlers, thisValue: JSValue, err: []const JSValue) bool {
+ const onError = this.onError;
+ if (onError == .zero) {
+ if (err.len > 0)
+ this.vm.onUnhandledError(this.globalObject, err[0]);
+
+ return false;
+ }
+
+ const result = onError.callWithThis(this.globalObject, thisValue, err);
+ if (result.isAnyError()) {
+ this.vm.onUnhandledError(this.globalObject, result);
+ }
+
+ return true;
+ }
+
+ pub fn fromJS(globalObject: *JSC.JSGlobalObject, opts: JSC.JSValue, exception: JSC.C.ExceptionRef) ?Handlers {
+ var handlers = Handlers{
+ .vm = globalObject.bunVM(),
+ .globalObject = globalObject,
+ };
+
+ if (opts.isEmptyOrUndefinedOrNull() or opts.isBoolean() or !opts.isObject()) {
+ exception.* = JSC.toInvalidArguments("Expected \"handlers\" to be an object", .{}, globalObject).asObjectRef();
+ return null;
+ }
+
+ const pairs = .{
+ .{ "onStreamStart", "streamStart" },
+ .{ "onStreamHeaders", "streamHeaders" },
+ .{ "onStreamEnd", "streamEnd" },
+ .{ "onStreamData", "streamData" },
+ .{ "onStreamError", "streamError" },
+ .{ "onRemoteSettings", "remoteSettings" },
+ .{ "onLocalSettings", "localSettings" },
+ .{ "onError", "error" },
+ .{ "onWrite", "write" },
+ };
+ inline for (pairs) |pair| {
+ if (opts.getTruthy(globalObject, pair.@"1")) |callback_value| {
+ if (!callback_value.isCell() or !callback_value.isCallable(globalObject.vm())) {
+ exception.* = JSC.toInvalidArguments(comptime std.fmt.comptimePrint("Expected \"{s}\" callback to be a function", .{pair.@"1"}), .{}, globalObject).asObjectRef();
+ return null;
+ }
+
+ @field(handlers, pair.@"0") = callback_value;
+ }
+ }
+
+ if (handlers.onWrite == .zero) {
+ exception.* = JSC.toInvalidArguments("Expected at least \"write\" callback", .{}, globalObject).asObjectRef();
+ return null;
+ }
+
+ if (opts.getTruthy(globalObject, "binaryType")) |binary_type_value| {
+ if (!binary_type_value.isString()) {
+ exception.* = JSC.toInvalidArguments("Expected \"binaryType\" to be a string", .{}, globalObject).asObjectRef();
+ return null;
+ }
+
+ handlers.binary_type = BinaryType.fromJSValue(globalObject, binary_type_value) orelse {
+ exception.* = JSC.toInvalidArguments("Expected 'binaryType' to be 'arraybuffer', 'uint8array', 'buffer'", .{}, globalObject).asObjectRef();
+ return null;
+ };
+ }
+
+ return handlers;
+ }
+
+ pub fn unprotect(this: *Handlers) void {
+ this.onError.unprotect();
+ this.onWrite.unprotect();
+ this.onStreamError.unprotect();
+ this.onStreamStart.unprotect();
+ this.onStreamHeaders.unprotect();
+ this.onStreamEnd.unprotect();
+ this.onStreamData.unprotect();
+ this.onStreamError.unprotect();
+ this.onLocalSettings.unprotect();
+ this.onRemoteSettings.unprotect();
+ }
+
+ pub fn clear(this: *Handlers) void {
+ this.onError = .zero;
+ this.onWrite = .zero;
+ this.onStreamError = .zero;
+ this.onStreamStart = .zero;
+ this.onStreamHeaders = .zero;
+ this.onStreamEnd = .zero;
+ this.onStreamData = .zero;
+ this.onStreamError = .zero;
+ this.onLocalSettings = .zero;
+ this.onRemoteSettings = .zero;
+ }
+
+ pub fn protect(this: *Handlers) void {
+ this.onError.protect();
+ this.onWrite.protect();
+ this.onStreamError.protect();
+ this.onStreamStart.protect();
+ this.onStreamHeaders.protect();
+ this.onStreamEnd.protect();
+ this.onStreamData.protect();
+ this.onStreamError.protect();
+ this.onLocalSettings.protect();
+ this.onRemoteSettings.protect();
+ }
+};
+
+pub const H2FrameParser = struct {
+ pub const log = Output.scoped(.H2FrameParser, false);
+ pub usingnamespace JSC.Codegen.JSH2FrameParser;
+
+ strong_ctx: JSC.Strong = .{},
+ allocator: Allocator,
+ handlers: Handlers,
+ localSettings: FullSettingsPayload = .{},
+ // only available after receiving settings or ACK
+ remoteSettings: ?FullSettingsPayload = null,
+ // current frame being read
+ currentFrame: ?FrameHeader = null,
+ // remaining bytes to read for the current frame
+ remainingLength: i32 = 0,
+ // buffer if more data is needed for the current frame
+ readBuffer: MutableString,
+
+ pub fn dispatch(this: *H2FrameParser, comptime event: []const u8, 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 });
+ }
+
+ pub fn write(this: *H2FrameParser, bytes: []const u8) void {
+ JSC.markBinding(@src());
+ log("write", .{});
+
+ const output_value = this.handlers.binary_type.toJS(bytes, this.handlers.globalObject);
+ this.dispatch(.onWrite, output_value);
+ }
+
+ pub fn detach(this: *H2FrameParser, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue {
+ JSC.markBinding(@src());
+ log("detach", .{});
+ var handler = this.handlers;
+ defer handler.unprotect();
+ this.handlers.clear();
+
+ return JSC.JSValue.jsUndefined();
+ }
+ 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", .{});
+ 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", .{});
+ return data.len;
+ }
+ } else {
+ if (frame.flags & 0x1) {
+ // we received an ACK
+ this.remoteSettings = &this.localSettings;
+ this.dispatch(.onLocalSettings, this.localSettings.toJS(this.handlers.globalObject));
+ }
+ 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;
+ }
+
+ 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);
+ }
+ 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 } });
+
+ return end;
+ }
+
+ 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;
+ }
+
+ // nothing to do
+ if (bytes.len == 0) return bytes.len;
+
+ const buffered_data = this.readBuffer.list.items.len;
+
+ var header: FrameHeader = undefined;
+ // we can have less than 9 bytes buffered
+ if (buffered_data > 0) {
+ const total = buffered_data + bytes.len;
+ if (total < FrameHeader.byteSize) {
+ // buffer more data
+ _ = this.readBuffer.appendSlice(bytes) catch @panic("OOM");
+ return bytes.len;
+ }
+ FrameHeader.from(&header, this.readBuffer.list.items[0..buffered_data], 0, false);
+ const needed = FrameHeader.byteSize - buffered_data;
+ FrameHeader.from(&header, bytes[0..needed], buffered_data, true);
+ // ignore the reserved bit
+ const id = UInt31WithReserved.from(header.streamIdentifier);
+ header.streamIdentifier = @intCast(id.uint31);
+ // reset for later use
+ this.readBuffer.reset();
+
+ this.currentFrame = header;
+ this.remainingLength = header.length;
+ log("header {} {} {} {}", .{ header.type, header.length, header.flags, header.streamIdentifier });
+
+ return switch (@as(FrameType, @enumFromInt(header.type))) {
+ FrameType.HTTP_FRAME_SETTINGS => this.handleSettingsFrame(header, bytes[needed..]) + needed,
+ else => {
+ log("not implemented {}", .{header.type});
+ return bytes.len;
+ },
+ };
+ }
+
+ if (bytes.len < FrameHeader.byteSize) {
+ // buffer more dheaderata
+ this.readBuffer.appendSlice(bytes) catch @panic("OOM");
+ return bytes.len;
+ }
+
+ FrameHeader.from(&header, bytes[0..FrameHeader.byteSize], 0, true);
+
+ log("header {} {} {} {}", .{ header.type, header.length, header.flags, header.streamIdentifier });
+ this.currentFrame = header;
+ this.remainingLength = header.length;
+ return switch (@as(FrameType, @enumFromInt(header.type))) {
+ FrameType.HTTP_FRAME_SETTINGS => this.handleSettingsFrame(header, bytes[FrameHeader.byteSize..]) + FrameHeader.byteSize,
+ else => {
+ log("not implemented {}", .{header.type});
+ return bytes.len;
+ },
+ };
+ }
+
+ pub fn read(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 1 argument", .{});
+ return .zero;
+ }
+ const buffer = args_list.ptr[0];
+ if (buffer.asArrayBuffer(globalObject)) |array_buffer| {
+ var bytes = array_buffer.slice();
+
+ // read all the bytes
+ while (bytes.len > 0) {
+ const result = this.readBytes(bytes);
+ if (result >= bytes.len) {
+ break;
+ }
+ bytes = bytes[result..];
+ }
+ return JSC.JSValue.jsUndefined();
+ }
+ globalObject.throw("Expected data to be a Buffer or ArrayBuffer", .{});
+ return .zero;
+ }
+
+ pub fn constructor(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) ?*H2FrameParser {
+ const args_list = callframe.arguments(1);
+ if (args_list.len < 1) {
+ globalObject.throw("Expected 1 argument", .{});
+ return null;
+ }
+
+ const options = args_list.ptr[0];
+ if (options.isEmptyOrUndefinedOrNull() or options.isBoolean() or !options.isObject()) {
+ globalObject.throwInvalidArguments("expected options as argument", .{});
+ return null;
+ }
+
+ var exception: JSC.C.JSValueRef = null;
+ var context_obj = options.get(globalObject, "context") orelse {
+ globalObject.throw("Expected \"context\" option", .{});
+ return null;
+ };
+ const handlers = Handlers.fromJS(globalObject, options, &exception) orelse {
+ globalObject.throwValue(exception.?.value());
+ return null;
+ };
+ const allocator = getAllocator(globalObject);
+ var this = allocator.create(H2FrameParser) catch unreachable;
+
+ this.* = H2FrameParser{
+ .handlers = handlers,
+ .allocator = allocator,
+ .readBuffer = .{
+ .allocator = bun.default_allocator,
+ .list = .{
+ .items = &.{},
+ .capacity = 0,
+ },
+ },
+ };
+ 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);
+ return this;
+ }
+
+ pub fn finalize(
+ this: *H2FrameParser,
+ ) callconv(.C) void {
+ var allocator = this.allocator;
+ this.strong_ctx.deinit();
+ this.handlers.unprotect();
+ this.readBuffer.deinit();
+ allocator.destroy(this);
+ }
+};
diff --git a/src/bun.js/api/h2.classes.ts b/src/bun.js/api/h2.classes.ts
new file mode 100644
index 000000000..caadb5c40
--- /dev/null
+++ b/src/bun.js/api/h2.classes.ts
@@ -0,0 +1,21 @@
+import { define } from "../scripts/class-definitions";
+
+export default [
+ define({
+ name: "H2FrameParser",
+ JSType: "0b11101110",
+ proto: {
+ read: {
+ fn: "read",
+ length: 1,
+ },
+ detach: {
+ fn: "detach",
+ length: 0,
+ },
+ },
+ finalize: true,
+ construct: true,
+ klass: {},
+ }),
+];
diff --git a/src/bun.js/bindings/ZigGeneratedClasses.cpp b/src/bun.js/bindings/ZigGeneratedClasses.cpp
index 0ab7a1b5d..376260383 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses.cpp
+++ b/src/bun.js/bindings/ZigGeneratedClasses.cpp
@@ -10976,6 +10976,312 @@ void JSFileSystemRouter::visitOutputConstraintsImpl(JSCell* cell, Visitor& visit
}
DEFINE_VISIT_OUTPUT_CONSTRAINTS(JSFileSystemRouter);
+class JSH2FrameParserPrototype final : public JSC::JSNonFinalObject {
+public:
+ using Base = JSC::JSNonFinalObject;
+
+ static JSH2FrameParserPrototype* create(JSC::VM& vm, JSGlobalObject* globalObject, JSC::Structure* structure)
+ {
+ JSH2FrameParserPrototype* ptr = new (NotNull, JSC::allocateCell<JSH2FrameParserPrototype>(vm)) JSH2FrameParserPrototype(vm, globalObject, structure);
+ ptr->finishCreation(vm, globalObject);
+ return ptr;
+ }
+
+ DECLARE_INFO;
+ template<typename CellType, JSC::SubspaceAccess>
+ static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
+ {
+ return &vm.plainObjectSpace();
+ }
+ static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
+ {
+ return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
+ }
+
+private:
+ JSH2FrameParserPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure)
+ : Base(vm, structure)
+ {
+ }
+
+ void finishCreation(JSC::VM&, JSC::JSGlobalObject*);
+};
+
+class JSH2FrameParserConstructor final : public JSC::InternalFunction {
+public:
+ using Base = JSC::InternalFunction;
+ static JSH2FrameParserConstructor* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSH2FrameParserPrototype* prototype);
+
+ static constexpr unsigned StructureFlags = Base::StructureFlags;
+ static constexpr bool needsDestruction = false;
+
+ static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
+ {
+ return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), 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<JSH2FrameParserConstructor, WebCore::UseCustomHeapCellType::No>(
+ vm,
+ [](auto& spaces) { return spaces.m_clientSubspaceForH2FrameParserConstructor.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForH2FrameParserConstructor = std::forward<decltype(space)>(space); },
+ [](auto& spaces) { return spaces.m_subspaceForH2FrameParserConstructor.get(); },
+ [](auto& spaces, auto&& space) { spaces.m_subspaceForH2FrameParserConstructor = std::forward<decltype(space)>(space); });
+ }
+
+ void initializeProperties(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSH2FrameParserPrototype* prototype);
+
+ // Must be defined for each specialization class.
+ static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*);
+
+ DECLARE_EXPORT_INFO;
+
+private:
+ JSH2FrameParserConstructor(JSC::VM& vm, JSC::Structure* structure);
+ void finishCreation(JSC::VM&, JSC::JSGlobalObject* globalObject, JSH2FrameParserPrototype* prototype);
+};
+
+extern "C" void* H2FrameParserClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*);
+JSC_DECLARE_CUSTOM_GETTER(jsH2FrameParserConstructor);
+
+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__read(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
+JSC_DECLARE_HOST_FUNCTION(H2FrameParserPrototype__readCallback);
+
+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 } }
+};
+
+const ClassInfo JSH2FrameParserPrototype::s_info = { "H2FrameParser"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSH2FrameParserPrototype) };
+
+JSC_DEFINE_CUSTOM_GETTER(jsH2FrameParserConstructor, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName))
+{
+ VM& vm = JSC::getVM(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
+ auto* prototype = jsDynamicCast<JSH2FrameParserPrototype*>(JSValue::decode(thisValue));
+
+ if (UNLIKELY(!prototype))
+ return throwVMTypeError(lexicalGlobalObject, throwScope, "Cannot get constructor for H2FrameParser"_s);
+ return JSValue::encode(globalObject->JSH2FrameParserConstructor());
+}
+
+JSC_DEFINE_HOST_FUNCTION(H2FrameParserPrototype__detachCallback, (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__detach(thisObject->wrapped(), lexicalGlobalObject, callFrame);
+}
+
+JSC_DEFINE_HOST_FUNCTION(H2FrameParserPrototype__readCallback, (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__read(thisObject->wrapped(), lexicalGlobalObject, callFrame);
+}
+
+void JSH2FrameParserPrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
+{
+ Base::finishCreation(vm);
+ reifyStaticProperties(vm, JSH2FrameParser::info(), JSH2FrameParserPrototypeTableValues, *this);
+ JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
+}
+
+void JSH2FrameParserConstructor::finishCreation(VM& vm, JSC::JSGlobalObject* globalObject, JSH2FrameParserPrototype* prototype)
+{
+ Base::finishCreation(vm, 0, "H2FrameParser"_s, PropertyAdditionMode::WithoutStructureTransition);
+
+ putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
+ ASSERT(inherits(info()));
+}
+
+JSH2FrameParserConstructor::JSH2FrameParserConstructor(JSC::VM& vm, JSC::Structure* structure)
+ : Base(vm, structure, construct, construct)
+{
+}
+
+JSH2FrameParserConstructor* JSH2FrameParserConstructor::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSH2FrameParserPrototype* prototype)
+{
+ JSH2FrameParserConstructor* ptr = new (NotNull, JSC::allocateCell<JSH2FrameParserConstructor>(vm)) JSH2FrameParserConstructor(vm, structure);
+ ptr->finishCreation(vm, globalObject, prototype);
+ return ptr;
+}
+
+JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSH2FrameParserConstructor::construct(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame)
+{
+ Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
+ JSC::VM& vm = globalObject->vm();
+ JSObject* newTarget = asObject(callFrame->newTarget());
+ auto* constructor = globalObject->JSH2FrameParserConstructor();
+ Structure* structure = globalObject->JSH2FrameParserStructure();
+ if (constructor != newTarget) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ auto* functionGlobalObject = reinterpret_cast<Zig::GlobalObject*>(
+ // ShadowRealm functions belong to a different global object.
+ getFunctionRealm(globalObject, newTarget));
+ RETURN_IF_EXCEPTION(scope, {});
+ structure = InternalFunction::createSubclassStructure(
+ globalObject,
+ newTarget,
+ functionGlobalObject->JSH2FrameParserStructure());
+ }
+
+ void* ptr = H2FrameParserClass__construct(globalObject, callFrame);
+
+ if (UNLIKELY(!ptr)) {
+ return JSValue::encode(JSC::jsUndefined());
+ }
+
+ JSH2FrameParser* instance = JSH2FrameParser::create(vm, globalObject, structure, ptr);
+
+ return JSValue::encode(instance);
+}
+
+void JSH2FrameParserConstructor::initializeProperties(VM& vm, JSC::JSGlobalObject* globalObject, JSH2FrameParserPrototype* prototype)
+{
+}
+
+const ClassInfo JSH2FrameParserConstructor::s_info = { "Function"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSH2FrameParserConstructor) };
+
+extern "C" EncodedJSValue H2FrameParser__getConstructor(Zig::GlobalObject* globalObject)
+{
+ return JSValue::encode(globalObject->JSH2FrameParserConstructor());
+}
+
+JSH2FrameParser::~JSH2FrameParser()
+{
+ if (m_ctx) {
+ H2FrameParserClass__finalize(m_ctx);
+ }
+}
+void JSH2FrameParser::destroy(JSCell* cell)
+{
+ static_cast<JSH2FrameParser*>(cell)->JSH2FrameParser::~JSH2FrameParser();
+}
+
+const ClassInfo JSH2FrameParser::s_info = { "H2FrameParser"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSH2FrameParser) };
+
+void JSH2FrameParser::finishCreation(VM& vm)
+{
+ Base::finishCreation(vm);
+ ASSERT(inherits(info()));
+}
+
+JSH2FrameParser* JSH2FrameParser::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* ctx)
+{
+ JSH2FrameParser* ptr = new (NotNull, JSC::allocateCell<JSH2FrameParser>(vm)) JSH2FrameParser(vm, structure, ctx);
+ ptr->finishCreation(vm);
+ return ptr;
+}
+
+extern "C" void* H2FrameParser__fromJS(JSC::EncodedJSValue value)
+{
+ JSC::JSValue decodedValue = JSC::JSValue::decode(value);
+ if (decodedValue.isEmpty() || !decodedValue.isCell())
+ return nullptr;
+
+ JSC::JSCell* cell = decodedValue.asCell();
+ JSH2FrameParser* object = JSC::jsDynamicCast<JSH2FrameParser*>(cell);
+
+ if (!object)
+ return nullptr;
+
+ return object->wrapped();
+}
+
+extern "C" bool H2FrameParser__dangerouslySetPtr(JSC::EncodedJSValue value, void* ptr)
+{
+ JSH2FrameParser* object = JSC::jsDynamicCast<JSH2FrameParser*>(JSValue::decode(value));
+ if (!object)
+ return false;
+
+ object->m_ctx = ptr;
+ return true;
+}
+
+extern "C" const size_t H2FrameParser__ptrOffset = JSH2FrameParser::offsetOfWrapped();
+
+void JSH2FrameParser::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer)
+{
+ auto* thisObject = jsCast<JSH2FrameParser*>(cell);
+ if (void* wrapped = thisObject->wrapped()) {
+ // if (thisObject->scriptExecutionContext())
+ // analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string());
+ }
+ Base::analyzeHeap(cell, analyzer);
+}
+
+JSObject* JSH2FrameParser::createConstructor(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+{
+ return WebCore::JSH2FrameParserConstructor::create(vm, globalObject, WebCore::JSH2FrameParserConstructor::createStructure(vm, globalObject, globalObject->functionPrototype()), jsCast<WebCore::JSH2FrameParserPrototype*>(prototype));
+}
+
+JSObject* JSH2FrameParser::createPrototype(VM& vm, JSDOMGlobalObject* globalObject)
+{
+ return JSH2FrameParserPrototype::create(vm, globalObject, JSH2FrameParserPrototype::createStructure(vm, globalObject, globalObject->objectPrototype()));
+}
+
+extern "C" EncodedJSValue H2FrameParser__create(Zig::GlobalObject* globalObject, void* ptr)
+{
+ auto& vm = globalObject->vm();
+ JSC::Structure* structure = globalObject->JSH2FrameParserStructure();
+ JSH2FrameParser* instance = JSH2FrameParser::create(vm, globalObject, structure, ptr);
+
+ return JSValue::encode(instance);
+}
class JSHTMLRewriterPrototype final : public JSC::JSNonFinalObject {
public:
using Base = JSC::JSNonFinalObject;
diff --git a/src/bun.js/bindings/generated_classes.zig b/src/bun.js/bindings/generated_classes.zig
index e98b8f973..7d5571ff3 100644
--- a/src/bun.js/bindings/generated_classes.zig
+++ b/src/bun.js/bindings/generated_classes.zig
@@ -2945,6 +2945,80 @@ pub const JSFileSystemRouter = struct {
}
}
};
+pub const JSH2FrameParser = struct {
+ const H2FrameParser = Classes.H2FrameParser;
+ const GetterType = fn (*H2FrameParser, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue;
+ const GetterTypeWithThisValue = fn (*H2FrameParser, JSC.JSValue, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue;
+ const SetterType = fn (*H2FrameParser, *JSC.JSGlobalObject, JSC.JSValue) callconv(.C) bool;
+ const SetterTypeWithThisValue = fn (*H2FrameParser, JSC.JSValue, *JSC.JSGlobalObject, JSC.JSValue) callconv(.C) bool;
+ const CallbackType = fn (*H2FrameParser, *JSC.JSGlobalObject, *JSC.CallFrame) callconv(.C) JSC.JSValue;
+
+ /// Return the pointer to the wrapped object.
+ /// If the object does not match the type, return null.
+ pub fn fromJS(value: JSC.JSValue) ?*H2FrameParser {
+ JSC.markBinding(@src());
+ return H2FrameParser__fromJS(value);
+ }
+
+ /// Get the H2FrameParser constructor value.
+ /// This loads lazily from the global object.
+ pub fn getConstructor(globalObject: *JSC.JSGlobalObject) JSC.JSValue {
+ JSC.markBinding(@src());
+ return H2FrameParser__getConstructor(globalObject);
+ }
+
+ /// Create a new instance of H2FrameParser
+ pub fn toJS(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject) JSC.JSValue {
+ JSC.markBinding(@src());
+ if (comptime Environment.allow_assert) {
+ const value__ = H2FrameParser__create(globalObject, this);
+ std.debug.assert(value__.as(H2FrameParser).? == this); // If this fails, likely a C ABI issue.
+ return value__;
+ } else {
+ return H2FrameParser__create(globalObject, this);
+ }
+ }
+
+ /// Modify the internal ptr to point to a new instance of H2FrameParser.
+ pub fn dangerouslySetPtr(value: JSC.JSValue, ptr: ?*H2FrameParser) bool {
+ JSC.markBinding(@src());
+ return H2FrameParser__dangerouslySetPtr(value, ptr);
+ }
+
+ /// Detach the ptr from the thisValue
+ pub fn detachPtr(_: *H2FrameParser, value: JSC.JSValue) void {
+ JSC.markBinding(@src());
+ std.debug.assert(H2FrameParser__dangerouslySetPtr(value, null));
+ }
+
+ extern fn H2FrameParser__fromJS(JSC.JSValue) ?*H2FrameParser;
+ extern fn H2FrameParser__getConstructor(*JSC.JSGlobalObject) JSC.JSValue;
+
+ extern fn H2FrameParser__create(globalObject: *JSC.JSGlobalObject, ptr: ?*H2FrameParser) JSC.JSValue;
+
+ extern fn H2FrameParser__dangerouslySetPtr(JSC.JSValue, ?*H2FrameParser) bool;
+
+ comptime {
+ if (@TypeOf(H2FrameParser.constructor) != (fn (*JSC.JSGlobalObject, *JSC.CallFrame) callconv(.C) ?*H2FrameParser)) {
+ @compileLog("H2FrameParser.constructor is not a constructor");
+ }
+
+ if (@TypeOf(H2FrameParser.finalize) != (fn (*H2FrameParser) callconv(.C) void)) {
+ @compileLog("H2FrameParser.finalize is not a finalizer");
+ }
+
+ if (@TypeOf(H2FrameParser.detach) != CallbackType)
+ @compileLog("Expected H2FrameParser.detach to be a callback but received " ++ @typeName(@TypeOf(H2FrameParser.detach)));
+ if (@TypeOf(H2FrameParser.read) != CallbackType)
+ @compileLog("Expected H2FrameParser.read to be a callback but received " ++ @typeName(@TypeOf(H2FrameParser.read)));
+ if (!JSC.is_bindgen) {
+ @export(H2FrameParser.constructor, .{ .name = "H2FrameParserClass__construct" });
+ @export(H2FrameParser.detach, .{ .name = "H2FrameParserPrototype__detach" });
+ @export(H2FrameParser.finalize, .{ .name = "H2FrameParserClass__finalize" });
+ @export(H2FrameParser.read, .{ .name = "H2FrameParserPrototype__read" });
+ }
+ }
+};
pub const JSHTMLRewriter = struct {
const HTMLRewriter = Classes.HTMLRewriter;
const GetterType = fn (*HTMLRewriter, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue;
@@ -6804,6 +6878,7 @@ comptime {
_ = JSFFI;
_ = JSFSWatcher;
_ = JSFileSystemRouter;
+ _ = JSH2FrameParser;
_ = JSHTMLRewriter;
_ = JSHTTPSServer;
_ = JSHTTPServer;
diff --git a/src/bun.js/bindings/generated_classes_list.zig b/src/bun.js/bindings/generated_classes_list.zig
index 20f36a458..2cb3d8d39 100644
--- a/src/bun.js/bindings/generated_classes_list.zig
+++ b/src/bun.js/bindings/generated_classes_list.zig
@@ -53,4 +53,5 @@ pub const Classes = struct {
pub const DebugHTTPSServer = JSC.API.DebugHTTPSServer;
pub const Crypto = JSC.WebCore.Crypto;
pub const FFI = JSC.FFI;
+ pub const H2FrameParser = JSC.API.H2FrameParser;
};
diff --git a/src/jsc.zig b/src/jsc.zig
index 1e844464a..b6c3f1323 100644
--- a/src/jsc.zig
+++ b/src/jsc.zig
@@ -44,6 +44,7 @@ pub const API = struct {
pub const TCPSocket = @import("./bun.js/api/bun/socket.zig").TCPSocket;
pub const TLSSocket = @import("./bun.js/api/bun/socket.zig").TLSSocket;
pub const Listener = @import("./bun.js/api/bun/socket.zig").Listener;
+ pub const H2FrameParser = @import("./bun.js/api/bun/h2_frame_parser.zig").H2FrameParser;
};
pub const DNS = @import("./bun.js/api/bun/dns_resolver.zig");
pub const FFI = @import("./bun.js/api/ffi.zig").FFI;