aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-06-27 05:29:25 -0700
committerGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-06-27 05:29:25 -0700
commit07050901a63ea46d2469be178b519582e50213e4 (patch)
treea4fb3cc2158fa1d37be255d93f6695e68acddff5 /src
parentf70784a6d14d7858076bb925216d204a7abc4b93 (diff)
downloadbun-07050901a63ea46d2469be178b519582e50213e4.tar.gz
bun-07050901a63ea46d2469be178b519582e50213e4.tar.zst
bun-07050901a63ea46d2469be178b519582e50213e4.zip
[fetch] Fix data corruption bug
Diffstat (limited to 'src')
-rw-r--r--src/http/async_socket.zig15
-rw-r--r--src/http_client_async.zig85
2 files changed, 51 insertions, 49 deletions
diff --git a/src/http/async_socket.zig b/src/http/async_socket.zig
index 3cf25944b..72980ec7c 100644
--- a/src/http/async_socket.zig
+++ b/src/http/async_socket.zig
@@ -15,6 +15,8 @@ const SOCKET_FLAGS: u32 = @import("../http_client_async.zig").SOCKET_FLAGS;
const getAllocator = @import("../http_client_async.zig").getAllocator;
const OPEN_SOCKET_FLAGS: u32 = @import("../http_client_async.zig").OPEN_SOCKET_FLAGS;
+const log = Output.scoped(.AsyncSocket, true);
+
const SSLFeatureFlags = struct {
pub const early_data_enabled = true;
};
@@ -291,6 +293,8 @@ pub inline fn bufferedReadAmount(_: *AsyncSocket) usize {
pub fn read(
this: *AsyncSocket,
bytes: []u8,
+ /// offset is necessary here to be consistent with HTTPS
+ /// HTTPs must have the same buffer pointer for each read
offset: u64,
) RecvError!u64 {
this.read_context = bytes;
@@ -303,7 +307,7 @@ pub fn read(
Reader.on_read,
&this.read_completion,
this.socket,
- bytes,
+ bytes[original_read_offset..],
);
suspend {
@@ -315,6 +319,15 @@ pub fn read(
return @errSetCast(RecvError, err);
}
+ log(
+ \\recv(offset: {d}, len: {d}, read_offset: {d})
+ \\
+ , .{
+ offset,
+ bytes[original_read_offset..].len,
+ this.read_offset,
+ });
+
return this.read_offset - original_read_offset;
}
diff --git a/src/http_client_async.zig b/src/http_client_async.zig
index 59adbfb9a..686718318 100644
--- a/src/http_client_async.zig
+++ b/src/http_client_async.zig
@@ -35,6 +35,8 @@ pub const URLPath = @import("./http/url_path.zig");
pub var default_allocator: std.mem.Allocator = undefined;
pub var default_arena: Arena = undefined;
+const log = Output.scoped(.fetch, true);
+
pub fn onThreadStart(_: ?*anyopaque) ?*anyopaque {
default_arena = Arena.init() catch unreachable;
default_allocator = default_arena.allocator();
@@ -494,6 +496,7 @@ pub fn buildRequest(this: *HTTPClient, body_len: usize) picohttp.Request {
var override_accept_encoding = false;
var override_accept_header = false;
+ var override_host_header = false;
var override_user_agent = false;
for (header_names) |head, i| {
@@ -504,7 +507,6 @@ pub fn buildRequest(this: *HTTPClient, body_len: usize) picohttp.Request {
// Skip host and connection header
// we manage those
switch (hash) {
- host_header_hash,
connection_header_hash,
content_length_header_hash,
=> continue,
@@ -513,6 +515,9 @@ pub fn buildRequest(this: *HTTPClient, body_len: usize) picohttp.Request {
this.if_modified_since = this.headerStr(header_values[i]);
}
},
+ host_header_hash => {
+ override_host_header = true;
+ },
accept_header_hash => {
override_accept_header = true;
},
@@ -544,8 +549,8 @@ pub fn buildRequest(this: *HTTPClient, body_len: usize) picohttp.Request {
header_count += 1;
}
- // request_headers_buf[header_count] = connection_header;
- // header_count += 1;
+ request_headers_buf[header_count] = connection_header;
+ header_count += 1;
if (!override_user_agent) {
request_headers_buf[header_count] = user_agent_header;
@@ -557,11 +562,13 @@ pub fn buildRequest(this: *HTTPClient, body_len: usize) picohttp.Request {
header_count += 1;
}
- request_headers_buf[header_count] = picohttp.Header{
- .name = host_header_name,
- .value = this.url.hostname,
- };
- header_count += 1;
+ if (!override_host_header) {
+ request_headers_buf[header_count] = picohttp.Header{
+ .name = host_header_name,
+ .value = this.url.hostname,
+ };
+ header_count += 1;
+ }
if (!override_accept_encoding) {
request_headers_buf[header_count] = accept_encoding_header;
@@ -698,7 +705,12 @@ pub fn sendHTTP(this: *HTTPClient, body: []const u8, body_out_str: *MutableStrin
pub fn processResponse(this: *HTTPClient, comptime report_progress: bool, comptime Client: type, client: Client, body_out_str: *MutableString) !picohttp.Response {
defer if (this.verbose) Output.flush();
- var response: picohttp.Response = undefined;
+ var response: picohttp.Response = .{
+ .minor_version = 1,
+ .status_code = 0,
+ .status = "",
+ .headers = &[_]picohttp.Header{},
+ };
var request_message = AsyncMessage.get(default_allocator);
defer request_message.release();
var request_buffer: []u8 = request_message.buf;
@@ -712,23 +724,21 @@ pub fn processResponse(this: *HTTPClient, comptime report_progress: bool, compti
restart: while (req_buf_read != 0) {
req_buf_read = try client.read(request_buffer, read_length);
read_length += req_buf_read;
+ var request_body = request_buffer[0..read_length];
+ log("request_body ({d}):\n{s}", .{ read_length, request_body });
if (comptime report_progress) {
this.progress_node.?.activate();
this.progress_node.?.setCompletedItems(read_length);
this.progress_node.?.context.maybeRefresh();
}
- var request_body = request_buffer[0..read_length];
- read_headers_up_to = if (read_headers_up_to > read_length) read_length else read_headers_up_to;
+ read_headers_up_to = @minimum(read_headers_up_to, read_length);
response = picohttp.Response.parseParts(request_body, &this.response_headers_buf, &read_headers_up_to) catch |err| {
+ log("read_headers_up_to: {d}", .{read_headers_up_to});
switch (err) {
- error.ShortRead => {
- continue :restart;
- },
- else => {
- return err;
- },
+ error.ShortRead => continue :restart,
+ else => return err,
}
};
break :restart;
@@ -751,6 +761,7 @@ pub fn processResponse(this: *HTTPClient, comptime report_progress: bool, compti
maybe_keepalive = false;
}
var content_encoding_i = response.headers.len + 1;
+
for (response.headers) |header, header_i| {
switch (hashHeaderName(header.name)) {
content_length_header_hash => {
@@ -890,7 +901,7 @@ pub fn processResponse(this: *HTTPClient, comptime report_progress: bool, compti
}
var remainder = request_buffer[@intCast(usize, response.bytes_read)..read_length];
last_read = remainder.len;
- try buffer.inflate(std.math.max(remainder.len, 2048));
+ try buffer.inflate(@maximum(remainder.len, 2048));
buffer.list.expandToCapacity();
std.mem.copy(u8, buffer.list.items, remainder);
}
@@ -910,41 +921,17 @@ pub fn processResponse(this: *HTTPClient, comptime report_progress: bool, compti
while (pret == -2) {
var buffered_amount = client.bufferedReadAmount();
if (buffer.list.items.len < total_size + 512 or buffer.list.items[total_size..].len < @intCast(usize, @maximum(decoder.bytes_left_in_chunk, buffered_amount)) or buffer.list.items[total_size..].len < 512) {
- try buffer.inflate(std.math.max((buffered_amount + total_size) * 2, 1024));
- if (comptime Environment.isDebug) {
- var temp_buffer = buffer;
- temp_buffer.list.expandToCapacity();
- @memset(temp_buffer.list.items.ptr + buffer.list.items.len, 0, temp_buffer.list.items.len - buffer.list.items.len);
- buffer = temp_buffer;
- }
+ try buffer.inflate(@maximum((buffered_amount + total_size) * 2, 1024));
buffer.list.expandToCapacity();
}
// while (true) {
- if (extremely_verbose) {
- Output.prettyErrorln(
- \\ Buffered: {d}
- \\ Chunk
- \\ {d} left / {d} bytes total (buffer: {d})
- \\ Read
- \\ {d} bytes / {d} total ({d} parsed)
- , .{
- client.bufferedReadAmount(),
- decoder.bytes_left_in_chunk,
- total_size,
- buffer.list.items.len,
- rret,
- total_size,
- total_size,
- });
- }
-
var remainder = buffer.list.items[total_size..];
const errorable_read = client.read(remainder, 0);
rret = errorable_read catch |err| {
- if (extremely_verbose) Output.prettyErrorln("Chunked transfoer encoding error: {s}", .{@errorName(err)});
+ if (extremely_verbose) Output.prettyErrorln("Chunked transfer encoding error: {s}", .{@errorName(err)});
return err;
};
@@ -955,11 +942,12 @@ pub fn processResponse(this: *HTTPClient, comptime report_progress: bool, compti
remainder = buffer.list.items[total_size..];
remainder = remainder[rret..][0..buffered_amount];
rret += client.read(remainder, 0) catch |err| {
- if (extremely_verbose) Output.prettyErrorln("Chunked transfoer encoding error: {s}", .{@errorName(err)});
+ if (extremely_verbose) Output.prettyErrorln("Chunked transfer encoding error: {s}", .{@errorName(err)});
return err;
};
}
+ // socket hang up, there was a parsing error, etc
if (rret == 0) {
if (extremely_verbose) Output.prettyErrorln("Unexpected 0", .{});
@@ -986,9 +974,9 @@ pub fn processResponse(this: *HTTPClient, comptime report_progress: bool, compti
decoder,
});
+
return error.ChunkedEncodingParseError;
}
-
total_size += rsize;
if (comptime report_progress) {
@@ -1000,7 +988,6 @@ pub fn processResponse(this: *HTTPClient, comptime report_progress: bool, compti
buffer.list.shrinkRetainingCapacity(total_size);
buffer_.* = buffer;
-
switch (encoding) {
Encoding.gzip, Encoding.deflate => {
var gzip_timer: std.time.Timer = undefined;
@@ -1035,7 +1022,9 @@ pub fn processResponse(this: *HTTPClient, comptime report_progress: bool, compti
this.progress_node.?.context.maybeRefresh();
}
- this.body_size = @intCast(u32, body_out_str.list.items.len);
+ this.body_size = @truncate(u32, body_out_str.list.items.len);
+ std.debug.assert(body_out_str.list.items.len == buffer.list.items.len);
+
return response;
}