mirror of
https://github.com/oven-sh/bun
synced 2026-02-13 12:29:07 +00:00
Replace `catch bun.outOfMemory()`, which can accidentally catch non-OOM-related errors, with either `bun.handleOom` or a manual `catch |err| switch (err)`. (For internal tracking: fixes STAB-1070) --------- Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
186 lines
6.0 KiB
Zig
186 lines
6.0 KiB
Zig
const Headers = @This();
|
|
|
|
pub const Entry = struct {
|
|
name: api.StringPointer,
|
|
value: api.StringPointer,
|
|
|
|
pub const List = bun.MultiArrayList(Entry);
|
|
};
|
|
|
|
entries: Entry.List = .{},
|
|
buf: std.ArrayListUnmanaged(u8) = .{},
|
|
allocator: std.mem.Allocator,
|
|
|
|
pub fn memoryCost(this: *const Headers) usize {
|
|
return this.buf.items.len + this.entries.memoryCost();
|
|
}
|
|
|
|
pub fn clone(this: *Headers) !Headers {
|
|
return Headers{
|
|
.entries = try this.entries.clone(this.allocator),
|
|
.buf = try this.buf.clone(this.allocator),
|
|
.allocator = this.allocator,
|
|
};
|
|
}
|
|
|
|
pub fn get(this: *const Headers, name: []const u8) ?[]const u8 {
|
|
const entries = this.entries.slice();
|
|
const names = entries.items(.name);
|
|
const values = entries.items(.value);
|
|
for (names, 0..) |name_ptr, i| {
|
|
if (bun.strings.eqlCaseInsensitiveASCII(this.asStr(name_ptr), name, true)) {
|
|
return this.asStr(values[i]);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
pub fn append(this: *Headers, name: []const u8, value: []const u8) !void {
|
|
var offset: u32 = @truncate(this.buf.items.len);
|
|
try this.buf.ensureUnusedCapacity(this.allocator, name.len + value.len);
|
|
const name_ptr = api.StringPointer{
|
|
.offset = offset,
|
|
.length = @truncate(name.len),
|
|
};
|
|
this.buf.appendSliceAssumeCapacity(name);
|
|
offset = @truncate(this.buf.items.len);
|
|
this.buf.appendSliceAssumeCapacity(value);
|
|
|
|
const value_ptr = api.StringPointer{
|
|
.offset = offset,
|
|
.length = @truncate(value.len),
|
|
};
|
|
try this.entries.append(this.allocator, .{
|
|
.name = name_ptr,
|
|
.value = value_ptr,
|
|
});
|
|
}
|
|
|
|
pub fn deinit(this: *Headers) void {
|
|
this.entries.deinit(this.allocator);
|
|
this.buf.clearAndFree(this.allocator);
|
|
}
|
|
pub fn getContentType(this: *const Headers) ?[]const u8 {
|
|
if (this.entries.len == 0 or this.buf.items.len == 0) {
|
|
return null;
|
|
}
|
|
const header_entries = this.entries.slice();
|
|
const header_names = header_entries.items(.name);
|
|
const header_values = header_entries.items(.value);
|
|
|
|
for (header_names, 0..header_names.len) |name, i| {
|
|
if (bun.strings.eqlCaseInsensitiveASCII(this.asStr(name), "content-type", true)) {
|
|
return this.asStr(header_values[i]);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
pub fn asStr(this: *const Headers, ptr: api.StringPointer) []const u8 {
|
|
return if (ptr.offset + ptr.length <= this.buf.items.len)
|
|
this.buf.items[ptr.offset..][0..ptr.length]
|
|
else
|
|
"";
|
|
}
|
|
|
|
pub const Options = struct {
|
|
body: ?*const Blob.Any = null,
|
|
};
|
|
|
|
pub fn fromPicoHttpHeaders(headers: []const picohttp.Header, allocator: std.mem.Allocator) !Headers {
|
|
const header_count = headers.len;
|
|
var result = Headers{
|
|
.entries = .{},
|
|
.buf = .{},
|
|
.allocator = allocator,
|
|
};
|
|
|
|
var buf_len: usize = 0;
|
|
for (headers) |header| {
|
|
buf_len += header.name.len + header.value.len;
|
|
}
|
|
bun.handleOom(result.entries.ensureTotalCapacity(allocator, header_count));
|
|
result.entries.len = headers.len;
|
|
bun.handleOom(result.buf.ensureTotalCapacityPrecise(allocator, buf_len));
|
|
result.buf.items.len = buf_len;
|
|
var offset: u32 = 0;
|
|
for (headers, 0..headers.len) |header, i| {
|
|
const name_offset = offset;
|
|
bun.copy(u8, result.buf.items[offset..][0..header.name.len], header.name);
|
|
offset += @truncate(header.name.len);
|
|
const value_offset = offset;
|
|
bun.copy(u8, result.buf.items[offset..][0..header.value.len], header.value);
|
|
offset += @truncate(header.value.len);
|
|
|
|
result.entries.set(i, .{
|
|
.name = .{
|
|
.offset = name_offset,
|
|
.length = @truncate(header.name.len),
|
|
},
|
|
.value = .{
|
|
.offset = value_offset,
|
|
.length = @truncate(header.value.len),
|
|
},
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
pub fn from(fetch_headers_ref: ?*FetchHeaders, allocator: std.mem.Allocator, options: Options) !Headers {
|
|
var header_count: u32 = 0;
|
|
var buf_len: u32 = 0;
|
|
if (fetch_headers_ref) |headers_ref|
|
|
headers_ref.count(&header_count, &buf_len);
|
|
var headers = Headers{
|
|
.entries = .{},
|
|
.buf = .{},
|
|
.allocator = allocator,
|
|
};
|
|
const buf_len_before_content_type = buf_len;
|
|
const needs_content_type = brk: {
|
|
if (options.body) |body| {
|
|
if (body.hasContentTypeFromUser() and (fetch_headers_ref == null or !fetch_headers_ref.?.fastHas(.ContentType))) {
|
|
header_count += 1;
|
|
buf_len += @as(u32, @truncate(body.contentType().len + "Content-Type".len));
|
|
break :brk true;
|
|
}
|
|
}
|
|
break :brk false;
|
|
};
|
|
bun.handleOom(headers.entries.ensureTotalCapacity(allocator, header_count));
|
|
headers.entries.len = header_count;
|
|
bun.handleOom(headers.buf.ensureTotalCapacityPrecise(allocator, buf_len));
|
|
headers.buf.items.len = buf_len;
|
|
var sliced = headers.entries.slice();
|
|
var names = sliced.items(.name);
|
|
var values = sliced.items(.value);
|
|
if (fetch_headers_ref) |headers_ref|
|
|
headers_ref.copyTo(names.ptr, values.ptr, headers.buf.items.ptr);
|
|
|
|
// TODO: maybe we should send Content-Type header first instead of last?
|
|
if (needs_content_type) {
|
|
bun.copy(u8, headers.buf.items[buf_len_before_content_type..], "Content-Type");
|
|
names[header_count - 1] = .{
|
|
.offset = buf_len_before_content_type,
|
|
.length = "Content-Type".len,
|
|
};
|
|
|
|
bun.copy(u8, headers.buf.items[buf_len_before_content_type + "Content-Type".len ..], options.body.?.contentType());
|
|
values[header_count - 1] = .{
|
|
.offset = buf_len_before_content_type + @as(u32, "Content-Type".len),
|
|
.length = @as(u32, @truncate(options.body.?.contentType().len)),
|
|
};
|
|
}
|
|
|
|
return headers;
|
|
}
|
|
|
|
const std = @import("std");
|
|
|
|
const bun = @import("bun");
|
|
const picohttp = bun.picohttp;
|
|
const api = bun.schema.api;
|
|
|
|
const Blob = bun.webcore.Blob;
|
|
const FetchHeaders = bun.webcore.FetchHeaders;
|