Compare commits

...

2 Commits

Author SHA1 Message Date
Don Isaac
44e7925516 Merge branch 'main' into don/fix/picohttp-usingnamespace 2025-03-25 17:01:01 -07:00
Don Isaac
d42f992567 fix(http): remove usingnamespace in picohttp 2025-03-24 11:07:12 -07:00
3 changed files with 108 additions and 85 deletions

View File

@@ -13,6 +13,8 @@ const fmt = std.fmt;
const assert = bun.assert;
pub const ChunkedDecoder = c.ChunkedDecoder;
pub const Header = struct {
name: []const u8,
value: []const u8,
@@ -369,5 +371,3 @@ pub const Headers = struct {
};
}
};
pub usingnamespace c;

View File

@@ -1,21 +1,66 @@
pub usingnamespace @import("std").zig.c_builtins;
const std = @import("std");
/// contains name and value of a header (name == `null` if is a continuing line of
/// a multiline header)
pub const struct_phr_header = extern struct {
name: [*c]const u8,
name_len: usize,
value: [*c]const u8,
value_len: usize,
};
/// returns number of bytes consumed if successful, -2 if request is partial, -1
/// if failed
pub extern fn phr_parse_request(buf: [*c]const u8, len: usize, method: [*c][*c]const u8, method_len: [*c]usize, path: [*c][*c]const u8, path_len: [*c]usize, minor_version: [*c]c_int, headers: [*c]struct_phr_header, num_headers: [*c]usize, last_len: usize) c_int;
/// returns number of bytes consumed if successful, -2 if request is partial, -1
/// if failed
pub extern fn phr_parse_response(_buf: [*c]const u8, len: usize, minor_version: [*c]c_int, status: [*c]c_int, msg: [*c][*c]const u8, msg_len: [*c]usize, headers: [*c]struct_phr_header, num_headers: [*c]usize, last_len: usize) c_int;
/// returns number of bytes consumed if successful, -2 if request is partial, -1
/// if failed
pub extern fn phr_parse_headers(buf: [*c]const u8, len: usize, headers: [*c]struct_phr_header, num_headers: [*c]usize, last_len: usize) c_int;
pub const struct_phr_chunked_decoder = extern struct {
pub const ChunkedDecoder = extern struct {
bytes_left_in_chunk: usize = 0,
consume_trailer: u8 = 0,
_hex_count: u8 = 0,
_state: u8 = 0,
pub const Error = error{
InvalidHTTPResponse,
};
/// Decode chunked data from the buffer.
///
/// This method rewrites the buffer given as (`buf`, `bufsize`) in-place,
/// removing chunked-encoding headers. Callers should repeatedly call this
/// method when it returns `null`, every time supplying newly
/// arrived data. When the end of chunked-encoded data is found, returns the
/// number of octets left uncoded, that starts from the end of `buf`.
pub fn decodeChunked(self: *ChunkedDecoder, buf: [*]u8, bufsize: *usize) Error!?usize {
return switch (phr_decode_chunked(self, buf, bufsize)) {
-1 => error.InvalidHTTPResponse,
-2 => null,
else => |len| blk: {
std.debug.assert(len >= 0);
break :blk @intCast(len);
},
};
}
/// Returns true if the decoder is in the middle of chunked data
pub fn isInData(self: *ChunkedDecoder) bool {
@branchHint(.likely);
return phr_decode_chunked_is_in_data(self) != 0;
}
/// the function rewrites the buffer given as (buf, bufsz) removing the chunked-
/// encoding headers. When the function returns without an error, bufsz is
/// updated to the length of the decoded data available. Applications should
/// repeatedly call the function while it returns -2 (incomplete) every time
/// supplying newly arrived data. If the end of the chunked-encoded data is
/// found, the function returns a non-negative number indicating the number of
/// octets left undecoded, that starts from the offset returned by `*bufsz`.
/// Returns -1 on error.
extern fn phr_decode_chunked(decoder: *ChunkedDecoder, buf: [*]u8, bufsz: *usize) isize;
extern fn phr_decode_chunked_is_in_data(decoder: *ChunkedDecoder) c_int;
};
pub extern fn phr_decode_chunked(decoder: *struct_phr_chunked_decoder, buf: [*]u8, bufsz: *usize) isize;
pub extern fn phr_decode_chunked_is_in_data(decoder: *struct_phr_chunked_decoder) c_int;
pub const phr_header = struct_phr_header;
pub const phr_chunked_decoder = struct_phr_chunked_decoder;

View File

@@ -1706,7 +1706,7 @@ pub fn onClose(
// as if the transfer had complete, browsers appear to ignore
// a missing 0\r\n chunk
if (client.state.isChunkedEncoding()) {
if (picohttp.phr_decode_chunked_is_in_data(&client.state.chunked_decoder) == 0) {
if (!client.state.chunked_decoder.isInData()) {
const buf = client.state.getBodyBuffer();
if (buf.list.items.len > 0) {
client.state.flags.received_last_chunk = true;
@@ -2015,7 +2015,7 @@ pub const InternalState = struct {
transfer_encoding: Encoding = Encoding.identity,
encoding: Encoding = Encoding.identity,
content_encoding_i: u8 = std.math.maxInt(u8),
chunked_decoder: picohttp.phr_chunked_decoder = .{},
chunked_decoder: picohttp.ChunkedDecoder = .{},
decompressor: Decompressor = .{ .none = {} },
stage: Stage = Stage.pending,
/// This is owned by the user and should not be freed here
@@ -4139,54 +4139,44 @@ fn handleResponseBodyChunkedEncodingFromMultiplePackets(
var bytes_decoded = incoming_data.len;
// phr_decode_chunked mutates in-place
const pret = picohttp.phr_decode_chunked(
decoder,
const needs_more_data = try decoder.decodeChunked(
buffer.list.items.ptr + (buffer.list.items.len -| incoming_data.len),
&bytes_decoded,
);
buffer.list.items.len -|= incoming_data.len - bytes_decoded;
this.state.total_body_received += bytes_decoded;
buffer_ptr.* = buffer;
if (needs_more_data == null) {
if (this.progress_node) |progress| {
progress.activate();
progress.setCompletedItems(buffer.list.items.len);
progress.context.maybeRefresh();
}
// streaming chunks
if (this.signals.get(.body_streaming)) {
// If we're streaming, we cannot use the libdeflate fast path
this.state.flags.is_libdeflate_fast_path_disabled = true;
return try this.state.processBodyBuffer(buffer, false);
}
switch (pret) {
// Invalid HTTP response body
-1 => return error.InvalidHTTPResponse,
// Needs more data
-2 => {
if (this.progress_node) |progress| {
progress.activate();
progress.setCompletedItems(buffer.list.items.len);
progress.context.maybeRefresh();
}
// streaming chunks
if (this.signals.get(.body_streaming)) {
// If we're streaming, we cannot use the libdeflate fast path
this.state.flags.is_libdeflate_fast_path_disabled = true;
return try this.state.processBodyBuffer(buffer, false);
}
return false;
},
// Done
else => {
this.state.flags.received_last_chunk = true;
_ = try this.state.processBodyBuffer(
buffer,
true,
);
if (this.progress_node) |progress| {
progress.activate();
progress.setCompletedItems(buffer.list.items.len);
progress.context.maybeRefresh();
}
return true;
},
return false;
}
unreachable;
this.state.flags.received_last_chunk = true;
_ = try this.state.processBodyBuffer(
buffer,
true,
);
if (this.progress_node) |progress| {
progress.activate();
progress.setCompletedItems(buffer.list.items.len);
progress.context.maybeRefresh();
}
return true;
}
// the first packet for Transfer-Encoding: chunked
@@ -4217,55 +4207,43 @@ fn handleResponseBodyChunkedEncodingFromSinglePacket(
var bytes_decoded = incoming_data.len;
// phr_decode_chunked mutates in-place
const pret = picohttp.phr_decode_chunked(
decoder,
const needs_more_data = try decoder.decodeChunked(
buffer.ptr + (buffer.len -| incoming_data.len),
&bytes_decoded,
);
buffer.len -|= incoming_data.len - bytes_decoded;
this.state.total_body_received += bytes_decoded;
switch (pret) {
// Invalid HTTP response body
-1 => {
return error.InvalidHTTPResponse;
},
// Needs more data
-2 => {
if (this.progress_node) |progress| {
progress.activate();
progress.setCompletedItems(buffer.len);
progress.context.maybeRefresh();
}
const body_buffer = this.state.getBodyBuffer();
try body_buffer.appendSliceExact(buffer);
if (needs_more_data == null) {
if (this.progress_node) |progress| {
progress.activate();
progress.setCompletedItems(buffer.len);
progress.context.maybeRefresh();
}
const body_buffer = this.state.getBodyBuffer();
try body_buffer.appendSliceExact(buffer);
// streaming chunks
if (this.signals.get(.body_streaming)) {
// If we're streaming, we cannot use the libdeflate fast path
this.state.flags.is_libdeflate_fast_path_disabled = true;
// streaming chunks
if (this.signals.get(.body_streaming)) {
// If we're streaming, we cannot use the libdeflate fast path
this.state.flags.is_libdeflate_fast_path_disabled = true;
return try this.state.processBodyBuffer(body_buffer.*, true);
}
return try this.state.processBodyBuffer(body_buffer.*, true);
}
return false;
},
// Done
else => {
this.state.flags.received_last_chunk = true;
try this.handleResponseBodyFromSinglePacket(buffer);
assert(this.state.body_out_str.?.list.items.ptr != buffer.ptr);
if (this.progress_node) |progress| {
progress.activate();
progress.setCompletedItems(buffer.len);
progress.context.maybeRefresh();
}
return true;
},
return false;
}
unreachable;
this.state.flags.received_last_chunk = true;
try this.handleResponseBodyFromSinglePacket(buffer);
assert(this.state.body_out_str.?.list.items.ptr != buffer.ptr);
if (this.progress_node) |progress| {
progress.activate();
progress.setCompletedItems(buffer.len);
progress.context.maybeRefresh();
}
return true;
}
const ShouldContinue = enum {