mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Add UI for common headers/values
- Wrap HTML node script in IFFE (isolate module level vars & functions) - Add UI elements for setting headers in http req node edit form - Update built in help - Add tests
This commit is contained in:
parent
6a5c50ff77
commit
31b3a4c342
@ -104,10 +104,100 @@
|
|||||||
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
||||||
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
|
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-row" style="margin-bottom:0;">
|
||||||
|
<label><i class="fa fa-list"></i> <span data-i18n="httpin.label.headers"></span></label>
|
||||||
|
</div>
|
||||||
|
<div class="form-row node-input-headers-container-row">
|
||||||
|
<ol id="node-input-headers-container"></ol>
|
||||||
|
</div>
|
||||||
<div class="form-tips" id="tip-json" hidden><span data-i18n="httpin.tip.req"></span></div>
|
<div class="form-tips" id="tip-json" hidden><span data-i18n="httpin.tip.req"></span></div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
(function() {
|
||||||
|
const headerTypes = [
|
||||||
|
{ value: "Accept", label: "Accept", hasValue: false },
|
||||||
|
{ value: "Accept-Charset", label: "Accept-Charset", hasValue: false },
|
||||||
|
{ value: "Accept-Encoding", label: "Accept-Encoding", hasValue: false },
|
||||||
|
{ value: "Accept-Language", label: "Accept-Language", hasValue: false },
|
||||||
|
{ value: "Authorization", label: "Authorization", hasValue: false },
|
||||||
|
{ value: "content-type", label: "Content-Type", hasValue: false },
|
||||||
|
{ value: "Cache-Control", label: "Cache-Control", hasValue: false },
|
||||||
|
{ value: "User-Agent", label: "User-Agent", hasValue: false },
|
||||||
|
{ value: "location", label: "Location", hasValue: false },
|
||||||
|
{ value: "other", label: "other", hasValue: true, icon: "red/images/typedInput/az.png" },
|
||||||
|
{ value: "msg", label: "msg.", hasValue: true },
|
||||||
|
]
|
||||||
|
const headerOptions = {};
|
||||||
|
const defaultOptions = [
|
||||||
|
{ value: "other", label: "other", hasValue: true, icon: "red/images/typedInput/az.png" },
|
||||||
|
{ value: "msg", label: "msg.", hasValue: true },
|
||||||
|
];
|
||||||
|
headerOptions["accept"] = [
|
||||||
|
{ value: "text/plain", label: "text/plain", hasValue: false },
|
||||||
|
{ value: "text/html", label: "text/html", hasValue: false },
|
||||||
|
{ value: "application/json", label: "application/json", hasValue: false },
|
||||||
|
{ value: "application/xml", label: "application/xml", hasValue: false },
|
||||||
|
...defaultOptions,
|
||||||
|
];
|
||||||
|
headerOptions["accept-encoding"] = [
|
||||||
|
{ value: "gzip", label: "gzip", hasValue: false },
|
||||||
|
{ value: "deflate", label: "deflate", hasValue: false },
|
||||||
|
{ value: "gzip", label: "gzip", hasValue: false },
|
||||||
|
{ value: "gzip, deflate", label: "gzip, deflate", hasValue: false },
|
||||||
|
{ value: "gzip, deflate, br", label: "gzip, deflate, br", hasValue: false },
|
||||||
|
...defaultOptions,
|
||||||
|
];
|
||||||
|
headerOptions["accept-language"] = [
|
||||||
|
{ value: "*", label: "*", hasValue: false },
|
||||||
|
{ value: "en-GB, en-US, en;q=0.9", label: "en-GB, en-US, en;q=0.9", hasValue: false },
|
||||||
|
{ 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 },
|
||||||
|
{ value: "es-mx,es,en;q=0.5", label: "es-mx,es,en;q=0.5", hasValue: false },
|
||||||
|
{ value: "fr-CH, fr;q=0.9, en;q=0.8", label: "fr-CH, fr;q=0.9, en;q=0.8", hasValue: false },
|
||||||
|
{ 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 },
|
||||||
|
{ value: "jp", label: "jp", hasValue: false },
|
||||||
|
...defaultOptions,
|
||||||
|
];
|
||||||
|
headerOptions["content-type"] = [
|
||||||
|
{ value: "text/css", label: "text/css", hasValue: false },
|
||||||
|
{ value: "text/plain", label: "text/plain", hasValue: false },
|
||||||
|
{ value: "text/html", label: "text/html", hasValue: false },
|
||||||
|
{ value: "application/json", label: "application/json", hasValue: false },
|
||||||
|
{ value: "application/octet-stream", label: "application/octet-stream", hasValue: false },
|
||||||
|
{ value: "application/pdf", label: "application/pdf", hasValue: false },
|
||||||
|
{ value: "application/xml", label: "application/xml", hasValue: false },
|
||||||
|
{ value: "application/zip", label: "application/zip", hasValue: false },
|
||||||
|
{ value: "multipart/form-data", label: "multipart/form-data", hasValue: false },
|
||||||
|
{ value: "audio/aac", label: "audio/aac", hasValue: false },
|
||||||
|
{ value: "audio/ac3", label: "audio/ac3", hasValue: false },
|
||||||
|
{ value: "audio/basic", label: "audio/basic", hasValue: false },
|
||||||
|
{ value: "audio/mp4", label: "audio/mp4", hasValue: false },
|
||||||
|
{ value: "audio/ogg", label: "audio/ogg", hasValue: false },
|
||||||
|
{ value: "image/bmp", label: "image/bmp", hasValue: false },
|
||||||
|
{ value: "image/gif", label: "image/gif", hasValue: false },
|
||||||
|
{ value: "image/jpeg", label: "image/jpeg", hasValue: false },
|
||||||
|
{ value: "image/png", label: "image/png", hasValue: false },
|
||||||
|
{ value: "image/tiff", label: "image/tiff", hasValue: false },
|
||||||
|
...defaultOptions,
|
||||||
|
];
|
||||||
|
headerOptions["cache-control"] = [
|
||||||
|
{ value: "max-age=0", label: "max-age=0", hasValue: false },
|
||||||
|
{ value: "max-age=86400", label: "max-age=86400", hasValue: false },
|
||||||
|
{ value: "no-cache", label: "no-cache", hasValue: false },
|
||||||
|
...defaultOptions,
|
||||||
|
];
|
||||||
|
|
||||||
|
headerOptions["user-agent"] = [
|
||||||
|
{ value: "Mozilla/5.0", label: "Mozilla/5.0", hasValue: false },
|
||||||
|
...defaultOptions,
|
||||||
|
];
|
||||||
|
|
||||||
|
function getHeaderOptions(headerName) {
|
||||||
|
const lc = (headerName || "").toLowerCase();
|
||||||
|
let opts = headerOptions[lc];
|
||||||
|
return opts || defaultOptions;
|
||||||
|
}
|
||||||
|
|
||||||
RED.nodes.registerType('http request',{
|
RED.nodes.registerType('http request',{
|
||||||
category: 'network',
|
category: 'network',
|
||||||
color:"rgb(231, 231, 174)",
|
color:"rgb(231, 231, 174)",
|
||||||
@ -121,7 +211,8 @@
|
|||||||
persist: {value:false},
|
persist: {value:false},
|
||||||
proxy: {type:"http proxy",required: false},
|
proxy: {type:"http proxy",required: false},
|
||||||
authType: {value: ""},
|
authType: {value: ""},
|
||||||
senderr: {value: false}
|
senderr: {value: false},
|
||||||
|
headers: { value: [] }
|
||||||
},
|
},
|
||||||
credentials: {
|
credentials: {
|
||||||
user: {type:"text"},
|
user: {type:"text"},
|
||||||
@ -144,8 +235,9 @@
|
|||||||
return this.name?"node_label_italic":"";
|
return this.name?"node_label_italic":"";
|
||||||
},
|
},
|
||||||
oneditprepare: function() {
|
oneditprepare: function() {
|
||||||
|
const node = this;
|
||||||
$("#node-input-useAuth").on("change", function() {
|
$("#node-input-useAuth").on("change", function() {
|
||||||
if ($(this).is(":checked")) {
|
if ($(node).is(":checked")) {
|
||||||
$(".node-input-useAuth-row").show();
|
$(".node-input-useAuth-row").show();
|
||||||
// Nodes (< version 0.20.x) with credentials but without authentication type, need type 'basic'
|
// Nodes (< version 0.20.x) with credentials but without authentication type, need type 'basic'
|
||||||
if (!$('#node-input-authType').val()) {
|
if (!$('#node-input-authType').val()) {
|
||||||
@ -157,9 +249,10 @@
|
|||||||
$('#node-input-user').val('');
|
$('#node-input-user').val('');
|
||||||
$('#node-input-password').val('');
|
$('#node-input-password').val('');
|
||||||
}
|
}
|
||||||
|
RED.tray.resize();
|
||||||
});
|
});
|
||||||
$("#node-input-authType-select").on("change", function() {
|
$("#node-input-authType-select").on("change", function() {
|
||||||
var val = $(this).val();
|
const val = $(node).val();
|
||||||
$("#node-input-authType").val(val);
|
$("#node-input-authType").val(val);
|
||||||
if (val === "basic" || val === "digest") {
|
if (val === "basic" || val === "digest") {
|
||||||
$(".node-input-basic-row").show();
|
$(".node-input-basic-row").show();
|
||||||
@ -171,24 +264,26 @@
|
|||||||
$('#node-span-token').show();
|
$('#node-span-token').show();
|
||||||
$('#node-input-user').val('');
|
$('#node-input-user').val('');
|
||||||
}
|
}
|
||||||
|
RED.tray.resize();
|
||||||
});
|
});
|
||||||
$("#node-input-method").on("change", function() {
|
$("#node-input-method").on("change", function() {
|
||||||
if ($(this).val() == "GET") {
|
if ($(node).val() == "GET") {
|
||||||
$(".node-input-paytoqs-row").show();
|
$(".node-input-paytoqs-row").show();
|
||||||
} else {
|
} else {
|
||||||
$(".node-input-paytoqs-row").hide();
|
$(".node-input-paytoqs-row").hide();
|
||||||
}
|
}
|
||||||
|
RED.tray.resize();
|
||||||
});
|
});
|
||||||
if (this.paytoqs === true || this.paytoqs == "query") {
|
if (node.paytoqs === true || node.paytoqs == "query") {
|
||||||
$("#node-input-paytoqs").val("query");
|
$("#node-input-paytoqs").val("query");
|
||||||
} else if (this.paytoqs === "body") {
|
} else if (node.paytoqs === "body") {
|
||||||
$("#node-input-paytoqs").val("body");
|
$("#node-input-paytoqs").val("body");
|
||||||
} else {
|
} else {
|
||||||
$("#node-input-paytoqs").val("ignore");
|
$("#node-input-paytoqs").val("ignore");
|
||||||
}
|
}
|
||||||
if (this.authType) {
|
if (node.authType) {
|
||||||
$('#node-input-useAuth').prop('checked', true);
|
$('#node-input-useAuth').prop('checked', true);
|
||||||
$("#node-input-authType-select").val(this.authType);
|
$("#node-input-authType-select").val(node.authType);
|
||||||
$("#node-input-authType-select").change();
|
$("#node-input-authType-select").change();
|
||||||
} else {
|
} else {
|
||||||
$('#node-input-useAuth').prop('checked', false);
|
$('#node-input-useAuth').prop('checked', false);
|
||||||
@ -201,8 +296,9 @@
|
|||||||
} else {
|
} else {
|
||||||
$("#node-row-tls").hide();
|
$("#node-row-tls").hide();
|
||||||
}
|
}
|
||||||
|
RED.tray.resize();
|
||||||
}
|
}
|
||||||
if (this.tls) {
|
if (node.tls) {
|
||||||
$('#node-input-usetls').prop('checked', true);
|
$('#node-input-usetls').prop('checked', true);
|
||||||
} else {
|
} else {
|
||||||
$('#node-input-usetls').prop('checked', false);
|
$('#node-input-usetls').prop('checked', false);
|
||||||
@ -218,8 +314,9 @@
|
|||||||
} else {
|
} else {
|
||||||
$("#node-input-useProxy-row").hide();
|
$("#node-input-useProxy-row").hide();
|
||||||
}
|
}
|
||||||
|
RED.tray.resize();
|
||||||
}
|
}
|
||||||
if (this.proxy) {
|
if (node.proxy) {
|
||||||
$("#node-input-useProxy").prop("checked", true);
|
$("#node-input-useProxy").prop("checked", true);
|
||||||
} else {
|
} else {
|
||||||
$("#node-input-useProxy").prop("checked", false);
|
$("#node-input-useProxy").prop("checked", false);
|
||||||
@ -235,7 +332,70 @@
|
|||||||
} else {
|
} else {
|
||||||
$("#tip-json").hide();
|
$("#tip-json").hide();
|
||||||
}
|
}
|
||||||
|
RED.tray.resize();
|
||||||
});
|
});
|
||||||
|
const hasMatch = function (arr, value) {
|
||||||
|
return arr.some(function (ht) {
|
||||||
|
return ht.value === value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const headerList = $("#node-input-headers-container").css('min-height', '150px').css('min-width', '450px').editableList({
|
||||||
|
addItem: function (container, i, header) {
|
||||||
|
const row = $('<div/>').css({
|
||||||
|
overflow: 'hidden',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
display: 'flex'
|
||||||
|
}).appendTo(container);
|
||||||
|
const propertNameCell = $('<div/>').css({ 'flex-grow': 1 }).appendTo(row);
|
||||||
|
const propertyName = $('<input/>', { class: "node-input-header-name", type: "text", style: "width: 100%" })
|
||||||
|
.appendTo(propertNameCell)
|
||||||
|
.typedInput({ types: headerTypes });
|
||||||
|
|
||||||
|
const propertyValueCell = $('<div/>').css({ 'flex-grow': 1, 'margin-left': '10px' }).appendTo(row);
|
||||||
|
const propertyValue = $('<input/>', { class: "node-input-header-value", type: "text", style: "width: 100%" })
|
||||||
|
.appendTo(propertyValueCell)
|
||||||
|
.typedInput({
|
||||||
|
types: getHeaderOptions(header.keyType)
|
||||||
|
});
|
||||||
|
|
||||||
|
const setup = function(_header) {
|
||||||
|
const headerTypeIsAPreset = function(h) {return hasMatch(headerTypes, h) };
|
||||||
|
const headerValueIsAPreset = function(h, v) {return hasMatch(getHeaderOptions(h), v) };
|
||||||
|
const {keyType, keyValue, valueType, valueValue} = header;
|
||||||
|
if(keyType == "msg" || keyType == "other") {
|
||||||
|
propertyName.typedInput('type', keyType);
|
||||||
|
propertyName.typedInput('value', keyValue);
|
||||||
|
} else if (headerTypeIsAPreset(keyType)) {
|
||||||
|
propertyName.typedInput('type', keyType);
|
||||||
|
} else {
|
||||||
|
propertyName.typedInput('type', "other");
|
||||||
|
propertyName.typedInput('value', keyValue);
|
||||||
|
}
|
||||||
|
if(valueType == "msg" || valueType == "other") {
|
||||||
|
propertyValue.typedInput('type', valueType);
|
||||||
|
propertyValue.typedInput('value', valueValue);
|
||||||
|
} else if (headerValueIsAPreset(propertyName.typedInput('type'), valueType)) {
|
||||||
|
propertyValue.typedInput('type', valueType);
|
||||||
|
} else {
|
||||||
|
propertyValue.typedInput('type', "other");
|
||||||
|
propertyValue.typedInput('value', valueValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setup(header);
|
||||||
|
|
||||||
|
propertyName.on('change', function (event) {
|
||||||
|
propertyValue.typedInput('types', getHeaderOptions(propertyName.typedInput('type')));
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
removable: true
|
||||||
|
});
|
||||||
|
if (node.headers) {
|
||||||
|
for (let index = 0; index < node.headers.length; index++) {
|
||||||
|
const element = node.headers[index];
|
||||||
|
headerList.editableList('addItem', node.headers[index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
oneditsave: function() {
|
oneditsave: function() {
|
||||||
if (!$("#node-input-usetls").is(':checked')) {
|
if (!$("#node-input-usetls").is(':checked')) {
|
||||||
@ -244,6 +404,36 @@
|
|||||||
if (!$("#node-input-useProxy").is(":checked")) {
|
if (!$("#node-input-useProxy").is(":checked")) {
|
||||||
$("#node-input-proxy").val("_ADD_");
|
$("#node-input-proxy").val("_ADD_");
|
||||||
}
|
}
|
||||||
|
const headers = $("#node-input-headers-container").editableList('items');
|
||||||
|
const node = this;
|
||||||
|
node.headers = [];
|
||||||
|
headers.each(function(i) {
|
||||||
|
const header = $(node);
|
||||||
|
const keyType = header.find(".node-input-header-name").typedInput('type');
|
||||||
|
const keyValue = header.find(".node-input-header-name").typedInput('value');
|
||||||
|
const valueType = header.find(".node-input-header-value").typedInput('type');
|
||||||
|
const valueValue = header.find(".node-input-header-value").typedInput('value');
|
||||||
|
if (keyType !== '' || keyType === 'other' || keyType === 'msg') {
|
||||||
|
node.headers.push({
|
||||||
|
keyType, keyValue, valueType, valueValue
|
||||||
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
oneditresize: function(size) {
|
||||||
|
const dlg = $("#dialog-form");
|
||||||
|
const expandRow = dlg.find('.node-input-headers-container-row');
|
||||||
|
let height = dlg.height() - 5;
|
||||||
|
if(expandRow && expandRow.length){
|
||||||
|
const siblingRows = dlg.find('> .form-row:not(.node-input-headers-container-row)');
|
||||||
|
for (let i = 0; i < siblingRows.size(); i++) {
|
||||||
|
const cr = $(siblingRows[i]);
|
||||||
|
if(cr.is(":visible"))
|
||||||
|
height -= cr.outerHeight(true);
|
||||||
|
}
|
||||||
|
$("#node-input-headers-container").editableList('height',height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
@ -73,7 +73,7 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
|||||||
var paytobody = false;
|
var paytobody = false;
|
||||||
var redirectList = [];
|
var redirectList = [];
|
||||||
var sendErrorsToCatch = n.senderr;
|
var sendErrorsToCatch = n.senderr;
|
||||||
|
node.headers = n.headers || [];
|
||||||
var nodeHTTPPersistent = n["persist"];
|
var nodeHTTPPersistent = n["persist"];
|
||||||
if (n.tls) {
|
if (n.tls) {
|
||||||
var tlsNode = RED.nodes.getNode(n.tls);
|
var tlsNode = RED.nodes.getNode(n.tls);
|
||||||
@ -105,6 +105,37 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
|||||||
timingLog = RED.settings.httpRequestTimingLog;
|
timingLog = RED.settings.httpRequestTimingLog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Case insensitive header value update util function
|
||||||
|
* @param {object} headersObject The opt.headers object to update
|
||||||
|
* @param {string} name The header name
|
||||||
|
* @param {string} value The header value to set (if blank, header is removed)
|
||||||
|
*/
|
||||||
|
const updateHeader = function(headersObject, name, value ) {
|
||||||
|
const hn = name.toLowerCase();
|
||||||
|
const keys = Object.keys(headersObject);
|
||||||
|
const matchingKeys = keys.filter(e => e.toLowerCase() == hn)
|
||||||
|
const updateKey = (k,v) => {
|
||||||
|
delete headersObject[k]; //delete incase of case change
|
||||||
|
if(v) { headersObject[name] = v } //re-add with requested name & value
|
||||||
|
}
|
||||||
|
if(matchingKeys.length == 0) {
|
||||||
|
updateKey(name, value)
|
||||||
|
} else {
|
||||||
|
matchingKeys.forEach(k => {
|
||||||
|
updateKey(k, value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {Object} headersObject
|
||||||
|
* @param {string} name
|
||||||
|
* @return {any} value
|
||||||
|
*/
|
||||||
|
const getHeaderValue = (headersObject, name) => {
|
||||||
|
const asLowercase = name.toLowercase();
|
||||||
|
return headersObject[Object.keys(headersObject).find(k => k.toLowerCase() === asLowercase)];
|
||||||
|
}
|
||||||
this.on("input",function(msg,nodeSend,nodeDone) {
|
this.on("input",function(msg,nodeSend,nodeDone) {
|
||||||
checkNodeAgentPatch();
|
checkNodeAgentPatch();
|
||||||
//reset redirectList on each request
|
//reset redirectList on each request
|
||||||
@ -183,7 +214,6 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
|||||||
// TODO: add UI option to auto decompress. Setting to false for 1.x compatibility
|
// TODO: add UI option to auto decompress. Setting to false for 1.x compatibility
|
||||||
opts.decompress = false;
|
opts.decompress = false;
|
||||||
opts.method = method;
|
opts.method = method;
|
||||||
opts.headers = {};
|
|
||||||
opts.retry = 0;
|
opts.retry = 0;
|
||||||
opts.responseType = 'buffer';
|
opts.responseType = 'buffer';
|
||||||
opts.maxRedirects = 21;
|
opts.maxRedirects = 21;
|
||||||
@ -229,33 +259,84 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
var ctSet = "Content-Type"; // set default camel case
|
let ctSet = "Content-Type"; // set default camel case
|
||||||
var clSet = "Content-Length";
|
let clSet = "Content-Length";
|
||||||
|
const normaliseKnownHeader = function (name) {
|
||||||
|
const _name = name.toLowerCase();
|
||||||
|
// only normalise the known headers used later in this
|
||||||
|
// function. Otherwise leave them alone.
|
||||||
|
switch (_name) {
|
||||||
|
case "content-type":
|
||||||
|
ctSet = name;
|
||||||
|
name = _name;
|
||||||
|
break;
|
||||||
|
case "content-length":
|
||||||
|
clSet = name;
|
||||||
|
name = _name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.headers = {};
|
||||||
|
//add msg.headers
|
||||||
|
//NOTE: ui headers will take precidence over msg.headers
|
||||||
if (msg.headers) {
|
if (msg.headers) {
|
||||||
if (msg.headers.hasOwnProperty('x-node-red-request-node')) {
|
if (msg.headers.hasOwnProperty('x-node-red-request-node')) {
|
||||||
var headerHash = msg.headers['x-node-red-request-node'];
|
const headerHash = msg.headers['x-node-red-request-node'];
|
||||||
delete msg.headers['x-node-red-request-node'];
|
delete msg.headers['x-node-red-request-node'];
|
||||||
var hash = hashSum(msg.headers);
|
const hash = hashSum(msg.headers);
|
||||||
if (hash === headerHash) {
|
if (hash === headerHash) {
|
||||||
delete msg.headers;
|
delete msg.headers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (msg.headers) {
|
if (msg.headers) {
|
||||||
for (var v in msg.headers) {
|
for (let hn in msg.headers) {
|
||||||
if (msg.headers.hasOwnProperty(v)) {
|
const name = normaliseKnownHeader(hn);
|
||||||
var name = v.toLowerCase();
|
updateHeader(opts.headers, name, msg.headers[hn]);
|
||||||
if (name !== "content-type" && name !== "content-length") {
|
|
||||||
// only normalise the known headers used later in this
|
|
||||||
// function. Otherwise leave them alone.
|
|
||||||
name = v;
|
|
||||||
}
|
|
||||||
else if (name === 'content-type') { ctSet = v; }
|
|
||||||
else { clSet = v; }
|
|
||||||
opts.headers[name] = msg.headers[v];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//add/remove/update headers from UI.
|
||||||
|
if (node.headers.length) {
|
||||||
|
for (let index = 0; index < node.headers.length; index++) {
|
||||||
|
const header = node.headers[index];
|
||||||
|
let headerName, headerValue;
|
||||||
|
if (header.keyType === "other") {
|
||||||
|
headerName = header.keyValue
|
||||||
|
} else if (header.keyType === "msg") {
|
||||||
|
RED.util.evaluateNodeProperty(header.keyValue, header.keyType, node, msg, (err, value) => {
|
||||||
|
if (err) {
|
||||||
|
//ignore header
|
||||||
|
} else {
|
||||||
|
headerName = value;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
headerName = header.keyType
|
||||||
|
}
|
||||||
|
if (!headerName) {
|
||||||
|
continue; //skip if header name is empyy
|
||||||
|
}
|
||||||
|
if (header.valueType === "other") {
|
||||||
|
headerValue = header.valueValue
|
||||||
|
} else if (header.valueType === "msg") {
|
||||||
|
RED.util.evaluateNodeProperty(header.valueValue, header.valueType, node, msg, (err, value) => {
|
||||||
|
if (err) {
|
||||||
|
//ignore header
|
||||||
|
} else {
|
||||||
|
headerValue = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
headerValue = header.valueType
|
||||||
|
}
|
||||||
|
const hn = normaliseKnownHeader(headerName);
|
||||||
|
updateHeader(opts.headers, hn, headerValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (msg.hasOwnProperty('followRedirects')) {
|
if (msg.hasOwnProperty('followRedirects')) {
|
||||||
opts.followRedirect = !!msg.followRedirects;
|
opts.followRedirect = !!msg.followRedirects;
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
<dd>If not configured in the node, this optional property sets the HTTP method of the request.
|
<dd>If not configured in the node, this optional property sets the HTTP method of the request.
|
||||||
Must be one of <code>GET</code>, <code>PUT</code>, <code>POST</code>, <code>PATCH</code> or <code>DELETE</code>.</dd>
|
Must be one of <code>GET</code>, <code>PUT</code>, <code>POST</code>, <code>PATCH</code> or <code>DELETE</code>.</dd>
|
||||||
<dt class="optional">headers <span class="property-type">object</span></dt>
|
<dt class="optional">headers <span class="property-type">object</span></dt>
|
||||||
<dd>Sets the HTTP headers of the request.</dd>
|
<dd>Sets the HTTP headers of the request. NOTE: Any headers set in the UI will overwrite any matching headers in <code>msg.headers</code> </dd>
|
||||||
<dt class="optional">cookies <span class="property-type">object</span></dt>
|
<dt class="optional">cookies <span class="property-type">object</span></dt>
|
||||||
<dd>If set, can be used to send cookies with the request.</dd>
|
<dd>If set, can be used to send cookies with the request.</dd>
|
||||||
<dt class="optional">payload</dt>
|
<dt class="optional">payload</dt>
|
||||||
|
@ -1500,6 +1500,50 @@ describe('HTTP Request Node', function() {
|
|||||||
n1.receive({payload:{foo:"bar"}, headers: { 'content-type': 'text/plain', "x-node-red-request-node":"INVALID_SUM"}});
|
n1.receive({payload:{foo:"bar"}, headers: { 'content-type': 'text/plain', "x-node-red-request-node":"INVALID_SUM"}});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should use ui headers', function (done) {
|
||||||
|
const flow = [
|
||||||
|
{
|
||||||
|
id: "n1", type: "http request", wires: [["n2"]], method: "GET", ret: "obj", url: getTestURL('/rawHeaders'),
|
||||||
|
"headers": [
|
||||||
|
{ "keyType": "Accept", "keyValue": "", "valueType": "application/json", "valueValue": "" },//set "Accept" to "application/json"
|
||||||
|
{ "keyType": "Content-Type", "keyValue": "", "valueType": "application/json", "valueValue": "" },//overwrite msg.headers['content-type'] with UI header 'Content-Type']
|
||||||
|
{ "keyType": "msg", "keyValue": "dynamicHeaderName", "valueType": "msg", "valueValue": "dynamicHeaderValue" }, //dynamic msg.dynamicHeaderName/msg.dynamicHeaderValue
|
||||||
|
{ "keyType": "other", "keyValue": "static-header-name", "valueType": "other", "valueValue": "static-header-value" }, //static "other" header and value
|
||||||
|
{ "keyType": "Location", "keyValue": "", "valueType": "other", "valueValue": "" }, //delete "Location" header (initially set in msg.headers['location'] by passing empty string for value
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ id: "n2", type: "helper" }];
|
||||||
|
helper.load(httpRequestNode, flow, function () {
|
||||||
|
const n1 = helper.getNode("n1");
|
||||||
|
const n2 = helper.getNode("n2");
|
||||||
|
n2.on("input", function (msg) {
|
||||||
|
try {
|
||||||
|
msg.should.have.property('statusCode',200);
|
||||||
|
msg.payload.should.have.property('headers');
|
||||||
|
//msg.headers['Accept'] should be set by Flow UI
|
||||||
|
msg.payload.headers.should.have.property('Accept').which.startWith('application/json');
|
||||||
|
//msg.headers['content-type'] should be updated to 'Content-Type' by the value set in the Flow UI
|
||||||
|
msg.payload.headers.should.have.property('Content-Type').which.startWith('application/json');
|
||||||
|
//msg.dynamicHeaderName should be present in headers with the value of msg.dynamicHeaderValue
|
||||||
|
msg.payload.headers.should.have.property('dyn-header-name').which.startWith('dyn-header-value');
|
||||||
|
//static (custom) header set in Flow UI should be present
|
||||||
|
msg.payload.headers.should.have.property('static-header-name').which.startWith('static-header-value');
|
||||||
|
//msg.headers['location'] should be deleted because Flow UI "Location" header has a blank value
|
||||||
|
//ensures headers with matching characters but different case are eliminated
|
||||||
|
msg.payload.headers.should.not.have.property('location');
|
||||||
|
msg.payload.headers.should.not.have.property('Location');
|
||||||
|
done();
|
||||||
|
} catch (err) {
|
||||||
|
done(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Pass in a headers property with a "content-type" & "location" header
|
||||||
|
// Pass in dynamicHeaderName & dynamicHeaderValue properties to set the Flow UI `msg.xxx` header entries
|
||||||
|
n1.receive({ payload: { foo: "bar" }, dynamicHeaderName: "dyn-header-name", dynamicHeaderValue: "dyn-header-value", headers: { 'content-type': 'text/plain', 'location': 'london' } });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('protocol', function() {
|
describe('protocol', function() {
|
||||||
@ -1976,8 +2020,14 @@ describe('HTTP Request Node', function() {
|
|||||||
|
|
||||||
describe('file-upload', function() {
|
describe('file-upload', function() {
|
||||||
it('should upload a file', function(done) {
|
it('should upload a file', function(done) {
|
||||||
var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'POST',ret:'obj',url:getTestURL('/file-upload')},
|
const flow = [
|
||||||
{id:"n2", type:"helper"}];
|
{
|
||||||
|
id: 'n1', type: 'http request', wires: [['n2']], method: 'POST', ret: 'obj', url: getTestURL('/file-upload'), headers: [
|
||||||
|
{ "keyType": "Content-Type", "keyValue": "", "valueType": "multipart/form-data", "valueValue": "" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ id: "n2", type: "helper" }
|
||||||
|
];
|
||||||
helper.load(httpRequestNode, flow, function() {
|
helper.load(httpRequestNode, flow, function() {
|
||||||
var n1 = helper.getNode("n1");
|
var n1 = helper.getNode("n1");
|
||||||
var n2 = helper.getNode("n2");
|
var n2 = helper.getNode("n2");
|
||||||
@ -1995,9 +2045,6 @@ describe('HTTP Request Node', function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
n1.receive({
|
n1.receive({
|
||||||
headers: {
|
|
||||||
'content-type':'multipart/form-data'
|
|
||||||
},
|
|
||||||
payload: {
|
payload: {
|
||||||
file: {
|
file: {
|
||||||
value: Buffer.from("Hello World"),
|
value: Buffer.from("Hello World"),
|
||||||
|
Loading…
Reference in New Issue
Block a user