@node-red/nodes 2.2.1 → 3.0.0-beta.2
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/99-sample.html.demo +1 -1
- 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 +59 -6
- package/core/common/21-debug.js +57 -27
- package/core/common/60-link.html +65 -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 +19 -12
- 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 +252 -105
- package/core/network/21-httpin.html +10 -6
- package/core/network/21-httprequest.html +219 -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 +6 -2
- 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 +19 -3
- package/locales/en-US/messages.json +71 -18
- 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 +13 -0
- package/locales/ja/messages.json +84 -32
- package/locales/ja/network/21-httprequest.html +1 -1
- package/locales/ja/storage/10-file.html +8 -6
- 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,109 @@
|
|
|
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: RED._("node-red:httpin.label.other"),
|
|
131
|
+
hasValue: true, icon: "red/images/typedInput/az.svg" },
|
|
132
|
+
{ value: "msg", label: "msg.", hasValue: true },
|
|
133
|
+
]
|
|
134
|
+
const headerOptions = {};
|
|
135
|
+
const defaultOptions = [
|
|
136
|
+
{ value: "other", label: RED._("node-red:httpin.label.other"),
|
|
137
|
+
hasValue: true, icon: "red/images/typedInput/az.svg" },
|
|
138
|
+
{ value: "msg", label: "msg.", hasValue: true },
|
|
139
|
+
];
|
|
140
|
+
headerOptions["accept"] = [
|
|
141
|
+
{ value: "text/plain", label: "text/plain", hasValue: false },
|
|
142
|
+
{ value: "text/html", label: "text/html", hasValue: false },
|
|
143
|
+
{ value: "application/json", label: "application/json", hasValue: false },
|
|
144
|
+
{ value: "application/xml", label: "application/xml", hasValue: false },
|
|
145
|
+
...defaultOptions,
|
|
146
|
+
];
|
|
147
|
+
headerOptions["accept-encoding"] = [
|
|
148
|
+
{ value: "gzip", label: "gzip", hasValue: false },
|
|
149
|
+
{ value: "deflate", label: "deflate", hasValue: false },
|
|
150
|
+
{ value: "compress", label: "compress", hasValue: false },
|
|
151
|
+
{ value: "br", label: "br", hasValue: false },
|
|
152
|
+
{ value: "gzip, deflate", label: "gzip, deflate", hasValue: false },
|
|
153
|
+
{ value: "gzip, deflate, br", label: "gzip, deflate, br", hasValue: false },
|
|
154
|
+
...defaultOptions,
|
|
155
|
+
];
|
|
156
|
+
headerOptions["accept-language"] = [
|
|
157
|
+
{ value: "*", label: "*", hasValue: false },
|
|
158
|
+
{ value: "en-GB, en-US, en;q=0.9", label: "en-GB, en-US, en;q=0.9", hasValue: false },
|
|
159
|
+
{ 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 },
|
|
160
|
+
{ value: "es-mx,es,en;q=0.5", label: "es-mx,es,en;q=0.5", hasValue: false },
|
|
161
|
+
{ value: "fr-CH, fr;q=0.9, en;q=0.8", label: "fr-CH, fr;q=0.9, en;q=0.8", hasValue: false },
|
|
162
|
+
{ 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 },
|
|
163
|
+
{ value: "ja-JP, jp", label: "ja-JP, jp", hasValue: false },
|
|
164
|
+
...defaultOptions,
|
|
165
|
+
];
|
|
166
|
+
headerOptions["content-type"] = [
|
|
167
|
+
{ value: "text/css", label: "text/css", hasValue: false },
|
|
168
|
+
{ value: "text/plain", label: "text/plain", hasValue: false },
|
|
169
|
+
{ value: "text/html", label: "text/html", hasValue: false },
|
|
170
|
+
{ value: "application/json", label: "application/json", hasValue: false },
|
|
171
|
+
{ value: "application/octet-stream", label: "application/octet-stream", hasValue: false },
|
|
172
|
+
{ value: "application/pdf", label: "application/pdf", hasValue: false },
|
|
173
|
+
{ value: "application/xml", label: "application/xml", hasValue: false },
|
|
174
|
+
{ value: "application/zip", label: "application/zip", hasValue: false },
|
|
175
|
+
{ value: "multipart/form-data", label: "multipart/form-data", hasValue: false },
|
|
176
|
+
{ value: "audio/aac", label: "audio/aac", hasValue: false },
|
|
177
|
+
{ value: "audio/ac3", label: "audio/ac3", hasValue: false },
|
|
178
|
+
{ value: "audio/basic", label: "audio/basic", hasValue: false },
|
|
179
|
+
{ value: "audio/mp4", label: "audio/mp4", hasValue: false },
|
|
180
|
+
{ value: "audio/ogg", label: "audio/ogg", hasValue: false },
|
|
181
|
+
{ value: "image/bmp", label: "image/bmp", hasValue: false },
|
|
182
|
+
{ value: "image/gif", label: "image/gif", hasValue: false },
|
|
183
|
+
{ value: "image/jpeg", label: "image/jpeg", hasValue: false },
|
|
184
|
+
{ value: "image/png", label: "image/png", hasValue: false },
|
|
185
|
+
{ value: "image/tiff", label: "image/tiff", hasValue: false },
|
|
186
|
+
...defaultOptions,
|
|
187
|
+
];
|
|
188
|
+
headerOptions["cache-control"] = [
|
|
189
|
+
{ value: "max-age=0", label: "max-age=0", hasValue: false },
|
|
190
|
+
{ value: "max-age=86400", label: "max-age=86400", hasValue: false },
|
|
191
|
+
{ value: "no-cache", label: "no-cache", hasValue: false },
|
|
192
|
+
...defaultOptions,
|
|
193
|
+
];
|
|
194
|
+
|
|
195
|
+
headerOptions["user-agent"] = [
|
|
196
|
+
{ value: "Mozilla/5.0", label: "Mozilla/5.0", hasValue: false },
|
|
197
|
+
...defaultOptions,
|
|
198
|
+
];
|
|
199
|
+
|
|
200
|
+
function getHeaderOptions(headerName) {
|
|
201
|
+
const lc = (headerName || "").toLowerCase();
|
|
202
|
+
let opts = headerOptions[lc];
|
|
203
|
+
return opts || defaultOptions;
|
|
204
|
+
}
|
|
205
|
+
|
|
111
206
|
RED.nodes.registerType('http request',{
|
|
112
207
|
category: 'network',
|
|
113
208
|
color:"rgb(231, 231, 174)",
|
|
@@ -116,12 +211,25 @@
|
|
|
116
211
|
method:{value:"GET"},
|
|
117
212
|
ret: {value:"txt"},
|
|
118
213
|
paytoqs: {value: false},
|
|
119
|
-
url:{
|
|
120
|
-
|
|
214
|
+
url:{
|
|
215
|
+
value:"",
|
|
216
|
+
validate: function(v, opt) {
|
|
217
|
+
if ((v.trim().length === 0) ||
|
|
218
|
+
(v.indexOf("://") === -1) ||
|
|
219
|
+
(v.trim().indexOf("http") === 0)) {
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
return RED._("node-red:httpin.errors.invalid-url");
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
tls: {type:"tls-config",required: false,
|
|
226
|
+
label:RED._("node-red:httpin.tls-config") },
|
|
121
227
|
persist: {value:false},
|
|
122
|
-
proxy: {type:"http proxy",required: false
|
|
228
|
+
proxy: {type:"http proxy",required: false,
|
|
229
|
+
label:RED._("node-red:httpin.proxy-config") },
|
|
123
230
|
authType: {value: ""},
|
|
124
|
-
senderr: {value: false}
|
|
231
|
+
senderr: {value: false},
|
|
232
|
+
headers: { value: [] }
|
|
125
233
|
},
|
|
126
234
|
credentials: {
|
|
127
235
|
user: {type:"text"},
|
|
@@ -144,6 +252,7 @@
|
|
|
144
252
|
return this.name?"node_label_italic":"";
|
|
145
253
|
},
|
|
146
254
|
oneditprepare: function() {
|
|
255
|
+
const node = this;
|
|
147
256
|
$("#node-input-useAuth").on("change", function() {
|
|
148
257
|
if ($(this).is(":checked")) {
|
|
149
258
|
$(".node-input-useAuth-row").show();
|
|
@@ -157,9 +266,10 @@
|
|
|
157
266
|
$('#node-input-user').val('');
|
|
158
267
|
$('#node-input-password').val('');
|
|
159
268
|
}
|
|
269
|
+
RED.tray.resize();
|
|
160
270
|
});
|
|
161
271
|
$("#node-input-authType-select").on("change", function() {
|
|
162
|
-
|
|
272
|
+
const val = $(this).val();
|
|
163
273
|
$("#node-input-authType").val(val);
|
|
164
274
|
if (val === "basic" || val === "digest") {
|
|
165
275
|
$(".node-input-basic-row").show();
|
|
@@ -171,6 +281,7 @@
|
|
|
171
281
|
$('#node-span-token').show();
|
|
172
282
|
$('#node-input-user').val('');
|
|
173
283
|
}
|
|
284
|
+
RED.tray.resize();
|
|
174
285
|
});
|
|
175
286
|
$("#node-input-method").on("change", function() {
|
|
176
287
|
if ($(this).val() == "GET") {
|
|
@@ -178,17 +289,18 @@
|
|
|
178
289
|
} else {
|
|
179
290
|
$(".node-input-paytoqs-row").hide();
|
|
180
291
|
}
|
|
292
|
+
RED.tray.resize();
|
|
181
293
|
});
|
|
182
|
-
if (
|
|
294
|
+
if (node.paytoqs === true || node.paytoqs == "query") {
|
|
183
295
|
$("#node-input-paytoqs").val("query");
|
|
184
|
-
} else if (
|
|
296
|
+
} else if (node.paytoqs === "body") {
|
|
185
297
|
$("#node-input-paytoqs").val("body");
|
|
186
298
|
} else {
|
|
187
299
|
$("#node-input-paytoqs").val("ignore");
|
|
188
300
|
}
|
|
189
|
-
if (
|
|
301
|
+
if (node.authType) {
|
|
190
302
|
$('#node-input-useAuth').prop('checked', true);
|
|
191
|
-
$("#node-input-authType-select").val(
|
|
303
|
+
$("#node-input-authType-select").val(node.authType);
|
|
192
304
|
$("#node-input-authType-select").change();
|
|
193
305
|
} else {
|
|
194
306
|
$('#node-input-useAuth').prop('checked', false);
|
|
@@ -201,8 +313,9 @@
|
|
|
201
313
|
} else {
|
|
202
314
|
$("#node-row-tls").hide();
|
|
203
315
|
}
|
|
316
|
+
RED.tray.resize();
|
|
204
317
|
}
|
|
205
|
-
if (
|
|
318
|
+
if (node.tls) {
|
|
206
319
|
$('#node-input-usetls').prop('checked', true);
|
|
207
320
|
} else {
|
|
208
321
|
$('#node-input-usetls').prop('checked', false);
|
|
@@ -218,8 +331,9 @@
|
|
|
218
331
|
} else {
|
|
219
332
|
$("#node-input-useProxy-row").hide();
|
|
220
333
|
}
|
|
334
|
+
RED.tray.resize();
|
|
221
335
|
}
|
|
222
|
-
if (
|
|
336
|
+
if (node.proxy) {
|
|
223
337
|
$("#node-input-useProxy").prop("checked", true);
|
|
224
338
|
} else {
|
|
225
339
|
$("#node-input-useProxy").prop("checked", false);
|
|
@@ -235,7 +349,70 @@
|
|
|
235
349
|
} else {
|
|
236
350
|
$("#tip-json").hide();
|
|
237
351
|
}
|
|
352
|
+
RED.tray.resize();
|
|
238
353
|
});
|
|
354
|
+
const hasMatch = function (arr, value) {
|
|
355
|
+
return arr.some(function (ht) {
|
|
356
|
+
return ht.value === value
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
const headerList = $("#node-input-headers-container").css('min-height', '150px').css('min-width', '450px').editableList({
|
|
360
|
+
addItem: function (container, i, header) {
|
|
361
|
+
const row = $('<div/>').css({
|
|
362
|
+
overflow: 'hidden',
|
|
363
|
+
whiteSpace: 'nowrap',
|
|
364
|
+
display: 'flex'
|
|
365
|
+
}).appendTo(container);
|
|
366
|
+
const propertNameCell = $('<div/>').css({ 'flex-grow': 1 }).appendTo(row);
|
|
367
|
+
const propertyName = $('<input/>', { class: "node-input-header-name", type: "text", style: "width: 100%" })
|
|
368
|
+
.appendTo(propertNameCell)
|
|
369
|
+
.typedInput({ types: headerTypes });
|
|
370
|
+
|
|
371
|
+
const propertyValueCell = $('<div/>').css({ 'flex-grow': 1, 'margin-left': '10px' }).appendTo(row);
|
|
372
|
+
const propertyValue = $('<input/>', { class: "node-input-header-value", type: "text", style: "width: 100%" })
|
|
373
|
+
.appendTo(propertyValueCell)
|
|
374
|
+
.typedInput({
|
|
375
|
+
types: getHeaderOptions(header.keyType)
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
const setup = function(_header) {
|
|
379
|
+
const headerTypeIsAPreset = function(h) {return hasMatch(headerTypes, h) };
|
|
380
|
+
const headerValueIsAPreset = function(h, v) {return hasMatch(getHeaderOptions(h), v) };
|
|
381
|
+
const {keyType, keyValue, valueType, valueValue} = header;
|
|
382
|
+
if(keyType == "msg" || keyType == "other") {
|
|
383
|
+
propertyName.typedInput('type', keyType);
|
|
384
|
+
propertyName.typedInput('value', keyValue);
|
|
385
|
+
} else if (headerTypeIsAPreset(keyType)) {
|
|
386
|
+
propertyName.typedInput('type', keyType);
|
|
387
|
+
} else {
|
|
388
|
+
propertyName.typedInput('type', "other");
|
|
389
|
+
propertyName.typedInput('value', keyValue);
|
|
390
|
+
}
|
|
391
|
+
if(valueType == "msg" || valueType == "other") {
|
|
392
|
+
propertyValue.typedInput('type', valueType);
|
|
393
|
+
propertyValue.typedInput('value', valueValue);
|
|
394
|
+
} else if (headerValueIsAPreset(propertyName.typedInput('type'), valueType)) {
|
|
395
|
+
propertyValue.typedInput('type', valueType);
|
|
396
|
+
} else {
|
|
397
|
+
propertyValue.typedInput('type', "other");
|
|
398
|
+
propertyValue.typedInput('value', valueValue);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
setup(header);
|
|
402
|
+
|
|
403
|
+
propertyName.on('change', function (event) {
|
|
404
|
+
propertyValue.typedInput('types', getHeaderOptions(propertyName.typedInput('type')));
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
},
|
|
408
|
+
removable: true
|
|
409
|
+
});
|
|
410
|
+
if (node.headers) {
|
|
411
|
+
for (let index = 0; index < node.headers.length; index++) {
|
|
412
|
+
const element = node.headers[index];
|
|
413
|
+
headerList.editableList('addItem', node.headers[index]);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
239
416
|
},
|
|
240
417
|
oneditsave: function() {
|
|
241
418
|
if (!$("#node-input-usetls").is(':checked')) {
|
|
@@ -244,6 +421,36 @@
|
|
|
244
421
|
if (!$("#node-input-useProxy").is(":checked")) {
|
|
245
422
|
$("#node-input-proxy").val("_ADD_");
|
|
246
423
|
}
|
|
424
|
+
const headers = $("#node-input-headers-container").editableList('items');
|
|
425
|
+
const node = this;
|
|
426
|
+
node.headers = [];
|
|
427
|
+
headers.each(function(i) {
|
|
428
|
+
const header = $(this);
|
|
429
|
+
const keyType = header.find(".node-input-header-name").typedInput('type');
|
|
430
|
+
const keyValue = header.find(".node-input-header-name").typedInput('value');
|
|
431
|
+
const valueType = header.find(".node-input-header-value").typedInput('type');
|
|
432
|
+
const valueValue = header.find(".node-input-header-value").typedInput('value');
|
|
433
|
+
if (keyType !== '' || keyType === 'other' || keyType === 'msg') {
|
|
434
|
+
node.headers.push({
|
|
435
|
+
keyType, keyValue, valueType, valueValue
|
|
436
|
+
})
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
},
|
|
440
|
+
oneditresize: function(size) {
|
|
441
|
+
const dlg = $("#dialog-form");
|
|
442
|
+
const expandRow = dlg.find('.node-input-headers-container-row');
|
|
443
|
+
let height = dlg.height() - 5;
|
|
444
|
+
if(expandRow && expandRow.length){
|
|
445
|
+
const siblingRows = dlg.find('> .form-row:not(.node-input-headers-container-row)');
|
|
446
|
+
for (let i = 0; i < siblingRows.size(); i++) {
|
|
447
|
+
const cr = $(siblingRows[i]);
|
|
448
|
+
if(cr.is(":visible"))
|
|
449
|
+
height -= cr.outerHeight(true);
|
|
450
|
+
}
|
|
451
|
+
$("#node-input-headers-container").editableList('height',height);
|
|
452
|
+
}
|
|
247
453
|
}
|
|
248
454
|
});
|
|
455
|
+
})();
|
|
249
456
|
</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
|
}
|