aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/bun.js/base.zig7
-rw-r--r--src/bun.js/bindings/bindings.cpp108
-rw-r--r--src/bun.js/bindings/bindings.zig40
-rw-r--r--src/bun.js/bindings/headers-cpp.h2
-rw-r--r--src/bun.js/bindings/headers.h14
-rw-r--r--src/bun.js/bindings/headers.zig12
-rw-r--r--src/bun.js/bindings/webcore/HTTPParsers.cpp6
-rw-r--r--src/bun.js/bindings/webcore/JSFetchHeaders.cpp26
-rw-r--r--src/bun.js/webcore/body.zig8
-rw-r--r--src/bun.js/webcore/request.zig10
-rw-r--r--src/bun.js/webcore/response.zig31
-rw-r--r--test/bun.js/fetch_headers.test.js58
12 files changed, 228 insertions, 94 deletions
diff --git a/src/bun.js/base.zig b/src/bun.js/base.zig
index b8b900cee..bdb516836 100644
--- a/src/bun.js/base.zig
+++ b/src/bun.js/base.zig
@@ -1718,6 +1718,13 @@ pub fn getAllocator(_: js.JSContextRef) std.mem.Allocator {
return default_allocator;
}
+/// Print a JSValue to stdout; this is only meant for debugging purposes
+pub fn dump(value: JSValue, globalObject: *JSC.JSGlobalObject) !void {
+ var formatter = JSC.ZigConsoleClient.Formatter{ .globalThis = globalObject };
+ try Output.errorWriter().print("{}\n", .{value.toFmt(globalObject, &formatter)});
+ Output.flush();
+}
+
pub const JSStringList = std.ArrayList(js.JSStringRef);
pub const ArrayBuffer = extern struct {
diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp
index 4b614788d..bb01e4101 100644
--- a/src/bun.js/bindings/bindings.cpp
+++ b/src/bun.js/bindings/bindings.cpp
@@ -97,19 +97,27 @@ static void copyToUWS(WebCore::FetchHeaders* headers, UWSResponse* res)
auto& internalHeaders = headers->internalHeaders();
for (auto& value : internalHeaders.getSetCookieHeaders()) {
- res->writeHeader(std::string_view("set-cookie", 10), std::string_view(reinterpret_cast<const char*>(value.characters8()), value.length()));
+ res->writeHeader(std::string_view("set-cookie", 10), std::string_view(
+ value.is8Bit() ? reinterpret_cast<const char*>(value.characters8()) : value.utf8().data(), value.length()
+ ));
}
for (auto& header : internalHeaders.commonHeaders()) {
const auto& name = WebCore::httpHeaderNameString(header.key);
auto& value = header.value;
- res->writeHeader(std::string_view(reinterpret_cast<const char*>(name.characters8()), name.length()), std::string_view(reinterpret_cast<const char*>(value.characters8()), value.length()));
+ res->writeHeader(
+ std::string_view(name.is8Bit() ? reinterpret_cast<const char*>(name.characters8()) : name.utf8().data(), name.length()),
+ std::string_view(value.is8Bit() ? reinterpret_cast<const char*>(value.characters8()) : value.utf8().data(), value.length())
+ );
}
for (auto& header : internalHeaders.uncommonHeaders()) {
auto& name = header.key;
auto& value = header.value;
- res->writeHeader(std::string_view(reinterpret_cast<const char*>(name.characters8()), name.length()), std::string_view(reinterpret_cast<const char*>(value.characters8()), value.length()));
+ res->writeHeader(
+ std::string_view(name.is8Bit() ? reinterpret_cast<const char*>(name.characters8()) : name.utf8().data(), name.length()),
+ std::string_view(value.is8Bit() ? reinterpret_cast<const char*>(value.characters8()) : value.utf8().data(), value.length())
+ );
}
}
@@ -709,9 +717,13 @@ WebCore__FetchHeaders* WebCore__FetchHeaders__createEmpty()
{
return new WebCore::FetchHeaders({ WebCore::FetchHeaders::Guard::None, {} });
}
-void WebCore__FetchHeaders__append(WebCore__FetchHeaders* headers, const ZigString* arg1, const ZigString* arg2)
+void WebCore__FetchHeaders__append(WebCore__FetchHeaders* headers, const ZigString* arg1, const ZigString* arg2,
+ JSC__JSGlobalObject* lexicalGlobalObject)
{
- headers->append(Zig::toString(*arg1), Zig::toString(*arg2));
+ auto throwScope = DECLARE_THROW_SCOPE(lexicalGlobalObject->vm());
+ WebCore::propagateException(*lexicalGlobalObject, throwScope,
+ headers->append(Zig::toString(*arg1), Zig::toString(*arg2))
+ );
}
WebCore__FetchHeaders* WebCore__FetchHeaders__cast_(JSC__JSValue JSValue0, JSC__VM* vm)
{
@@ -724,12 +736,25 @@ WebCore__FetchHeaders* WebCore__FetchHeaders__createFromJS(JSC__JSGlobalObject*
{
Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
EnsureStillAliveScope argument0 = JSC::JSValue::decode(argument0_);
+
auto throwScope = DECLARE_THROW_SCOPE(lexicalGlobalObject->vm());
- auto init = argument0.value().isUndefined() ? std::optional<Converter<IDLUnion<IDLSequence<IDLSequence<IDLByteString>>, IDLRecord<IDLByteString, IDLByteString>>>::ReturnType>() : std::optional<Converter<IDLUnion<IDLSequence<IDLSequence<IDLByteString>>, IDLRecord<IDLByteString, IDLByteString>>>::ReturnType>(convert<IDLUnion<IDLSequence<IDLSequence<IDLByteString>>, IDLRecord<IDLByteString, IDLByteString>>>(*lexicalGlobalObject, argument0.value()));
+ // Note that we use IDLDOMString here rather than IDLByteString: while headers
+ // should be ASCII only, we want the headers->fill implementation to discover
+ // and error on invalid names and values
+ using TargetType = IDLUnion<IDLSequence<IDLSequence<IDLDOMString>>, IDLRecord<IDLDOMString, IDLDOMString>>;
+ using Converter = std::optional<Converter<TargetType>::ReturnType>;
+ auto init = argument0.value().isUndefined() ? Converter() : Converter(convert<TargetType>(*lexicalGlobalObject, argument0.value()));
RETURN_IF_EXCEPTION(throwScope, nullptr);
+
auto* headers = new WebCore::FetchHeaders({ WebCore::FetchHeaders::Guard::None, {} });
- if (init)
- headers->fill(WTFMove(init.value()));
+ if (init) {
+ // `fill` doesn't set an exception on the VM if it fails, it returns an
+ // ExceptionOr<void>. So we need to check for the exception and, if set,
+ // translate it to JSValue and throw it.
+ WebCore::propagateException(*lexicalGlobalObject, throwScope,
+ headers->fill(WTFMove(init.value()))
+ );
+ }
return headers;
}
@@ -741,16 +766,22 @@ JSC__JSValue WebCore__FetchHeaders__toJS(WebCore__FetchHeaders* headers, JSC__JS
}
JSC__JSValue WebCore__FetchHeaders__clone(WebCore__FetchHeaders* headers, JSC__JSGlobalObject* arg1)
{
+ auto throwScope = DECLARE_THROW_SCOPE(arg1->vm());
Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(arg1);
auto* clone = new WebCore::FetchHeaders({ WebCore::FetchHeaders::Guard::None, {} });
- clone->fill(*headers);
+ WebCore::propagateException(*arg1, throwScope,
+ clone->fill(*headers)
+ );
return JSC::JSValue::encode(WebCore::toJSNewlyCreated(arg1, globalObject, WTFMove(clone)));
}
-WebCore__FetchHeaders* WebCore__FetchHeaders__cloneThis(WebCore__FetchHeaders* headers)
+WebCore__FetchHeaders* WebCore__FetchHeaders__cloneThis(WebCore__FetchHeaders* headers, JSC__JSGlobalObject* lexicalGlobalObject)
{
+ auto throwScope = DECLARE_THROW_SCOPE(lexicalGlobalObject->vm());
auto* clone = new WebCore::FetchHeaders({ WebCore::FetchHeaders::Guard::None, {} });
- clone->fill(*headers);
+ WebCore::propagateException(*lexicalGlobalObject, throwScope,
+ clone->fill(*headers)
+ );
return clone;
}
@@ -764,14 +795,25 @@ void WebCore__FetchHeaders__copyTo(WebCore__FetchHeaders* headers, StringPointer
auto iter = headers->createIterator();
uint32_t i = 0;
unsigned count = 0;
+
for (auto pair = iter.next(); pair; pair = iter.next()) {
auto name = pair->key;
auto value = pair->value;
names[count] = { i, name.length() };
- memcpy(&buf[i], name.characters8(), name.length());
+
+ if (name.is8Bit())
+ memcpy(&buf[i], name.characters8(), name.length());
+ else {
+ StringImpl::copyCharacters(&buf[i], name.characters16(), name.length());
+ }
+
i += name.length();
values[count++] = { i, value.length() };
- memcpy(&buf[i], value.characters8(), value.length());
+ if (value.is8Bit())
+ memcpy(&buf[i], value.characters8(), value.length());
+ else
+ StringImpl::copyCharacters(&buf[i], value.characters16(), value.length());
+
i += value.length();
}
}
@@ -883,6 +925,7 @@ void WebCore__FetchHeaders__deref(WebCore__FetchHeaders* arg0)
JSC__JSValue WebCore__FetchHeaders__createValue(JSC__JSGlobalObject* arg0, StringPointer* arg1, StringPointer* arg2, const ZigString* arg3, uint32_t count)
{
+ auto throwScope = DECLARE_THROW_SCOPE(arg0->vm());
Vector<KeyValuePair<String, String>> pairs;
pairs.reserveCapacity(count);
ZigString buf = *arg3;
@@ -893,25 +936,44 @@ JSC__JSValue WebCore__FetchHeaders__createValue(JSC__JSGlobalObject* arg0, Strin
}
Ref<WebCore::FetchHeaders> headers = WebCore::FetchHeaders::create();
- headers->fill(WebCore::FetchHeaders::Init(WTFMove(pairs)));
+ WebCore::propagateException(*arg0, throwScope,
+ headers->fill(WebCore::FetchHeaders::Init(WTFMove(pairs)))
+ );
pairs.releaseBuffer();
return JSC::JSValue::encode(WebCore::toJSNewlyCreated(arg0, reinterpret_cast<Zig::GlobalObject*>(arg0), WTFMove(headers)));
}
-void WebCore__FetchHeaders__get_(WebCore__FetchHeaders* headers, const ZigString* arg1, ZigString* arg2)
+void WebCore__FetchHeaders__get_(WebCore__FetchHeaders* headers, const ZigString* arg1, ZigString* arg2, JSC__JSGlobalObject* global)
{
- *arg2 = Zig::toZigString(headers->get(Zig::toString(*arg1)).releaseReturnValue());
+ auto throwScope = DECLARE_THROW_SCOPE(global->vm());
+ auto result = headers->get(Zig::toString(*arg1));
+ if (result.hasException())
+ WebCore::propagateException(*global, throwScope, result.releaseException());
+ else
+ *arg2 = Zig::toZigString(result.releaseReturnValue());
}
-bool WebCore__FetchHeaders__has(WebCore__FetchHeaders* headers, const ZigString* arg1)
+bool WebCore__FetchHeaders__has(WebCore__FetchHeaders* headers, const ZigString* arg1, JSC__JSGlobalObject* global)
{
- return headers->has(Zig::toString(*arg1)).releaseReturnValue();
+ auto throwScope = DECLARE_THROW_SCOPE(global->vm());
+ auto result = headers->has(Zig::toString(*arg1));
+ if (result.hasException()) {
+ WebCore::propagateException(*global, throwScope, result.releaseException());
+ return false;
+ } else
+ return result.releaseReturnValue();
}
-void WebCore__FetchHeaders__put_(WebCore__FetchHeaders* headers, const ZigString* arg1, const ZigString* arg2)
+void WebCore__FetchHeaders__put_(WebCore__FetchHeaders* headers, const ZigString* arg1, const ZigString* arg2, JSC__JSGlobalObject* global)
{
- headers->set(Zig::toString(*arg1), Zig::toString(*arg2));
+ auto throwScope = DECLARE_THROW_SCOPE(global->vm());
+ WebCore::propagateException(*global, throwScope,
+ headers->set(Zig::toString(*arg1), Zig::toString(*arg2))
+ );
}
-void WebCore__FetchHeaders__remove(WebCore__FetchHeaders* headers, const ZigString* arg1)
+void WebCore__FetchHeaders__remove(WebCore__FetchHeaders* headers, const ZigString* arg1, JSC__JSGlobalObject* global)
{
- headers->remove(Zig::toString(*arg1));
+ auto throwScope = DECLARE_THROW_SCOPE(global->vm());
+ WebCore::propagateException(*global, throwScope,
+ headers->remove(Zig::toString(*arg1))
+ );
}
void WebCore__FetchHeaders__fastRemove_(WebCore__FetchHeaders* headers, unsigned char headerName)
@@ -3876,4 +3938,4 @@ CPP_DECL JSC__JSValue WebCore__DOMFormData__create(JSC__JSGlobalObject* arg0)
CPP_DECL WebCore__DOMFormData* WebCore__DOMFormData__fromJS(JSC__JSValue JSValue1)
{
return WebCoreCast<WebCore::JSDOMFormData, WebCore__DOMFormData>(JSValue1);
-} \ No newline at end of file
+}
diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig
index 6b5386a68..64b2b060d 100644
--- a/src/bun.js/bindings/bindings.zig
+++ b/src/bun.js/bindings/bindings.zig
@@ -987,12 +987,12 @@ pub const FetchHeaders = opaque {
});
}
- pub fn putDefault(this: *FetchHeaders, name_: []const u8, value: []const u8) void {
- if (this.has(&ZigString.init(name_))) {
+ pub fn putDefault(this: *FetchHeaders, name_: []const u8, value: []const u8, global: *JSGlobalObject) void {
+ if (this.has(&ZigString.init(name_), global)) {
return;
}
- this.put_(&ZigString.init(name_), &ZigString.init(value));
+ this.put_(&ZigString.init(name_), &ZigString.init(value), global);
}
pub fn from(
@@ -1064,11 +1064,13 @@ pub const FetchHeaders = opaque {
this: *FetchHeaders,
name_: *const ZigString,
value: *const ZigString,
+ global: *JSGlobalObject,
) void {
return shim.cppFn("append", .{
this,
name_,
value,
+ global,
});
}
@@ -1076,11 +1078,13 @@ pub const FetchHeaders = opaque {
this: *FetchHeaders,
name_: *const ZigString,
value: *const ZigString,
+ global: *JSGlobalObject,
) void {
return shim.cppFn("put_", .{
this,
name_,
value,
+ global,
});
}
@@ -1088,28 +1092,32 @@ pub const FetchHeaders = opaque {
this: *FetchHeaders,
name_: []const u8,
value: []const u8,
+ global: *JSGlobalObject,
) void {
- this.put_(&ZigString.init(name_), &ZigString.init(value));
+ this.put_(&ZigString.init(name_), &ZigString.init(value), global);
}
pub fn get_(
this: *FetchHeaders,
name_: *const ZigString,
out: *ZigString,
+ global: *JSGlobalObject,
) void {
shim.cppFn("get_", .{
this,
name_,
out,
+ global,
});
}
pub fn get(
this: *FetchHeaders,
name_: []const u8,
+ global: *JSGlobalObject,
) ?[]const u8 {
var out = ZigString.Empty;
- get_(this, &ZigString.init(name_), &out);
+ get_(this, &ZigString.init(name_), &out, global);
if (out.len > 0) {
return out.slice();
}
@@ -1120,10 +1128,12 @@ pub const FetchHeaders = opaque {
pub fn has(
this: *FetchHeaders,
name_: *const ZigString,
+ global: *JSGlobalObject,
) bool {
return shim.cppFn("has", .{
this,
name_,
+ global,
});
}
@@ -1285,10 +1295,12 @@ pub const FetchHeaders = opaque {
pub fn remove(
this: *FetchHeaders,
name_: *const ZigString,
+ global: *JSGlobalObject,
) void {
return shim.cppFn("remove", .{
this,
name_,
+ global,
});
}
@@ -1328,9 +1340,11 @@ pub const FetchHeaders = opaque {
pub fn cloneThis(
this: *FetchHeaders,
+ global: *JSGlobalObject,
) ?*FetchHeaders {
return shim.cppFn("cloneThis", .{
this,
+ global,
});
}
@@ -2876,22 +2890,6 @@ pub const JSValue = enum(JSValueReprInt) {
return @intToEnum(JSValue, @bitCast(i64, @ptrToInt(ptr)));
}
- pub const Formatter = struct {
- value: JSValue,
- global: *JSGlobalObject,
-
- pub fn format(formatter: Formatter, comptime fmt: []const u8, opts: fmt.FormatOptions, writer: anytype) !void {
- const self = formatter.value;
- const kind: JSType = jsType(self);
- if (kind.isStringLike()) {
- var zig_str = self.getZigString();
- return try zig_str.format(fmt, opts, writer);
- }
-
- if (kind) {}
- }
- };
-
pub fn coerceToInt32(this: JSValue, globalThis: *JSC.JSGlobalObject) i32 {
return cppFn("coerceToInt32", .{ this, globalThis });
}
diff --git a/src/bun.js/bindings/headers-cpp.h b/src/bun.js/bindings/headers-cpp.h
index d0abc7db0..c57c65e18 100644
--- a/src/bun.js/bindings/headers-cpp.h
+++ b/src/bun.js/bindings/headers-cpp.h
@@ -1,4 +1,4 @@
-//-- AUTOGENERATED FILE -- 1676470760
+//-- AUTOGENERATED FILE -- 1676656020
// clang-format off
#pragma once
diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h
index 59450ecf1..a81695ac1 100644
--- a/src/bun.js/bindings/headers.h
+++ b/src/bun.js/bindings/headers.h
@@ -1,5 +1,5 @@
// clang-format off
-//-- AUTOGENERATED FILE -- 1676470760
+//-- AUTOGENERATED FILE -- 1676656020
#pragma once
#include <stddef.h>
@@ -163,10 +163,10 @@ CPP_DECL WebCore__DOMFormData* WebCore__DOMFormData__fromJS(JSC__JSValue JSValue
#pragma mark - WebCore::FetchHeaders
-CPP_DECL void WebCore__FetchHeaders__append(WebCore__FetchHeaders* arg0, const ZigString* arg1, const ZigString* arg2);
+CPP_DECL void WebCore__FetchHeaders__append(WebCore__FetchHeaders* arg0, const ZigString* arg1, const ZigString* arg2, JSC__JSGlobalObject* arg3);
CPP_DECL WebCore__FetchHeaders* WebCore__FetchHeaders__cast_(JSC__JSValue JSValue0, JSC__VM* arg1);
CPP_DECL JSC__JSValue WebCore__FetchHeaders__clone(WebCore__FetchHeaders* arg0, JSC__JSGlobalObject* arg1);
-CPP_DECL WebCore__FetchHeaders* WebCore__FetchHeaders__cloneThis(WebCore__FetchHeaders* arg0);
+CPP_DECL WebCore__FetchHeaders* WebCore__FetchHeaders__cloneThis(WebCore__FetchHeaders* arg0, JSC__JSGlobalObject* arg1);
CPP_DECL void WebCore__FetchHeaders__copyTo(WebCore__FetchHeaders* arg0, StringPointer* arg1, StringPointer* arg2, unsigned char* arg3);
CPP_DECL void WebCore__FetchHeaders__count(WebCore__FetchHeaders* arg0, uint32_t* arg1, uint32_t* arg2);
CPP_DECL WebCore__FetchHeaders* WebCore__FetchHeaders__createEmpty();
@@ -178,10 +178,10 @@ CPP_DECL void WebCore__FetchHeaders__deref(WebCore__FetchHeaders* arg0);
CPP_DECL void WebCore__FetchHeaders__fastGet_(WebCore__FetchHeaders* arg0, unsigned char arg1, ZigString* arg2);
CPP_DECL bool WebCore__FetchHeaders__fastHas_(WebCore__FetchHeaders* arg0, unsigned char arg1);
CPP_DECL void WebCore__FetchHeaders__fastRemove_(WebCore__FetchHeaders* arg0, unsigned char arg1);
-CPP_DECL void WebCore__FetchHeaders__get_(WebCore__FetchHeaders* arg0, const ZigString* arg1, ZigString* arg2);
-CPP_DECL bool WebCore__FetchHeaders__has(WebCore__FetchHeaders* arg0, const ZigString* arg1);
-CPP_DECL void WebCore__FetchHeaders__put_(WebCore__FetchHeaders* arg0, const ZigString* arg1, const ZigString* arg2);
-CPP_DECL void WebCore__FetchHeaders__remove(WebCore__FetchHeaders* arg0, const ZigString* arg1);
+CPP_DECL void WebCore__FetchHeaders__get_(WebCore__FetchHeaders* arg0, const ZigString* arg1, ZigString* arg2, JSC__JSGlobalObject* arg3);
+CPP_DECL bool WebCore__FetchHeaders__has(WebCore__FetchHeaders* arg0, const ZigString* arg1, JSC__JSGlobalObject* arg2);
+CPP_DECL void WebCore__FetchHeaders__put_(WebCore__FetchHeaders* arg0, const ZigString* arg1, const ZigString* arg2, JSC__JSGlobalObject* arg3);
+CPP_DECL void WebCore__FetchHeaders__remove(WebCore__FetchHeaders* arg0, const ZigString* arg1, JSC__JSGlobalObject* arg2);
CPP_DECL JSC__JSValue WebCore__FetchHeaders__toJS(WebCore__FetchHeaders* arg0, JSC__JSGlobalObject* arg1);
CPP_DECL void WebCore__FetchHeaders__toUWSResponse(WebCore__FetchHeaders* arg0, bool arg1, void* arg2);
CPP_DECL JSC__JSValue SystemError__toErrorInstance(const SystemError* arg0, JSC__JSGlobalObject* arg1);
diff --git a/src/bun.js/bindings/headers.zig b/src/bun.js/bindings/headers.zig
index 9f2e1580d..ac936e280 100644
--- a/src/bun.js/bindings/headers.zig
+++ b/src/bun.js/bindings/headers.zig
@@ -103,10 +103,10 @@ pub extern fn WebCore__DOMFormData__count(arg0: ?*bindings.DOMFormData) usize;
pub extern fn WebCore__DOMFormData__create(arg0: *bindings.JSGlobalObject) JSC__JSValue;
pub extern fn WebCore__DOMFormData__createFromURLQuery(arg0: *bindings.JSGlobalObject, arg1: [*c]ZigString) JSC__JSValue;
pub extern fn WebCore__DOMFormData__fromJS(JSValue0: JSC__JSValue) ?*bindings.DOMFormData;
-pub extern fn WebCore__FetchHeaders__append(arg0: ?*bindings.FetchHeaders, arg1: [*c]const ZigString, arg2: [*c]const ZigString) void;
+pub extern fn WebCore__FetchHeaders__append(arg0: ?*bindings.FetchHeaders, arg1: [*c]const ZigString, arg2: [*c]const ZigString, arg3: *bindings.JSGlobalObject) void;
pub extern fn WebCore__FetchHeaders__cast_(JSValue0: JSC__JSValue, arg1: *bindings.VM) ?*bindings.FetchHeaders;
pub extern fn WebCore__FetchHeaders__clone(arg0: ?*bindings.FetchHeaders, arg1: *bindings.JSGlobalObject) JSC__JSValue;
-pub extern fn WebCore__FetchHeaders__cloneThis(arg0: ?*bindings.FetchHeaders) ?*bindings.FetchHeaders;
+pub extern fn WebCore__FetchHeaders__cloneThis(arg0: ?*bindings.FetchHeaders, arg1: *bindings.JSGlobalObject) ?*bindings.FetchHeaders;
pub extern fn WebCore__FetchHeaders__copyTo(arg0: ?*bindings.FetchHeaders, arg1: [*c]StringPointer, arg2: [*c]StringPointer, arg3: [*c]u8) void;
pub extern fn WebCore__FetchHeaders__count(arg0: ?*bindings.FetchHeaders, arg1: [*c]u32, arg2: [*c]u32) void;
pub extern fn WebCore__FetchHeaders__createEmpty(...) ?*bindings.FetchHeaders;
@@ -118,10 +118,10 @@ pub extern fn WebCore__FetchHeaders__deref(arg0: ?*bindings.FetchHeaders) void;
pub extern fn WebCore__FetchHeaders__fastGet_(arg0: ?*bindings.FetchHeaders, arg1: u8, arg2: [*c]ZigString) void;
pub extern fn WebCore__FetchHeaders__fastHas_(arg0: ?*bindings.FetchHeaders, arg1: u8) bool;
pub extern fn WebCore__FetchHeaders__fastRemove_(arg0: ?*bindings.FetchHeaders, arg1: u8) void;
-pub extern fn WebCore__FetchHeaders__get_(arg0: ?*bindings.FetchHeaders, arg1: [*c]const ZigString, arg2: [*c]ZigString) void;
-pub extern fn WebCore__FetchHeaders__has(arg0: ?*bindings.FetchHeaders, arg1: [*c]const ZigString) bool;
-pub extern fn WebCore__FetchHeaders__put_(arg0: ?*bindings.FetchHeaders, arg1: [*c]const ZigString, arg2: [*c]const ZigString) void;
-pub extern fn WebCore__FetchHeaders__remove(arg0: ?*bindings.FetchHeaders, arg1: [*c]const ZigString) void;
+pub extern fn WebCore__FetchHeaders__get_(arg0: ?*bindings.FetchHeaders, arg1: [*c]const ZigString, arg2: [*c]ZigString, arg3: *bindings.JSGlobalObject) void;
+pub extern fn WebCore__FetchHeaders__has(arg0: ?*bindings.FetchHeaders, arg1: [*c]const ZigString, arg2: *bindings.JSGlobalObject) bool;
+pub extern fn WebCore__FetchHeaders__put_(arg0: ?*bindings.FetchHeaders, arg1: [*c]const ZigString, arg2: [*c]const ZigString, arg3: *bindings.JSGlobalObject) void;
+pub extern fn WebCore__FetchHeaders__remove(arg0: ?*bindings.FetchHeaders, arg1: [*c]const ZigString, arg2: *bindings.JSGlobalObject) void;
pub extern fn WebCore__FetchHeaders__toJS(arg0: ?*bindings.FetchHeaders, arg1: *bindings.JSGlobalObject) JSC__JSValue;
pub extern fn WebCore__FetchHeaders__toUWSResponse(arg0: ?*bindings.FetchHeaders, arg1: bool, arg2: ?*anyopaque) void;
pub extern fn SystemError__toErrorInstance(arg0: [*c]const SystemError, arg1: *bindings.JSGlobalObject) JSC__JSValue;
diff --git a/src/bun.js/bindings/webcore/HTTPParsers.cpp b/src/bun.js/bindings/webcore/HTTPParsers.cpp
index cb4985281..a696be94c 100644
--- a/src/bun.js/bindings/webcore/HTTPParsers.cpp
+++ b/src/bun.js/bindings/webcore/HTTPParsers.cpp
@@ -131,6 +131,10 @@ bool isValidHTTPHeaderValue(const String& value)
c = value[i];
if (c == 0x00 || c == 0x0A || c == 0x0D)
return false;
+
+ //NOTE: The spec doesn't require ASCII or Latin1 but common
+ // implementations, including Node, disallow codepoints > 255
+ if (c > 255) return false;
}
return true;
}
@@ -1074,4 +1078,4 @@ CrossOriginResourcePolicy parseCrossOriginResourcePolicyHeader(StringView header
return CrossOriginResourcePolicy::Invalid;
}
-} \ No newline at end of file
+}
diff --git a/src/bun.js/bindings/webcore/JSFetchHeaders.cpp b/src/bun.js/bindings/webcore/JSFetchHeaders.cpp
index 815989ad8..53e104152 100644
--- a/src/bun.js/bindings/webcore/JSFetchHeaders.cpp
+++ b/src/bun.js/bindings/webcore/JSFetchHeaders.cpp
@@ -123,7 +123,7 @@ template<> EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSFetchHeadersDOMConstructor:
ASSERT(castedThis);
EnsureStillAliveScope argument0 = callFrame->argument(0);
- auto init = std::optional<Converter<IDLUnion<IDLSequence<IDLSequence<IDLByteString>>, IDLRecord<IDLByteString, IDLByteString>>>::ReturnType>();
+ auto init = std::optional<Converter<IDLUnion<IDLSequence<IDLSequence<IDLDOMString>>, IDLRecord<IDLDOMString, IDLDOMString>>>::ReturnType>();
if (argument0.value() && !argument0.value().isUndefined()) {
if (auto* existingJsFetchHeaders = jsDynamicCast<JSFetchHeaders*>(argument0.value())) {
@@ -135,7 +135,7 @@ template<> EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSFetchHeadersDOMConstructor:
RETURN_IF_EXCEPTION(throwScope, {});
return JSValue::encode(jsValue);
}
- init = std::optional<Converter<IDLUnion<IDLSequence<IDLSequence<IDLByteString>>, IDLRecord<IDLByteString, IDLByteString>>>::ReturnType>(convert<IDLUnion<IDLSequence<IDLSequence<IDLByteString>>, IDLRecord<IDLByteString, IDLByteString>>>(*lexicalGlobalObject, argument0.value()));
+ init = std::optional<Converter<IDLUnion<IDLSequence<IDLSequence<IDLDOMString>>, IDLRecord<IDLDOMString, IDLDOMString>>>::ReturnType>(convert<IDLUnion<IDLSequence<IDLSequence<IDLDOMString>>, IDLRecord<IDLDOMString, IDLDOMString>>>(*lexicalGlobalObject, argument0.value()));
}
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
@@ -199,7 +199,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFetchHeadersPrototypeFunction_getAll, (JSGlobalObject
return JSValue::encode(jsUndefined());
}
- auto name = convert<IDLByteString>(*lexicalGlobalObject, callFrame->uncheckedArgument(0));
+ auto name = convert<IDLDOMString>(*lexicalGlobalObject, callFrame->uncheckedArgument(0));
RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined()));
auto& impl = castedThis->wrapped();
@@ -371,10 +371,10 @@ static inline JSC::EncodedJSValue jsFetchHeadersPrototypeFunction_appendBody(JSC
if (UNLIKELY(callFrame->argumentCount() < 2))
return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
- auto name = convert<IDLByteString>(*lexicalGlobalObject, argument0.value());
+ auto name = convert<IDLDOMString>(*lexicalGlobalObject, argument0.value());
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1);
- auto value = convert<IDLByteString>(*lexicalGlobalObject, argument1.value());
+ auto value = convert<IDLDOMString>(*lexicalGlobalObject, argument1.value());
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.append(WTFMove(name), WTFMove(value)); })));
}
@@ -477,7 +477,7 @@ static inline JSC::EncodedJSValue jsFetchHeadersPrototypeFunction_deleteBody(JSC
if (UNLIKELY(callFrame->argumentCount() < 1))
return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
- auto name = convert<IDLByteString>(*lexicalGlobalObject, argument0.value());
+ auto name = convert<IDLDOMString>(*lexicalGlobalObject, argument0.value());
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.remove(WTFMove(name)); })));
}
@@ -497,9 +497,9 @@ static inline JSC::EncodedJSValue jsFetchHeadersPrototypeFunction_getBody(JSC::J
if (UNLIKELY(callFrame->argumentCount() < 1))
return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
- auto name = convert<IDLByteString>(*lexicalGlobalObject, argument0.value());
+ auto name = convert<IDLDOMString>(*lexicalGlobalObject, argument0.value());
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
- RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLNullable<IDLByteString>>(*lexicalGlobalObject, throwScope, impl.get(WTFMove(name)))));
+ RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLNullable<IDLDOMString>>(*lexicalGlobalObject, throwScope, impl.get(WTFMove(name)))));
}
JSC_DEFINE_HOST_FUNCTION(jsFetchHeadersPrototypeFunction_get, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
@@ -517,7 +517,7 @@ static inline JSC::EncodedJSValue jsFetchHeadersPrototypeFunction_hasBody(JSC::J
if (UNLIKELY(callFrame->argumentCount() < 1))
return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
- auto name = convert<IDLByteString>(*lexicalGlobalObject, argument0.value());
+ auto name = convert<IDLDOMString>(*lexicalGlobalObject, argument0.value());
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLBoolean>(*lexicalGlobalObject, throwScope, impl.has(WTFMove(name)))));
}
@@ -537,10 +537,10 @@ static inline JSC::EncodedJSValue jsFetchHeadersPrototypeFunction_setBody(JSC::J
if (UNLIKELY(callFrame->argumentCount() < 2))
return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
- auto name = convert<IDLByteString>(*lexicalGlobalObject, argument0.value());
+ auto name = convert<IDLDOMString>(*lexicalGlobalObject, argument0.value());
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1);
- auto value = convert<IDLByteString>(*lexicalGlobalObject, argument1.value());
+ auto value = convert<IDLDOMString>(*lexicalGlobalObject, argument1.value());
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.set(WTFMove(name), WTFMove(value)); })));
}
@@ -552,8 +552,8 @@ JSC_DEFINE_HOST_FUNCTION(jsFetchHeadersPrototypeFunction_set, (JSGlobalObject *
struct FetchHeadersIteratorTraits {
static constexpr JSDOMIteratorType type = JSDOMIteratorType::Map;
- using KeyType = IDLByteString;
- using ValueType = IDLByteString;
+ using KeyType = IDLDOMString;
+ using ValueType = IDLDOMString;
};
using FetchHeadersIteratorBase = JSDOMIteratorBase<JSFetchHeaders, FetchHeadersIteratorTraits>;
diff --git a/src/bun.js/webcore/body.zig b/src/bun.js/webcore/body.zig
index 23f00d16e..c4e2e1324 100644
--- a/src/bun.js/webcore/body.zig
+++ b/src/bun.js/webcore/body.zig
@@ -128,11 +128,11 @@ pub const Body = struct {
status_code: u16,
method: Method = Method.GET,
- pub fn clone(this: Init, _: *JSGlobalObject) Init {
+ pub fn clone(this: Init, ctx: *JSGlobalObject) Init {
var that = this;
var headers = this.headers;
if (headers) |head| {
- that.headers = head.cloneThis();
+ that.headers = head.cloneThis(ctx);
}
return that;
@@ -149,7 +149,7 @@ pub const Body = struct {
// we can skip calling JS getters
if (response_init.as(Request)) |req| {
if (req.headers) |headers| {
- result.headers = headers.cloneThis();
+ result.headers = headers.cloneThis(ctx);
}
result.method = req.method;
@@ -163,7 +163,7 @@ pub const Body = struct {
if (response_init.fastGet(ctx, .headers)) |headers| {
if (headers.as(FetchHeaders)) |orig| {
- result.headers = orig.cloneThis();
+ result.headers = orig.cloneThis(ctx);
} else {
result.headers = FetchHeaders.createFromJS(ctx.ptr(), headers);
}
diff --git a/src/bun.js/webcore/request.zig b/src/bun.js/webcore/request.zig
index fb0e40d2e..04fb3f7e2 100644
--- a/src/bun.js/webcore/request.zig
+++ b/src/bun.js/webcore/request.zig
@@ -269,7 +269,7 @@ pub const Request = struct {
globalObject: *JSC.JSGlobalObject,
) callconv(.C) JSC.JSValue {
if (this.headers) |headers_ref| {
- if (headers_ref.get("referrer")) |referrer| {
+ if (headers_ref.get("referrer", globalObject)) |referrer| {
return ZigString.init(referrer).toValueGC(globalObject);
}
}
@@ -427,7 +427,7 @@ pub const Request = struct {
request.body.Blob.content_type.len > 0 and
!request.headers.?.fastHas(.ContentType))
{
- request.headers.?.put("content-type", request.body.Blob.content_type);
+ request.headers.?.put("content-type", request.body.Blob.content_type, globalThis);
}
return request;
@@ -474,7 +474,7 @@ pub const Request = struct {
if (this.body == .Blob) {
const content_type = this.body.Blob.content_type;
if (content_type.len > 0) {
- this.headers.?.put("content-type", content_type);
+ this.headers.?.put("content-type", content_type, globalThis);
}
}
}
@@ -501,10 +501,10 @@ pub const Request = struct {
};
if (this.headers) |head| {
- req.headers = head.cloneThis();
+ req.headers = head.cloneThis(globalThis);
} else if (this.uws_request) |uws_req| {
req.headers = FetchHeaders.createFromUWS(globalThis, uws_req);
- this.headers = req.headers.?.cloneThis().?;
+ this.headers = req.headers.?.cloneThis(globalThis).?;
}
}
diff --git a/src/bun.js/webcore/response.zig b/src/bun.js/webcore/response.zig
index 725a4e64c..5c0cd9c8e 100644
--- a/src/bun.js/webcore/response.zig
+++ b/src/bun.js/webcore/response.zig
@@ -200,14 +200,14 @@ pub const Response = struct {
return JSValue.jsBoolean(this.isOK());
}
- fn getOrCreateHeaders(this: *Response) *FetchHeaders {
+ fn getOrCreateHeaders(this: *Response, globalThis: *JSC.JSGlobalObject) *FetchHeaders {
if (this.body.init.headers == null) {
this.body.init.headers = FetchHeaders.createEmpty();
if (this.body.value == .Blob) {
const content_type = this.body.value.Blob.content_type;
if (content_type.len > 0) {
- this.body.init.headers.?.put("content-type", content_type);
+ this.body.init.headers.?.put("content-type", content_type, globalThis);
}
}
}
@@ -219,7 +219,7 @@ pub const Response = struct {
this: *Response,
globalThis: *JSC.JSGlobalObject,
) callconv(.C) JSC.JSValue {
- return this.getOrCreateHeaders().toJS(globalThis);
+ return this.getOrCreateHeaders(globalThis).toJS(globalThis);
}
pub fn doClone(
@@ -230,7 +230,7 @@ pub const Response = struct {
var cloned = this.clone(getAllocator(globalThis), globalThis);
const val = Response.makeMaybePooled(globalThis, cloned);
if (this.body.init.headers) |headers| {
- cloned.body.init.headers = headers.cloneThis();
+ cloned.body.init.headers = headers.cloneThis(globalThis);
}
return val;
@@ -406,8 +406,8 @@ pub const Response = struct {
}
}
- var headers_ref = response.getOrCreateHeaders();
- headers_ref.putDefault("content-type", MimeType.json.value);
+ var headers_ref = response.getOrCreateHeaders(globalThis);
+ headers_ref.putDefault("content-type", MimeType.json.value, globalThis);
var ptr = response.allocator.create(Response) catch unreachable;
ptr.* = response;
@@ -453,9 +453,9 @@ pub const Response = struct {
}
}
- response.body.init.headers = response.getOrCreateHeaders();
+ response.body.init.headers = response.getOrCreateHeaders(globalThis);
var headers_ref = response.body.init.headers.?;
- headers_ref.put("location", url_string_slice.slice());
+ headers_ref.put("location", url_string_slice.slice(), globalThis);
var ptr = response.allocator.create(Response) catch unreachable;
ptr.* = response;
@@ -521,7 +521,7 @@ pub const Response = struct {
response.body.value.Blob.content_type.len > 0 and
!response.body.init.headers.?.fastHas(.ContentType))
{
- response.body.init.headers.?.put("content-type", response.body.value.Blob.content_type);
+ response.body.init.headers.?.put("content-type", response.body.value.Blob.content_type, globalThis);
}
return response;
@@ -1031,8 +1031,12 @@ pub const Fetch = struct {
headers = Headers.from(headers__, bun.default_allocator) catch unreachable;
// TODO: make this one pass
} else if (FetchHeaders.createFromJS(ctx.ptr(), headers_)) |headers__| {
+ defer headers__.deref();
headers = Headers.from(headers__, bun.default_allocator) catch unreachable;
- headers__.deref();
+ } else {
+ // Converting the headers failed; return null and
+ // let the set exception get thrown
+ return null;
}
}
@@ -1185,7 +1189,8 @@ pub const Fetch = struct {
return null;
}
- var deferred_promise = JSC.C.JSObjectMakeDeferredPromise(globalThis, null, null, null);
+ var promise = JSPromise.Strong.init(globalThis);
+ var promise_val = promise.value();
if (!method.hasRequestBody() and body.size() > 0) {
const err = JSC.toTypeError(.ERR_INVALID_ARG_VALUE, fetch_error_unexpected_body, .{}, ctx);
@@ -1199,9 +1204,9 @@ pub const Fetch = struct {
.{ .method = method, .url = url, .headers = headers orelse Headers{
.allocator = bun.default_allocator,
}, .body = body, .timeout = std.time.ns_per_hour, .disable_keepalive = disable_keepalive, .disable_timeout = disable_timeout, .follow_redirects = follow_redirects, .verbose = verbose, .proxy = proxy, .url_proxy_buffer = url_proxy_buffer, .signal = signal, .globalThis = globalThis },
- JSC.JSValue.fromRef(deferred_promise),
+ promise_val,
) catch unreachable;
- return deferred_promise;
+ return promise_val.asRef();
}
};
diff --git a/test/bun.js/fetch_headers.test.js b/test/bun.js/fetch_headers.test.js
new file mode 100644
index 000000000..7f8fab188
--- /dev/null
+++ b/test/bun.js/fetch_headers.test.js
@@ -0,0 +1,58 @@
+import { describe, it, expect, beforeAll, afterAll } from "bun:test";
+const port = 3009;
+const url = `http://localhost:${port}`;
+let server;
+
+describe("Headers", async () => {
+ // Start up a single server and reuse it between tests
+ beforeAll(() => {
+ server = Bun.serve({
+ fetch(req) {
+ const hdr = req.headers.get("x-test");
+ return new Response(hdr);
+ },
+ port: port,
+ });
+ });
+ afterAll(() => {
+ server.stop();
+ });
+
+ it("Headers should work", async () => {
+ expect(await fetchContent({"x-test": "header 1"})).toBe("header 1");
+ });
+
+ it("Header names must be valid", async () => {
+ expect(() => fetch(url, {headers: {"a\tb:c": "foo" }})).toThrow("Invalid header name: 'a\tb:c'");
+ expect(() => fetch(url, {headers: {"❤️": "foo" }})).toThrow("Invalid header name: '❤️'");
+ });
+
+ it("Header values must be valid", async () => {
+ expect(() => fetch(url, {headers: {"x-test": "\0" }})).toThrow("Header 'x-test' has invalid value: '\0'");
+ expect(() => fetch(url, {headers: {"x-test": "❤️" }})).toThrow("Header 'x-test' has invalid value: '❤️'");
+ });
+
+ it("repro 1602", async () => {
+ const origString = "😂1234".slice(3);
+
+ var encoder = new TextEncoder();
+ var decoder = new TextDecoder();
+ const roundTripString = decoder.decode(encoder.encode(origString));
+
+ expect(roundTripString).toBe(origString);
+
+ // This one will pass
+ expect(await fetchContent({"x-test": roundTripString})).toBe(roundTripString);
+ // This would hang
+ expect(await fetchContent({"x-test": origString})).toBe(origString);
+ });
+});
+
+async function fetchContent(headers) {
+ const res = await fetch(
+ url,
+ { headers: headers },
+ { verbose: true }
+ );
+ return await res.text();
+}