diff --git a/packages/node_modules/@node-red/nodes/core/network/21-httprequest.html b/packages/node_modules/@node-red/nodes/core/network/21-httprequest.html
index bf1f76b7f..9b54f5136 100644
--- a/packages/node_modules/@node-red/nodes/core/network/21-httprequest.html
+++ b/packages/node_modules/@node-red/nodes/core/network/21-httprequest.html
@@ -100,14 +100,107 @@
+
+
+
+
+
+
+
+
-
diff --git a/packages/node_modules/@node-red/nodes/core/network/21-httprequest.js b/packages/node_modules/@node-red/nodes/core/network/21-httprequest.js
index bf677b490..6a19d2ed2 100644
--- a/packages/node_modules/@node-red/nodes/core/network/21-httprequest.js
+++ b/packages/node_modules/@node-red/nodes/core/network/21-httprequest.js
@@ -73,7 +73,7 @@ in your Node-RED user directory (${RED.settings.userDir}).
var paytobody = false;
var redirectList = [];
var sendErrorsToCatch = n.senderr;
-
+ node.headers = n.headers || [];
var nodeHTTPPersistent = n["persist"];
if (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;
}
+ /**
+ * 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) {
checkNodeAgentPatch();
//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
opts.decompress = false;
opts.method = method;
- opts.headers = {};
opts.retry = 0;
opts.responseType = 'buffer';
opts.maxRedirects = 21;
@@ -229,34 +259,85 @@ in your Node-RED user directory (${RED.settings.userDir}).
]
}
- var ctSet = "Content-Type"; // set default camel case
- var clSet = "Content-Length";
+ let ctSet = "Content-Type"; // set default camel case
+ 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.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'];
- var hash = hashSum(msg.headers);
+ const hash = hashSum(msg.headers);
if (hash === headerHash) {
delete msg.headers;
}
}
if (msg.headers) {
- for (var v in msg.headers) {
- if (msg.headers.hasOwnProperty(v)) {
- var name = v.toLowerCase();
- 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];
- }
+ for (let hn in msg.headers) {
+ const name = normaliseKnownHeader(hn);
+ updateHeader(opts.headers, name, msg.headers[hn]);
}
}
}
+ //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')) {
opts.followRedirect = !!msg.followRedirects;
}
diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/network/21-httprequest.html b/packages/node_modules/@node-red/nodes/locales/en-US/network/21-httprequest.html
index 68d9d8f1b..b8d7fc048 100644
--- a/packages/node_modules/@node-red/nodes/locales/en-US/network/21-httprequest.html
+++ b/packages/node_modules/@node-red/nodes/locales/en-US/network/21-httprequest.html
@@ -25,7 +25,7 @@
If not configured in the node, this optional property sets the HTTP method of the request.
Must be one of GET
, PUT
, POST
, PATCH
or DELETE
.
headers object
- Sets the HTTP headers of the request.
+ Sets the HTTP headers of the request. NOTE: Any headers set in the node configuration will overwrite any matching headers in msg.headers
cookies object
If set, can be used to send cookies with the request.
payload
diff --git a/test/nodes/core/network/21-httprequest_spec.js b/test/nodes/core/network/21-httprequest_spec.js
index 07bed5a01..688776289 100644
--- a/test/nodes/core/network/21-httprequest_spec.js
+++ b/test/nodes/core/network/21-httprequest_spec.js
@@ -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"}});
});
});
+
+ 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() {
@@ -1976,8 +2020,14 @@ describe('HTTP Request Node', function() {
describe('file-upload', function() {
it('should upload a file', function(done) {
- var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'POST',ret:'obj',url:getTestURL('/file-upload')},
- {id:"n2", type:"helper"}];
+ const flow = [
+ {
+ 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() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
@@ -1995,9 +2045,6 @@ describe('HTTP Request Node', function() {
}
});
n1.receive({
- headers: {
- 'content-type':'multipart/form-data'
- },
payload: {
file: {
value: Buffer.from("Hello World"),