aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js/api/server.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/bun.js/api/server.zig')
-rw-r--r--src/bun.js/api/server.zig120
1 files changed, 120 insertions, 0 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());