aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-03-23 19:54:13 -0700
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-03-23 19:54:13 -0700
commitdead6e3afcf62f25c9b760b8c3f6f3951e2e10db (patch)
tree21e087f291ae85663ed37d106aeadc77a7a2780c /src
parentdc3d0c41fec16206300d529ebade26a8f3409bb0 (diff)
downloadbun-dead6e3afcf62f25c9b760b8c3f6f3951e2e10db.tar.gz
bun-dead6e3afcf62f25c9b760b8c3f6f3951e2e10db.tar.zst
bun-dead6e3afcf62f25c9b760b8c3f6f3951e2e10db.zip
Support `Request.headers` and `Request.url` in http server
Diffstat (limited to 'src')
-rw-r--r--src/deps/_libusockets.h2
-rw-r--r--src/deps/libuwsockets.cpp37
-rw-r--r--src/deps/uws.zig15
-rw-r--r--src/javascript/jsc/api/server.zig42
-rw-r--r--src/javascript/jsc/webcore/response.zig68
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);
}
}