diff options
author | 2022-03-23 22:53:22 -0700 | |
---|---|---|
committer | 2022-03-23 22:53:22 -0700 | |
commit | f105a2fea731d8ee93e11837deaad9adaf30255c (patch) | |
tree | 41e5939828ddde143961bef0723fe592f4a09e46 | |
parent | 6d230fc93ed77718dc917d0fccfa32bf61912390 (diff) | |
download | bun-f105a2fea731d8ee93e11837deaad9adaf30255c.tar.gz bun-f105a2fea731d8ee93e11837deaad9adaf30255c.tar.zst bun-f105a2fea731d8ee93e11837deaad9adaf30255c.zip |
handle bodies of 0 length better
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | src/deps/_libusockets.h | 3 | ||||
-rw-r--r-- | src/deps/libuwsockets.cpp | 13 | ||||
-rw-r--r-- | src/deps/uws.zig | 9 | ||||
-rw-r--r-- | src/javascript/jsc/api/server.zig | 68 | ||||
-rw-r--r-- | src/javascript/jsc/webcore/response.zig | 33 |
6 files changed, 86 insertions, 42 deletions
@@ -437,7 +437,7 @@ build-obj-wasm-small: build-obj-safe: $(ZIG) build obj -Drelease-safe -UWS_CC_FLAGS = -pthread -DLIBUS_USE_OPENSSL=1 -DLIBUS_USE_BORINGSSL=1 -DWITH_BORINGSSL=1 -Wpedantic -Wall -Wextra -Wsign-conversion -Wconversion -Isrc -IuSockets/src -DUWS_WITH_PROXY +UWS_CC_FLAGS = -pthread -DUWS_HTTPRESPONSE_NO_WRITEMARK=1 -DLIBUS_USE_OPENSSL=1 -DLIBUS_USE_BORINGSSL=1 -DWITH_BORINGSSL=1 -Wpedantic -Wall -Wextra -Wsign-conversion -Wconversion -Isrc -IuSockets/src -DUWS_WITH_PROXY UWS_CXX_FLAGS = $(UWS_CC_FLAGS) -std=gnu++17 UWS_LDFLAGS = -I$(BUN_DEPS_DIR)/boringssl/include diff --git a/src/deps/_libusockets.h b/src/deps/_libusockets.h index e8a510392..f0825e294 100644 --- a/src/deps/_libusockets.h +++ b/src/deps/_libusockets.h @@ -296,7 +296,8 @@ 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); - +bool uws_res_try_end(int ssl, uws_res_t *res, const char *bytes, size_t len, + size_t total_len); #ifdef __cplusplus } #endif diff --git a/src/deps/libuwsockets.cpp b/src/deps/libuwsockets.cpp index 1bc370b17..47f0c0a77 100644 --- a/src/deps/libuwsockets.cpp +++ b/src/deps/libuwsockets.cpp @@ -795,12 +795,10 @@ void uws_res_write_header_int(int ssl, uws_res_t *res, const char *key, void uws_res_end_without_body(int ssl, uws_res_t *res) { if (ssl) { uWS::HttpResponse<true> *uwsRes = (uWS::HttpResponse<true> *)res; - uwsRes->setWriteOffset(0); uwsRes->endWithoutBody(0); } else { uWS::HttpResponse<false> *uwsRes = (uWS::HttpResponse<false> *)res; - uwsRes->setWriteOffset(0); uwsRes->endWithoutBody(0); } } @@ -1074,6 +1072,17 @@ void uws_res_cork(int ssl, uws_res_t *res, void *ctx, } } +bool uws_res_try_end(int ssl, uws_res_t *res, const char *bytes, size_t len, + size_t total_len) { + if (ssl) { + uWS::HttpResponse<true> *uwsRes = (uWS::HttpResponse<true> *)res; + return uwsRes->tryEnd(std::string_view(bytes, len), total_len).first; + } else { + uWS::HttpResponse<false> *uwsRes = (uWS::HttpResponse<false> *)res; + return uwsRes->tryEnd(std::string_view(bytes, len), total_len).first; + } +} + void *uws_res_get_native_handle(int ssl, uws_res_t *res) { if (ssl) { uWS::HttpResponse<true> *uwsRes = (uWS::HttpResponse<true> *)res; diff --git a/src/deps/uws.zig b/src/deps/uws.zig index 792c11aa2..2769373f5 100644 --- a/src/deps/uws.zig +++ b/src/deps/uws.zig @@ -428,6 +428,10 @@ pub fn NewApp(comptime ssl: bool) type { uws_res_end(ssl_flag, res.downcast(), data.ptr, data.len, close_connection); } + pub fn tryEnd(res: *Response, data: []const u8, total: usize) bool { + return uws_res_try_end(ssl_flag, res.downcast(), data.ptr, data.len, total); + } + pub fn uncork(_: *Response) void { // uws_res_uncork( // ssl_flag, @@ -522,14 +526,14 @@ pub fn NewApp(comptime ssl: bool) type { @call(.{ .modifier = .always_inline }, handler, .{ void{}, castRes(this), - chunk_ptr[0..len], + if (len > 0) chunk_ptr[0..len] else "", last, }); } else { @call(.{ .modifier = .always_inline }, handler, .{ @ptrCast(UserDataType, @alignCast(@alignOf(UserDataType), user_data.?)), castRes(this), - chunk_ptr[0..len], + if (len > 0) chunk_ptr[0..len] else "", last, }); } @@ -673,6 +677,7 @@ extern fn uws_ws_get_remote_address_as_text(ssl: c_int, ws: ?*uws_websocket_t, d const uws_res = opaque {}; extern fn uws_res_uncork(ssl: c_int, res: *uws_res) void; extern fn uws_res_end(ssl: c_int, res: *uws_res, data: [*c]const u8, length: usize, close_connection: bool) void; +extern fn uws_res_try_end(ssl: c_int, res: *uws_res, data: [*c]const u8, length: usize, total: usize) bool; extern fn uws_res_pause(ssl: c_int, res: *uws_res) void; extern fn uws_res_resume(ssl: c_int, res: *uws_res) void; extern fn uws_res_write_continue(ssl: c_int, res: *uws_res) void; diff --git a/src/javascript/jsc/api/server.zig b/src/javascript/jsc/api/server.zig index 15afe86e5..7a1cfad23 100644 --- a/src/javascript/jsc/api/server.zig +++ b/src/javascript/jsc/api/server.zig @@ -277,7 +277,6 @@ pub fn NewServer(comptime ssl_enabled: bool) type { const names = entries.items(.name); const values = entries.items(.value); - this.resp.writeHeaderInt("content-length", this.blob.size); this.resp.writeHeaders(names, values, headers.buf.items); } @@ -366,6 +365,22 @@ pub fn NewServer(comptime ssl_enabled: bool) type { return true; } + pub fn onWritableBytes(this: *RequestContext, write_offset: c_ulong, resp: *App.Response) callconv(.C) bool { + if (this.aborted) + return false; + + var bytes = this.blob.sharedView(); + + bytes = bytes[@minimum(bytes.len, @truncate(usize, write_offset))..]; + if (resp.tryEnd(bytes, this.blob.size)) { + this.finalize(); + return true; + } else { + this.resp.onWritable(*RequestContext, onWritableBytes, this); + return true; + } + } + pub fn onWritableSendfile(this: *RequestContext, _: c_ulong, _: *App.Response) callconv(.C) bool { return this.onSendfile(); } @@ -389,18 +404,6 @@ pub fn NewServer(comptime ssl_enabled: bool) type { return; }; this.blob.size = size_; - const code = this.response_ptr.?.statusCode(); - if (size_ == 0 and code >= 200 and code < 300) { - this.writeStatus(204); - } else { - this.writeStatus(code); - } - - if (this.response_ptr.?.body.init.headers) |headers_| { - this.writeHeaders(headers_); - } else { - this.resp.writeHeaderInt("content-length", size_); - } this.sendfile = .{ .fd = fd, @@ -408,6 +411,8 @@ pub fn NewServer(comptime ssl_enabled: bool) type { .socket_fd = this.resp.getNativeHandle(), }; + this.resp.runCorked(*RequestContext, renderMetadata, this); + if (size_ == 0) { this.cleanupAfterSendfile(); this.finalize(); @@ -460,31 +465,47 @@ pub fn NewServer(comptime ssl_enabled: bool) type { } } - this.renderBytes(response); + if (this.has_abort_handler) + this.resp.runCorked(*RequestContext, renderMetadata, this) + else + this.renderMetadata(); + + this.renderBytes(); } - pub fn renderBytes(this: *RequestContext, response: *JSC.WebCore.Response) void { - const status = response.statusCode(); + pub fn renderMetadata(this: *RequestContext) void { + var response: *JSC.WebCore.Response = this.response_ptr.?; + var status = response.statusCode(); + const size = this.blob.size; + status = if (status == 200 and size == 0) + 204 + else + status; this.writeStatus(status); if (response.body.init.headers) |headers_| { this.writeHeaders(headers_); } + } - if (status == 302 or status == 202 or this.blob.size == 0) { - this.resp.endWithoutBody(); - this.finalize(); + pub fn renderBytes(this: *RequestContext) void { + const bytes = this.blob.sharedView(); + + if (!this.resp.tryEnd( + bytes, + bytes.len, + )) { + this.resp.onWritable(*RequestContext, onWritableBytes, this); return; } - 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.doRender(); } @@ -495,7 +516,10 @@ pub fn NewServer(comptime ssl_enabled: bool) type { var bytes = this.request_body_buf.toOwnedSlice(bun.default_allocator); var old = req.body; req.body = .{ - .Blob = Blob.init(bytes, bun.default_allocator, this.server.globalThis), + .Blob = if (bytes.len > 0) + Blob.init(bytes, bun.default_allocator, this.server.globalThis) + else + Blob.initEmpty(this.server.globalThis), }; old.resolve(&req.body, this.server.globalThis); VirtualMachine.vm.tick(); diff --git a/src/javascript/jsc/webcore/response.zig b/src/javascript/jsc/webcore/response.zig index 407d1fc6f..6123e72e2 100644 --- a/src/javascript/jsc/webcore/response.zig +++ b/src/javascript/jsc/webcore/response.zig @@ -3356,8 +3356,12 @@ pub const Blob = struct { } pub fn initWithAllASCII(bytes: []u8, allocator: std.mem.Allocator, globalThis: *JSGlobalObject, is_all_ascii: bool) Blob { - var store = Blob.Store.init(bytes, allocator) catch unreachable; - store.is_all_ascii = is_all_ascii; + // avoid allocating a Blob.Store if the buffer is actually empty + var store: ?*Blob.Store = null; + if (bytes.len > 0) { + store = Blob.Store.init(bytes, allocator) catch unreachable; + store.?.is_all_ascii = is_all_ascii; + } return Blob{ .size = @truncate(SizeType, bytes.len), .store = store, @@ -3371,7 +3375,10 @@ pub const Blob = struct { pub fn init(bytes: []u8, allocator: std.mem.Allocator, globalThis: *JSGlobalObject) Blob { return Blob{ .size = @truncate(SizeType, bytes.len), - .store = Blob.Store.init(bytes, allocator) catch unreachable, + .store = if (bytes.len > 0) + Blob.Store.init(bytes, allocator) catch unreachable + else + null, .allocator = null, .content_type = "", .globalThis = globalThis, @@ -3793,7 +3800,7 @@ pub const Blob = struct { => { var sliced = top_value.toSlice(global, bun.default_allocator); const is_all_ascii = !sliced.allocated; - if (!sliced.allocated) { + if (!sliced.allocated and sliced.len > 0) { sliced.ptr = @ptrCast([*]const u8, (try bun.default_allocator.dupe(u8, sliced.slice())).ptr); sliced.allocated = true; } @@ -4119,14 +4126,16 @@ pub const Body = struct { deinit: bool = false, action: Action = Action.none, - pub fn setPromise(value: *PendingValue, globalThis: *JSC.JSGlobalObject, action: Action) void { + pub fn setPromise(value: *PendingValue, globalThis: *JSC.JSGlobalObject, action: Action) JSValue { value.action = action; var promise = JSC.JSPromise.create(globalThis); - value.promise = promise.asValue(globalThis); + const promise_value = promise.asValue(globalThis); + value.promise = promise_value; if (value.onRequestData) |onRequestData| { value.onRequestData = null; onRequestData(value.task.?); } + return promise_value; } pub const Action = enum { @@ -4732,8 +4741,7 @@ fn BlobInterface(comptime Type: type) type { ) js.JSValueRef { var value = this.getBodyValue(); if (value.* == .Locked) { - value.Locked.setPromise(ctx.ptr(), .getText); - return value.Locked.promise.?.asObjectRef(); + return value.Locked.setPromise(ctx.ptr(), .getText).asObjectRef(); } var blob = this.body.use(); @@ -4750,8 +4758,7 @@ fn BlobInterface(comptime Type: type) type { ) js.JSValueRef { var value = this.getBodyValue(); if (value.* == .Locked) { - value.Locked.setPromise(ctx.ptr(), .getJSON); - return value.Locked.promise.?.asObjectRef(); + return value.Locked.setPromise(ctx.ptr(), .getJSON).asObjectRef(); } var blob = this.body.use(); @@ -4768,8 +4775,7 @@ fn BlobInterface(comptime Type: type) type { var value = this.getBodyValue(); if (value.* == .Locked) { - value.Locked.setPromise(ctx.ptr(), .getArrayBuffer); - return value.Locked.promise.?.asObjectRef(); + return value.Locked.setPromise(ctx.ptr(), .getArrayBuffer).asObjectRef(); } var blob = this.body.use(); @@ -4786,8 +4792,7 @@ fn BlobInterface(comptime Type: type) type { ) js.JSValueRef { var value = this.getBodyValue(); if (value.* == .Locked) { - value.Locked.setPromise(ctx.ptr(), .getBlob); - return value.Locked.promise.?.asObjectRef(); + return value.Locked.setPromise(ctx.ptr(), .getBlob).asObjectRef(); } var blob = this.body.use(); |