@node-red/nodes 2.2.2 → 3.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/common/05-junction.html +5 -0
- package/core/common/05-junction.js +12 -0
- package/core/common/20-inject.html +25 -13
- package/core/common/21-debug.html +60 -6
- package/core/common/21-debug.js +57 -27
- package/core/common/60-link.html +66 -29
- package/core/common/60-link.js +169 -20
- package/core/common/lib/debug/debug-utils.js +34 -1
- package/core/function/10-function.html +57 -21
- package/core/function/10-switch.html +3 -1
- package/core/function/10-switch.js +1 -0
- package/core/function/15-change.html +40 -12
- package/core/function/16-range.html +14 -5
- package/core/function/80-template.html +16 -12
- package/core/function/89-delay.html +46 -6
- package/core/function/89-trigger.html +12 -4
- package/core/function/rbe.html +7 -3
- package/core/network/05-tls.html +10 -4
- package/core/network/06-httpproxy.html +10 -1
- package/core/network/10-mqtt.html +73 -17
- package/core/network/10-mqtt.js +191 -80
- package/core/network/21-httpin.html +6 -2
- package/core/network/21-httprequest.html +217 -12
- package/core/network/21-httprequest.js +98 -17
- package/core/network/22-websocket.html +19 -5
- package/core/network/22-websocket.js +16 -13
- package/core/network/31-tcpin.html +47 -10
- package/core/network/31-tcpin.js +8 -3
- package/core/network/32-udp.html +14 -2
- package/core/parsers/70-CSV.html +4 -1
- package/core/parsers/70-JSON.html +3 -2
- package/core/parsers/70-XML.html +2 -1
- package/core/parsers/70-YAML.html +2 -1
- package/core/sequence/17-split.html +5 -1
- package/core/sequence/19-batch.html +28 -4
- package/core/storage/10-file.html +68 -8
- package/core/storage/10-file.js +46 -3
- package/core/storage/23-watch.html +2 -1
- package/core/storage/23-watch.js +21 -43
- package/locales/de/messages.json +1 -0
- package/locales/en-US/common/60-link.html +18 -3
- package/locales/en-US/messages.json +68 -17
- package/locales/en-US/network/21-httprequest.html +1 -1
- package/locales/en-US/storage/10-file.html +6 -2
- package/locales/ja/common/60-link.html +12 -0
- package/locales/ja/messages.json +63 -16
- package/locales/ko/messages.json +1 -0
- package/locales/ru/messages.json +1 -0
- package/locales/zh-CN/messages.json +1 -0
- package/locales/zh-TW/messages.json +1 -0
- package/package.json +12 -12
|
@@ -100,14 +100,107 @@
|
|
|
100
100
|
<option value="obj" data-i18n="httpin.json"></option>
|
|
101
101
|
</select>
|
|
102
102
|
</div>
|
|
103
|
+
|
|
104
|
+
<div class="form-row form-tips" id="tip-json" hidden><span data-i18n="httpin.tip.req"></span></div>
|
|
105
|
+
|
|
106
|
+
<div class="form-row" style="margin-bottom:0;">
|
|
107
|
+
<label><i class="fa fa-list"></i> <span data-i18n="httpin.label.headers"></span></label>
|
|
108
|
+
</div>
|
|
109
|
+
<div class="form-row node-input-headers-container-row">
|
|
110
|
+
<ol id="node-input-headers-container"></ol>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
103
113
|
<div class="form-row">
|
|
104
114
|
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
|
105
115
|
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
|
|
106
116
|
</div>
|
|
107
|
-
<div class="form-tips" id="tip-json" hidden><span data-i18n="httpin.tip.req"></span></div>
|
|
108
117
|
</script>
|
|
109
118
|
|
|
110
119
|
<script type="text/javascript">
|
|
120
|
+
(function() {
|
|
121
|
+
const headerTypes = [
|
|
122
|
+
{ value: "Accept", label: "Accept", hasValue: false },
|
|
123
|
+
{ value: "Accept-Encoding", label: "Accept-Encoding", hasValue: false },
|
|
124
|
+
{ value: "Accept-Language", label: "Accept-Language", hasValue: false },
|
|
125
|
+
{ value: "Authorization", label: "Authorization", hasValue: false },
|
|
126
|
+
{ value: "Content-Type", label: "Content-Type", hasValue: false },
|
|
127
|
+
{ value: "Cache-Control", label: "Cache-Control", hasValue: false },
|
|
128
|
+
{ value: "User-Agent", label: "User-Agent", hasValue: false },
|
|
129
|
+
{ value: "Location", label: "Location", hasValue: false },
|
|
130
|
+
{ value: "other", label: "other", hasValue: true, icon: "red/images/typedInput/az.png" },
|
|
131
|
+
{ value: "msg", label: "msg.", hasValue: true },
|
|
132
|
+
]
|
|
133
|
+
const headerOptions = {};
|
|
134
|
+
const defaultOptions = [
|
|
135
|
+
{ value: "other", label: "other", hasValue: true, icon: "red/images/typedInput/az.png" },
|
|
136
|
+
{ value: "msg", label: "msg.", hasValue: true },
|
|
137
|
+
];
|
|
138
|
+
headerOptions["accept"] = [
|
|
139
|
+
{ value: "text/plain", label: "text/plain", hasValue: false },
|
|
140
|
+
{ value: "text/html", label: "text/html", hasValue: false },
|
|
141
|
+
{ value: "application/json", label: "application/json", hasValue: false },
|
|
142
|
+
{ value: "application/xml", label: "application/xml", hasValue: false },
|
|
143
|
+
...defaultOptions,
|
|
144
|
+
];
|
|
145
|
+
headerOptions["accept-encoding"] = [
|
|
146
|
+
{ value: "gzip", label: "gzip", hasValue: false },
|
|
147
|
+
{ value: "deflate", label: "deflate", hasValue: false },
|
|
148
|
+
{ value: "compress", label: "compress", hasValue: false },
|
|
149
|
+
{ value: "br", label: "br", hasValue: false },
|
|
150
|
+
{ value: "gzip, deflate", label: "gzip, deflate", hasValue: false },
|
|
151
|
+
{ value: "gzip, deflate, br", label: "gzip, deflate, br", hasValue: false },
|
|
152
|
+
...defaultOptions,
|
|
153
|
+
];
|
|
154
|
+
headerOptions["accept-language"] = [
|
|
155
|
+
{ value: "*", label: "*", hasValue: false },
|
|
156
|
+
{ value: "en-GB, en-US, en;q=0.9", label: "en-GB, en-US, en;q=0.9", hasValue: false },
|
|
157
|
+
{ value: "de-AT, de-DE;q=0.9, en;q=0.5", label: "de-AT, de-DE;q=0.9, en;q=0.5", hasValue: false },
|
|
158
|
+
{ value: "es-mx,es,en;q=0.5", label: "es-mx,es,en;q=0.5", hasValue: false },
|
|
159
|
+
{ value: "fr-CH, fr;q=0.9, en;q=0.8", label: "fr-CH, fr;q=0.9, en;q=0.8", hasValue: false },
|
|
160
|
+
{ value: "zh-CN, zh-TW; q = 0.9, zh-HK; q = 0.8, zh; q = 0.7, en; q = 0.6", label: "zh-CN, zh-TW; q = 0.9, zh-HK; q = 0.8, zh; q = 0.7, en; q = 0.6", hasValue: false },
|
|
161
|
+
{ value: "ja-JP, jp", label: "ja-JP, jp", hasValue: false },
|
|
162
|
+
...defaultOptions,
|
|
163
|
+
];
|
|
164
|
+
headerOptions["content-type"] = [
|
|
165
|
+
{ value: "text/css", label: "text/css", hasValue: false },
|
|
166
|
+
{ value: "text/plain", label: "text/plain", hasValue: false },
|
|
167
|
+
{ value: "text/html", label: "text/html", hasValue: false },
|
|
168
|
+
{ value: "application/json", label: "application/json", hasValue: false },
|
|
169
|
+
{ value: "application/octet-stream", label: "application/octet-stream", hasValue: false },
|
|
170
|
+
{ value: "application/pdf", label: "application/pdf", hasValue: false },
|
|
171
|
+
{ value: "application/xml", label: "application/xml", hasValue: false },
|
|
172
|
+
{ value: "application/zip", label: "application/zip", hasValue: false },
|
|
173
|
+
{ value: "multipart/form-data", label: "multipart/form-data", hasValue: false },
|
|
174
|
+
{ value: "audio/aac", label: "audio/aac", hasValue: false },
|
|
175
|
+
{ value: "audio/ac3", label: "audio/ac3", hasValue: false },
|
|
176
|
+
{ value: "audio/basic", label: "audio/basic", hasValue: false },
|
|
177
|
+
{ value: "audio/mp4", label: "audio/mp4", hasValue: false },
|
|
178
|
+
{ value: "audio/ogg", label: "audio/ogg", hasValue: false },
|
|
179
|
+
{ value: "image/bmp", label: "image/bmp", hasValue: false },
|
|
180
|
+
{ value: "image/gif", label: "image/gif", hasValue: false },
|
|
181
|
+
{ value: "image/jpeg", label: "image/jpeg", hasValue: false },
|
|
182
|
+
{ value: "image/png", label: "image/png", hasValue: false },
|
|
183
|
+
{ value: "image/tiff", label: "image/tiff", hasValue: false },
|
|
184
|
+
...defaultOptions,
|
|
185
|
+
];
|
|
186
|
+
headerOptions["cache-control"] = [
|
|
187
|
+
{ value: "max-age=0", label: "max-age=0", hasValue: false },
|
|
188
|
+
{ value: "max-age=86400", label: "max-age=86400", hasValue: false },
|
|
189
|
+
{ value: "no-cache", label: "no-cache", hasValue: false },
|
|
190
|
+
...defaultOptions,
|
|
191
|
+
];
|
|
192
|
+
|
|
193
|
+
headerOptions["user-agent"] = [
|
|
194
|
+
{ value: "Mozilla/5.0", label: "Mozilla/5.0", hasValue: false },
|
|
195
|
+
...defaultOptions,
|
|
196
|
+
];
|
|
197
|
+
|
|
198
|
+
function getHeaderOptions(headerName) {
|
|
199
|
+
const lc = (headerName || "").toLowerCase();
|
|
200
|
+
let opts = headerOptions[lc];
|
|
201
|
+
return opts || defaultOptions;
|
|
202
|
+
}
|
|
203
|
+
|
|
111
204
|
RED.nodes.registerType('http request',{
|
|
112
205
|
category: 'network',
|
|
113
206
|
color:"rgb(231, 231, 174)",
|
|
@@ -116,12 +209,25 @@
|
|
|
116
209
|
method:{value:"GET"},
|
|
117
210
|
ret: {value:"txt"},
|
|
118
211
|
paytoqs: {value: false},
|
|
119
|
-
url:{
|
|
120
|
-
|
|
212
|
+
url:{
|
|
213
|
+
value:"",
|
|
214
|
+
validate: function(v, opt) {
|
|
215
|
+
if ((v.trim().length === 0) ||
|
|
216
|
+
(v.indexOf("://") === -1) ||
|
|
217
|
+
(v.trim().indexOf("http") === 0)) {
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
return RED._("node-red:httpin.errors.invalid-url");
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
tls: {type:"tls-config",required: false,
|
|
224
|
+
label:RED._("node-red:httpin.tls-config") },
|
|
121
225
|
persist: {value:false},
|
|
122
|
-
proxy: {type:"http proxy",required: false
|
|
226
|
+
proxy: {type:"http proxy",required: false,
|
|
227
|
+
label:RED._("node-red:httpin.proxy-config") },
|
|
123
228
|
authType: {value: ""},
|
|
124
|
-
senderr: {value: false}
|
|
229
|
+
senderr: {value: false},
|
|
230
|
+
headers: { value: [] }
|
|
125
231
|
},
|
|
126
232
|
credentials: {
|
|
127
233
|
user: {type:"text"},
|
|
@@ -144,6 +250,7 @@
|
|
|
144
250
|
return this.name?"node_label_italic":"";
|
|
145
251
|
},
|
|
146
252
|
oneditprepare: function() {
|
|
253
|
+
const node = this;
|
|
147
254
|
$("#node-input-useAuth").on("change", function() {
|
|
148
255
|
if ($(this).is(":checked")) {
|
|
149
256
|
$(".node-input-useAuth-row").show();
|
|
@@ -157,9 +264,10 @@
|
|
|
157
264
|
$('#node-input-user').val('');
|
|
158
265
|
$('#node-input-password').val('');
|
|
159
266
|
}
|
|
267
|
+
RED.tray.resize();
|
|
160
268
|
});
|
|
161
269
|
$("#node-input-authType-select").on("change", function() {
|
|
162
|
-
|
|
270
|
+
const val = $(this).val();
|
|
163
271
|
$("#node-input-authType").val(val);
|
|
164
272
|
if (val === "basic" || val === "digest") {
|
|
165
273
|
$(".node-input-basic-row").show();
|
|
@@ -171,6 +279,7 @@
|
|
|
171
279
|
$('#node-span-token').show();
|
|
172
280
|
$('#node-input-user').val('');
|
|
173
281
|
}
|
|
282
|
+
RED.tray.resize();
|
|
174
283
|
});
|
|
175
284
|
$("#node-input-method").on("change", function() {
|
|
176
285
|
if ($(this).val() == "GET") {
|
|
@@ -178,17 +287,18 @@
|
|
|
178
287
|
} else {
|
|
179
288
|
$(".node-input-paytoqs-row").hide();
|
|
180
289
|
}
|
|
290
|
+
RED.tray.resize();
|
|
181
291
|
});
|
|
182
|
-
if (
|
|
292
|
+
if (node.paytoqs === true || node.paytoqs == "query") {
|
|
183
293
|
$("#node-input-paytoqs").val("query");
|
|
184
|
-
} else if (
|
|
294
|
+
} else if (node.paytoqs === "body") {
|
|
185
295
|
$("#node-input-paytoqs").val("body");
|
|
186
296
|
} else {
|
|
187
297
|
$("#node-input-paytoqs").val("ignore");
|
|
188
298
|
}
|
|
189
|
-
if (
|
|
299
|
+
if (node.authType) {
|
|
190
300
|
$('#node-input-useAuth').prop('checked', true);
|
|
191
|
-
$("#node-input-authType-select").val(
|
|
301
|
+
$("#node-input-authType-select").val(node.authType);
|
|
192
302
|
$("#node-input-authType-select").change();
|
|
193
303
|
} else {
|
|
194
304
|
$('#node-input-useAuth').prop('checked', false);
|
|
@@ -201,8 +311,9 @@
|
|
|
201
311
|
} else {
|
|
202
312
|
$("#node-row-tls").hide();
|
|
203
313
|
}
|
|
314
|
+
RED.tray.resize();
|
|
204
315
|
}
|
|
205
|
-
if (
|
|
316
|
+
if (node.tls) {
|
|
206
317
|
$('#node-input-usetls').prop('checked', true);
|
|
207
318
|
} else {
|
|
208
319
|
$('#node-input-usetls').prop('checked', false);
|
|
@@ -218,8 +329,9 @@
|
|
|
218
329
|
} else {
|
|
219
330
|
$("#node-input-useProxy-row").hide();
|
|
220
331
|
}
|
|
332
|
+
RED.tray.resize();
|
|
221
333
|
}
|
|
222
|
-
if (
|
|
334
|
+
if (node.proxy) {
|
|
223
335
|
$("#node-input-useProxy").prop("checked", true);
|
|
224
336
|
} else {
|
|
225
337
|
$("#node-input-useProxy").prop("checked", false);
|
|
@@ -235,7 +347,70 @@
|
|
|
235
347
|
} else {
|
|
236
348
|
$("#tip-json").hide();
|
|
237
349
|
}
|
|
350
|
+
RED.tray.resize();
|
|
238
351
|
});
|
|
352
|
+
const hasMatch = function (arr, value) {
|
|
353
|
+
return arr.some(function (ht) {
|
|
354
|
+
return ht.value === value
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
const headerList = $("#node-input-headers-container").css('min-height', '150px').css('min-width', '450px').editableList({
|
|
358
|
+
addItem: function (container, i, header) {
|
|
359
|
+
const row = $('<div/>').css({
|
|
360
|
+
overflow: 'hidden',
|
|
361
|
+
whiteSpace: 'nowrap',
|
|
362
|
+
display: 'flex'
|
|
363
|
+
}).appendTo(container);
|
|
364
|
+
const propertNameCell = $('<div/>').css({ 'flex-grow': 1 }).appendTo(row);
|
|
365
|
+
const propertyName = $('<input/>', { class: "node-input-header-name", type: "text", style: "width: 100%" })
|
|
366
|
+
.appendTo(propertNameCell)
|
|
367
|
+
.typedInput({ types: headerTypes });
|
|
368
|
+
|
|
369
|
+
const propertyValueCell = $('<div/>').css({ 'flex-grow': 1, 'margin-left': '10px' }).appendTo(row);
|
|
370
|
+
const propertyValue = $('<input/>', { class: "node-input-header-value", type: "text", style: "width: 100%" })
|
|
371
|
+
.appendTo(propertyValueCell)
|
|
372
|
+
.typedInput({
|
|
373
|
+
types: getHeaderOptions(header.keyType)
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
const setup = function(_header) {
|
|
377
|
+
const headerTypeIsAPreset = function(h) {return hasMatch(headerTypes, h) };
|
|
378
|
+
const headerValueIsAPreset = function(h, v) {return hasMatch(getHeaderOptions(h), v) };
|
|
379
|
+
const {keyType, keyValue, valueType, valueValue} = header;
|
|
380
|
+
if(keyType == "msg" || keyType == "other") {
|
|
381
|
+
propertyName.typedInput('type', keyType);
|
|
382
|
+
propertyName.typedInput('value', keyValue);
|
|
383
|
+
} else if (headerTypeIsAPreset(keyType)) {
|
|
384
|
+
propertyName.typedInput('type', keyType);
|
|
385
|
+
} else {
|
|
386
|
+
propertyName.typedInput('type', "other");
|
|
387
|
+
propertyName.typedInput('value', keyValue);
|
|
388
|
+
}
|
|
389
|
+
if(valueType == "msg" || valueType == "other") {
|
|
390
|
+
propertyValue.typedInput('type', valueType);
|
|
391
|
+
propertyValue.typedInput('value', valueValue);
|
|
392
|
+
} else if (headerValueIsAPreset(propertyName.typedInput('type'), valueType)) {
|
|
393
|
+
propertyValue.typedInput('type', valueType);
|
|
394
|
+
} else {
|
|
395
|
+
propertyValue.typedInput('type', "other");
|
|
396
|
+
propertyValue.typedInput('value', valueValue);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
setup(header);
|
|
400
|
+
|
|
401
|
+
propertyName.on('change', function (event) {
|
|
402
|
+
propertyValue.typedInput('types', getHeaderOptions(propertyName.typedInput('type')));
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
},
|
|
406
|
+
removable: true
|
|
407
|
+
});
|
|
408
|
+
if (node.headers) {
|
|
409
|
+
for (let index = 0; index < node.headers.length; index++) {
|
|
410
|
+
const element = node.headers[index];
|
|
411
|
+
headerList.editableList('addItem', node.headers[index]);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
239
414
|
},
|
|
240
415
|
oneditsave: function() {
|
|
241
416
|
if (!$("#node-input-usetls").is(':checked')) {
|
|
@@ -244,6 +419,36 @@
|
|
|
244
419
|
if (!$("#node-input-useProxy").is(":checked")) {
|
|
245
420
|
$("#node-input-proxy").val("_ADD_");
|
|
246
421
|
}
|
|
422
|
+
const headers = $("#node-input-headers-container").editableList('items');
|
|
423
|
+
const node = this;
|
|
424
|
+
node.headers = [];
|
|
425
|
+
headers.each(function(i) {
|
|
426
|
+
const header = $(this);
|
|
427
|
+
const keyType = header.find(".node-input-header-name").typedInput('type');
|
|
428
|
+
const keyValue = header.find(".node-input-header-name").typedInput('value');
|
|
429
|
+
const valueType = header.find(".node-input-header-value").typedInput('type');
|
|
430
|
+
const valueValue = header.find(".node-input-header-value").typedInput('value');
|
|
431
|
+
if (keyType !== '' || keyType === 'other' || keyType === 'msg') {
|
|
432
|
+
node.headers.push({
|
|
433
|
+
keyType, keyValue, valueType, valueValue
|
|
434
|
+
})
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
},
|
|
438
|
+
oneditresize: function(size) {
|
|
439
|
+
const dlg = $("#dialog-form");
|
|
440
|
+
const expandRow = dlg.find('.node-input-headers-container-row');
|
|
441
|
+
let height = dlg.height() - 5;
|
|
442
|
+
if(expandRow && expandRow.length){
|
|
443
|
+
const siblingRows = dlg.find('> .form-row:not(.node-input-headers-container-row)');
|
|
444
|
+
for (let i = 0; i < siblingRows.size(); i++) {
|
|
445
|
+
const cr = $(siblingRows[i]);
|
|
446
|
+
if(cr.is(":visible"))
|
|
447
|
+
height -= cr.outerHeight(true);
|
|
448
|
+
}
|
|
449
|
+
$("#node-input-headers-container").editableList('height',height);
|
|
450
|
+
}
|
|
247
451
|
}
|
|
248
452
|
});
|
|
453
|
+
})();
|
|
249
454
|
</script>
|
|
@@ -73,7 +73,7 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
|
|
73
73
|
var paytobody = false;
|
|
74
74
|
var redirectList = [];
|
|
75
75
|
var sendErrorsToCatch = n.senderr;
|
|
76
|
-
|
|
76
|
+
node.headers = n.headers || [];
|
|
77
77
|
var nodeHTTPPersistent = n["persist"];
|
|
78
78
|
if (n.tls) {
|
|
79
79
|
var tlsNode = RED.nodes.getNode(n.tls);
|
|
@@ -105,6 +105,37 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
|
|
105
105
|
timingLog = RED.settings.httpRequestTimingLog;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Case insensitive header value update util function
|
|
110
|
+
* @param {object} headersObject The opt.headers object to update
|
|
111
|
+
* @param {string} name The header name
|
|
112
|
+
* @param {string} value The header value to set (if blank, header is removed)
|
|
113
|
+
*/
|
|
114
|
+
const updateHeader = function(headersObject, name, value ) {
|
|
115
|
+
const hn = name.toLowerCase();
|
|
116
|
+
const keys = Object.keys(headersObject);
|
|
117
|
+
const matchingKeys = keys.filter(e => e.toLowerCase() == hn)
|
|
118
|
+
const updateKey = (k,v) => {
|
|
119
|
+
delete headersObject[k]; //delete incase of case change
|
|
120
|
+
if(v) { headersObject[name] = v } //re-add with requested name & value
|
|
121
|
+
}
|
|
122
|
+
if(matchingKeys.length == 0) {
|
|
123
|
+
updateKey(name, value)
|
|
124
|
+
} else {
|
|
125
|
+
matchingKeys.forEach(k => {
|
|
126
|
+
updateKey(k, value);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* @param {Object} headersObject
|
|
132
|
+
* @param {string} name
|
|
133
|
+
* @return {any} value
|
|
134
|
+
*/
|
|
135
|
+
const getHeaderValue = (headersObject, name) => {
|
|
136
|
+
const asLowercase = name.toLowercase();
|
|
137
|
+
return headersObject[Object.keys(headersObject).find(k => k.toLowerCase() === asLowercase)];
|
|
138
|
+
}
|
|
108
139
|
this.on("input",function(msg,nodeSend,nodeDone) {
|
|
109
140
|
checkNodeAgentPatch();
|
|
110
141
|
//reset redirectList on each request
|
|
@@ -183,7 +214,6 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
|
|
183
214
|
// TODO: add UI option to auto decompress. Setting to false for 1.x compatibility
|
|
184
215
|
opts.decompress = false;
|
|
185
216
|
opts.method = method;
|
|
186
|
-
opts.headers = {};
|
|
187
217
|
opts.retry = 0;
|
|
188
218
|
opts.responseType = 'buffer';
|
|
189
219
|
opts.maxRedirects = 21;
|
|
@@ -229,34 +259,85 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
|
|
229
259
|
]
|
|
230
260
|
}
|
|
231
261
|
|
|
232
|
-
|
|
233
|
-
|
|
262
|
+
let ctSet = "Content-Type"; // set default camel case
|
|
263
|
+
let clSet = "Content-Length";
|
|
264
|
+
const normaliseKnownHeader = function (name) {
|
|
265
|
+
const _name = name.toLowerCase();
|
|
266
|
+
// only normalise the known headers used later in this
|
|
267
|
+
// function. Otherwise leave them alone.
|
|
268
|
+
switch (_name) {
|
|
269
|
+
case "content-type":
|
|
270
|
+
ctSet = name;
|
|
271
|
+
name = _name;
|
|
272
|
+
break;
|
|
273
|
+
case "content-length":
|
|
274
|
+
clSet = name;
|
|
275
|
+
name = _name;
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
return name;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
opts.headers = {};
|
|
282
|
+
//add msg.headers
|
|
283
|
+
//NOTE: ui headers will take precidence over msg.headers
|
|
234
284
|
if (msg.headers) {
|
|
235
285
|
if (msg.headers.hasOwnProperty('x-node-red-request-node')) {
|
|
236
|
-
|
|
286
|
+
const headerHash = msg.headers['x-node-red-request-node'];
|
|
237
287
|
delete msg.headers['x-node-red-request-node'];
|
|
238
|
-
|
|
288
|
+
const hash = hashSum(msg.headers);
|
|
239
289
|
if (hash === headerHash) {
|
|
240
290
|
delete msg.headers;
|
|
241
291
|
}
|
|
242
292
|
}
|
|
243
293
|
if (msg.headers) {
|
|
244
|
-
for (
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
294
|
+
for (let hn in msg.headers) {
|
|
295
|
+
const name = normaliseKnownHeader(hn);
|
|
296
|
+
updateHeader(opts.headers, name, msg.headers[hn]);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
//add/remove/update headers from UI.
|
|
302
|
+
if (node.headers.length) {
|
|
303
|
+
for (let index = 0; index < node.headers.length; index++) {
|
|
304
|
+
const header = node.headers[index];
|
|
305
|
+
let headerName, headerValue;
|
|
306
|
+
if (header.keyType === "other") {
|
|
307
|
+
headerName = header.keyValue
|
|
308
|
+
} else if (header.keyType === "msg") {
|
|
309
|
+
RED.util.evaluateNodeProperty(header.keyValue, header.keyType, node, msg, (err, value) => {
|
|
310
|
+
if (err) {
|
|
311
|
+
//ignore header
|
|
312
|
+
} else {
|
|
313
|
+
headerName = value;
|
|
251
314
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
315
|
+
});
|
|
316
|
+
} else {
|
|
317
|
+
headerName = header.keyType
|
|
318
|
+
}
|
|
319
|
+
if (!headerName) {
|
|
320
|
+
continue; //skip if header name is empyy
|
|
256
321
|
}
|
|
322
|
+
if (header.valueType === "other") {
|
|
323
|
+
headerValue = header.valueValue
|
|
324
|
+
} else if (header.valueType === "msg") {
|
|
325
|
+
RED.util.evaluateNodeProperty(header.valueValue, header.valueType, node, msg, (err, value) => {
|
|
326
|
+
if (err) {
|
|
327
|
+
//ignore header
|
|
328
|
+
} else {
|
|
329
|
+
headerValue = value;
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
} else {
|
|
333
|
+
headerValue = header.valueType
|
|
334
|
+
}
|
|
335
|
+
const hn = normaliseKnownHeader(headerName);
|
|
336
|
+
updateHeader(opts.headers, hn, headerValue);
|
|
257
337
|
}
|
|
258
338
|
}
|
|
259
339
|
|
|
340
|
+
|
|
260
341
|
if (msg.hasOwnProperty('followRedirects')) {
|
|
261
342
|
opts.followRedirect = !!msg.followRedirects;
|
|
262
343
|
}
|
|
@@ -83,13 +83,19 @@
|
|
|
83
83
|
return true;
|
|
84
84
|
}
|
|
85
85
|
else {
|
|
86
|
-
|
|
86
|
+
if (RED.nodes.node(this.server) != null) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
return RED._("node-red:websocket.errors.missing-server");
|
|
87
90
|
}
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
function ws_validateclient() {
|
|
91
94
|
if ($("#node-input-mode").val() === 'client' || (this.client && !this.server)) {
|
|
92
|
-
|
|
95
|
+
if (RED.nodes.node(this.client) != null) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
return RED._("node-red:websocket.errors.missing-client");
|
|
93
99
|
}
|
|
94
100
|
else {
|
|
95
101
|
return true;
|
|
@@ -138,7 +144,9 @@
|
|
|
138
144
|
RED.nodes.registerType('websocket-listener',{
|
|
139
145
|
category: 'config',
|
|
140
146
|
defaults: {
|
|
141
|
-
path: {value:"",required:true,
|
|
147
|
+
path: {value:"",required:true,
|
|
148
|
+
label:RED._("node-red:websocket.label.path"),
|
|
149
|
+
validate:RED.validators.regex(/^((?!\/debug\/ws).)*$/)},
|
|
142
150
|
wholemsg: {value:"false"}
|
|
143
151
|
},
|
|
144
152
|
inputs:0,
|
|
@@ -174,10 +182,16 @@
|
|
|
174
182
|
RED.nodes.registerType('websocket-client',{
|
|
175
183
|
category: 'config',
|
|
176
184
|
defaults: {
|
|
177
|
-
path: {
|
|
185
|
+
path: {
|
|
186
|
+
value:"",required:true,
|
|
187
|
+
label:RED._("node-red:websocket.label.path"),
|
|
188
|
+
validate:RED.validators.regex(/^((?!\/debug\/ws).)*$/)},
|
|
178
189
|
tls: {type:"tls-config",required: false},
|
|
179
190
|
wholemsg: {value:"false"},
|
|
180
|
-
hb: {
|
|
191
|
+
hb: {
|
|
192
|
+
value: "",
|
|
193
|
+
label:RED._("node-red:websocket.sendheartbeat"),
|
|
194
|
+
validate: RED.validators.number(/*blank allowed*/true) },
|
|
181
195
|
subprotocol: {value:"",required: false}
|
|
182
196
|
},
|
|
183
197
|
inputs:0,
|
|
@@ -35,8 +35,6 @@ module.exports = function(RED) {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
var listenerNodes = {};
|
|
38
|
-
var activeListenerNodes = 0;
|
|
39
|
-
|
|
40
38
|
|
|
41
39
|
// A node red node that sets up a local websocket server
|
|
42
40
|
function WebSocketListenerNode(n) {
|
|
@@ -166,7 +164,6 @@ module.exports = function(RED) {
|
|
|
166
164
|
}
|
|
167
165
|
|
|
168
166
|
if (node.isServer) {
|
|
169
|
-
activeListenerNodes++;
|
|
170
167
|
if (!serverUpgradeAdded) {
|
|
171
168
|
RED.server.on('upgrade', handleServerUpgrade);
|
|
172
169
|
serverUpgradeAdded = true
|
|
@@ -210,7 +207,7 @@ module.exports = function(RED) {
|
|
|
210
207
|
startconn(); // start outbound connection
|
|
211
208
|
}
|
|
212
209
|
|
|
213
|
-
node.on("close", function() {
|
|
210
|
+
node.on("close", function(done) {
|
|
214
211
|
if (node.heartbeatInterval) {
|
|
215
212
|
clearInterval(node.heartbeatInterval);
|
|
216
213
|
}
|
|
@@ -218,19 +215,25 @@ module.exports = function(RED) {
|
|
|
218
215
|
delete listenerNodes[node.fullPath];
|
|
219
216
|
node.server.close();
|
|
220
217
|
node._inputNodes = [];
|
|
221
|
-
activeListenerNodes--;
|
|
222
|
-
// if (activeListenerNodes === 0 && serverUpgradeAdded) {
|
|
223
|
-
// RED.server.removeListener('upgrade', handleServerUpgrade);
|
|
224
|
-
// serverUpgradeAdded = false;
|
|
225
|
-
// }
|
|
226
218
|
}
|
|
227
219
|
else {
|
|
228
220
|
node.closing = true;
|
|
229
221
|
node.server.close();
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
222
|
+
//wait 20*50 (1000ms max) for ws to close.
|
|
223
|
+
//call done when readyState === ws.CLOSED (or 1000ms, whichever comes fist)
|
|
224
|
+
const closeMonitorInterval = 20;
|
|
225
|
+
let closeMonitorCount = 50;
|
|
226
|
+
let si = setInterval(() => {
|
|
227
|
+
if(node.server.readyState === ws.CLOSED || closeMonitorCount <= 0) {
|
|
228
|
+
if (node.tout) {
|
|
229
|
+
clearTimeout(node.tout);
|
|
230
|
+
node.tout = null;
|
|
231
|
+
}
|
|
232
|
+
clearInterval(si);
|
|
233
|
+
return done();
|
|
234
|
+
}
|
|
235
|
+
closeMonitorCount--;
|
|
236
|
+
}, closeMonitorInterval);
|
|
234
237
|
}
|
|
235
238
|
});
|
|
236
239
|
}
|