[windows] fix(http2) (#8734)

* fix win http2

* add includes

* cleanup

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Ciro Spaciari
2024-02-06 20:37:39 -03:00
committed by GitHub
parent d3d5c7a1d6
commit 53814dda00
7 changed files with 313 additions and 596 deletions

View File

@@ -1286,7 +1286,9 @@ else()
endif()
if(USE_CUSTOM_LSHPACK)
include_directories(${BUN_DEPS_DIR}/ls-hpack)
if(WIN32)
include_directories(${BUN_DEPS_DIR}/ls-hpack/compat/queue)
target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/lshpack.lib")
else()
target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/liblshpack.a")
@@ -1332,4 +1334,4 @@ endif()
if(NO_CODEGEN)
message(STATUS "NOTE: NO_CODEGEN is ON, this build expects ./codegen to exist")
endif()
endif()

View File

@@ -5,7 +5,7 @@ const std = @import("std");
const Allocator = std.mem.Allocator;
const JSC = bun.JSC;
const MutableString = bun.MutableString;
const lshpack = @import("./lshpack.translated.zig");
const lshpack = @import("./lshpack.zig");
const JSValue = JSC.JSValue;
@@ -540,10 +540,15 @@ pub const H2FrameParser = struct {
// used window size for the connection
usedWindowSize: u32 = 0,
lastStreamID: u32 = 0,
firstSettingsACK: bool = false,
// we buffer requests until we get the first settings ACK
writeBuffer: bun.ByteList = .{},
streams: bun.U32HashMap(Stream),
decoder: lshpack.lshpack_dec = undefined,
encoder: lshpack.lshpack_enc = undefined,
hpack: ?*lshpack.HPACK = null,
threadlocal var shared_request_buffer: [16384]u8 = undefined;
const Stream = struct {
id: u32 = 0,
@@ -620,50 +625,18 @@ pub const H2FrameParser = struct {
}
};
const HeaderValue = struct {
name: []const u8,
value: []const u8,
next: usize,
};
const HeaderValue = lshpack.HPACK.DecodeResult;
pub fn decode(this: *H2FrameParser, 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);
const next = try lshpack.lshpack_decode(&this.decoder, src_buffer.ptr, src_buffer.len, &xhdr);
const name = lshpack.lsxpack_header_get_name(&xhdr);
if (name.len == 0) {
return error.EmptyHeaderName;
pub fn decode(this: *H2FrameParser, src_buffer: []const u8) !HeaderValue {
if (this.hpack) |hpack| {
return try hpack.decode(src_buffer);
}
return .{
.name = name,
.value = lshpack.lsxpack_header_get_value(&xhdr),
.next = next,
};
return error.UnableToDecode;
}
pub fn encode(this: *H2FrameParser, 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;
}
const start = @intFromPtr(dst_buffer.ptr);
log("encode xhdr name {} {} val {} {}", .{ xhdr.name_offset, xhdr.name_len, xhdr.val_offset, xhdr.val_len });
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;
pub fn encode(this: *H2FrameParser, dst_buffer: []u8, dst_offset: usize, name: []const u8, value: []const u8, never_index: bool) !usize {
if (this.hpack) |hpack| {
return try hpack.encode(name, value, never_index, dst_buffer, dst_offset);
}
return error.UnableToEncode;
}
@@ -846,6 +819,7 @@ pub const H2FrameParser = struct {
JSC.markBinding(@src());
const ctx_value = this.strong_ctx.get() orelse return;
value.ensureStillAlive();
extra.ensureStillAlive();
_ = this.handlers.callEventHandler(event, ctx_value, &[_]JSC.JSValue{ ctx_value, value, extra });
}
@@ -853,13 +827,19 @@ pub const H2FrameParser = struct {
JSC.markBinding(@src());
const ctx_value = this.strong_ctx.get() orelse return;
value.ensureStillAlive();
extra.ensureStillAlive();
extra2.ensureStillAlive();
_ = this.handlers.callEventHandler(event, ctx_value, &[_]JSC.JSValue{ ctx_value, value, extra, extra2 });
}
fn bufferWrite(this: *H2FrameParser, bytes: []const u8) void {
log("bufferWrite", .{});
_ = this.writeBuffer.write(this.allocator, bytes) catch 0;
}
pub fn write(this: *H2FrameParser, bytes: []const u8) void {
JSC.markBinding(@src());
log("write", .{});
const output_value = this.handlers.binary_type.toJS(bytes, this.handlers.globalObject);
this.dispatch(.onWrite, output_value);
}
@@ -877,7 +857,7 @@ pub const H2FrameParser = struct {
this.remainingLength -= @intCast(end);
if (this.remainingLength > 0) {
// buffer more data
_ = this.readBuffer.appendSlice(payload) catch @panic("OOM");
_ = this.readBuffer.appendSlice(payload) catch bun.outOfMemory();
return null;
} else if (this.remainingLength < 0) {
this.sendGoAway(streamIdentifier, ErrorCode.FRAME_SIZE_ERROR, "Invalid frame size", this.lastStreamID);
@@ -885,6 +865,16 @@ pub const H2FrameParser = struct {
}
this.currentFrame = null;
if (this.readBuffer.list.items.len > 0) {
// return buffered data
_ = this.readBuffer.appendSlice(payload) catch bun.outOfMemory();
return .{
.data = this.readBuffer.list.items,
.end = end,
};
}
return .{
.data = payload,
.end = end,
@@ -919,14 +909,13 @@ pub const H2FrameParser = struct {
pub fn decodeHeaderBlock(this: *H2FrameParser, payload: []const u8, stream_id: u32, 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.createEmptyObject(globalObject, 0);
while (true) {
const header = this.decode(&header_buffer, payload[offset..]) catch break;
const header = this.decode(payload[offset..]) catch break;
offset += header.next;
log("header {s} {s}", .{ header.name, header.value });
const value = JSC.ZigString.fromUTF8(header.value).toValueGC(globalObject);
@@ -1042,12 +1031,12 @@ pub const H2FrameParser = struct {
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);
this.readBuffer.reset();
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;
@@ -1096,12 +1085,13 @@ pub const H2FrameParser = struct {
if (handleIncommingPayload(this, data, frame.streamIdentifier)) |content| {
const payload = content.data;
const isNotACK = frame.flags & @intFromEnum(PingFrameFlags.ACK) == 0;
this.dispatchWithExtra(.onPing, this.handlers.binary_type.toJS(payload, this.handlers.globalObject), JSC.JSValue.jsBoolean(!isNotACK));
// if is not ACK send response
if (isNotACK) {
this.sendPing(true, payload);
}
const buffer = this.handlers.binary_type.toJS(payload, this.handlers.globalObject);
this.readBuffer.reset();
this.dispatchWithExtra(.onPing, buffer, JSC.JSValue.jsBoolean(!isNotACK));
return content.end;
}
return data.len;
@@ -1156,7 +1146,6 @@ pub const H2FrameParser = struct {
stream.isWaitingMoreHeaders = false;
}
this.readBuffer.reset();
return content.end;
}
@@ -1196,10 +1185,12 @@ pub const H2FrameParser = struct {
}
const end = payload.len - padding;
if (offset > end) {
this.readBuffer.reset();
this.sendGoAway(frame.streamIdentifier, ErrorCode.FRAME_SIZE_ERROR, "invalid Headers frame size", this.lastStreamID);
return data.len;
}
this.decodeHeaderBlock(payload[offset..end], stream.id, frame.flags);
this.readBuffer.reset();
stream.isWaitingMoreHeaders = frame.flags & @intFromEnum(HeadersFrameFlags.END_HEADERS) == 0;
if (frame.flags & @intFromEnum(HeadersFrameFlags.END_STREAM) != 0) {
if (stream.isWaitingMoreHeaders) {
@@ -1214,8 +1205,6 @@ pub const H2FrameParser = struct {
if (stream.endAfterHeaders) {
this.endStream(stream, ErrorCode.NO_ERROR);
}
this.readBuffer.reset();
return content.end;
}
@@ -1239,6 +1228,9 @@ pub const H2FrameParser = struct {
if (frame.flags & 0x1 != 0) {
// we received an ACK
log("settings frame ACK", .{});
// we can now write any request
this.firstSettingsACK = true;
this.flush();
this.remoteSettings = this.localSettings;
this.dispatch(.onLocalSettings, this.localSettings.toJS(this.handlers.globalObject));
}
@@ -1282,7 +1274,7 @@ pub const H2FrameParser = struct {
// new stream open
const settings = this.remoteSettings orelse this.localSettings;
const entry = this.streams.getOrPut(streamIdentifier) catch @panic("OOM");
const entry = this.streams.getOrPut(streamIdentifier) catch bun.outOfMemory();
entry.value_ptr.* = Stream.init(streamIdentifier, settings.initialWindowSize, this);
this.dispatch(.onStreamStart, JSC.JSValue.jsNumber(streamIdentifier));
@@ -1295,16 +1287,16 @@ pub const H2FrameParser = struct {
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),
return switch (header.type) {
@intFromEnum(FrameType.HTTP_FRAME_SETTINGS) => this.handleSettingsFrame(header, bytes),
@intFromEnum(FrameType.HTTP_FRAME_WINDOW_UPDATE) => this.handleWindowUpdateFrame(header, bytes, stream),
@intFromEnum(FrameType.HTTP_FRAME_HEADERS) => this.handleHeadersFrame(header, bytes, stream),
@intFromEnum(FrameType.HTTP_FRAME_DATA) => this.handleDataFrame(header, bytes, stream),
@intFromEnum(FrameType.HTTP_FRAME_CONTINUATION) => this.handleContinuationFrame(header, bytes, stream),
@intFromEnum(FrameType.HTTP_FRAME_PRIORITY) => this.handlePriorityFrame(header, bytes, stream),
@intFromEnum(FrameType.HTTP_FRAME_PING) => this.handlePingFrame(header, bytes, stream),
@intFromEnum(FrameType.HTTP_FRAME_GOAWAY) => this.handleGoAwayFrame(header, bytes, stream),
@intFromEnum(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;
@@ -1323,7 +1315,7 @@ pub const H2FrameParser = struct {
const total = buffered_data + bytes.len;
if (total < FrameHeader.byteSize) {
// buffer more data
_ = this.readBuffer.appendSlice(bytes) catch @panic("OOM");
_ = this.readBuffer.appendSlice(bytes) catch bun.outOfMemory();
return bytes.len;
}
FrameHeader.from(&header, this.readBuffer.list.items[0..buffered_data], 0, false);
@@ -1340,16 +1332,16 @@ pub const H2FrameParser = struct {
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,
return switch (header.type) {
@intFromEnum(FrameType.HTTP_FRAME_SETTINGS) => this.handleSettingsFrame(header, bytes[needed..]) + needed,
@intFromEnum(FrameType.HTTP_FRAME_WINDOW_UPDATE) => this.handleWindowUpdateFrame(header, bytes[needed..], stream) + needed,
@intFromEnum(FrameType.HTTP_FRAME_HEADERS) => this.handleHeadersFrame(header, bytes[needed..], stream) + needed,
@intFromEnum(FrameType.HTTP_FRAME_DATA) => this.handleDataFrame(header, bytes[needed..], stream) + needed,
@intFromEnum(FrameType.HTTP_FRAME_CONTINUATION) => this.handleContinuationFrame(header, bytes[needed..], stream) + needed,
@intFromEnum(FrameType.HTTP_FRAME_PRIORITY) => this.handlePriorityFrame(header, bytes[needed..], stream) + needed,
@intFromEnum(FrameType.HTTP_FRAME_PING) => this.handlePingFrame(header, bytes[needed..], stream) + needed,
@intFromEnum(FrameType.HTTP_FRAME_GOAWAY) => this.handleGoAwayFrame(header, bytes[needed..], stream) + needed,
@intFromEnum(FrameType.HTTP_FRAME_RST_STREAM) => this.handleRSTStreamFrame(header, bytes[needed..], stream) + needed,
else => {
this.sendGoAway(header.streamIdentifier, ErrorCode.PROTOCOL_ERROR, "Unknown frame type", this.lastStreamID);
return bytes.len;
@@ -1359,7 +1351,7 @@ pub const H2FrameParser = struct {
if (bytes.len < FrameHeader.byteSize) {
// buffer more dheaderata
this.readBuffer.appendSlice(bytes) catch @panic("OOM");
this.readBuffer.appendSlice(bytes) catch bun.outOfMemory();
return bytes.len;
}
@@ -1370,16 +1362,16 @@ pub const H2FrameParser = struct {
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,
return switch (header.type) {
@intFromEnum(FrameType.HTTP_FRAME_SETTINGS) => this.handleSettingsFrame(header, bytes[FrameHeader.byteSize..]) + FrameHeader.byteSize,
@intFromEnum(FrameType.HTTP_FRAME_WINDOW_UPDATE) => this.handleWindowUpdateFrame(header, bytes[FrameHeader.byteSize..], stream) + FrameHeader.byteSize,
@intFromEnum(FrameType.HTTP_FRAME_HEADERS) => this.handleHeadersFrame(header, bytes[FrameHeader.byteSize..], stream) + FrameHeader.byteSize,
@intFromEnum(FrameType.HTTP_FRAME_DATA) => this.handleDataFrame(header, bytes[FrameHeader.byteSize..], stream) + FrameHeader.byteSize,
@intFromEnum(FrameType.HTTP_FRAME_CONTINUATION) => this.handleContinuationFrame(header, bytes[FrameHeader.byteSize..], stream) + FrameHeader.byteSize,
@intFromEnum(FrameType.HTTP_FRAME_PRIORITY) => this.handlePriorityFrame(header, bytes[FrameHeader.byteSize..], stream) + FrameHeader.byteSize,
@intFromEnum(FrameType.HTTP_FRAME_PING) => this.handlePingFrame(header, bytes[FrameHeader.byteSize..], stream) + FrameHeader.byteSize,
@intFromEnum(FrameType.HTTP_FRAME_GOAWAY) => this.handleGoAwayFrame(header, bytes[FrameHeader.byteSize..], stream) + FrameHeader.byteSize,
@intFromEnum(FrameType.HTTP_FRAME_RST_STREAM) => this.handleRSTStreamFrame(header, bytes[FrameHeader.byteSize..], stream) + FrameHeader.byteSize,
else => {
this.sendGoAway(header.streamIdentifier, ErrorCode.PROTOCOL_ERROR, "Unknown frame type", this.lastStreamID);
return bytes.len;
@@ -1389,15 +1381,34 @@ pub const H2FrameParser = struct {
const DirectWriterStruct = struct {
writer: *H2FrameParser,
shouldBuffer: bool = true,
pub fn write(this: *const DirectWriterStruct, data: []const u8) !bool {
if (this.shouldBuffer) {
_ = this.writer.writeBuffer.write(this.writer.allocator, data) catch return false;
return true;
}
this.writer.write(data);
return true;
}
};
fn toWriter(this: *H2FrameParser) DirectWriterStruct {
return DirectWriterStruct{ .writer = this };
return DirectWriterStruct{ .writer = this, .shouldBuffer = false };
}
fn getBufferWriter(this: *H2FrameParser) DirectWriterStruct {
return DirectWriterStruct{ .writer = this, .shouldBuffer = true };
}
fn flush(this: *H2FrameParser) void {
if (this.writeBuffer.len > 0) {
const slice = this.writeBuffer.slice();
this.write(slice);
// we will only flush one time
this.writeBuffer.deinitWithAllocator(this.allocator);
}
}
pub fn setEncoding(this: *H2FrameParser, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue {
JSC.markBinding(@src());
const args_list = callframe.arguments(1);
@@ -1887,7 +1898,9 @@ pub const H2FrameParser = struct {
return JSC.JSValue.jsBoolean(true);
}
fn sendData(this: *H2FrameParser, stream_id: u32, payload: []const u8, close: bool) void {
const writer = this.toWriter();
log("sendData({}, {}, {})", .{ stream_id, payload.len, close });
const writer = if (this.firstSettingsACK) this.toWriter() else this.getBufferWriter();
if (payload.len == 0) {
// empty payload we still need to send a frame
var dataHeader: FrameHeader = .{
@@ -1914,7 +1927,7 @@ pub const H2FrameParser = struct {
.length = size,
};
dataHeader.write(@TypeOf(writer), writer);
this.write(slice);
_ = writer.write(slice) catch 0;
}
}
}
@@ -1958,9 +1971,7 @@ pub const H2FrameParser = struct {
}
// max frame size will be always at least 16384
var buffer: [16384 - FrameHeader.byteSize]u8 = undefined;
var header_buffer: [MAX_HPACK_HEADER_SIZE]u8 = undefined;
@memset(&buffer, 0);
var buffer = shared_request_buffer[0 .. shared_request_buffer.len - FrameHeader.byteSize];
var encoded_size: usize = 0;
@@ -2016,7 +2027,7 @@ pub const H2FrameParser = struct {
defer value_slice.deinit();
const value = value_slice.slice();
log("encode header {s} {s}", .{ name, value });
encoded_size += this.encode(&header_buffer, buffer[encoded_size..], name, value_slice.slice(), never_index) catch {
encoded_size += this.encode(buffer, encoded_size, name, value, never_index) catch {
stream.state = .CLOSED;
stream.rstCode = @intFromEnum(ErrorCode.COMPRESSION_ERROR);
this.dispatchWithExtra(.onStreamError, JSC.JSValue.jsNumber(stream_id), JSC.JSValue.jsNumber(stream.rstCode));
@@ -2036,7 +2047,7 @@ pub const H2FrameParser = struct {
defer value_slice.deinit();
const value = value_slice.slice();
log("encode header {s} {s}", .{ name, value });
encoded_size += this.encode(&header_buffer, buffer[encoded_size..], name, value_slice.slice(), never_index) catch {
encoded_size += this.encode(buffer, encoded_size, name, value, never_index) catch {
stream.state = .CLOSED;
stream.rstCode = @intFromEnum(ErrorCode.COMPRESSION_ERROR);
this.dispatchWithExtra(.onStreamError, JSC.JSValue.jsNumber(stream_id), JSC.JSValue.jsNumber(stream.rstCode));
@@ -2053,9 +2064,9 @@ pub const H2FrameParser = struct {
.streamIdentifier = stream.id,
.length = @intCast(encoded_size),
};
const writer = this.toWriter();
const writer = if (this.firstSettingsACK) this.toWriter() else this.getBufferWriter();
frame.write(@TypeOf(writer), writer);
this.write(buffer[0..encoded_size]);
_ = writer.write(buffer[0..encoded_size]) catch 0;
return JSC.JSValue.jsUndefined();
}
@@ -2154,9 +2165,7 @@ pub const H2FrameParser = struct {
}
// 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 buffer = shared_request_buffer[0 .. shared_request_buffer.len - FrameHeader.byteSize - 5];
var encoded_size: usize = 0;
@@ -2227,7 +2236,7 @@ pub const H2FrameParser = struct {
defer value_slice.deinit();
const value = value_slice.slice();
log("encode header {s} {s}", .{ name, value });
encoded_size += this.encode(&header_buffer, buffer[encoded_size..], name, value_slice.slice(), never_index) catch {
encoded_size += this.encode(buffer, encoded_size, name, value, never_index) catch {
const stream = this.handleReceivedStreamID(stream_id) orelse {
return JSC.JSValue.jsNumber(-1);
};
@@ -2251,7 +2260,7 @@ pub const H2FrameParser = struct {
defer value_slice.deinit();
const value = value_slice.slice();
log("encode header {s} {s}", .{ name, value });
encoded_size += this.encode(&header_buffer, buffer[encoded_size..], name, value_slice.slice(), never_index) catch {
encoded_size += this.encode(buffer, encoded_size, name, value, never_index) catch {
const stream = this.handleReceivedStreamID(stream_id) orelse {
return JSC.JSValue.jsNumber(-1);
};
@@ -2374,7 +2383,8 @@ pub const H2FrameParser = struct {
.streamIdentifier = stream.id,
.length = @intCast(encoded_size),
};
const writer = this.toWriter();
const writer = if (this.firstSettingsACK) this.toWriter() else this.getBufferWriter();
frame.write(@TypeOf(writer), writer);
//https://datatracker.ietf.org/doc/html/rfc7540#section-6.2
if (has_priority) {
@@ -2391,7 +2401,7 @@ pub const H2FrameParser = struct {
priority.write(@TypeOf(writer), writer);
}
this.write(buffer[0..encoded_size]);
_ = writer.write(buffer[0..encoded_size]) catch 0;
if (end_stream) {
stream.state = .HALF_CLOSED_LOCAL;
@@ -2414,9 +2424,9 @@ pub const H2FrameParser = struct {
return .zero;
}
const buffer = args_list.ptr[0];
buffer.ensureStillAlive();
if (buffer.asArrayBuffer(globalObject)) |array_buffer| {
var bytes = array_buffer.byteSlice();
// read all the bytes
while (bytes.len > 0) {
const result = this.readBytes(bytes);
@@ -2482,13 +2492,7 @@ pub const H2FrameParser = struct {
this.strong_ctx.set(globalObject, context_obj);
if (lshpack.lshpack_enc_init(&this.encoder) != 0) {
@panic("OOM");
}
lshpack.lshpack_dec_init(&this.decoder);
lshpack.lshpack_enc_set_max_capacity(&this.encoder, this.localSettings.headerTableSize);
lshpack.lshpack_dec_set_max_capacity(&this.decoder, this.localSettings.headerTableSize);
this.hpack = lshpack.HPACK.init(this.localSettings.headerTableSize);
this.sendPrefaceAndSettings();
return this;
}
@@ -2499,9 +2503,12 @@ pub const H2FrameParser = struct {
this.strong_ctx.deinit();
this.handlers.deinit();
this.readBuffer.deinit();
this.writeBuffer.deinitWithAllocator(allocator);
lshpack.lshpack_dec_cleanup(&this.decoder);
lshpack.lshpack_enc_cleanup(&this.encoder);
if (this.hpack) |hpack| {
hpack.deinit();
this.hpack = null;
}
var it = this.streams.iterator();
while (it.next()) |*entry| {

View File

@@ -1,448 +0,0 @@
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 = i32;
pub const __uint32_t = u32;
pub const __int64_t = i64;
pub const __uint64_t = u64;
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_offset_t = i32;
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 = null,
/// 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_offset_t = 0,
/// the offset for value in the buffer
val_offset: lsxpack_offset_t = 0,
/// the length of name
name_len: 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 = @intCast(arg_name_offset);
arg_hdr.val_offset = @intCast(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 = @intCast(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.?[@as(usize, @intCast(hdr.name_offset)) .. @as(usize, @intCast(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.?[@as(usize, @intCast(hdr.val_offset)) .. @as(usize, @intCast(hdr.val_offset)) + hdr.val_len];
return "";
}
pub fn lsxpack_header_get_dec_size(hdr: ?*const lsxpack_header_t) callconv(.C) usize {
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(hdr: ?*lsxpack_header_t) callconv(.C) void {
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 = @import("std").mem.zeroes(?*struct_lshpack_enc_table_entry),
stqh_last: [*c]?*struct_lshpack_enc_table_entry = @import("std").mem.zeroes([*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 = @import("std").mem.zeroes(c_uint),
hpe_max_capacity: c_uint = @import("std").mem.zeroes(c_uint),
hpe_next_id: c_uint = @import("std").mem.zeroes(c_uint),
hpe_nelem: c_uint = @import("std").mem.zeroes(c_uint),
hpe_nbits: c_uint = @import("std").mem.zeroes(c_uint),
hpe_all_entries: struct_lshpack_enc_head = @import("std").mem.zeroes(struct_lshpack_enc_head),
hpe_buckets: ?*struct_lshpack_double_enc_head = @import("std").mem.zeroes(?*struct_lshpack_double_enc_head),
hpe_hist_buf: [*c]u32 = @import("std").mem.zeroes([*c]u32),
hpe_hist_size: c_uint = @import("std").mem.zeroes(c_uint),
hpe_hist_idx: c_uint = @import("std").mem.zeroes(c_uint),
hpe_hist_wrapped: c_int = @import("std").mem.zeroes(c_int),
hpe_flags: enum_unnamed_1 = @import("std").mem.zeroes(enum_unnamed_1),
};
pub const struct_lshpack_arr = extern struct {
nalloc: c_uint = @import("std").mem.zeroes(c_uint),
nelem: c_uint = @import("std").mem.zeroes(c_uint),
off: c_uint = @import("std").mem.zeroes(c_uint),
els: [*c]usize = @import("std").mem.zeroes([*c]usize),
};
pub const struct_lshpack_dec = extern struct {
hpd_dyn_table: struct_lshpack_arr = @import("std").mem.zeroes(struct_lshpack_arr),
hpd_max_capacity: c_uint = @import("std").mem.zeroes(c_uint),
hpd_cur_max_capacity: c_uint = @import("std").mem.zeroes(c_uint),
hpd_cur_capacity: c_uint = @import("std").mem.zeroes(c_uint),
hpd_state: c_uint = @import("std").mem.zeroes(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: *[*c]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 fn lshpack_decode(dec: [*c]struct_lshpack_dec, src: [*]const u8, src_len: usize, output: ?*struct_lsxpack_header) !usize {
var s: [*c]const u8 = src;
const rc: c_int = lshpack_dec_decode(dec, &s, s + src_len, output);
if (rc != 0) {
return error.UnableToDecode;
}
return @intFromPtr(s) - @intFromPtr(src);
}
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;

View File

@@ -0,0 +1,61 @@
const bun = @import("root").bun;
const lshpack_header = extern struct {
name: [*]const u8 = undefined,
name_len: usize = 0,
value: [*]const u8 = undefined,
value_len: usize = 0,
};
/// wrapper implemented at src/bun.js/bindings/c-bindings.cpp
pub const HPACK = extern struct {
self: *anyopaque,
const mimalloc = @import("../../../allocators/mimalloc.zig");
pub const DecodeResult = struct {
name: []const u8,
value: []const u8,
// offset of the next header position in src
next: usize,
};
pub const LSHPACK_MAX_HEADER_SIZE: usize = 65536;
pub fn init(max_capacity: u32) *HPACK {
return lshpack_wrapper_init(mimalloc.mi_malloc, mimalloc.mi_free, max_capacity) orelse bun.outOfMemory();
}
/// DecodeResult name and value uses a thread_local shared buffer and should be copy/cloned before the next decode/encode call
pub fn decode(self: *HPACK, src: []const u8) !DecodeResult {
var header: lshpack_header = .{};
const offset = lshpack_wrapper_decode(self, src.ptr, src.len, &header);
if (offset == 0) return error.UnableToDecode;
if (header.name_len == 0) return error.EmptyHeaderName;
return .{
.name = header.name[0..header.name_len],
.value = header.value[0..header.value_len],
.next = offset,
};
}
/// encode name, value with never_index option into dst_buffer
/// if name + value length is greater than LSHPACK_MAX_HEADER_SIZE this will return UnableToEncode
pub fn encode(self: *HPACK, name: []const u8, value: []const u8, never_index: bool, dst_buffer: []u8, dst_buffer_offset: usize) !usize {
const offset = lshpack_wrapper_encode(self, name.ptr, name.len, value.ptr, value.len, @intFromBool(never_index), dst_buffer.ptr, dst_buffer.len, dst_buffer_offset);
if (offset <= 0) return error.UnableToEncode;
return offset;
}
pub fn deinit(self: *HPACK) void {
lshpack_wrapper_deinit(self);
}
};
const lshpack_wrapper_alloc = ?*const fn (size: usize) callconv(.C) ?*anyopaque;
const lshpack_wrapper_free = ?*const fn (ptr: ?*anyopaque) callconv(.C) void;
extern fn lshpack_wrapper_init(alloc: lshpack_wrapper_alloc, free: lshpack_wrapper_free, capacity: usize) ?*HPACK;
extern fn lshpack_wrapper_deinit(self: *HPACK) void;
extern fn lshpack_wrapper_decode(self: *HPACK, src: [*]const u8, src_len: usize, output: *lshpack_header) usize;
extern fn lshpack_wrapper_encode(self: *HPACK, name: [*]const u8, name_len: usize, value: [*]const u8, value_len: usize, never_index: c_int, buffer: [*]u8, buffer_len: usize, buffer_offset: usize) usize;

View File

@@ -13,6 +13,7 @@
#include <uv.h>
#include <windows.h>
#endif // !OS(WINDOWS)
#include <lshpack.h>
#if CPU(X86_64) && !OS(WINDOWS)
extern "C" void bun_warn_avx_missing(const char* url)
@@ -183,4 +184,91 @@ extern "C" void on_before_reload_process_linux()
sigprocmask(SIG_SETMASK, &signal_set, nullptr);
}
#endif
#endif
#define LSHPACK_MAX_HEADER_SIZE 65536
static thread_local char shared_header_buffer[LSHPACK_MAX_HEADER_SIZE];
extern "C" {
typedef void* (*lshpack_wrapper_alloc)(size_t size);
typedef void (*lshpack_wrapper_free)(void*);
typedef struct {
struct lshpack_enc enc;
struct lshpack_dec dec;
lshpack_wrapper_free free;
} lshpack_wrapper;
typedef struct {
const char* name;
size_t name_len;
const char* value;
size_t value_len;
} lshpack_header;
lshpack_wrapper* lshpack_wrapper_init(lshpack_wrapper_alloc alloc, lshpack_wrapper_free free, unsigned max_capacity)
{
lshpack_wrapper* coders = (lshpack_wrapper*)alloc(sizeof(lshpack_wrapper));
if (!coders)
return nullptr;
coders->free = free;
if (lshpack_enc_init(&coders->enc) != 0)
return nullptr;
lshpack_dec_init(&coders->dec);
lshpack_enc_set_max_capacity(&coders->enc, max_capacity);
lshpack_dec_set_max_capacity(&coders->dec, max_capacity);
return coders;
}
size_t lshpack_wrapper_encode(lshpack_wrapper* self,
const unsigned char* name, size_t name_len,
const unsigned char* val, size_t val_len,
int never_index,
unsigned char* buffer, size_t buffer_len, size_t buffer_offset)
{
if (name_len + val_len > LSHPACK_MAX_HEADER_SIZE)
return 0;
lsxpack_header_t hdr;
memset(&hdr, 0, sizeof(lsxpack_header_t));
memcpy(&shared_header_buffer[0], name, name_len);
memcpy(&shared_header_buffer[name_len], val, val_len);
lsxpack_header_set_offset2(&hdr, &shared_header_buffer[0], 0, name_len, name_len, val_len);
if (never_index) {
hdr.indexed_type = 2;
}
auto* start = buffer + buffer_offset;
auto* ptr = lshpack_enc_encode(&self->enc, start, buffer + buffer_len, &hdr);
if (!ptr)
return 0;
return ptr - start;
}
size_t lshpack_wrapper_decode(lshpack_wrapper* self,
const unsigned char* src, size_t src_len,
lshpack_header* output)
{
lsxpack_header_t hdr;
memset(&hdr, 0, sizeof(lsxpack_header_t));
lsxpack_header_prepare_decode(&hdr, &shared_header_buffer[0], 0, LSHPACK_MAX_HEADER_SIZE);
const unsigned char* s = src;
auto rc = lshpack_dec_decode(&self->dec, &s, s + src_len, &hdr);
if (rc != 0)
return 0;
output->name = lsxpack_header_get_name(&hdr);
output->name_len = hdr.name_len;
output->value = lsxpack_header_get_value(&hdr);
output->value_len = hdr.val_len;
return s - src;
}
void lshpack_wrapper_deinit(lshpack_wrapper* self)
{
lshpack_dec_cleanup(&self->dec);
lshpack_enc_cleanup(&self->enc);
self->free(self);
}
}

View File

@@ -679,7 +679,10 @@ function emitAbortedNT(self, streams, streamId, error) {
self.emit("streamError", error_instance);
}
class ClientHttp2Session extends Http2Session {
/// close indicates that we called closed
#closed: boolean = false;
/// connected indicates that the connection/socket is connected
#connected: boolean = false;
#queue: Array<Buffer> = [];
#connections: number = 0;
[bunHTTP2Socket]: TLSSocket | Socket | null;
@@ -763,33 +766,33 @@ class ClientHttp2Session extends Http2Session {
) {
if (!self) return;
var stream = self.#streams.get(streamId);
if (stream) {
let status: string | number = headers[":status"] as string;
if (status) {
// client status is always number
status = parseInt(status as string, 10);
(headers as Record<string, string | number>)[":status"] = status;
}
if (!stream) return;
let set_cookies = headers["set-cookie"];
if (typeof set_cookies === "string") {
(headers as Record<string, string | string[]>)["set-cookie"] = [set_cookies];
}
let status: string | number = headers[":status"] as string;
if (status) {
// client status is always number
status = parseInt(status as string, 10);
(headers as Record<string, string | number>)[":status"] = status;
}
let cookie = headers["cookie"];
if ($isArray(cookie)) {
headers["cookie"] = (headers["cookie"] as string[]).join(";");
}
if (stream[bunHTTP2StreamResponded]) {
try {
stream.emit("trailers", headers, flags);
} catch {
process.nextTick(emitStreamErrorNT, self, self.#streams, streamId, constants.NGHTTP2_PROTOCOL_ERROR, true);
}
} else {
stream[bunHTTP2StreamResponded] = true;
stream.emit("response", headers, flags);
let set_cookies = headers["set-cookie"];
if (typeof set_cookies === "string") {
(headers as Record<string, string | string[]>)["set-cookie"] = [set_cookies];
}
let cookie = headers["cookie"];
if ($isArray(cookie)) {
headers["cookie"] = (headers["cookie"] as string[]).join(";");
}
if (stream[bunHTTP2StreamResponded]) {
try {
stream.emit("trailers", headers, flags);
} catch {
process.nextTick(emitStreamErrorNT, self, self.#streams, streamId, constants.NGHTTP2_PROTOCOL_ERROR, true);
}
} else {
stream[bunHTTP2StreamResponded] = true;
stream.emit("response", headers, flags);
}
},
localSettings(self: ClientHttp2Session, settings: Settings) {
@@ -874,12 +877,13 @@ class ClientHttp2Session extends Http2Session {
write(self: ClientHttp2Session, buffer: Buffer) {
if (!self) return;
const socket = self[bunHTTP2Socket];
if (self.#closed) {
//queue
self.#queue.push(buffer);
} else {
if (!socket) return;
if (self.#connected) {
// redirect writes to socket
socket.write(buffer);
} else {
//queue
self.#queue.push(buffer);
}
},
};
@@ -899,6 +903,7 @@ class ClientHttp2Session extends Http2Session {
#onConnect() {
const socket = this[bunHTTP2Socket];
if (!socket) return;
this.#connected = true;
// check if h2 is supported only for TLSSocket
if (socket instanceof TLSSocket) {
if (socket.alpnProtocol !== "h2") {
@@ -918,6 +923,7 @@ class ClientHttp2Session extends Http2Session {
// TODO: make a native bindings on data and write and fallback to non-native
socket.on("data", this.#onRead.bind(this));
// redirect the queued buffers
const queue = this.#queue;
while (queue.length) {
@@ -1129,6 +1135,7 @@ class ClientHttp2Session extends Http2Session {
destroy(error?: Error, code?: number) {
const socket = this[bunHTTP2Socket];
this.#closed = true;
this.#connected = false;
code = code || constants.NGHTTP2_NO_ERROR;
if (socket) {
this.goaway(code, 0, Buffer.alloc(0));

View File

@@ -1064,7 +1064,7 @@ describe("Client Basics", () => {
const settings = new http2utils.SettingsFrame(true);
socket.write(settings.data);
const frame = new http2utils.Frame(7, 7, 0, 0).data;
socket.end(Buffer.concat([frame, Buffer.alloc(7)]));
socket.write(Buffer.concat([frame, Buffer.alloc(7)]));
});
server.listen(0, "127.0.0.1", async () => {
const url = `http://127.0.0.1:${server.address().port}`;
@@ -1095,7 +1095,7 @@ describe("Client Basics", () => {
socket.write(settings.data);
await waitToWrite;
const frame = new http2utils.DataFrame(1, Buffer.alloc(16384 * 2), 0, 1).data;
socket.end(frame);
socket.write(frame);
});
server.listen(0, "127.0.0.1", async () => {
const url = `http://127.0.0.1:${server.address().port}`;
@@ -1127,7 +1127,7 @@ describe("Client Basics", () => {
socket.write(settings.data);
await waitToWrite;
const frame = new http2utils.Frame(4, 3, 0, 0).data;
socket.end(Buffer.concat([frame, Buffer.alloc(4)]));
socket.write(Buffer.concat([frame, Buffer.alloc(4)]));
});
server.listen(0, "127.0.0.1", async () => {
const url = `http://127.0.0.1:${server.address().port}`;
@@ -1159,7 +1159,7 @@ describe("Client Basics", () => {
socket.write(settings.data);
await waitToWrite;
const frame = new http2utils.Frame(3, 3, 0, 1).data;
socket.end(Buffer.concat([frame, Buffer.alloc(3)]));
socket.write(Buffer.concat([frame, Buffer.alloc(3)]));
});
server.listen(0, "127.0.0.1", async () => {
const url = `http://127.0.0.1:${server.address().port}`;
@@ -1192,7 +1192,7 @@ describe("Client Basics", () => {
await waitToWrite;
const buffer = Buffer.alloc(16384 * 2);
const frame = new http2utils.Frame(buffer.byteLength, 3, 0, 1).data;
socket.end(Buffer.concat([frame, buffer]));
socket.write(Buffer.concat([frame, buffer]));
});
server.listen(0, "127.0.0.1", async () => {
const url = `http://127.0.0.1:${server.address().port}`;
@@ -1262,7 +1262,7 @@ describe("Client Basics", () => {
await waitToWrite;
const frame = new http2utils.Frame(4, 2, 0, 1).data;
socket.end(Buffer.concat([frame, Buffer.alloc(4)]));
socket.write(Buffer.concat([frame, Buffer.alloc(4)]));
});
server.listen(0, "127.0.0.1", async () => {
const url = `http://127.0.0.1:${server.address().port}`;