Compare commits

...

2 Commits

Author SHA1 Message Date
Jarred Sumner
8045d03356 Update Body.zig 2025-08-03 22:58:07 -07:00
Jarred Sumner
a19dd6a491 Report Response, Request, NodeHTTPResponse memory cost more accurately 2025-08-03 20:46:34 -07:00
12 changed files with 92 additions and 2 deletions

View File

@@ -67,6 +67,10 @@ struct BackPressure {
size_t totalLength() {
return buffer.length();
}
size_t memoryCost() const {
return buffer.capacity();
}
};
/* Depending on how we want AsyncSocket to function, this will need to change */
@@ -83,6 +87,10 @@ struct AsyncSocketData {
/* Or empty */
AsyncSocketData() = default;
size_t memoryCost() const {
return buffer.memoryCost();
}
};
}

View File

@@ -743,6 +743,9 @@ public:
httpResponseData->offset = offset;
}
size_t memoryCost() {
return sizeof(HttpResponse<SSL>) + this->getHttpResponseData()->memoryCost();
}
};
}

View File

@@ -106,6 +106,10 @@ struct HttpResponseData : AsyncSocketData<SSL>, HttpParser {
#ifdef UWS_WITH_PROXY
ProxyParser proxyParser;
#endif
size_t memoryCost() {
return sizeof(HttpResponseData<SSL>) + this->AsyncSocketData<SSL>::memoryCost();
}
};
}

View File

@@ -74,7 +74,7 @@ public:
};
size_t memoryCost() {
return getBufferedAmount() + sizeof(WebSocket);
return sizeof(WebSocket<SSL, isServer, USERDATA>) + this->getAsyncSocketData()->memoryCost();
}
/* Sending fragmented messages puts a bit of effort on the user; you must not interleave regular sends

View File

@@ -1073,6 +1073,15 @@ fn handleCorked(globalObject: *jsc.JSGlobalObject, function: jsc.JSValue, result
};
}
pub fn memoryCost(this: *const NodeHTTPResponse) usize {
var counter: usize = @sizeOf(NodeHTTPResponse);
counter += this.buffered_request_body_data_during_pause.memoryCost();
if (!this.flags.socket_closed) {
counter += this.raw_response.memoryCost();
}
return counter;
}
pub fn setTimeout(this: *NodeHTTPResponse, seconds: u8) void {
if (this.flags.request_has_completed or this.flags.socket_closed) {
return;

View File

@@ -60,7 +60,11 @@ pub fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool,
pub fn memoryCost(this: *const RequestContext) usize {
// The Sink and ByteStream aren't owned by this.
return @sizeOf(RequestContext) + this.request_body_buf.capacity + this.response_buf_owned.capacity + this.blob.memoryCost();
return @sizeOf(RequestContext) +
this.request_body_buf.capacity +
this.response_buf_owned.capacity +
this.blob.memoryCost() +
(if (this.resp) |response| response.memoryCost() else 0);
}
pub inline fn isAsync(this: *const RequestContext) bool {

View File

@@ -22,6 +22,14 @@ pub fn clone(this: *Body, globalThis: *JSGlobalObject) bun.JSError!Body {
};
}
pub fn memoryCost(this: *const Body) usize {
return this.value.memoryCost();
}
pub fn estimatedSize(this: *const Body) usize {
return this.value.estimatedSize();
}
pub fn writeFormat(this: *Body, comptime Formatter: type, formatter: *Formatter, writer: anytype, comptime enable_ansi_colors: bool) !void {
const Writer = @TypeOf(writer);
@@ -392,6 +400,7 @@ pub const Value = union(Tag) {
return switch (this.*) {
.InternalBlob => this.InternalBlob.bytes.items.len,
.WTFStringImpl => this.WTFStringImpl.memoryCost(),
// TODO: make this track if the readablestream has been set as the current JSValue
.Locked => this.Locked.sizeHint(),
// .InlineBlob => this.InlineBlob.sliceConst().len,
else => 0,

View File

@@ -38,6 +38,15 @@ pub fn estimatedSize(this: *Response) callconv(.C) usize {
return this.reported_estimated_size;
}
pub fn memoryCost(this: *const Response) usize {
var counter: usize = @sizeOf(Response);
counter += this.body.memoryCost();
counter += this.url.estimatedSize();
counter += this.init.memoryCost();
return counter;
}
pub fn calculateEstimatedByteSize(this: *Response) void {
this.reported_estimated_size = this.body.value.estimatedSize() +
this.url.byteSlice().len +
@@ -614,6 +623,10 @@ pub const Init = struct {
status_text: bun.String = bun.String.empty,
method: Method = Method.GET,
pub fn memoryCost(this: *const Init) usize {
return this.status_text.estimatedSize();
}
pub fn clone(this: Init, ctx: *JSGlobalObject) bun.JSError!Init {
var that = this;
const headers = this.headers;

View File

@@ -72,6 +72,7 @@ export default [
JSType: "0b11101110",
configurable: false,
estimatedSize: true,
memoryCost: true,
overridesToJS: true,
klass: {
json: {

View File

@@ -1807,6 +1807,14 @@ __attribute__((callback (corker, ctx)))
}
}
size_t uws_res_memory_cost(int ssl, uws_res_r res) {
if (ssl) {
return ((uWS::HttpResponse<true>*)res)->memoryCost();
} else {
return ((uWS::HttpResponse<false>*)res)->memoryCost();
}
}
size_t uws_ws_memory_cost(int ssl, uws_websocket_t *ws) {
if (ssl) {
return ((TLSWebSocket*)ws)->memoryCost();

View File

@@ -306,6 +306,10 @@ pub fn NewResponse(ssl_flag: i32) type {
ctx,
);
}
pub fn memoryCost(this: *Response) usize {
return c.uws_res_memory_cost(ssl_flag, this.downcast());
}
};
}
@@ -316,6 +320,13 @@ pub const AnyResponse = union(enum) {
SSL: *uws.NewApp(true).Response,
TCP: *uws.NewApp(false).Response,
pub fn memoryCost(this: AnyResponse) usize {
return switch (this) {
.SSL => |resp| resp.memoryCost(),
.TCP => |resp| resp.memoryCost(),
};
}
pub fn assertSSL(this: AnyResponse) *uws.NewApp(true).Response {
return switch (this) {
.SSL => |resp| resp,
@@ -670,6 +681,7 @@ const c = struct {
pub extern fn uws_res_prepare_for_sendfile(ssl: i32, res: *c.uws_res) void;
pub extern fn uws_res_get_native_handle(ssl: i32, res: *c.uws_res) *Socket;
pub extern fn uws_res_get_remote_address_as_text(ssl: i32, res: *c.uws_res, dest: *[*]const u8) usize;
pub extern fn uws_res_memory_cost(ssl: i32, res: *c.uws_res) usize;
pub extern fn uws_res_on_data(
ssl: i32,

View File

@@ -47,6 +47,25 @@ describe("Native types report their size correctly", () => {
delete globalThis.request;
});
it("Response includes status_text", () => {
var withStatusText = new Response("hello", {
headers: {
"Content-Type": "text/plain",
},
statusText: "yo yo yo",
});
var withoutStatusText = new Response("hello", {
headers: {
"Content-Type": "text/plain",
},
});
expect(estimateShallowMemoryUsageOf(withStatusText)).toBeGreaterThan(
estimateShallowMemoryUsageOf(withoutStatusText),
);
});
it("Response", () => {
var response = new Response(Buffer.alloc(1024 * 1024 * 4, "yoo"), {
headers: {