diff options
-rw-r--r-- | src/deps/_libusockets.h | 2 | ||||
-rw-r--r-- | src/deps/libuwsockets.cpp | 37 | ||||
-rw-r--r-- | src/deps/uws.zig | 15 | ||||
-rw-r--r-- | src/javascript/jsc/api/server.zig | 42 | ||||
-rw-r--r-- | src/javascript/jsc/webcore/response.zig | 68 |
5 files changed, 134 insertions, 30 deletions
diff --git a/src/deps/_libusockets.h b/src/deps/_libusockets.h index 3b8c61f74..e8a510392 100644 --- a/src/deps/_libusockets.h +++ b/src/deps/_libusockets.h @@ -295,6 +295,8 @@ void *uws_res_get_native_handle(int ssl, uws_res_t *res); void uws_res_uncork(int ssl, uws_res_t *res); void uws_res_set_write_offset(int ssl, uws_res_t *res, size_t off); void us_socket_mark_needs_more_not_ssl(uws_res_t *res); +void uws_req_clone_headers(uws_req_t *req_, void *ctx); + #ifdef __cplusplus } #endif diff --git a/src/deps/libuwsockets.cpp b/src/deps/libuwsockets.cpp index 39e0e27fc..1bc370b17 100644 --- a/src/deps/libuwsockets.cpp +++ b/src/deps/libuwsockets.cpp @@ -897,7 +897,7 @@ void uws_req_set_field(uws_req_t *res, bool yield) { size_t uws_req_get_url(uws_req_t *res, const char **dest) { uWS::HttpRequest *uwsReq = (uWS::HttpRequest *)res; - std::string_view value = uwsReq->getUrl(); + std::string_view value = uwsReq->getFullUrl(); *dest = value.data(); return value.length(); } @@ -1005,12 +1005,43 @@ void uws_res_write_headers(int ssl, uws_res_t *res, const StringPointer *names, } } +extern "C" void Headers__preallocate(void *ctx, size_t len, + size_t header_count); +extern "C" void Headers__appendHeaderNormalized(void *ctx, const char *name, + size_t name_length, + const char *value, + size_t value_length); + +void uws_req_clone_headers(uws_req_t *req_, void *ctx) { + + size_t buffer_len = 0; + size_t header_count = 0; + + uWS::HttpRequest *req = (uWS::HttpRequest *)req_; + uWS::HttpRequest iterator = *req; + + for (const auto &header : iterator) { + buffer_len += header.first.length() + header.second.length(); + header_count++; + } + + Headers__preallocate(ctx, buffer_len, header_count); + + for (const auto &header : iterator) { + Headers__appendHeaderNormalized(ctx, header.first.data(), + header.first.length(), header.second.data(), + header.second.length()); + } +} + void uws_res_uncork(int ssl, uws_res_t *res) { // if (ssl) { - // uWS::HttpResponse<true> *uwsRes = (uWS::HttpResponse<true> *)res; + // uWS::HttpResponse<true> *uwsRes = + // (uWS::HttpResponse<true> *)res; // uwsRes->uncork(); // } else { - // uWS::HttpResponse<false> *uwsRes = (uWS::HttpResponse<false> *)res; + // uWS::HttpResponse<false> *uwsRes = + // (uWS::HttpResponse<false> *)res; // uwsRes->uncork(); // } } diff --git a/src/deps/uws.zig b/src/deps/uws.zig index a9753406e..2f590606c 100644 --- a/src/deps/uws.zig +++ b/src/deps/uws.zig @@ -170,6 +170,21 @@ pub const Request = opaque { var ptr: [*]const u8 = undefined; return ptr[0..req.uws_req_get_parameter(@intCast(c_ushort, index), &ptr)]; } + + pub fn cloneHeaders( + req: *Request, + ctx: *anyopaque, + ) void { + uws_req_clone_headers(req, ctx); + } + + extern fn uws_req_clone_headers( + req_: *Request, + ctx: *anyopaque, + //preallocate: fn (ctx: ?*anyopaque, len: usize, header_count: usize) callconv(.C) void, + //append: fn (ctx: ?*anyopaque, name: [*]const u8, name_length: usize, value: [*]const u8, value_length: usize) callconv(.C) void, + ) void; + extern fn uws_req_is_ancient(res: *Request) bool; extern fn uws_req_get_yield(res: *Request) bool; extern fn uws_req_set_field(res: *Request, yield: bool) void; diff --git a/src/javascript/jsc/api/server.zig b/src/javascript/jsc/api/server.zig index 1360f4d52..d7e7ccd64 100644 --- a/src/javascript/jsc/api/server.zig +++ b/src/javascript/jsc/api/server.zig @@ -87,6 +87,12 @@ const SendfileContext = struct { has_listener: bool = false, has_set_on_writable: bool = false, }; +const linux = std.os.linux; + +pub const ServerConfig = struct { + port: u16 = 0, + hostnames: std.ArrayList([*:0]const u8) = .{}, +}; pub fn NewServer(comptime ssl_enabled: bool) type { return struct { @@ -145,6 +151,7 @@ pub fn NewServer(comptime ssl_enabled: bool) type { has_abort_handler: bool = false, has_sendfile_ctx: bool = false, sendfile: SendfileContext = undefined, + request_js_object: JSC.C.JSObjectRef = null, pub threadlocal var pool: *RequestContextStackAllocator = undefined; pub fn setAbortHandler(this: *RequestContext) void { @@ -219,6 +226,14 @@ pub fn NewServer(comptime ssl_enabled: bool) type { this.response_jsvalue = JSC.JSValue.zero; } + if (this.request_js_object != null) { + if (JSC.JSValue.fromRef(this.request_js_object).as(Request)) |req| { + req.uws_request = null; + JSC.C.JSValueUnprotect(this.server.globalThis.ref(), this.request_js_object); + this.request_js_object = null; + } + } + if (this.promise != null) { JSC.C.JSValueUnprotect(this.server.globalThis.ref(), this.promise.?.asObjectRef()); this.promise = null; @@ -281,10 +296,11 @@ pub fn NewServer(comptime ssl_enabled: bool) type { var signed_offset = @intCast(i64, this.sendfile.offset); const start = this.sendfile.offset; const val = - std.os.linux.sendfile(this.sendfile.socket_fd, this.sendfile.fd, &signed_offset, this.sendfile.remain); + // this does the syscall directly, without libc + linux.sendfile(this.sendfile.socket_fd, this.sendfile.fd, &signed_offset, this.sendfile.remain); this.sendfile.offset = @intCast(Blob.SizeType, signed_offset); - const errcode = std.os.linux.getErrno(val); + const errcode = linux.getErrno(val); this.sendfile.remain -= @intCast(Blob.SizeType, this.sendfile.offset - start); @@ -300,17 +316,6 @@ pub fn NewServer(comptime ssl_enabled: bool) type { var sbytes: std.os.off_t = adjusted_count; const signed_offset = @bitCast(i64, @as(u64, this.sendfile.offset)); - // var sf_hdr_trailer: std.os.darwin.sf_hdtr = .{ - // .headers = &separator_iovec, - // .hdr_cnt = 1, - // .trailers = undefined, - // .trl_cnt = 0, - // }; - // const headers = if (this.sendfile.offset == 0) - // &sf_hdr_trailer - // else - // null; - const errcode = std.c.getErrno(std.c.sendfile( this.sendfile.fd, this.sendfile.socket_fd, @@ -337,8 +342,9 @@ pub fn NewServer(comptime ssl_enabled: bool) type { this.sendfile.has_set_on_writable = true; this.resp.onWritable(*RequestContext, onWritableSendfile, this); } - if (comptime !ssl_enabled) - this.resp.markNeedsMore(); + + this.resp.markNeedsMore(); + return true; } @@ -415,6 +421,7 @@ pub fn NewServer(comptime ssl_enabled: bool) type { } var response = this.response_ptr.?; var body = &response.body; + this.blob = response.body.use(); if (body.value == .Error) { this.resp.writeStatus("500 Internal Server Error"); @@ -428,7 +435,6 @@ pub fn NewServer(comptime ssl_enabled: bool) type { if (body.value == .Blob) { if (body.value.Blob.needsToReadFile()) { - this.blob = response.body.use(); this.req.setYield(false); this.setAbortHandler(); if (!this.has_sendfile_ctx) this.renderSendFile(this.blob); @@ -474,8 +480,12 @@ pub fn NewServer(comptime ssl_enabled: bool) type { request_object.* = .{ .url = JSC.ZigString.init(ctx.url), .method = ctx.method, + .uws_request = req, }; + // We keep the Request object alive for the duration of the request so that we can remove the pointer to the UWS request object. var args = [_]JSC.C.JSValueRef{JSC.WebCore.Request.Class.make(this.globalThis.ref(), request_object)}; + ctx.request_js_object = args[0]; + JSC.C.JSValueProtect(this.globalThis.ref(), args[0]); ctx.response_jsvalue = JSC.C.JSObjectCallAsFunctionReturnValue(this.globalThis.ref(), this.callback.asObjectRef(), null, 1, &args); defer JSC.VirtualMachine.vm.tick(); if (ctx.aborted) { diff --git a/src/javascript/jsc/webcore/response.zig b/src/javascript/jsc/webcore/response.zig index 0c9e71a85..b9cfdc415 100644 --- a/src/javascript/jsc/webcore/response.zig +++ b/src/javascript/jsc/webcore/response.zig @@ -42,6 +42,7 @@ const Task = @import("../javascript.zig").Task; const JSPrinter = @import("../../../js_printer.zig"); const picohttp = @import("picohttp"); const StringJoiner = @import("../../../string_joiner.zig"); +const uws = @import("uws"); pub const Response = struct { pub const Pool = struct { response_objects_pool: [127]JSC.C.JSObjectRef = undefined, @@ -1043,6 +1044,24 @@ pub const Headers = struct { headers.entries.deinit(headers.allocator); } + pub fn clonefromUWSRequest(headers: *Headers, req: *uws.Request) void { + req.cloneHeaders(headers); + } + + pub fn preallocate(this: *Headers, buf_size: usize, header_count: usize) callconv(.C) void { + this.buf.ensureTotalCapacityPrecise(this.allocator, buf_size) catch unreachable; + this.entries.ensureTotalCapacity(this.allocator, header_count) catch unreachable; + } + + comptime { + if (!JSC.is_bindgen) { + // Initially, these were function pointers + // However, some Zig Stage1 C ABI bug causes EXC_BAD_ACCESS + @export(preallocate, .{ .name = "Headers__preallocate" }); + @export(appendHeaderNormalizedC, .{ .name = "Headers__appendHeaderNormalized" }); + } + } + pub fn empty(allocator: std.mem.Allocator) Headers { return Headers{ .entries = .{}, @@ -1486,6 +1505,16 @@ pub const Headers = struct { return null; } + pub fn appendHeaderNormalizedC( + headers: *Headers, + key: [*]const u8, + key_len: usize, + value: [*]const u8, + value_len: usize, + ) callconv(.C) void { + headers.appendHeader(key[0..key_len], value[0..value_len], false, false); + } + pub fn appendHeader( headers: *Headers, key: string, @@ -4318,6 +4347,7 @@ pub const Request = struct { headers: ?*Headers.RefCountedHeaders = null, body: Body.Value = Body.Value{ .Empty = .{} }, method: Method = Method.GET, + uws_request: ?*uws.Request = null, pub fn fromRequestContext(ctx: *RequestContext) !Request { var req = Request{ @@ -4367,17 +4397,24 @@ pub const Request = struct { .name = "Request", .read_only = true, }, - .{ .finalize = finalize, .text = .{ - .rfn = Request.getText, - }, .json = .{ - .rfn = Request.getJSON, - }, .arrayBuffer = .{ - .rfn = Request.getArrayBuffer, - }, .blob = .{ - .rfn = Request.getBlob, - }, .clone = .{ - .rfn = Request.doClone, - } }, + .{ + .finalize = finalize, + .text = .{ + .rfn = Request.getText, + }, + .json = .{ + .rfn = Request.getJSON, + }, + .arrayBuffer = .{ + .rfn = Request.getArrayBuffer, + }, + .blob = .{ + .rfn = Request.getBlob, + }, + .clone = .{ + .rfn = Request.doClone, + }, + }, .{ .@"cache" = .{ .@"get" = getCache, @@ -4636,6 +4673,11 @@ pub const Request = struct { ) js.JSValueRef { if (this.headers == null) { this.headers = Headers.RefCountedHeaders.init(Headers.empty(bun.default_allocator), bun.default_allocator) catch unreachable; + + if (this.uws_request) |req| { + this.headers.?.value.allocator = bun.default_allocator; + this.headers.?.value.clonefromUWSRequest(req); + } } return Headers.Class.make(ctx, this.headers.?.getRef()); @@ -4645,11 +4687,15 @@ pub const Request = struct { req.* = Request{ .body = this.body.clone(allocator), .url = ZigString.init(allocator.dupe(u8, this.url.slice()) catch unreachable), + .method = this.method, }; if (this.headers) |head| { var new_headers = Headers.RefCountedHeaders.init(undefined, allocator) catch unreachable; head.leak().clone(&new_headers.value) catch unreachable; req.headers = new_headers; + } else if (this.uws_request) |uws_req| { + req.headers = Headers.RefCountedHeaders.init(Headers.empty(allocator), allocator) catch unreachable; + req.headers.?.value.clonefromUWSRequest(uws_req); } } |