Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/runtime/socket/Handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,11 @@ impl Handlers {

#[cfg(debug_assertions)]
{
debug_assert!(self.protection_count > 0);
// `Drop` runs even on the early-error returns in `from_generated`
// (before `protect()`); nothing to unprotect in that case.
if self.protection_count == 0 {
return;
}
self.protection_count -= 1;
}
self.on_open.unprotect();
Expand Down
21 changes: 21 additions & 0 deletions test/js/bun/net/socket.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,27 @@ it("should throw on empty unix path from truthy non-string value", () => {
expect(() => Bun.connect({ unix: [] as any, socket })).toThrow("SocketOptions.unix must be a string");
});

it("should throw when socket handlers have no data or drain callback", () => {
// Handlers validation fails before protect() is called; Drop must not
// assert on an unprotected Handlers.
expect(() => Bun.listen({ hostname: "localhost", port: 0, socket: {} as any })).toThrow(
'Expected at least "data" or "drain" callback',
);
expect(() => Bun.connect({ hostname: "localhost", port: 0, socket: {} as any })).toThrow(
'Expected at least "data" or "drain" callback',
);
});

it("should throw when a socket handler is not a function", () => {
const socket = { open() {}, close: "not a function" } as any;
expect(() => Bun.listen({ hostname: "localhost", port: 0, socket })).toThrow(
'Expected "onClose" callback to be a function',
);
expect(() => Bun.connect({ hostname: "localhost", port: 0, socket })).toThrow(
'Expected "onClose" callback to be a function',
);
});

it("reading .listener on a closed client socket does not use-after-free handlers", async () => {
// Client-mode Handlers is heap-allocated per-connect and freed in
// markInactive once the socket closes. `socket.listener` read
Expand Down
Loading