mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Allow websocket client node to send pings
This commit is contained in:
parent
46c4e2d212
commit
d8ee766860
@ -176,7 +176,8 @@
|
|||||||
defaults: {
|
defaults: {
|
||||||
path: {value:"",required:true,validate:RED.validators.regex(/^((?!\/debug\/ws).)*$/)},
|
path: {value:"",required:true,validate:RED.validators.regex(/^((?!\/debug\/ws).)*$/)},
|
||||||
tls: {type:"tls-config",required: false},
|
tls: {type:"tls-config",required: false},
|
||||||
wholemsg: {value:"false"}
|
wholemsg: {value:"false"},
|
||||||
|
hb: {value: "", validate: RED.validators.number(/*blank allowed*/true) }
|
||||||
},
|
},
|
||||||
inputs:0,
|
inputs:0,
|
||||||
outputs:0,
|
outputs:0,
|
||||||
@ -188,11 +189,24 @@
|
|||||||
$(".node-config-row-tls").toggle(/^wss:/i.test($(this).val()))
|
$(".node-config-row-tls").toggle(/^wss:/i.test($(this).val()))
|
||||||
});
|
});
|
||||||
$("#node-config-input-path").change();
|
$("#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() {
|
oneditsave: function() {
|
||||||
if (!/^wss:/i.test($("#node-config-input-path").val())) {
|
if (!/^wss:/i.test($("#node-config-input-path").val())) {
|
||||||
$("#node-config-input-tls").val("_ADD_");
|
$("#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>
|
<option value="true" data-i18n="websocket.message"></option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</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">
|
<div class="form-tips">
|
||||||
<p><span data-i18n="[html]websocket.tip.url1"></span></p>
|
<p><span data-i18n="[html]websocket.tip.url1"></span></p>
|
||||||
<span data-i18n="[html]websocket.tip.url2"></span>
|
<span data-i18n="[html]websocket.tip.url2"></span>
|
||||||
|
@ -55,6 +55,13 @@ module.exports = function(RED) {
|
|||||||
node.closing = false;
|
node.closing = false;
|
||||||
node.tls = n.tls;
|
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
|
function startconn() { // Connect to remote endpoint
|
||||||
node.tout = null;
|
node.tout = null;
|
||||||
var prox, noprox;
|
var prox, noprox;
|
||||||
@ -93,9 +100,24 @@ module.exports = function(RED) {
|
|||||||
|
|
||||||
function handleConnection(/*socket*/socket) {
|
function handleConnection(/*socket*/socket) {
|
||||||
var id = RED.util.generateId();
|
var id = RED.util.generateId();
|
||||||
|
socket.nrId = id;
|
||||||
|
socket.nrPendingHeartbeat = false;
|
||||||
if (node.isServer) {
|
if (node.isServer) {
|
||||||
node._clients[id] = socket;
|
node._clients[id] = socket;
|
||||||
node.emit('opened',{count:Object.keys(node._clients).length,id:id});
|
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() {
|
socket.on('open',function() {
|
||||||
if (!node.isServer) {
|
if (!node.isServer) {
|
||||||
@ -103,6 +125,7 @@ module.exports = function(RED) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
socket.on('close',function() {
|
socket.on('close',function() {
|
||||||
|
clearInterval(node.heartbeatInterval);
|
||||||
if (node.isServer) {
|
if (node.isServer) {
|
||||||
delete node._clients[id];
|
delete node._clients[id];
|
||||||
node.emit('closed',{count:Object.keys(node._clients).length,id: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) {
|
socket.on('message',function(data,flags) {
|
||||||
node.handleEvent(id,socket,'message',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});
|
node.emit('erro',{err:err,id:id});
|
||||||
if (!node.closing && !node.isServer) {
|
if (!node.closing && !node.isServer) {
|
||||||
clearTimeout(node.tout);
|
clearTimeout(node.tout);
|
||||||
node.tout = setTimeout(function() { startconn(); }, 3000); // try to reconnect every 3 secs... bit fast ?
|
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) {
|
if (node.isServer) {
|
||||||
@ -152,6 +183,19 @@ module.exports = function(RED) {
|
|||||||
node.server = new ws.Server(serverOptions);
|
node.server = new ws.Server(serverOptions);
|
||||||
node.server.setMaxListeners(0);
|
node.server.setMaxListeners(0);
|
||||||
node.server.on('connection', handleConnection);
|
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 {
|
else {
|
||||||
node.closing = false;
|
node.closing = false;
|
||||||
@ -159,6 +203,9 @@ module.exports = function(RED) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
node.on("close", function() {
|
node.on("close", function() {
|
||||||
|
if (node.heartbeatInterval) {
|
||||||
|
clearInterval(node.heartbeatInterval);
|
||||||
|
}
|
||||||
if (node.isServer) {
|
if (node.isServer) {
|
||||||
delete listenerNodes[node.fullPath];
|
delete listenerNodes[node.fullPath];
|
||||||
node.server.close();
|
node.server.close();
|
||||||
@ -168,8 +215,6 @@ module.exports = function(RED) {
|
|||||||
// RED.server.removeListener('upgrade', handleServerUpgrade);
|
// RED.server.removeListener('upgrade', handleServerUpgrade);
|
||||||
// serverUpgradeAdded = false;
|
// serverUpgradeAdded = false;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
node.closing = true;
|
node.closing = true;
|
||||||
|
@ -512,6 +512,7 @@
|
|||||||
"sendrec": "Send/Receive",
|
"sendrec": "Send/Receive",
|
||||||
"payload": "payload",
|
"payload": "payload",
|
||||||
"message": "entire message",
|
"message": "entire message",
|
||||||
|
"sendheartbeat": "Send heartbeat",
|
||||||
"tip": {
|
"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.",
|
"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>.",
|
"path2": "This path will be relative to <code>__path__</code>.",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user