mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Merge pull request #3056 from node-red/ws-ping
Allow websocket client node to send pings
This commit is contained in:
commit
ed5567fc73
@ -176,7 +176,8 @@
|
||||
defaults: {
|
||||
path: {value:"",required:true,validate:RED.validators.regex(/^((?!\/debug\/ws).)*$/)},
|
||||
tls: {type:"tls-config",required: false},
|
||||
wholemsg: {value:"false"}
|
||||
wholemsg: {value:"false"},
|
||||
hb: {value: "", validate: RED.validators.number(/*blank allowed*/true) }
|
||||
},
|
||||
inputs:0,
|
||||
outputs:0,
|
||||
@ -188,11 +189,24 @@
|
||||
$(".node-config-row-tls").toggle(/^wss:/i.test($(this).val()))
|
||||
});
|
||||
$("#node-config-input-path").change();
|
||||
|
||||
var heartbeatActive = (this.hb && this.hb != "0");
|
||||
$("#node-config-input-hb-cb").prop("checked",heartbeatActive);
|
||||
$("#node-config-input-hb-cb").on("change", function(evt) {
|
||||
$("#node-config-input-hb-row").toggle(this.checked);
|
||||
})
|
||||
$("#node-config-input-hb-cb").trigger("change");
|
||||
if (!heartbeatActive) {
|
||||
$("#node-config-input-hb").val("");
|
||||
}
|
||||
},
|
||||
oneditsave: function() {
|
||||
if (!/^wss:/i.test($("#node-config-input-path").val())) {
|
||||
$("#node-config-input-tls").val("_ADD_");
|
||||
}
|
||||
if (!$("#node-config-input-hb-cb").prop("checked")) {
|
||||
$("#node-config-input-hb").val("0");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -259,6 +273,14 @@
|
||||
<option value="true" data-i18n="websocket.message"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row" style="display: flex; align-items: center; min-height: 34px">
|
||||
<label for="node-config-input-hb-cb" data-i18n="websocket.sendheartbeat"></label>
|
||||
<input type="checkbox" style="margin: 0 8px; width:auto" id="node-config-input-hb-cb">
|
||||
<span id="node-config-input-hb-row" class="hide" >
|
||||
<input type="text" style="width: 70px; margin-right: 3px" id="node-config-input-hb">
|
||||
<span data-i18n="inject.seconds"></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="form-tips">
|
||||
<p><span data-i18n="[html]websocket.tip.url1"></span></p>
|
||||
<span data-i18n="[html]websocket.tip.url2"></span>
|
||||
|
@ -55,6 +55,13 @@ module.exports = function(RED) {
|
||||
node.closing = false;
|
||||
node.tls = n.tls;
|
||||
|
||||
if (n.hb) {
|
||||
var heartbeat = parseInt(n.hb);
|
||||
if (heartbeat > 0) {
|
||||
node.heartbeat = heartbeat * 1000;
|
||||
}
|
||||
}
|
||||
|
||||
function startconn() { // Connect to remote endpoint
|
||||
node.tout = null;
|
||||
var prox, noprox;
|
||||
@ -93,9 +100,24 @@ module.exports = function(RED) {
|
||||
|
||||
function handleConnection(/*socket*/socket) {
|
||||
var id = RED.util.generateId();
|
||||
socket.nrId = id;
|
||||
socket.nrPendingHeartbeat = false;
|
||||
if (node.isServer) {
|
||||
node._clients[id] = socket;
|
||||
node.emit('opened',{count:Object.keys(node._clients).length,id:id});
|
||||
} else {
|
||||
if (node.heartbeat) {
|
||||
node.heartbeatInterval = setInterval(function() {
|
||||
if (socket.nrPendingHeartbeat) {
|
||||
// No pong received
|
||||
socket.terminate();
|
||||
socket.nrErrorHandler(new Error("timeout"));
|
||||
return;
|
||||
}
|
||||
socket.nrPendingHeartbeat = true;
|
||||
socket.ping();
|
||||
},node.heartbeat);
|
||||
}
|
||||
}
|
||||
socket.on('open',function() {
|
||||
if (!node.isServer) {
|
||||
@ -103,6 +125,7 @@ module.exports = function(RED) {
|
||||
}
|
||||
});
|
||||
socket.on('close',function() {
|
||||
clearInterval(node.heartbeatInterval);
|
||||
if (node.isServer) {
|
||||
delete node._clients[id];
|
||||
node.emit('closed',{count:Object.keys(node._clients).length,id:id});
|
||||
@ -117,13 +140,21 @@ module.exports = function(RED) {
|
||||
socket.on('message',function(data,flags) {
|
||||
node.handleEvent(id,socket,'message',data,flags);
|
||||
});
|
||||
socket.on('error', function(err) {
|
||||
socket.nrErrorHandler = function(err) {
|
||||
clearInterval(node.heartbeatInterval);
|
||||
node.emit('erro',{err:err,id:id});
|
||||
if (!node.closing && !node.isServer) {
|
||||
clearTimeout(node.tout);
|
||||
node.tout = setTimeout(function() { startconn(); }, 3000); // try to reconnect every 3 secs... bit fast ?
|
||||
}
|
||||
});
|
||||
}
|
||||
socket.on('error',socket.nrErrorHandler);
|
||||
socket.on('ping', function() {
|
||||
socket.nrPendingHeartbeat = false;
|
||||
})
|
||||
socket.on('pong', function() {
|
||||
socket.nrPendingHeartbeat = false;
|
||||
})
|
||||
}
|
||||
|
||||
if (node.isServer) {
|
||||
@ -152,6 +183,19 @@ module.exports = function(RED) {
|
||||
node.server = new ws.Server(serverOptions);
|
||||
node.server.setMaxListeners(0);
|
||||
node.server.on('connection', handleConnection);
|
||||
// Not adding server-initiated heartbeats yet
|
||||
// node.heartbeatInterval = setInterval(function() {
|
||||
// node.server.clients.forEach(function(ws) {
|
||||
// if (ws.nrPendingHeartbeat) {
|
||||
// // No pong received
|
||||
// ws.terminate();
|
||||
// ws.nrErrorHandler(new Error("timeout"));
|
||||
// return;
|
||||
// }
|
||||
// ws.nrPendingHeartbeat = true;
|
||||
// ws.ping();
|
||||
// });
|
||||
// })
|
||||
}
|
||||
else {
|
||||
node.closing = false;
|
||||
@ -159,6 +203,9 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
node.on("close", function() {
|
||||
if (node.heartbeatInterval) {
|
||||
clearInterval(node.heartbeatInterval);
|
||||
}
|
||||
if (node.isServer) {
|
||||
delete listenerNodes[node.fullPath];
|
||||
node.server.close();
|
||||
@ -168,8 +215,6 @@ module.exports = function(RED) {
|
||||
// RED.server.removeListener('upgrade', handleServerUpgrade);
|
||||
// serverUpgradeAdded = false;
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
else {
|
||||
node.closing = true;
|
||||
|
@ -512,6 +512,7 @@
|
||||
"sendrec": "Send/Receive",
|
||||
"payload": "payload",
|
||||
"message": "entire message",
|
||||
"sendheartbeat": "Send heartbeat",
|
||||
"tip": {
|
||||
"path1": "By default, <code>payload</code> will contain the data to be sent over, or received from a websocket. The listener can be configured to send or receive the entire message object as a JSON formatted string.",
|
||||
"path2": "This path will be relative to <code>__path__</code>.",
|
||||
|
Loading…
Reference in New Issue
Block a user