diff options
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | .scripts/write-versions.sh | 10 | ||||
-rw-r--r-- | src/deps/_libusockets.h | 9 | ||||
-rw-r--r-- | src/deps/libuwsockets.cpp | 30 | ||||
-rw-r--r-- | src/deps/uws.zig | 36 | ||||
-rw-r--r-- | src/javascript/jsc/api/html_rewriter.zig | 6 | ||||
-rw-r--r-- | src/javascript/jsc/api/server.zig | 23 | ||||
-rw-r--r-- | src/javascript/jsc/javascript.zig | 2 | ||||
-rw-r--r-- | src/javascript/jsc/webcore/response.zig | 59 |
9 files changed, 154 insertions, 24 deletions
diff --git a/.gitmodules b/.gitmodules index 3df35adb2..f105a2a6d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -35,4 +35,5 @@ ignore = dirty [submodule "src/deps/uws"] path = src/deps/uws - url = https://github.com/uNetworking/uWebSockets + url = https://github.com/Jarred-Sumner/uWebSockets + ignore = dirty diff --git a/.scripts/write-versions.sh b/.scripts/write-versions.sh index af020f3f0..5fed3ee18 100644 --- a/.scripts/write-versions.sh +++ b/.scripts/write-versions.sh @@ -7,17 +7,19 @@ LIBARCHIVE_VERSION=$(git rev-parse HEAD:./src/deps/libarchive) PICOHTTPPARSER_VERSION=$(git rev-parse HEAD:./src/deps/picohttpparser) BORINGSSL_VERSION=$(git rev-parse HEAD:./src/deps/boringssl) ZLIB_VERSION=$(git rev-parse HEAD:./src/deps/zlib) +UWS_VERSION=$(git rev-parse HEAD:./src/deps/uws) rm -rf src/generated_versions_list.zig echo "// AUTO-GENERATED FILE. Created via .scripts/write-versions.sh" >src/generated_versions_list.zig echo "" >>src/generated_versions_list.zig -echo "pub const webkit = \"$WEBKIT_VERSION\";" >>src/generated_versions_list.zig -echo "pub const mimalloc = \"$MIMALLOC_VERSION\";" >>src/generated_versions_list.zig +echo "pub const boringssl = \"$BORINGSSL_VERSION\";" >>src/generated_versions_list.zig echo "pub const libarchive = \"$LIBARCHIVE_VERSION\";" >>src/generated_versions_list.zig +echo "pub const mimalloc = \"$MIMALLOC_VERSION\";" >>src/generated_versions_list.zig echo "pub const picohttpparser = \"$PICOHTTPPARSER_VERSION\";" >>src/generated_versions_list.zig -echo "pub const boringssl = \"$BORINGSSL_VERSION\";" >>src/generated_versions_list.zig -echo "pub const zlib = \"$ZLIB_VERSION\";" >>src/generated_versions_list.zig +echo "pub const uws = \"$UWS_VERSION\";" >>src/generated_versions_list.zig +echo "pub const webkit = \"$WEBKIT_VERSION\";" >>src/generated_versions_list.zig echo "pub const zig = @import(\"std\").fmt.comptimePrint(\"{}\", .{@import(\"builtin\").zig_version});" >>src/generated_versions_list.zig +echo "pub const zlib = \"$ZLIB_VERSION\";" >>src/generated_versions_list.zig echo "" >>src/generated_versions_list.zig zig fmt src/generated_versions_list.zig diff --git a/src/deps/_libusockets.h b/src/deps/_libusockets.h index 5456ff921..7042d3d14 100644 --- a/src/deps/_libusockets.h +++ b/src/deps/_libusockets.h @@ -282,6 +282,15 @@ void uws_loop_addPreHandler(us_loop_t *loop, void *key, void uws_loop_removePreHandler(us_loop_t *loop, void *ctx_); void uws_loop_defer(us_loop_t *loop, void *ctx, void (*cb)(void *ctx)); +typedef struct StringPointer { + uint32_t off; + uint32_t len; +} StringPointer; + +void uws_res_write_headers(int ssl, uws_res_t *res, const StringPointer *names, + const StringPointer *values, size_t count, + const char *buf); + #ifdef __cplusplus } #endif diff --git a/src/deps/libuwsockets.cpp b/src/deps/libuwsockets.cpp index ec082db43..132a22be8 100644 --- a/src/deps/libuwsockets.cpp +++ b/src/deps/libuwsockets.cpp @@ -981,4 +981,34 @@ void uws_loop_defer(us_loop_t *loop, void *ctx, void (*cb)(void *ctx)) { uWS::Loop *uwsLoop = (uWS::Loop *)loop; uwsLoop->defer([ctx, cb]() { cb(ctx); }); } + +void uws_res_write_headers(int ssl, uws_res_t *res, const StringPointer *names, + const StringPointer *values, size_t count, + const char *buf) { + if (ssl) { + uWS::HttpResponse<true> *uwsRes = (uWS::HttpResponse<true> *)res; + for (size_t i = 0; i < count; i++) { + uwsRes->writeHeader(std::string_view(&buf[names[i].off], names[i].len), + std::string_view(&buf[values[i].off], values[i].len)); + } + + } else { + uWS::HttpResponse<false> *uwsRes = (uWS::HttpResponse<false> *)res; + for (size_t i = 0; i < count; i++) { + uwsRes->writeHeader(std::string_view(&buf[names[i].off], names[i].len), + std::string_view(&buf[values[i].off], values[i].len)); + } + } +} + +void uws_res_cork(int ssl, uws_res_t *res, void *ctx, + void (*corker)(void *ctx)) { + if (ssl) { + uWS::HttpResponse<true> *uwsRes = (uWS::HttpResponse<true> *)res; + uwsRes->cork([ctx, corker]() { corker(ctx); }); + } else { + uWS::HttpResponse<false> *uwsRes = (uWS::HttpResponse<false> *)res; + uwsRes->cork([ctx, corker]() { corker(ctx); }); + } +} }
\ No newline at end of file diff --git a/src/deps/uws.zig b/src/deps/uws.zig index fc7a32865..30e23b39a 100644 --- a/src/deps/uws.zig +++ b/src/deps/uws.zig @@ -1,3 +1,4 @@ +const Api = @import("../api/schema.zig").Api; pub const u_int8_t = u8; pub const u_int16_t = c_ushort; pub const u_int32_t = c_uint; @@ -497,6 +498,38 @@ pub fn NewApp(comptime ssl: bool) type { uws_res_on_data(ssl_flag, res.downcast(), Wrapper.handle, opcional_data); } + + pub fn runCorked( + res: *Response, + comptime UserDataType: type, + comptime handler: fn (UserDataType) void, + opcional_data: UserDataType, + ) void { + const Wrapper = struct { + pub fn handle(user_data: ?*anyopaque) callconv(.C) void { + if (comptime UserDataType == void) { + @call(.{ .modifier = .always_inline }, handler, .{ + void{}, + }); + } else { + @call(.{ .modifier = .always_inline }, handler, .{ + @ptrCast(UserDataType, @alignCast(@alignOf(UserDataType), user_data.?)), + }); + } + } + }; + + uws_res_cork(ssl_flag, res.downcast(), opcional_data, Wrapper.handle); + } + + pub fn writeHeaders( + res: *Response, + names: []const Api.StringPointer, + values: []const Api.StringPointer, + buf: []const u8, + ) void { + uws_res_write_headers(ssl_flag, res.downcast(), names.ptr, values.ptr, values.len, buf.ptr); + } }; }; } @@ -578,7 +611,8 @@ extern fn uws_res_upgrade( sec_web_socket_extensions_length: usize, ws: ?*uws_socket_context_t, ) void; - +extern fn uws_res_cork(c_int, res: *uws_res, ctx: *anyopaque, corker: fn (?*anyopaque) callconv(.C) void) void; +extern fn uws_res_write_headers(c_int, res: *uws_res, names: [*]const Api.StringPointer, values: [*]const Api.StringPointer, count: usize, buf: [*]const u8) void; pub const LIBUS_RECV_BUFFER_LENGTH = @import("std").zig.c_translation.promoteIntLiteral(c_int, 524288, .decimal); pub const LIBUS_TIMEOUT_GRANULARITY = @as(c_int, 4); pub const LIBUS_RECV_BUFFER_PADDING = @as(c_int, 32); diff --git a/src/javascript/jsc/api/html_rewriter.zig b/src/javascript/jsc/api/html_rewriter.zig index c62a6503f..e314fc18d 100644 --- a/src/javascript/jsc/api/html_rewriter.zig +++ b/src/javascript/jsc/api/html_rewriter.zig @@ -216,7 +216,7 @@ pub const HTMLRewriter = struct { response.cloneInto(result, getAllocator(global.ref())); this.finalizeWithoutDestroy(); - return JSValue.fromRef(Response.Class.make(global.ref(), result)); + return JSValue.fromRef(Response.makeMaybePooled(global.ref(), result)); } var new_context = this.context; @@ -311,7 +311,7 @@ pub const HTMLRewriter = struct { }; return JSC.JSValue.fromRef( - Response.Class.make(sink.global.ref(), sink.response), + Response.makeMaybePooled(sink.global.ref(), sink.response), ); } @@ -327,7 +327,7 @@ pub const HTMLRewriter = struct { if (prev_value.Locked.promise) |promise| { prev_value.Locked.promise = null; promise.asInternalPromise().?.resolve(this.global, JSC.JSValue.fromRef( - Response.Class.make( + Response.makeMaybePooled( this.global.ref(), this.response, ), diff --git a/src/javascript/jsc/api/server.zig b/src/javascript/jsc/api/server.zig index 4b7805adb..8c64c112a 100644 --- a/src/javascript/jsc/api/server.zig +++ b/src/javascript/jsc/api/server.zig @@ -92,6 +92,7 @@ pub fn NewServer(comptime ssl_enabled: bool) type { app: *App = undefined, globalThis: *JSGlobalObject, default_server: URL = URL{ .host = "localhost", .port = "3000" }, + response_objects_pool: JSC.WebCore.Response.Pool = JSC.WebCore.Response.Pool{}, request_pool_allocator: std.mem.Allocator = undefined, @@ -116,6 +117,7 @@ pub fn NewServer(comptime ssl_enabled: bool) type { this.listener = socket; VirtualMachine.vm.uws_event_loop = uws.Loop.get(); + VirtualMachine.vm.response_objects_pool = &this.response_objects_pool; this.app.run(); } @@ -127,6 +129,7 @@ pub fn NewServer(comptime ssl_enabled: bool) type { method: HTTP.Method, aborted: bool = false, response_jsvalue: JSC.JSValue = JSC.JSValue.zero, + response_ptr: ?*JSC.WebCore.Response = null, blob: JSC.WebCore.Blob = JSC.WebCore.Blob{}, promise: ?*JSC.JSValue = null, response_headers: ?*JSC.WebCore.Headers.RefCountedHeaders = null, @@ -181,14 +184,13 @@ pub fn NewServer(comptime ssl_enabled: bool) type { .method = HTTP.Method.which(req.method()) orelse .GET, .server = server, }; - resp.onAborted(*RequestContext, onAbort, this); } pub fn onAbort(this: *RequestContext, _: *App.Response) void { this.aborted = true; this.req = undefined; if (!this.response_jsvalue.isEmpty()) { - JSC.C.JSValueUnprotect(this.server.globalThis.ref(), this.response_jsvalue.asObjectRef()); + this.server.response_objects_pool.push(this.server.globalThis, this.response_jsvalue); this.response_jsvalue = JSC.JSValue.zero; } } @@ -196,7 +198,7 @@ pub fn NewServer(comptime ssl_enabled: bool) type { pub fn finalize(this: *RequestContext) void { this.blob.detach(); if (!this.response_jsvalue.isEmpty()) { - JSC.C.JSValueUnprotect(this.server.globalThis.ref(), this.response_jsvalue.asObjectRef()); + this.server.response_objects_pool.push(this.server.globalThis, this.response_jsvalue); this.response_jsvalue = JSC.JSValue.zero; } @@ -213,11 +215,11 @@ pub fn NewServer(comptime ssl_enabled: bool) type { this.server.request_pool_allocator.destroy(this); } - pub fn render(this: *RequestContext, response: *JSC.WebCore.Response) void { + pub fn doRender(this: *RequestContext) void { if (this.aborted) { return; } - + var response = this.response_ptr.?; this.blob = response.body.use(); const status = response.statusCode(); @@ -236,9 +238,7 @@ pub fn NewServer(comptime ssl_enabled: bool) type { this.resp.writeStatus(std.fmt.bufPrint(&status_text_buf, "{d} HM", .{response.body.init.status_code}) catch unreachable); } - for (names) |name, i| { - this.resp.writeHeader(headers.asStr(name), headers.asStr(values[i])); - } + this.resp.writeHeaders(names, values, headers.buf.items); } if (status == 302 or status == 202 or this.blob.size == 0) { @@ -250,6 +250,12 @@ pub fn NewServer(comptime ssl_enabled: bool) type { this.resp.end(this.blob.sharedView(), false); this.finalize(); } + + pub fn render(this: *RequestContext, response: *JSC.WebCore.Response) void { + this.response_ptr = response; + this.resp.runCorked(*RequestContext, doRender, this); + this.response_ptr = null; + } }; pub fn onRequest(this: *ThisServer, req: *uws.Request, resp: *App.Response) void { @@ -286,6 +292,7 @@ pub fn NewServer(comptime ssl_enabled: bool) type { } if (ctx.response_jsvalue.jsTypeLoose() == .JSPromise) { + resp.onAborted(*RequestContext, RequestContext.onAbort, ctx); JSC.VirtualMachine.vm.tick(); ctx.response_jsvalue.then( diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig index 4a8fefd3c..d0dcb9ddc 100644 --- a/src/javascript/jsc/javascript.zig +++ b/src/javascript/jsc/javascript.zig @@ -480,7 +480,7 @@ pub const VirtualMachine = struct { ref_strings: JSC.RefString.Map = undefined, source_mappings: SavedSourceMap = undefined, - + response_objects_pool: ?*Response.Pool = null, pub inline fn eventLoop(this: *VirtualMachine) *EventLoop { return this.event_loop; } diff --git a/src/javascript/jsc/webcore/response.zig b/src/javascript/jsc/webcore/response.zig index 778b22171..6e9859352 100644 --- a/src/javascript/jsc/webcore/response.zig +++ b/src/javascript/jsc/webcore/response.zig @@ -42,6 +42,42 @@ const JSPrinter = @import("../../../js_printer.zig"); const picohttp = @import("picohttp"); const StringJoiner = @import("../../../string_joiner.zig"); pub const Response = struct { + pub const Pool = struct { + response_objects_pool: [127]JSC.C.JSObjectRef = undefined, + response_objects_used: u8 = 0, + + pub fn get(this: *Pool, ptr: *Response) ?JSC.C.JSObjectRef { + if (this.response_objects_used > 0) { + var result = this.response_objects_pool[this.response_objects_used - 1]; + this.response_objects_used -= 1; + if (JSC.C.JSObjectSetPrivate(result, JSPrivateDataPtr.init(ptr).ptr())) { + return result; + } else { + JSC.C.JSValueUnprotect(VirtualMachine.vm.global.ref(), result); + } + } + + return null; + } + + pub fn push(this: *Pool, globalThis: *JSC.JSGlobalObject, object: JSC.JSValue) void { + var remaining = this.response_objects_pool[@minimum(this.response_objects_used, this.response_objects_pool.len)..]; + if (remaining.len == 0) { + JSC.C.JSValueUnprotect(globalThis.ref(), object.asObjectRef()); + return; + } + + if (object.as(Response)) |resp| { + _ = JSC.C.JSObjectSetPrivate(object.asObjectRef(), null); + + _ = resp.body.use(); + resp.finalize(); + remaining[0] = object.asObjectRef(); + this.response_objects_used += 1; + } + } + }; + pub const Constructor = JSC.NewConstructor( Response, .{ @@ -262,7 +298,18 @@ pub const Response = struct { _: js.ExceptionRef, ) js.JSValueRef { var cloned = this.clone(getAllocator(ctx)); - return Response.Class.make(ctx, cloned); + return Response.makeMaybePooled(ctx, cloned); + } + + pub fn makeMaybePooled(ctx: js.JSContextRef, ptr: *Response) JSC.C.JSObjectRef { + if (JSC.VirtualMachine.vm.response_objects_pool) |pool| { + if (pool.get(ptr)) |object| { + JSC.C.JSValueUnprotect(ctx, object); + return object; + } + } + + return Response.Class.make(ctx, ptr); } pub fn cloneInto(this: *const Response, new_response: *Response, allocator: std.mem.Allocator) void { @@ -410,7 +457,7 @@ pub const Response = struct { var ptr = response.allocator.create(Response) catch unreachable; ptr.* = response; - return Response.Class.make(ctx, ptr); + return Response.makeMaybePooled(ctx, ptr); } pub fn constructRedirect( _: void, @@ -462,7 +509,7 @@ pub const Response = struct { var ptr = response.allocator.create(Response) catch unreachable; ptr.* = response; - return Response.Class.make(ctx, ptr); + return Response.makeMaybePooled(ctx, ptr); } pub fn constructError( _: void, @@ -485,7 +532,7 @@ pub const Response = struct { .url = "", }; - return Response.Class.make( + return Response.makeMaybePooled( ctx, response, ); @@ -522,7 +569,7 @@ pub const Response = struct { .allocator = getAllocator(ctx), .url = "", }; - return Response.Class.make( + return Response.makeMaybePooled( ctx, response, ); @@ -753,7 +800,7 @@ pub const Fetch = struct { }, }, }; - return JSValue.fromRef(Response.Class.make(@ptrCast(js.JSContextRef, this.global_this), response)); + return JSValue.fromRef(Response.makeMaybePooled(@ptrCast(js.JSContextRef, this.global_this), response)); } pub fn get( |