Skip to content

Commit

Permalink
net: add ability to reset a tcp socket
Browse files Browse the repository at this point in the history
make it possible to forcibly rest a tcp socket:

* add a new method `Socket.prototype.resetAndDestroy`

* add a new flag `resetAndClosing` to make `_destroy` calls
the `reset` instead of close while destroying a Socket.

* add new methods `TCPWrap::Reset` to be a wrap of `uv_tcp_close_reset`

* change `HandleWrap::state_` from private to protected.
This is essential for keeping the same behaviour between
`TCPWrap::Reset` and `HandleWrap::Close`

Fixes: nodejs#27428
  • Loading branch information
PupilTong committed May 15, 2022
1 parent d4cd7bd commit cfa6d57
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 5 deletions.
28 changes: 24 additions & 4 deletions lib/net.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ const {
ERR_INVALID_ARG_VALUE,
ERR_INVALID_FD_TYPE,
ERR_INVALID_IP_ADDRESS,
ERR_INVALID_HANDLE_TYPE,
ERR_SERVER_ALREADY_LISTEN,
ERR_SERVER_NOT_RUNNING,
ERR_SOCKET_CLOSED,
Expand Down Expand Up @@ -640,6 +641,16 @@ Socket.prototype.end = function(data, encoding, callback) {
return this;
};

Socket.prototype.resetAndDestroy = function() {
if (!(this._handle instanceof TCP))
throw new ERR_INVALID_HANDLE_TYPE();
if (this._handle) {
debug('reset');
this.resetAndClosing = true;
return this.destroy();
}
return this;
};

Socket.prototype.pause = function() {
if (this[kBuffer] && !this.connecting && this._handle &&
Expand Down Expand Up @@ -710,10 +721,19 @@ Socket.prototype._destroy = function(exception, cb) {
this[kBytesRead] = this._handle.bytesRead;
this[kBytesWritten] = this._handle.bytesWritten;

this._handle.close(() => {
debug('emit close');
this.emit('close', isException);
});
if (this.resetAndClosing) {
const err = this._handle.reset(() => {
debug('emit close');
this.emit('close', isException);
});
if (err)
throw errnoException(err, 'reset');
} else {
this._handle.close(() => {
debug('emit close');
this.emit('close', isException);
});
}
this._handle.onread = noop;
this._handle = null;
this._sockname = null;
Expand Down
2 changes: 1 addition & 1 deletion src/handle_wrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class HandleWrap : public AsyncWrap {
}

static void OnClose(uv_handle_t* handle);
enum { kInitialized, kClosing, kClosed } state_;

private:
friend class Environment;
Expand All @@ -109,7 +110,6 @@ class HandleWrap : public AsyncWrap {
// refer to `doc/contributing/node-postmortem-support.md`
friend int GenDebugSymbols();
ListNode<HandleWrap> handle_wrap_queue_;
enum { kInitialized, kClosing, kClosed } state_;
uv_handle_t* const handle_;
};

Expand Down
26 changes: 26 additions & 0 deletions src/tcp_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ void TCPWrap::Initialize(Local<Object> target,
GetSockOrPeerName<TCPWrap, uv_tcp_getpeername>);
env->SetProtoMethod(t, "setNoDelay", SetNoDelay);
env->SetProtoMethod(t, "setKeepAlive", SetKeepAlive);
env->SetProtoMethod(t, "reset", Reset);

#ifdef _WIN32
env->SetProtoMethod(t, "setSimultaneousAccepts", SetSimultaneousAccepts);
Expand Down Expand Up @@ -134,6 +135,7 @@ void TCPWrap::RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(GetSockOrPeerName<TCPWrap, uv_tcp_getpeername>);
registry->Register(SetNoDelay);
registry->Register(SetKeepAlive);
registry->Register(Reset);
#ifdef _WIN32
registry->Register(SetSimultaneousAccepts);
#endif
Expand Down Expand Up @@ -339,7 +341,31 @@ void TCPWrap::Connect(const FunctionCallbackInfo<Value>& args,

args.GetReturnValue().Set(err);
}
void TCPWrap::Reset(const FunctionCallbackInfo<Value>& args) {
TCPWrap* wrap;
ASSIGN_OR_RETURN_UNWRAP(&wrap,
args.Holder(),
args.GetReturnValue().Set(UV_EBADF));

int err = wrap->Reset(args[0]);

args.GetReturnValue().Set(err);
}

int TCPWrap::Reset(Local<Value> close_callback) {
if (state_ != kInitialized)
return 0;

int err = uv_tcp_close_reset(&handle_, OnClose);
state_ = kClosing;
if (!err & !close_callback.IsEmpty() && close_callback->IsFunction() &&
!persistent().IsEmpty()) {
object()->Set(env()->context(),
env()->handle_onclose_symbol(),
close_callback).Check();
}
return err;
}

// also used by udp_wrap.cc
MaybeLocal<Object> AddressToJS(Environment* env,
Expand Down
2 changes: 2 additions & 0 deletions src/tcp_wrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ class TCPWrap : public ConnectionWrap<TCPWrap, uv_tcp_t> {
const v8::FunctionCallbackInfo<v8::Value>& args,
int family,
std::function<int(const char* ip_address, int port, T* addr)> uv_ip_addr);
static void Reset(const v8::FunctionCallbackInfo<v8::Value>& args);
int Reset(v8::Local<v8::Value> close_callback = v8::Local<v8::Value>());

#ifdef _WIN32
static void SetSimultaneousAccepts(
Expand Down

0 comments on commit cfa6d57

Please sign in to comment.