Problem
The authentication REST listener of nethcti-server (port 20060) can become permanently unresponsive after a single internal reload event. Once in that state, every login request fails with ECONNREFUSED and the only way out is a full process restart.
The failure is the combination of three independent issues, each individually benign:
1. Response not closed on header errors (nethcti-server)
The sendHttp* helpers in plugins/util/util.js wrap their body in a try/catch and log from the catch but never call resp.end(). If resp.writeHead() throws — for example because a value passed in an HTTP header contains a character not allowed in header content — the response is never finalized and the underlying TCP connection is kept open.
2. Listener does not recover when connections are hanging (nethcti-server)
reset() in plugins/com_authentication_rest/server_com_authentication_rest.js calls start() only from the server.close() callback. The Node.js close() callback only fires after every existing connection has ended, so a single hanging request (such as one left over by #1) is enough to prevent it from firing — and to prevent the listener from being re-bound to its port indefinitely.
3. No timeout on the login backend HTTP client (nethcti-middleware)
The http.Client used in middleware/middleware.go to forward /login requests to the legacy backend was created without Timeout. If the backend leaves the response unfinished (as in #1), the goroutine handling the login stays parked and the TCP connection to the backend is kept open with no upper bound.
Combined effect
A request that triggers #1 leaves a TCP connection open between middleware and backend (because of #3). Any later reload event on the auth component then triggers #2, and the listener never comes back up.
Fix
Problem
The authentication REST listener of
nethcti-server(port20060) can become permanently unresponsive after a single internal reload event. Once in that state, every login request fails withECONNREFUSEDand the only way out is a full process restart.The failure is the combination of three independent issues, each individually benign:
1. Response not closed on header errors (
nethcti-server)The
sendHttp*helpers inplugins/util/util.jswrap their body in atry/catchand log from the catch but never callresp.end(). Ifresp.writeHead()throws — for example because a value passed in an HTTP header contains a character not allowed in header content — the response is never finalized and the underlying TCP connection is kept open.2. Listener does not recover when connections are hanging (
nethcti-server)reset()inplugins/com_authentication_rest/server_com_authentication_rest.jscallsstart()only from theserver.close()callback. The Node.jsclose()callback only fires after every existing connection has ended, so a single hanging request (such as one left over by #1) is enough to prevent it from firing — and to prevent the listener from being re-bound to its port indefinitely.3. No timeout on the login backend HTTP client (
nethcti-middleware)The
http.Clientused inmiddleware/middleware.goto forward/loginrequests to the legacy backend was created withoutTimeout. If the backend leaves the response unfinished (as in #1), the goroutine handling the login stays parked and the TCP connection to the backend is kept open with no upper bound.Combined effect
A request that triggers #1 leaves a TCP connection open between middleware and backend (because of #3). Any later reload event on the auth component then triggers #2, and the listener never comes back up.
Fix