aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-09-17 04:28:01 -0700
committerGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-09-17 04:28:32 -0700
commitcefec77646a1710fbc2994a18830f3c2a9ac5670 (patch)
tree48cfcacf0f69ba861ab581a1e1f2360aee7a8537 /src
parent7c7adc13610dfe5ba24f0b9098b985c5763bfeeb (diff)
downloadbun-cefec77646a1710fbc2994a18830f3c2a9ac5670.tar.gz
bun-cefec77646a1710fbc2994a18830f3c2a9ac5670.tar.zst
bun-cefec77646a1710fbc2994a18830f3c2a9ac5670.zip
Partially implement `server.fetch()` on Bun.serve
Diffstat (limited to 'src')
-rw-r--r--src/bun.js/api/server.zig120
-rw-r--r--src/bun.js/webcore/response.zig12
2 files changed, 127 insertions, 5 deletions
diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig
index 8381a15bd..22c8b748e 100644
--- a/src/bun.js/api/server.zig
+++ b/src/bun.js/api/server.zig
@@ -1862,6 +1862,9 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type {
.finalize = .{
.rfn = finalize,
},
+ .fetch = .{
+ .rfn = onFetch,
+ },
},
.{
.port = .{
@@ -1879,6 +1882,123 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type {
},
);
+ pub fn onFetch(
+ this: *ThisServer,
+ ctx: js.JSContextRef,
+ _: js.JSObjectRef,
+ _: js.JSObjectRef,
+ arguments: []const js.JSValueRef,
+ _: js.ExceptionRef,
+ ) js.JSObjectRef {
+ var globalThis = ctx.ptr();
+
+ if (arguments.len == 0) {
+ const fetch_error = WebCore.Fetch.fetch_error_no_args;
+ return JSPromise.rejectedPromiseValue(globalThis, ZigString.init(fetch_error).toErrorInstance(globalThis)).asRef();
+ }
+
+ var headers: ?*JSC.FetchHeaders = null;
+ var method = HTTP.Method.GET;
+ var args = JSC.Node.ArgumentsSlice.from(ctx.bunVM(), arguments);
+ defer args.deinit();
+
+ var url: URL = undefined;
+ var first_arg = args.nextEat().?;
+ var body: JSC.WebCore.Body.Value = .{ .Empty = .{} };
+ var existing_request: ?WebCore.Request = null;
+ // TODO: set Host header
+ // TODO: set User-Agent header
+ if (first_arg.isString()) {
+ var url_zig_str = ZigString.init("");
+ JSValue.fromRef(arguments[0]).toZigString(&url_zig_str, globalThis);
+ var temp_url_str = url_zig_str.slice();
+
+ if (temp_url_str.len == 0) {
+ const fetch_error = JSC.WebCore.Fetch.fetch_error_blank_url;
+ return JSPromise.rejectedPromiseValue(globalThis, ZigString.init(fetch_error).toErrorInstance(globalThis)).asRef();
+ }
+
+ url = URL.parse(temp_url_str);
+
+ if (url.hostname.len == 0) {
+ url = URL.parse(
+ strings.append(this.allocator, this.base_url_string_for_joining, url.pathname) catch unreachable,
+ );
+ } else {
+ temp_url_str = this.allocator.dupe(u8, temp_url_str) catch unreachable;
+ url = URL.parse(temp_url_str);
+ }
+
+ if (arguments.len >= 2 and arguments[1].?.value().isObject()) {
+ var opts = JSValue.fromRef(arguments[1]);
+ if (opts.fastGet(ctx.ptr(), .method)) |method_| {
+ var slice_ = method_.toSlice(ctx.ptr(), getAllocator(ctx));
+ defer slice_.deinit();
+ method = HTTP.Method.which(slice_.slice()) orelse method;
+ }
+
+ if (opts.fastGet(ctx.ptr(), .headers)) |headers_| {
+ if (headers_.as(JSC.FetchHeaders)) |headers__| {
+ headers = headers__;
+ } else if (JSC.FetchHeaders.createFromJS(ctx.ptr(), headers_)) |headers__| {
+ headers = headers__;
+ }
+ }
+
+ if (opts.fastGet(ctx.ptr(), .body)) |body__| {
+ if (Blob.fromJS(ctx.ptr(), body__, true, false)) |new_blob| {
+ body = .{ .Blob = new_blob };
+ } else |_| {
+ return JSPromise.rejectedPromiseValue(globalThis, ZigString.init("fetch() received invalid body").toErrorInstance(globalThis)).asRef();
+ }
+ }
+ }
+ } else if (first_arg.as(Request)) |request_| {
+ existing_request = request_.*;
+ } else {
+ const fetch_error = WebCore.Fetch.fetch_type_error_strings.get(js.JSValueGetType(ctx, arguments[0]));
+ return JSPromise.rejectedPromiseValue(globalThis, ZigString.init(fetch_error).toErrorInstance(globalThis)).asRef();
+ }
+
+ if (existing_request == null) {
+ existing_request = Request{
+ .url = ZigString.init(url.href),
+ .headers = headers,
+ .body = body,
+ .method = method,
+ };
+ }
+
+ var request = ctx.bunVM().allocator.create(Request) catch unreachable;
+ request.* = existing_request.?;
+ request.url.mark();
+
+ var args_ = [_]JSC.C.JSValueRef{request.toJS(this.globalThis).asObjectRef()};
+ const response_value = JSC.C.JSObjectCallAsFunctionReturnValue(
+ this.globalThis.ref(),
+ this.config.onRequest.asObjectRef(),
+ this.thisObject.asObjectRef(),
+ 1,
+ &args_,
+ );
+
+ if (response_value.isAnyError(ctx)) {
+ return JSC.JSPromise.rejectedPromiseValue(ctx, response_value).asObjectRef();
+ }
+
+ if (response_value.isEmptyOrUndefinedOrNull()) {
+ return JSC.JSPromise.rejectedPromiseValue(ctx, ZigString.init("fetch() returned an empty value").toErrorInstance(ctx)).asObjectRef();
+ }
+
+ // TODO: do this for promises too
+
+ if (response_value.as(JSC.WebCore.Response)) |resp| {
+ resp.url = this.allocator.dupe(u8, url.href) catch unreachable;
+ }
+
+ return JSC.JSPromise.resolvedPromiseValue(ctx, response_value).asObjectRef();
+ }
+
pub fn stopFromJS(this: *ThisServer) JSC.JSValue {
if (this.listener != null) {
JSC.C.JSValueUnprotect(this.globalThis.ref(), this.thisObject.asObjectRef());
diff --git a/src/bun.js/webcore/response.zig b/src/bun.js/webcore/response.zig
index 56d6fd5e3..65d9c272f 100644
--- a/src/bun.js/webcore/response.zig
+++ b/src/bun.js/webcore/response.zig
@@ -444,10 +444,10 @@ pub const Fetch = struct {
const JSType = js.JSType;
- const fetch_error_no_args = "fetch() expects a string but received no arguments.";
- const fetch_error_blank_url = "fetch() URL must not be a blank string.";
+ pub const fetch_error_no_args = "fetch() expects a string but received no arguments.";
+ pub const fetch_error_blank_url = "fetch() URL must not be a blank string.";
const JSTypeErrorEnum = std.enums.EnumArray(JSType, string);
- const fetch_type_error_names: JSTypeErrorEnum = brk: {
+ pub const fetch_type_error_names: JSTypeErrorEnum = brk: {
var errors = JSTypeErrorEnum.initUndefined();
errors.set(JSType.kJSTypeUndefined, "Undefined");
errors.set(JSType.kJSTypeNull, "Null");
@@ -459,7 +459,7 @@ pub const Fetch = struct {
break :brk errors;
};
- const fetch_type_error_string_values = .{
+ pub const fetch_type_error_string_values = .{
std.fmt.comptimePrint("fetch() expects a string, but received {s}", .{fetch_type_error_names.get(JSType.kJSTypeUndefined)}),
std.fmt.comptimePrint("fetch() expects a string, but received {s}", .{fetch_type_error_names.get(JSType.kJSTypeNull)}),
std.fmt.comptimePrint("fetch() expects a string, but received {s}", .{fetch_type_error_names.get(JSType.kJSTypeBoolean)}),
@@ -469,7 +469,7 @@ pub const Fetch = struct {
std.fmt.comptimePrint("fetch() expects a string, but received {s}", .{fetch_type_error_names.get(JSType.kJSTypeSymbol)}),
};
- const fetch_type_error_strings: JSTypeErrorEnum = brk: {
+ pub const fetch_type_error_strings: JSTypeErrorEnum = brk: {
var errors = JSTypeErrorEnum.initUndefined();
errors.set(
JSType.kJSTypeUndefined,
@@ -726,6 +726,8 @@ pub const Fetch = struct {
var headers: ?Headers = null;
var method = Method.GET;
var args = JSC.Node.ArgumentsSlice.from(ctx.bunVM(), arguments);
+ defer args.deinit();
+
var url: ZigURL = undefined;
var first_arg = args.nextEat().?;
var body: Blob = Blob.initEmpty(ctx);