aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-03-23 22:53:22 -0700
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-03-23 22:53:22 -0700
commitf105a2fea731d8ee93e11837deaad9adaf30255c (patch)
tree41e5939828ddde143961bef0723fe592f4a09e46
parent6d230fc93ed77718dc917d0fccfa32bf61912390 (diff)
downloadbun-f105a2fea731d8ee93e11837deaad9adaf30255c.tar.gz
bun-f105a2fea731d8ee93e11837deaad9adaf30255c.tar.zst
bun-f105a2fea731d8ee93e11837deaad9adaf30255c.zip
handle bodies of 0 length better
-rw-r--r--Makefile2
-rw-r--r--src/deps/_libusockets.h3
-rw-r--r--src/deps/libuwsockets.cpp13
-rw-r--r--src/deps/uws.zig9
-rw-r--r--src/javascript/jsc/api/server.zig68
-rw-r--r--src/javascript/jsc/webcore/response.zig33
6 files changed, 86 insertions, 42 deletions
diff --git a/Makefile b/Makefile
index 02299301a..6ac55af16 100644
--- a/Makefile
+++ b/Makefile
@@ -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();