Skip to content

Commit

Permalink
feat: add bindSync and unbindSync for the sockets
Browse files Browse the repository at this point in the history
  • Loading branch information
aminya committed Dec 30, 2024
1 parent d473c10 commit f56863d
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 67 deletions.
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
"devDependencies": {
"@types/benchmark": "~2.1.5",
"@types/chai": "^4",
"@types/deasync": "~0.1.5",
"@types/eslint": "~9.6.1",
"@types/fs-extra": "^11.0.4",
"@types/gh-pages": "~6.1.0",
Expand All @@ -38,7 +37,6 @@
"benchmark": "^2.1.4",
"chai": "^4",
"cross-env": "^7.0.3",
"deasync": "^0.1.30",
"downlevel-dts": "^0.11.0",
"electron": "^33.2.1",
"electron-mocha": "^13.0.1",
Expand Down
37 changes: 0 additions & 37 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 4 additions & 26 deletions src/compat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,34 +327,12 @@ class Socket extends EventEmitter {
)
}

bindSync(...args: Parameters<Socket["bind"]>) {
try {
Object.defineProperty(this, "bindSync", {
value: require("deasync")(this.bind),
})
} catch (err) {
throw new Error(
"bindSync() has been removed from compatibility mode; " +
"use bind() instead, or add 'deasync' to your project dependencies",
)
}

this.bindSync(...args)
bindSync(address: string) {
this._socket.bindSync(address)
}

unbindSync(...args: Parameters<Socket["unbind"]>) {
try {
Object.defineProperty(this, "unbindSync", {
value: require("deasync")(this.unbind),
})
} catch (err) {
throw new Error(
"unbindSync() has been removed from compatibility mode; " +
"use unbind() instead, or add 'deasync' to your project dependencies",
)
}

this.unbindSync(...args)
unbindSync(address: string) {
this._socket.unbindSync(address)
}

pause() {
Expand Down
30 changes: 30 additions & 0 deletions src/native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,26 @@ export declare abstract class Socket {
*/
bind(address: string): Promise<void>

/**
* Binds the socket to the given address. During {@link bind}() the socket
* cannot be used. Do not call any other methods until the returned promise
* resolves. Make sure to use `await`.
*
* You can use `*` in place of a hostname to bind on all interfaces/addresses,
* and you can use `*` in place of a port to bind to a random port (which can
* be retrieved with {@link lastEndpoint} later).
*
* ```typescript
* socket.bindSync("tcp://127.0.0.1:3456")
* socket.bindSync("tcp://*:3456") // binds on all interfaces
* socket.bindSync("tcp://127.0.0.1:*") // binds on random port
* ```
*
* @param address Address to bind this socket to.
* @returns Resolved when the socket was successfully bound.
*/
bindSync(address: string): void

/**
* Unbinds the socket to the given address. During {@link unbind}() the socket
* cannot be used. Do not call any other methods until the returned promise
Expand All @@ -549,6 +569,16 @@ export declare abstract class Socket {
*/
unbind(address: string): Promise<void>

/**
* Unbinds the socket to the given address. During {@link unbind}() the socket
* cannot be used. Do not call any other methods until the returned promise
* resolves. Make sure to use `await`.
*
* @param address Address to unbind this socket from.
* @returns Resolved when the socket was successfully unbound.
* */
unbindSync(address: string): void

/**
* Connects to the socket at the given remote address and returns immediately.
* The connection will be made asynchronously in the background.
Expand Down
82 changes: 80 additions & 2 deletions src/socket.cc
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ Napi::Value Socket::Bind(const Napi::CallbackInfo& info) {
if (run_ctx->error != 0) {
res.Reject(ErrnoException(
Env(), static_cast<int32_t>(run_ctx->error), run_ctx->address)
.Value());
.Value());
return;
}

Expand All @@ -407,6 +407,44 @@ Napi::Value Socket::Bind(const Napi::CallbackInfo& info) {
return res.Promise();
}

Napi::Value Socket::BindSync(const Napi::CallbackInfo& info) {
Arg::Validator const args{
Arg::Required<Arg::String>("Address must be a string"),
};

if (args.ThrowIfInvalid(info)) {
return Env().Undefined();
}

if (!ValidateOpen()) {
return Env().Undefined();
}

state = Socket::State::Blocked;
auto run_ctx = std::make_shared<AddressContext>(info[0].As<Napi::String>());

while (zmq_bind(socket, run_ctx->address.c_str()) < 0) {
if (zmq_errno() != EINTR) {
run_ctx->error = static_cast<uint32_t>(zmq_errno());
break;
}
}

state = Socket::State::Open;
endpoints++;

if (request_close) {
Close();
}

if (run_ctx->error != 0) {
ErrnoException(Env(), static_cast<int32_t>(run_ctx->error), run_ctx->address)
.ThrowAsJavaScriptException();
}

return Env().Undefined();
}

Napi::Value Socket::Unbind(const Napi::CallbackInfo& info) {
Arg::Validator const args{
Arg::Required<Arg::String>("Address must be a string"),
Expand Down Expand Up @@ -448,7 +486,7 @@ Napi::Value Socket::Unbind(const Napi::CallbackInfo& info) {
if (run_ctx->error != 0) {
res.Reject(ErrnoException(
Env(), static_cast<int32_t>(run_ctx->error), run_ctx->address)
.Value());
.Value());
return;
}

Expand All @@ -463,6 +501,42 @@ Napi::Value Socket::Unbind(const Napi::CallbackInfo& info) {
return res.Promise();
}

Napi::Value Socket::UnbindSync(const Napi::CallbackInfo& info) {
Arg::Validator const args{
Arg::Required<Arg::String>("Address must be a string"),
};

if (args.ThrowIfInvalid(info)) {
return Env().Undefined();
}

if (!ValidateOpen()) {
return Env().Undefined();
}

state = Socket::State::Blocked;

while (zmq_unbind(socket, info[0].As<Napi::String>().Utf8Value().c_str()) < 0) {
if (zmq_errno() != EINTR) {
break;
}
}

if (zmq_errno() == EADDRNOTAVAIL) {
// Unbind operation failed due to address not being bound.
return Env().Undefined();
}

state = Socket::State::Open;
--endpoints;

if (request_close) {
Close();
}

return Env().Undefined();
}

void Socket::Connect(const Napi::CallbackInfo& info) {
Arg::Validator const args{
Arg::Required<Arg::String>("Address must be a string"),
Expand Down Expand Up @@ -919,6 +993,10 @@ void Socket::Initialize(Module& module, Napi::Object& exports) {
auto proto = {
InstanceMethod<&Socket::Bind>("bind"),
InstanceMethod<&Socket::Unbind>("unbind"),

InstanceMethod<&Socket::BindSync>("bindSync"),
InstanceMethod<&Socket::BindSync>("unbindSync"),

InstanceMethod<&Socket::Connect>("connect"),
InstanceMethod<&Socket::Disconnect>("disconnect"),
InstanceMethod<&Socket::Close>("close"),
Expand Down
3 changes: 3 additions & 0 deletions src/socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ class Socket : public Napi::ObjectWrap<Socket>, public Closable {
inline void Close(const Napi::CallbackInfo& info);

inline Napi::Value Bind(const Napi::CallbackInfo& info);
inline Napi::Value BindSync(const Napi::CallbackInfo& info);

inline Napi::Value Unbind(const Napi::CallbackInfo& info);
inline Napi::Value UnbindSync(const Napi::CallbackInfo& info);

inline void Connect(const Napi::CallbackInfo& info);
inline void Disconnect(const Napi::CallbackInfo& info);
Expand Down

0 comments on commit f56863d

Please sign in to comment.