fetc h!!!

This commit is contained in:
Jarred Sumner
2021-09-09 05:40:07 -07:00
parent c30ec608b1
commit 8a02ad48a5
34 changed files with 6782 additions and 161 deletions

View File

@@ -726,11 +726,15 @@ pub fn NewClass(
var static_functions = brk: {
var funcs: [function_name_refs.len + 1]js.JSStaticFunction = undefined;
std.mem.set(js.JSStaticFunction, &funcs, js.JSStaticFunction{
.name = @intToPtr([*c]const u8, 0),
.callAsFunction = null,
.attributes = js.JSPropertyAttributes.kJSPropertyAttributeNone,
},);
std.mem.set(
js.JSStaticFunction,
&funcs,
js.JSStaticFunction{
.name = @intToPtr([*c]const u8, 0),
.callAsFunction = null,
.attributes = js.JSPropertyAttributes.kJSPropertyAttributeNone,
},
);
break :brk funcs;
};
var instance_functions = std.mem.zeroes([function_names.len]js.JSObjectRef);
@@ -738,36 +742,40 @@ pub fn NewClass(
var property_name_refs = std.mem.zeroes([property_names.len]js.JSStringRef);
const property_name_literals = property_names;
var static_properties = brk: {
var props: [property_names.len]js.JSStaticValue = undefined;
std.mem.set(js.JSStaticValue, &props, js.JSStaticValue{
.name = @intToPtr([*c]const u8, 0),
.getProperty = null,
.setProperty = null,
.attributes = js.JSPropertyAttributes.kJSPropertyAttributeNone,
},);
var props: [property_names.len]js.JSStaticValue = undefined;
std.mem.set(
js.JSStaticValue,
&props,
js.JSStaticValue{
.name = @intToPtr([*c]const u8, 0),
.getProperty = null,
.setProperty = null,
.attributes = js.JSPropertyAttributes.kJSPropertyAttributeNone,
},
);
break :brk props;
};
pub var ref: js.JSClassRef = null;
pub var loaded = false;
pub var definition: js.JSClassDefinition =.{
.version = 0,
.attributes = js.JSClassAttributes.kJSClassAttributeNone,
.className = name[0..:0].ptr,
.parentClass = null,
.staticValues = null,
.staticFunctions = null,
.initialize = null,
.finalize = null,
.hasProperty = null,
.getProperty = null,
.setProperty = null,
.deleteProperty = null,
.getPropertyNames = null,
.callAsFunction = null,
.callAsConstructor = null,
.hasInstance = null,
.convertToType = null,
pub var definition: js.JSClassDefinition = .{
.version = 0,
.attributes = js.JSClassAttributes.kJSClassAttributeNone,
.className = name[0.. :0].ptr,
.parentClass = null,
.staticValues = null,
.staticFunctions = null,
.initialize = null,
.finalize = null,
.hasProperty = null,
.getProperty = null,
.setProperty = null,
.deleteProperty = null,
.getPropertyNames = null,
.callAsFunction = null,
.callAsConstructor = null,
.hasInstance = null,
.convertToType = null,
};
const ConstructorWrapper = struct {
pub fn rfn(
@@ -1326,7 +1334,7 @@ pub fn NewClass(
.callAsConstructor = null,
.hasInstance = null,
.convertToType = null,
};
};
if (static_functions.len > 0) {
std.mem.set(js.JSStaticFunction, &static_functions, std.mem.zeroes(js.JSStaticFunction));
@@ -1338,6 +1346,8 @@ pub fn NewClass(
def.callAsConstructor = To.JS.Constructor(staticFunctions.constructor.rfn).rfn;
} else if (comptime strings.eqlComptime(function_names[i], "finalize")) {
def.finalize = To.JS.Finalize(ZigType, staticFunctions.finalize.rfn).rfn;
} else if (comptime strings.eqlComptime(function_names[i], "call")) {
def.callAsFunction = To.JS.Callback(ZigType, staticFunctions.call.rfn).rfn;
} else if (comptime strings.eqlComptime(function_names[i], "callAsFunction")) {
const ctxfn = @field(staticFunctions, function_names[i]).rfn;
const Func: std.builtin.TypeInfo.Fn = @typeInfo(@TypeOf(ctxfn)).Fn;
@@ -1379,6 +1389,8 @@ pub fn NewClass(
def.callAsConstructor = To.JS.Constructor(staticFunctions.constructor).rfn;
} else if (comptime strings.eqlComptime(function_names[i], "finalize")) {
def.finalize = To.JS.Finalize(ZigType, staticFunctions.finalize).rfn;
} else if (comptime strings.eqlComptime(function_names[i], "call")) {
def.callAsFunction = To.JS.Callback(ZigType, staticFunctions.call).rfn;
} else {
var callback = To.JS.Callback(
ZigType,

View File

@@ -165,7 +165,7 @@ void GlobalObject::setConsole(void *console) {
// and any other objects available globally.
void GlobalObject::installAPIGlobals(JSClassRef *globals, int count) {
WTF::Vector<GlobalPropertyInfo> extraStaticGlobals;
extraStaticGlobals.reserveCapacity((size_t)count + 1);
extraStaticGlobals.reserveCapacity((size_t)count + 2);
// This is not nearly a complete implementation. It's just enough to make some npm packages that
// were compiled with Webpack to run without crashing in this environment.
@@ -223,9 +223,7 @@ JSC::Identifier GlobalObject::moduleLoaderResolve(JSGlobalObject *globalObject,
res.success = false;
ZigString keyZ = toZigString(key, globalObject);
ZigString referrerZ = referrer.isString() ? toZigString(referrer, globalObject) : ZigStringEmpty;
Zig__GlobalObject__resolve(&res, globalObject, &keyZ,
&referrerZ
);
Zig__GlobalObject__resolve(&res, globalObject, &keyZ, &referrerZ);
if (res.success) {
return toIdentifier(res.result.value, globalObject);
@@ -250,11 +248,9 @@ JSC::JSInternalPromise *GlobalObject::moduleLoaderImportModule(JSGlobalObject *g
auto sourceURL = sourceOrigin.url();
ErrorableZigString resolved;
auto moduleNameZ = toZigString(moduleNameValue, globalObject);
auto sourceOriginZ = sourceURL.isEmpty() ? ZigStringCwd
: toZigString(sourceURL.fileSystemPath());
auto sourceOriginZ = sourceURL.isEmpty() ? ZigStringCwd : toZigString(sourceURL.fileSystemPath());
resolved.success = false;
Zig__GlobalObject__resolve(&resolved, globalObject, &moduleNameZ, &sourceOriginZ
);
Zig__GlobalObject__resolve(&resolved, globalObject, &moduleNameZ, &sourceOriginZ);
if (!resolved.success) {
throwException(scope, resolved.result.err, globalObject);
return promise->rejectWithCaughtException(globalObject, scope);
@@ -382,8 +378,7 @@ JSC::JSInternalPromise *GlobalObject::moduleLoaderFetch(JSGlobalObject *globalOb
res.result.err.code = 0;
res.result.err.ptr = nullptr;
Zig__GlobalObject__fetch(&res, globalObject, &moduleKeyZig,
&source );
Zig__GlobalObject__fetch(&res, globalObject, &moduleKeyZig, &source);
if (!res.success) {
throwException(scope, res.result.err, globalObject);

View File

@@ -1765,4 +1765,15 @@ void WTF__URL__setQuery(WTF__URL *arg0, bWTF__StringView arg1) {
void WTF__URL__setUser(WTF__URL *arg0, bWTF__StringView arg1) {
arg0->setUser(*Wrap<WTF::StringView, bWTF__StringView>::unwrap(&arg1));
};
JSC__JSValue JSC__JSPromise__rejectedPromiseValue(JSC__JSGlobalObject *arg0,
JSC__JSValue JSValue1) {
return JSC::JSValue::encode(
JSC::JSPromise::rejectedPromise(arg0, JSC::JSValue::decode(JSValue1)));
}
JSC__JSValue JSC__JSPromise__resolvedPromiseValue(JSC__JSGlobalObject *arg0,
JSC__JSValue JSValue1) {
return JSC::JSValue::encode(
JSC::JSPromise::resolvedPromise(arg0, JSC::JSValue::decode(JSValue1)));
}
}

View File

@@ -438,10 +438,19 @@ pub const JSPromise = extern struct {
pub fn resolvedPromise(globalThis: *JSGlobalObject, value: JSValue) *JSPromise {
return cppFn("resolvedPromise", .{ globalThis, value });
}
pub fn resolvedPromiseValue(globalThis: *JSGlobalObject, value: JSValue) JSValue {
return cppFn("resolvedPromiseValue", .{ globalThis, value });
}
pub fn rejectedPromise(globalThis: *JSGlobalObject, value: JSValue) *JSPromise {
return cppFn("rejectedPromise", .{ globalThis, value });
}
pub fn rejectedPromiseValue(globalThis: *JSGlobalObject, value: JSValue) JSValue {
return cppFn("rejectedPromiseValue", .{ globalThis, value });
}
pub fn resolve(this: *JSPromise, globalThis: *JSGlobalObject, value: JSValue) void {
cppFn("resolve", .{ this, globalThis, value });
}
@@ -470,6 +479,8 @@ pub const JSPromise = extern struct {
"rejectAsHandled",
// "rejectException",
"rejectAsHandledException",
"rejectedPromiseValue",
"resolvedPromiseValue",
};
};

View File

@@ -1,4 +1,4 @@
//-- AUTOGENERATED FILE -- 1631085611
//-- AUTOGENERATED FILE -- 1631179623
// clang-format off
#pragma once

View File

@@ -1,4 +1,4 @@
//-- AUTOGENERATED FILE -- 1631085611
//-- AUTOGENERATED FILE -- 1631179623
// clang-format: off
#pragma once
@@ -285,9 +285,11 @@ CPP_DECL void JSC__JSPromise__reject(JSC__JSPromise* arg0, JSC__JSGlobalObject*
CPP_DECL void JSC__JSPromise__rejectAsHandled(JSC__JSPromise* arg0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2);
CPP_DECL void JSC__JSPromise__rejectAsHandledException(JSC__JSPromise* arg0, JSC__JSGlobalObject* arg1, JSC__Exception* arg2);
CPP_DECL JSC__JSPromise* JSC__JSPromise__rejectedPromise(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1);
CPP_DECL JSC__JSValue JSC__JSPromise__rejectedPromiseValue(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1);
CPP_DECL void JSC__JSPromise__rejectWithCaughtException(JSC__JSPromise* arg0, JSC__JSGlobalObject* arg1, bJSC__ThrowScope arg2);
CPP_DECL void JSC__JSPromise__resolve(JSC__JSPromise* arg0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2);
CPP_DECL JSC__JSPromise* JSC__JSPromise__resolvedPromise(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1);
CPP_DECL JSC__JSValue JSC__JSPromise__resolvedPromiseValue(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1);
CPP_DECL JSC__JSValue JSC__JSPromise__result(const JSC__JSPromise* arg0, JSC__VM* arg1);
CPP_DECL uint32_t JSC__JSPromise__status(const JSC__JSPromise* arg0, JSC__VM* arg1);

File diff suppressed because one or more lines are too long

View File

@@ -33,6 +33,7 @@ pub const GlobalClasses = [_]type{
BuildError.Class,
ResolveError.Class,
Bun.Class,
Fetch.Class,
};
const Blob = @import("../../blob.zig");
@@ -276,6 +277,10 @@ pub const Bun = struct {
.rfn = Router.match,
.ts = Router.match_type_definition,
},
.fetch = .{
.rfn = Fetch.call,
.ts = d.ts{},
},
.getImportedStyles = .{
.rfn = Bun.getImportedStyles,
.ts = d.ts{
@@ -1348,7 +1353,6 @@ pub const EventListenerMixin = struct {
// Rely on JS finalizer
var fetch_event = try vm.allocator.create(FetchEvent);
fetch_event.* = FetchEvent{
.request_context = request_context,
.request = Request{ .request_context = request_context },

View File

@@ -4,19 +4,34 @@ const Api = @import("../../../api/schema.zig").Api;
const http = @import("../../../http.zig");
usingnamespace @import("../javascript.zig");
usingnamespace @import("../bindings/bindings.zig");
const ZigURL = @import("../../../query_string_map.zig").URL;
const HTTPClient = @import("../../../http_client.zig");
const picohttp = @import("picohttp");
pub const Response = struct {
pub const Class = NewClass(
Response,
.{ .name = "Response" },
.{
.@"constructor" = constructor,
.@"text" = .{
.rfn = getText,
.ts = d.ts{},
},
.@"json" = .{
.rfn = getJson,
.ts = d.ts{},
},
.@"arrayBuffer" = .{
.rfn = getArrayBuffer,
.ts = d.ts{},
},
},
.{
// .@"url" = .{
// .@"get" = getURL,
// .ro = true,
// },
.@"ok" = .{
.@"get" = getOK,
.ro = true,
@@ -30,6 +45,7 @@ pub const Response = struct {
allocator: *std.mem.Allocator,
body: Body,
status_text: string = "",
pub const Props = struct {};
@@ -41,7 +57,174 @@ pub const Response = struct {
exception: js.ExceptionRef,
) js.JSValueRef {
// https://developer.mozilla.org/en-US/docs/Web/API/Response/ok
return js.JSValueMakeBoolean(ctx, this.body.init.status_code >= 200 and this.body.init.status_code <= 299);
return js.JSValueMakeBoolean(ctx, this.body.init.status_code == 304 or (this.body.init.status_code >= 200 and this.body.init.status_code <= 299));
}
pub fn getText(
this: *Response,
ctx: js.JSContextRef,
function: js.JSObjectRef,
thisObject: js.JSObjectRef,
arguments: []const js.JSValueRef,
exception: js.ExceptionRef,
) js.JSValueRef {
// https://developer.mozilla.org/en-US/docs/Web/API/Response/text
defer this.body.value = .Empty;
return JSPromise.resolvedPromiseValue(
VirtualMachine.vm.global,
(brk: {
switch (this.body.value) {
.Unconsumed => {
if (this.body.ptr) |_ptr| {
break :brk ZigString.init(_ptr[0..this.body.len]).toValue(VirtualMachine.vm.global);
}
break :brk ZigString.init("").toValue(VirtualMachine.vm.global);
},
.Empty => {
break :brk ZigString.init("").toValue(VirtualMachine.vm.global);
},
.String => |str| {
break :brk ZigString.init(str).toValue(VirtualMachine.vm.global);
},
.ArrayBuffer => |buffer| {
break :brk ZigString.init(buffer.ptr[buffer.offset..buffer.byte_len]).toValue(VirtualMachine.vm.global);
},
}
}),
).asRef();
}
var temp_error_buffer: [4096]u8 = undefined;
var error_arg_list: [1]js.JSObjectRef = undefined;
pub fn getJson(
this: *Response,
ctx: js.JSContextRef,
function: js.JSObjectRef,
thisObject: js.JSObjectRef,
arguments: []const js.JSValueRef,
exception: js.ExceptionRef,
) js.JSValueRef {
defer this.body.value = .Empty;
var zig_string = ZigString.init("");
var js_string = (js.JSValueCreateJSONString(
ctx,
brk: {
switch (this.body.value) {
.Unconsumed => {
if (this.body.ptr) |_ptr| {
zig_string = ZigString.init(_ptr[0..this.body.len]);
break :brk zig_string.toJSStringRef();
}
break :brk zig_string.toJSStringRef();
},
.Empty => {
break :brk zig_string.toJSStringRef();
},
.String => |str| {
zig_string = ZigString.init(str);
break :brk zig_string.toJSStringRef();
},
.ArrayBuffer => |buffer| {
zig_string = ZigString.init(buffer.ptr[buffer.offset..buffer.byte_len]);
break :brk zig_string.toJSStringRef();
},
}
},
0,
exception,
) orelse {
var out = std.fmt.bufPrint(&temp_error_buffer, "Invalid JSON\n\n \"{s}\"", .{zig_string.slice()[0..std.math.min(zig_string.len, 4000)]}) catch unreachable;
error_arg_list[0] = ZigString.init(out).toValueGC(VirtualMachine.vm.global).asRef();
return JSPromise.rejectedPromiseValue(
VirtualMachine.vm.global,
JSValue.fromRef(
js.JSObjectMakeError(
ctx,
1,
&error_arg_list,
exception,
),
),
).asRef();
});
defer js.JSStringRelease(js_string);
return JSPromise.resolvedPromiseValue(
VirtualMachine.vm.global,
JSValue.fromRef(
js.JSValueMakeString(
ctx,
js_string,
),
),
).asRef();
}
pub fn getArrayBuffer(
this: *Response,
ctx: js.JSContextRef,
function: js.JSObjectRef,
thisObject: js.JSObjectRef,
arguments: []const js.JSValueRef,
exception: js.ExceptionRef,
) js.JSValueRef {
defer this.body.value = .Empty;
return JSPromise.resolvedPromiseValue(
VirtualMachine.vm.global,
JSValue.fromRef(
(brk: {
switch (this.body.value) {
.Unconsumed => {
if (this.body.ptr) |_ptr| {
break :brk js.JSObjectMakeTypedArrayWithBytesNoCopy(
ctx,
js.JSTypedArrayType.kJSTypedArrayTypeUint8Array,
_ptr,
this.body.len,
null,
null,
exception,
);
}
break :brk js.JSObjectMakeTypedArray(
ctx,
js.JSTypedArrayType.kJSTypedArrayTypeUint8Array,
0,
exception,
);
},
.Empty => {
break :brk js.JSObjectMakeTypedArray(ctx, js.JSTypedArrayType.kJSTypedArrayTypeUint8Array, 0, exception);
},
.String => |str| {
break :brk js.JSObjectMakeTypedArrayWithBytesNoCopy(
ctx,
js.JSTypedArrayType.kJSTypedArrayTypeUint8Array,
@intToPtr([*]u8, @ptrToInt(str.ptr)),
str.len,
null,
null,
exception,
);
},
.ArrayBuffer => |buffer| {
break :brk js.JSObjectMakeTypedArrayWithBytesNoCopy(
ctx,
buffer.typed_array_type,
buffer.ptr,
buffer.byte_len,
null,
null,
exception,
);
},
}
}),
),
).asRef();
}
pub fn getStatus(
@@ -87,7 +270,7 @@ pub const Response = struct {
return http.MimeType.html.value;
},
.ArrayBuffer => {
.Unconsumed, .ArrayBuffer => {
return "application/octet-stream";
},
}
@@ -134,6 +317,151 @@ pub const Response = struct {
}
};
pub const Fetch = struct {
const headers_string = "headers";
const method_string = "method";
var fetch_body_string: MutableString = undefined;
var fetch_body_string_loaded = false;
pub const Class = NewClass(
void,
.{ .name = "fetch" },
.{
.@"call" = .{
.rfn = Fetch.call,
.ts = d.ts{},
},
},
.{},
);
pub fn call(
this: void,
ctx: js.JSContextRef,
function: js.JSObjectRef,
thisObject: js.JSObjectRef,
arguments: []const js.JSValueRef,
exception: js.ExceptionRef,
) js.JSObjectRef {
if (arguments.len == 0 or arguments.len > 2) return js.JSValueMakeNull(ctx);
var http_client = HTTPClient.init(getAllocator(ctx), .GET, ZigURL{}, .{}, "");
var headers: ?Headers = null;
var body: string = "";
if (!js.JSValueIsString(ctx, arguments[0])) {
return js.JSValueMakeNull(ctx);
}
var url_zig_str = ZigString.init("");
JSValue.fromRef(arguments[0]).toZigString(
&url_zig_str,
VirtualMachine.vm.global,
);
var url_str = url_zig_str.slice();
if (url_str.len == 0) return js.JSValueMakeNull(ctx);
http_client.url = ZigURL.parse(url_str);
if (arguments.len == 2 and js.JSValueIsObject(ctx, arguments[1])) {
var array = js.JSObjectCopyPropertyNames(ctx, arguments[1]);
defer js.JSPropertyNameArrayRelease(array);
const count = js.JSPropertyNameArrayGetCount(array);
var i: usize = 0;
while (i < count) : (i += 1) {
var property_name_ref = js.JSPropertyNameArrayGetNameAtIndex(array, i);
switch (js.JSStringGetLength(property_name_ref)) {
"headers".len => {
if (js.JSStringIsEqualToUTF8CString(property_name_ref, "headers")) {
if (js.JSObjectGetProperty(ctx, arguments[1], property_name_ref, null)) |value| {
if (GetJSPrivateData(Headers, value)) |headers_ptr| {
headers = headers_ptr.*;
} else if (Headers.JS.headersInit(ctx, value) catch null) |headers_| {
headers = headers_;
}
}
}
},
"body".len => {
if (js.JSStringIsEqualToUTF8CString(property_name_ref, "body")) {
if (js.JSObjectGetProperty(ctx, arguments[1], property_name_ref, null)) |value| {
var body_ = Body.extractBody(ctx, value, false, null, exception);
if (exception != null) return js.JSValueMakeNull(ctx);
switch (body_.value) {
.ArrayBuffer => |arraybuffer| {
body = arraybuffer.ptr[0..arraybuffer.byte_len];
},
.String => |str| {
body = str;
},
else => {},
}
}
}
},
"method".len => {
if (js.JSStringIsEqualToUTF8CString(property_name_ref, "method")) {
if (js.JSObjectGetProperty(ctx, arguments[1], property_name_ref, null)) |value| {
var string_ref = js.JSValueToStringCopy(ctx, value, exception);
if (exception != null) return js.JSValueMakeNull(ctx);
defer js.JSStringRelease(string_ref);
var method_name_buf: [16]u8 = undefined;
var method_name = method_name_buf[0..js.JSStringGetUTF8CString(string_ref, &method_name_buf, method_name_buf.len)];
http_client.method = http.Method.which(method_name) orelse http_client.method;
}
}
},
else => {},
}
}
}
if (headers) |head| {
http_client.header_entries = head.entries;
http_client.header_buf = head.buf.items;
}
if (fetch_body_string_loaded) {
fetch_body_string.reset();
} else {
fetch_body_string = MutableString.init(VirtualMachine.vm.allocator, 0) catch unreachable;
fetch_body_string_loaded = true;
}
var http_response = http_client.send(body, &fetch_body_string) catch |err| {
const fetch_error = std.fmt.allocPrint(getAllocator(ctx), "Fetch error: {s}", .{@errorName(err)}) catch unreachable;
return JSPromise.rejectedPromiseValue(VirtualMachine.vm.global, ZigString.init(fetch_error).toErrorInstance(VirtualMachine.vm.global)).asRef();
};
var response_headers = Headers.fromPicoHeaders(getAllocator(ctx), http_response.headers) catch unreachable;
response_headers.guard = .immutable;
var response = getAllocator(ctx).create(Response) catch unreachable;
var allocator = getAllocator(ctx);
var duped = allocator.dupeZ(u8, fetch_body_string.list.items) catch unreachable;
response.* = Response{
.allocator = allocator,
.status_text = allocator.dupe(u8, http_response.status) catch unreachable,
.body = .{
.init = .{
.headers = response_headers,
.status_code = @truncate(u16, http_response.status_code),
},
.value = .{
.Unconsumed = 0,
},
.ptr = duped.ptr,
.len = duped.len,
.ptr_allocator = allocator,
},
};
return JSPromise.resolvedPromiseValue(
VirtualMachine.vm.global,
JSValue.fromRef(Response.Class.make(ctx, response)),
).asRef();
}
};
// https://developer.mozilla.org/en-US/docs/Web/API/Headers
pub const Headers = struct {
pub const Kv = struct {
@@ -272,6 +600,77 @@ pub const Headers = struct {
return js.JSValueMakeNull(ctx);
}
pub fn headersInit(ctx: js.JSContextRef, header_prop: js.JSObjectRef) !?Headers {
const header_keys = js.JSObjectCopyPropertyNames(ctx, header_prop);
defer js.JSPropertyNameArrayRelease(header_keys);
const total_header_count = js.JSPropertyNameArrayGetCount(header_keys);
if (total_header_count == 0) return null;
// 2 passes through the headers
// Pass #1: find the "real" count.
// The number of things which are strings or numbers.
// Anything else should be ignored.
// We could throw a TypeError, but ignoring silently is more JavaScript-like imo
var real_header_count: usize = 0;
var estimated_buffer_len: usize = 0;
var j: usize = 0;
while (j < total_header_count) : (j += 1) {
var key_ref = js.JSPropertyNameArrayGetNameAtIndex(header_keys, j);
var value_ref = js.JSObjectGetProperty(ctx, header_prop, key_ref, null);
switch (js.JSValueGetType(ctx, value_ref)) {
js.JSType.kJSTypeNumber => {
const key_len = js.JSStringGetLength(key_ref);
if (key_len > 0) {
real_header_count += 1;
estimated_buffer_len += key_len;
estimated_buffer_len += std.fmt.count("{d}", .{js.JSValueToNumber(ctx, value_ref, null)});
}
},
js.JSType.kJSTypeString => {
const key_len = js.JSStringGetLength(key_ref);
const value_len = js.JSStringGetLength(value_ref);
if (key_len > 0 and value_len > 0) {
real_header_count += 1;
estimated_buffer_len += key_len + value_len;
}
},
else => {},
}
}
if (real_header_count == 0 or estimated_buffer_len == 0) return null;
j = 0;
var allocator = getAllocator(ctx);
var headers = Headers{
.allocator = allocator,
.buf = try std.ArrayListUnmanaged(u8).initCapacity(allocator, estimated_buffer_len),
.entries = Headers.Entries{},
};
errdefer headers.deinit();
try headers.entries.ensureTotalCapacity(allocator, real_header_count);
headers.buf.expandToCapacity();
while (j < total_header_count) : (j += 1) {
var key_ref = js.JSPropertyNameArrayGetNameAtIndex(header_keys, j);
var value_ref = js.JSObjectGetProperty(ctx, header_prop, key_ref, null);
switch (js.JSValueGetType(ctx, value_ref)) {
js.JSType.kJSTypeNumber => {
if (js.JSStringGetLength(key_ref) == 0) continue;
try headers.appendInit(ctx, key_ref, .kJSTypeNumber, value_ref);
},
js.JSType.kJSTypeString => {
if (js.JSStringGetLength(value_ref) == 0 or js.JSStringGetLength(key_ref) == 0) continue;
try headers.appendInit(ctx, key_ref, .kJSTypeString, value_ref);
},
else => {},
}
}
return headers;
}
// https://developer.mozilla.org/en-US/docs/Web/API/Headers/Headers
pub fn constructor(
ctx: js.JSContextRef,
@@ -283,6 +682,14 @@ pub const Headers = struct {
if (arguments.len > 0 and js.JSValueIsObjectOfClass(ctx, arguments[0], Headers.Class.get().*)) {
var other = castObj(arguments[0], Headers);
other.clone(headers) catch unreachable;
} else if (arguments.len == 1 and js.JSValueIsObject(ctx, arguments[0])) {
headers.* = (JS.headersInit(ctx, arguments[0]) catch unreachable) orelse Headers{
.entries = @TypeOf(headers.entries){},
.buf = @TypeOf(headers.buf){},
.used = 0,
.allocator = getAllocator(ctx),
.guard = Guard.none,
};
} else {
headers.* = Headers{
.entries = @TypeOf(headers.entries){},
@@ -356,26 +763,25 @@ pub const Headers = struct {
none,
};
// TODO: is it worth making this lazy? instead of copying all the request headers, should we just do it on get/put/iterator?
pub fn fromRequestCtx(allocator: *std.mem.Allocator, request: *http.RequestContext) !Headers {
pub fn fromPicoHeaders(allocator: *std.mem.Allocator, picohttp_headers: []const picohttp.Header) !Headers {
var total_len: usize = 0;
for (request.request.headers) |header| {
for (picohttp_headers) |header| {
total_len += header.name.len;
total_len += header.value.len;
}
// for the null bytes
total_len += request.request.headers.len * 2;
total_len += picohttp_headers.len * 2;
var headers = Headers{
.allocator = allocator,
.entries = Entries{},
.buf = std.ArrayListUnmanaged(u8){},
};
try headers.entries.ensureTotalCapacity(allocator, request.request.headers.len);
try headers.entries.ensureTotalCapacity(allocator, picohttp_headers.len);
try headers.buf.ensureTotalCapacity(allocator, total_len);
headers.buf.expandToCapacity();
headers.guard = Guard.request;
for (request.request.headers) |header| {
for (picohttp_headers) |header| {
headers.entries.appendAssumeCapacity(Kv{
.name = headers.appendString(
string,
@@ -394,11 +800,14 @@ pub const Headers = struct {
});
}
headers.guard = Guard.immutable;
return headers;
}
// TODO: is it worth making this lazy? instead of copying all the request headers, should we just do it on get/put/iterator?
pub fn fromRequestCtx(allocator: *std.mem.Allocator, request: *http.RequestContext) !Headers {
return fromPicoHeaders(allocator, request.request.headers);
}
pub fn asStr(headers: *const Headers, ptr: Api.StringPointer) []u8 {
return headers.buf.items[ptr.offset..][0..ptr.length];
}
@@ -479,7 +888,7 @@ pub const Headers = struct {
),
.value = headers.appendString(
string,
key,
value,
needs_lowercase,
needs_normalize,
append_null,
@@ -577,6 +986,9 @@ pub const Headers = struct {
pub const Body = struct {
init: Init,
value: Value,
ptr: ?[*]u8 = null,
len: usize = 0,
ptr_allocator: ?*std.mem.Allocator = null,
pub fn deinit(this: *Body, allocator: *std.mem.Allocator) void {
if (this.init.headers) |headers| {
@@ -602,7 +1014,7 @@ pub const Body = struct {
defer js.JSPropertyNameArrayRelease(array);
const count = js.JSPropertyNameArrayGetCount(array);
var i: usize = 0;
upper: while (i < count) : (i += 1) {
while (i < count) : (i += 1) {
var property_name_ref = js.JSPropertyNameArrayGetNameAtIndex(array, i);
switch (js.JSStringGetLength(property_name_ref)) {
"headers".len => {
@@ -611,73 +1023,7 @@ pub const Body = struct {
if (js.JSObjectGetProperty(ctx, init_ref, property_name_ref, null)) |header_prop| {
switch (js.JSValueGetType(ctx, header_prop)) {
js.JSType.kJSTypeObject => {
const header_keys = js.JSObjectCopyPropertyNames(ctx, header_prop);
defer js.JSPropertyNameArrayRelease(header_keys);
const total_header_count = js.JSPropertyNameArrayGetCount(array);
if (total_header_count == 0) continue :upper;
// 2 passes through the headers
// Pass #1: find the "real" count.
// The number of things which are strings or numbers.
// Anything else should be ignored.
// We could throw a TypeError, but ignoring silently is more JavaScript-like imo
var real_header_count: usize = 0;
var estimated_buffer_len: usize = 0;
var j: usize = 0;
while (j < total_header_count) : (j += 1) {
var key_ref = js.JSPropertyNameArrayGetNameAtIndex(header_keys, j);
var value_ref = js.JSObjectGetProperty(ctx, header_prop, key_ref, null);
switch (js.JSValueGetType(ctx, value_ref)) {
js.JSType.kJSTypeNumber => {
const key_len = js.JSStringGetLength(key_ref);
if (key_len > 0) {
real_header_count += 1;
estimated_buffer_len += key_len;
estimated_buffer_len += std.fmt.count("{d}", .{js.JSValueToNumber(ctx, value_ref, null)});
}
},
js.JSType.kJSTypeString => {
const key_len = js.JSStringGetLength(key_ref);
const value_len = js.JSStringGetLength(value_ref);
if (key_len > 0 and value_len > 0) {
real_header_count += 1;
estimated_buffer_len += key_len + value_len;
}
},
else => {},
}
}
if (real_header_count == 0 or estimated_buffer_len == 0) continue :upper;
j = 0;
var headers = Headers{
.allocator = allocator,
.buf = try std.ArrayListUnmanaged(u8).initCapacity(allocator, estimated_buffer_len),
.entries = Headers.Entries{},
};
errdefer headers.deinit();
try headers.entries.ensureTotalCapacity(allocator, real_header_count);
while (j < total_header_count) : (j += 1) {
var key_ref = js.JSPropertyNameArrayGetNameAtIndex(header_keys, j);
var value_ref = js.JSObjectGetProperty(ctx, header_prop, key_ref, null);
switch (js.JSValueGetType(ctx, value_ref)) {
js.JSType.kJSTypeNumber => {
if (js.JSStringGetLength(key_ref) == 0) continue;
try headers.appendInit(ctx, key_ref, .kJSTypeNumber, value_ref);
},
js.JSType.kJSTypeString => {
if (js.JSStringGetLength(value_ref) == 0 or js.JSStringGetLength(key_ref) == 0) continue;
try headers.appendInit(ctx, key_ref, .kJSTypeString, value_ref);
},
else => {},
}
}
result.headers = headers;
result.headers = try Headers.JS.headersInit(ctx, header_prop);
},
else => {},
}
@@ -705,10 +1051,12 @@ pub const Body = struct {
ArrayBuffer: ArrayBuffer,
String: string,
Empty: u0,
Unconsumed: u0,
pub const Tag = enum {
ArrayBuffer,
String,
Empty,
Unconsumed,
};
pub fn length(value: *const Value) usize {
@@ -719,7 +1067,7 @@ pub const Body = struct {
.String => |str| {
return str.len;
},
.Empty => {
else => {
return 0;
},
}
@@ -783,6 +1131,8 @@ pub const Body = struct {
}
body.value = Value{ .String = str.characters8()[0..len] };
body.ptr = @intToPtr([*]u8, @ptrToInt(body.value.String.ptr));
body.len = body.value.String.len;
return body;
},
.kJSTypeObject => {
@@ -807,6 +1157,8 @@ pub const Body = struct {
} else |err| {}
}
body.value = Value{ .ArrayBuffer = buffer };
body.ptr = buffer.ptr[buffer.offset..buffer.byte_len].ptr;
body.len = buffer.ptr[buffer.offset..buffer.byte_len].len;
return body;
},
}