diff options
Diffstat (limited to 'src/bun.js')
-rw-r--r-- | src/bun.js/api/bun/h2_frame_parser.zig | 1483 | ||||
-rw-r--r-- | src/bun.js/api/bun/lshpack.translated.zig | 442 | ||||
-rw-r--r-- | src/bun.js/api/h2.classes.ts | 20 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h | 3 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h | 3 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h | 6 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h | 7 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGeneratedClasses.cpp | 130 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGeneratedClasses.h | 50 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGlobalObject.cpp | 9 | ||||
-rw-r--r-- | src/bun.js/bindings/generated_classes.zig | 12 |
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" }); } } }; |