diff options
| author | 2022-09-17 04:28:01 -0700 | |
|---|---|---|
| committer | 2022-09-17 04:28:32 -0700 | |
| commit | cefec77646a1710fbc2994a18830f3c2a9ac5670 (patch) | |
| tree | 48cfcacf0f69ba861ab581a1e1f2360aee7a8537 /src | |
| parent | 7c7adc13610dfe5ba24f0b9098b985c5763bfeeb (diff) | |
| download | bun-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.zig | 120 | ||||
| -rw-r--r-- | src/bun.js/webcore/response.zig | 12 |
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); |
