mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 18:38:55 +00:00
Phase 3, Step 3.2: Add ResponseMetadataHolder ownership wrapper
Add ResponseMetadataHolder wrapper struct to make metadata ownership transfer explicit and ensure metadata is only transferred once to the Response object. This prevents use-after-free and double-free bugs. Added: - ResponseMetadataHolder struct with private metadata and certificate fields - takeMetadata() method for one-time ownership transfer - takeCertificate() method for one-time ownership transfer - setMetadata() method that frees old metadata before storing new - setCertificate() method that frees old certificate before storing new - Single deinit() method with null-safe cleanup The take semantics ensure metadata can only be consumed once, with subsequent calls returning null. This makes the ownership contract explicit and prevents common memory safety bugs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -397,6 +397,66 @@ const RequestHeaders = struct {
|
||||
}
|
||||
};
|
||||
|
||||
/// Response metadata with explicit take semantics.
|
||||
/// Ensures metadata is only transferred once to Response object.
|
||||
///
|
||||
/// OWNERSHIP MODEL:
|
||||
/// - Metadata and certificate info are owned until taken
|
||||
/// - Take methods transfer ownership to caller
|
||||
/// - Set methods take ownership of new values
|
||||
/// - deinit() frees any remaining owned data
|
||||
const ResponseMetadataHolder = struct {
|
||||
#metadata: ?http.HTTPResponseMetadata = null,
|
||||
#certificate_info: ?http.CertificateInfo = null,
|
||||
allocator: std.mem.Allocator,
|
||||
|
||||
fn init(allocator: std.mem.Allocator) ResponseMetadataHolder {
|
||||
return .{ .allocator = allocator };
|
||||
}
|
||||
|
||||
/// Take metadata, transferring ownership to caller.
|
||||
/// Can only be called once - subsequent calls return null.
|
||||
fn takeMetadata(self: *ResponseMetadataHolder) ?http.HTTPResponseMetadata {
|
||||
const metadata = self.#metadata;
|
||||
self.#metadata = null; // Clear to prevent double-take
|
||||
return metadata;
|
||||
}
|
||||
|
||||
/// Take certificate info, transferring ownership to caller.
|
||||
fn takeCertificate(self: *ResponseMetadataHolder) ?http.CertificateInfo {
|
||||
const cert = self.#certificate_info;
|
||||
self.#certificate_info = null;
|
||||
return cert;
|
||||
}
|
||||
|
||||
/// Set metadata from HTTP result (takes ownership).
|
||||
/// Frees old metadata if present.
|
||||
fn setMetadata(self: *ResponseMetadataHolder, metadata: http.HTTPResponseMetadata) void {
|
||||
if (self.#metadata) |old| {
|
||||
old.deinit(self.allocator);
|
||||
}
|
||||
self.#metadata = metadata;
|
||||
}
|
||||
|
||||
/// Set certificate info from HTTP result (takes ownership).
|
||||
fn setCertificate(self: *ResponseMetadataHolder, cert: http.CertificateInfo) void {
|
||||
if (self.#certificate_info) |old| {
|
||||
old.deinit(self.allocator);
|
||||
}
|
||||
self.#certificate_info = cert;
|
||||
}
|
||||
|
||||
/// Single cleanup path
|
||||
fn deinit(self: *ResponseMetadataHolder) void {
|
||||
if (self.#metadata) |metadata| {
|
||||
metadata.deinit(self.allocator);
|
||||
}
|
||||
if (self.#certificate_info) |cert| {
|
||||
cert.deinit(self.allocator);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const FetchTasklet = struct {
|
||||
pub const ResumableSink = jsc.WebCore.ResumableFetchSink;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user