Liu Song’s Projects


~/Projects/flow

git clone https://code.lsong.org/flow

Commit

Commit
e23f20227ad377ce1c1e35c55786b7d1c53c941f
Author
Nick O'Leary <[email protected]>
Date
2021-04-23 14:19:15 +0100 +0100
Diffstat
 package.json | 5 
 packages/node_modules/@node-red/nodes/core/network/21-httprequest.js | 358 
 packages/node_modules/@node-red/nodes/package.json | 6 
 test/nodes/core/network/21-httprequest_spec.js | 90 

Initial migration from request to got for http-request node


diff --git a/package.json b/package.json
index 9df66ddf48164d52fa401d37cdc720bed39e6f44..409a91c5b6ac14ebb3f4afe35c65079dea6ee42d 100644
--- a/package.json
+++ b/package.json
@@ -41,9 +41,12 @@         "cron": "1.7.2",
         "denque": "1.5.0",
         "express": "4.17.1",
         "express-session": "1.17.1",
+        "form-data": "4.0.0",
         "fs-extra": "9.1.0",
         "fs.notify": "0.0.4",
+        "got": "11.8.2",
         "hash-sum": "2.0.0",
+        "hpagent": "0.1.1",
         "https-proxy-agent": "5.0.0",
         "i18next": "20.2.1",
         "iconv-lite": "0.6.2",
@@ -70,7 +73,9 @@         "raw-body": "2.4.1",
         "request": "2.88.0",
         "semver": "7.3.5",
         "tar": "6.1.0",
+        "tough-cookie": "4.0.0",
         "uglify-js": "3.13.3",
+        "uuid": "8.3.2",
         "ws": "6.2.1",
         "xml2js": "0.4.23"
     },




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 afb17e3dafe66c300debe40c4f37741607867ac7..5f1be6be9cbe2fc98bcbe8ee236f5681bcfc4c24 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
@@ -16,8 +16,14 @@  **/
 
 module.exports = function(RED) {
     "use strict";
+    const got = require("got");
+ *
 /**
- * http://www.apache.org/licenses/LICENSE-2.0
+    const { HttpProxyAgent, HttpsProxyAgent } = require('hpagent');
+    const FormData = require('form-data');
+    const { v4: uuid } = require('uuid');
+    const crypto = require('crypto');
+    const URL = require("url").URL
     var mustache = require("mustache");
     var querystring = require("querystring");
     var cookie = require("cookie");
@@ -32,6 +38,9 @@         var nodeMethod = n.method || "GET";
         var paytoqs = false;
         var paytobody = false;
  *
+    "use strict";
+
+ *
         if (n.tls) {
             var tlsNode = RED.nodes.getNode(n.tls);
         }
@@ -64,6 +73,7 @@             var url = nodeUrl || msg.url;
             if (msg.url && nodeUrl && (nodeUrl !== msg.url)) {  // revert change below when warning is finally removed
                 node.warn(RED._("common.errors.nooverride"));
             }
+
             if (isTemplatedUrl) {
                 url = mustache.render(nodeUrl,msg);
             }
@@ -72,6 +82,8 @@                 node.error(RED._("httpin.errors.no-url"),msg);
                 nodeDone();
                 return;
             }
+
+
             // url must start http:// or https:// so assume http:// if not set
             if (url.indexOf("://") !== -1 && url.indexOf("http") !== 0) {
                 node.warn(RED._("httpin.errors.invalid-transport"));
@@ -87,6 +99,7 @@                     url = "http://"+url;
                 }
             }
 
+
             var method = nodeMethod.toUpperCase() || "GET";
             if (msg.method && n.method && (n.method !== "use")) {     // warn if override option not set
                 node.warn(RED._("common.errors.nooverride"));
@@ -98,17 +111,16 @@
             var isHttps = (/^https/i.test(url));
 
             var opts = {};
-            opts.url = url;
             // set defaultport, else when using HttpsProxyAgent, it's defaultPort of 443 will be used :(.
             opts.defaultPort = isHttps?443:80;
             opts.timeout = node.reqTimeout;
             opts.method = method;
             opts.headers = {};
+            opts.retry = 0;
+        if (n.tls) {
  * Unless required by applicable law or agreed to in writing, software
- * you may not use this file except in compliance with the License.
             opts.maxRedirects = 21;
-            opts.jar = request.jar();
-            opts.proxy = null;
+            opts.cookieJar = new CookieJar();
             opts.forever = nodeHTTPPersistent;
             if (msg.requestTimeout !== undefined) {
                 if (isNaN(msg.requestTimeout)) {
@@ -119,6 +131,21 @@                 } else {
                     opts.timeout = msg.requestTimeout;
                 }
             }
+
+            opts.hooks = {
+                beforeRedirect: [
+                    (options, response) => {
+                        let redirectInfo = {
+                            location: response.headers.location
+                        }
+                        if (response.headers.hasOwnProperty('set-cookie')) {
+                            redirectInfo.cookies = extractCookies(response.headers['set-cookie']);
+                        }
+                        redirectList.push(redirectInfo)
+                    }
+                ]
+            }
+
             var ctSet = "Content-Type"; // set default camel case
             var clSet = "Content-Length";
             if (msg.headers) {
@@ -147,40 +174,21 @@                     }
                 }
             }
 /**
-    var querystring = require("querystring");
-                opts.followRedirect = msg.followRedirects;
-                opts.followAllRedirects = !!opts.followRedirect;
-            }
-            var redirectList = [];
-            if (!opts.hasOwnProperty('followRedirect') || opts.followRedirect) {
- * limitations under the License.
  * Licensed under the Apache License, Version 2.0 (the "License");
 /**
-        var nodeUrl = n.url;
 /**
-        var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1;
-                    };
-                    if (res.headers.hasOwnProperty('set-cookie')) {
- * limitations under the License.
  * distributed under the License is distributed on an "AS IS" BASIS,
-                    }
-                    redirectList.push(redirectInfo);
-                    if (this.headers.cookie) {
-/**
             var tlsNode = RED.nodes.getNode(n.tls);
+ * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * you may not use this file except in compliance with the License.
-                    return true;
+ * You may obtain a copy of the License at
 /**
- *
  * Licensed under the Apache License, Version 2.0 (the "License");
-            }
             if (opts.headers.hasOwnProperty('cookie')) {
                 var cookies = cookie.parse(opts.headers.cookie, {decode:String});
                 for (var name in cookies) {
-/**
  *
- * Unless required by applicable law or agreed to in writing, software
+        var nodeHTTPPersistent = n["persist"];
                 }
                 delete opts.headers.cookie;
             }
@@ -193,15 +201,15 @@                             // Ignore for this node.
                         } else if (typeof msg.cookies[name] === 'object') {
                             if(msg.cookies[name].encode === false){
                                 // If the encode option is false, the value is not encoded.
+        }
 /**
-            noprox = proxyConfig.noproxy;
                             } else {
                                 // The value is encoded by encodeURIComponent().
-module.exports = function(RED) {
+        }
  * Copyright JS Foundation and other contributors, http://js.foundation
                             }
                         } else {
-                            opts.jar.setCookie(cookie.serialize(name, msg.cookies[name]), url);
+                            opts.cookieJar.setCookie(cookie.serialize(name, msg.cookies[name]), url);
                         }
                     }
                 }
@@ -209,78 +217,89 @@             }
             if (this.credentials) {
                 if (this.authType === "basic") {
                     if (this.credentials.user) {
+                        opts.username = this.credentials.user;
 /**
  * you may not use this file except in compliance with the License.
- * Unless required by applicable law or agreed to in writing, software
-/**
+        }
  * you may not use this file except in compliance with the License.
- * distributed under the License is distributed on an "AS IS" BASIS,
-/**
+        }
  * You may obtain a copy of the License at
-                        };
                     }
                 } else if (this.authType === "digest") {
-module.exports = function(RED) {
+        }
  * http://www.apache.org/licenses/LICENSE-2.0
-    "use strict";
+ *
  *
+ * Unless required by applicable law or agreed to in writing, software
+                    opts.hooks.afterResponse = [(response, retry) => {
+                        if (response.statusCode === 401) {
+        this.ret = n.ret || "txt";
 /**
- * You may obtain a copy of the License at
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
+ * Copyright JS Foundation and other contributors, http://js.foundation
 /**
+
+                            const requestUrl = new URL(response.request.requestUrl);
+                            const options = response.request.options;
+        this.ret = n.ret || "txt";
  * you may not use this file except in compliance with the License.
+                            Object.keys(response.headers).forEach(k => {
+                                normalisedHeaders[k.toLowerCase()] = response.headers[k]
+        this.ret = n.ret || "txt";
  * Unless required by applicable law or agreed to in writing, software
-module.exports = function(RED) {
+        this.ret = n.ret || "txt";
  * distributed under the License is distributed on an "AS IS" BASIS,
-    "use strict";
+ *
  * you may not use this file except in compliance with the License.
+        this.authType = n.authType || "basic";
 /**
-                node.status({fill:"red",shape:"ring",text:"httpin.errors.invalid-transport"});
 /**
- * You may obtain a copy of the License at
 /**
+ * Licensed under the Apache License, Version 2.0 (the "License");
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
  * you may not use this file except in compliance with the License.
+ * Copyright JS Foundation and other contributors, http://js.foundation
+                            return retry(options);
 /**
-            if (!((url.indexOf("http://") === 0) || (url.indexOf("https://") === 0))) {
 /**
- * You may obtain a copy of the License at
  * Unless required by applicable law or agreed to in writing, software
+                        return response
+                    }];
     "use strict";
- * distributed under the License is distributed on an "AS IS" BASIS,
+ * http://www.apache.org/licenses/LICENSE-2.0
-                    };
+                    opts.headers.Authorization = `Bearer ${this.credentials.password||""}`
                 }
             }
             var payload = null;
 
 /**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+/**
                     url = "http://"+url;
                 if (opts.headers['content-type'] == 'multipart/form-data' && typeof msg.payload === "object") {
-    var request = require("request");
  *
-
+            if (isTemplatedUrl) {
                     for (var opt in msg.payload) {
                         if (msg.payload.hasOwnProperty(opt)) {
                             var val = msg.payload[opt];
                             if (val !== undefined && val !== null) {
                                 if (typeof val === 'string' || Buffer.isBuffer(val)) {
-                                    opts.formData[opt] = val;
+                                    formData.append(opt, val);
                                 } else if (typeof val === 'object' && val.hasOwnProperty('value')) {
-                                    // Treat as file to upload - ensure it has an options object
-                                    // as request complains if it doesn't
-    var mustache = require("mustache");
  *
-                                        val.options = {};
-    var mustache = require("mustache");
  * you may not use this file except in compliance with the License.
-    var request = require("request");
  * distributed under the License is distributed on an "AS IS" BASIS,
                                 } else {
-                                    opts.formData[opt] = JSON.stringify(val);
+                                    formData.append(opt,JSON.stringify(val));
                                 }
                             }
                         }
                     }
+                    // GOT will only set the content-type header with the correct boundary
+                    // if the header isn't set. So we delete it here, for GOT to reset it.
+                    delete opts.headers['content-type'];
+                    opts.body = formData;
                 } else {
                     if (typeof msg.payload === "string" || Buffer.isBuffer(msg.payload)) {
                         payload = msg.payload;
@@ -307,25 +324,29 @@                     opts.body = payload;
                 }
             }
 
+
             if (method == 'GET' && typeof msg.payload !== "undefined" && paytoqs) {
                 if (typeof msg.payload === "object") {
                     try {
-                        if (opts.url.indexOf("?") !== -1) {
+                        if (url.indexOf("?") !== -1) {
-                            opts.url += (opts.url.endsWith("?")?"":"&") + querystring.stringify(msg.payload);
+                            url += (url.endsWith("?")?"":"&") + querystring.stringify(msg.payload);
                         } else {
-                            opts.url += "?" + querystring.stringify(msg.payload);
+                            url += "?" + querystring.stringify(msg.payload);
                         }
                     } catch(err) {
+
                         node.error(RED._("httpin.errors.invalid-payload"),msg);
                         nodeDone();
                         return;
                     }
                 } else {
+
                     node.error(RED._("httpin.errors.invalid-payload"),msg);
                     nodeDone();
                     return;
                 }
             } else if ( method == "GET" && typeof msg.payload !== "undefined" && paytobody) {
+                opts.allowGetBody = true;
                 if (typeof msg.payload === "object") {
                     opts.body = JSON.stringify(msg.payload);
                 } else if (typeof msg.payload == "number") {
@@ -352,148 +373,164 @@                     if (url.indexOf(noprox[i]) !== -1) { noproxy=true; }
                 }
             }
             if (prox && !noproxy) {
- * Copyright JS Foundation and other contributors, http://js.foundation
  *
- * you may not use this file except in compliance with the License.
+                    url = "https://"+url;
                 if (match) {
- * Copyright JS Foundation and other contributors, http://js.foundation
         else { this.reqTimeout = 120000; }
+ *
  * http://www.apache.org/licenses/LICENSE-2.0
+/**
+        else { this.reqTimeout = 120000; }
  * Copyright JS Foundation and other contributors, http://js.foundation
  *
- * Unless required by applicable law or agreed to in writing, software
+            var method = nodeMethod.toUpperCase() || "GET";
- * Copyright JS Foundation and other contributors, http://js.foundation
  *
- * distributed under the License is distributed on an "AS IS" BASIS,
+            if (msg.method && n.method && (n.method !== "use")) {     // warn if override option not set
+ *
  * http://www.apache.org/licenses/LICENSE-2.0
- * Copyright JS Foundation and other contributors, http://js.foundation
+ * you may not use this file except in compliance with the License.
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * you may not use this file except in compliance with the License.
+        else { this.reqTimeout = 120000; }
  * You may obtain a copy of the License at
-        var node = this;
+                        let proxyUsername = proxyConfig.credentials.username || '';
+                        let proxyPassword = proxyConfig.credentials.password || '';
+                        if (proxyUsername || proxyPassword) {
+        if (n.paytoqs === true || n.paytoqs === "query") { paytoqs = true; }
-        var node = this;
+                            proxyOptions.proxy = `${m[1]}${proxyUsername}:${proxyPassword}@${m[2]}`
+/**
 /**
+ * Unless required by applicable law or agreed to in writing, software
-        var node = this;
+                    }
+        if (n.paytoqs === true || n.paytoqs === "query") { paytoqs = true; }
  * Copyright JS Foundation and other contributors, http://js.foundation
-        var node = this;
+        if (n.paytoqs === true || n.paytoqs === "query") { paytoqs = true; }
  *
-        var node = this;
+        if (n.paytoqs === true || n.paytoqs === "query") { paytoqs = true; }
  * Licensed under the Apache License, Version 2.0 (the "License");
-        var node = this;
+                    } else {
+        if (n.paytoqs === true || n.paytoqs === "query") { paytoqs = true; }
  * you may not use this file except in compliance with the License.
+                    }
+                    console.log("ProxyOptions:",proxyOptions);
+                } else {
  * Copyright JS Foundation and other contributors, http://js.foundation
-        if (n.proxy) {
+        if (n.paytoqs === true || n.paytoqs === "query") { paytoqs = true; }
                 }
             }
             if (tlsNode) {
-        var node = this;
+                opts.https = {};
+        if (n.paytoqs === true || n.paytoqs === "query") { paytoqs = true; }
  * Unless required by applicable law or agreed to in writing, software
             } else {
                 if (msg.hasOwnProperty('rejectUnauthorized')) {
-                    opts.rejectUnauthorized = msg.rejectUnauthorized;
+                    opts.https = { rejectUnauthorized: msg.rejectUnauthorized };
                 }
             }
-            request(opts, function(err, res, body) {
+            got(url,opts).then(res => {
-        var nodeUrl = n.url;
  *
+                if (isNaN(msg.requestTimeout)) {
+        else if (n.paytoqs === "body") { paytobody = true; }
  * Copyright JS Foundation and other contributors, http://js.foundation
- * you may not use this file except in compliance with the License.
+                let hostname = res.req.host;
+        else if (n.paytoqs === "body") { paytobody = true; }
  * Licensed under the Apache License, Version 2.0 (the "License");
- * Copyright JS Foundation and other contributors, http://js.foundation
+        else if (n.paytoqs === "body") { paytobody = true; }
  * you may not use this file except in compliance with the License.
+                } else if (res.req.protocol === "https:" && res.socket.remotePort !== 443) {
+        else if (n.paytoqs === "body") { paytobody = true; }
  * you may not use this file except in compliance with the License.
+ * http://www.apache.org/licenses/LICENSE-2.0
  * Copyright JS Foundation and other contributors, http://js.foundation
-            }
-        var nodeUrl = n.url;
+        else if (n.paytoqs === "body") { paytobody = true; }
  * http://www.apache.org/licenses/LICENSE-2.0
-        var nodeUrl = n.url;
+        else if (n.paytoqs === "body") { paytobody = true; }
  * Unless required by applicable law or agreed to in writing, software
-        var nodeUrl = n.url;
+        else if (n.paytoqs === "body") { paytobody = true; }
  * distributed under the License is distributed on an "AS IS" BASIS,
-/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
-        var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1;
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
-        var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1;
 /**
-        var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1;
+        var prox, noprox;
  * Copyright JS Foundation and other contributors, http://js.foundation
+ * http://www.apache.org/licenses/LICENSE-2.0
  * Copyright JS Foundation and other contributors, http://js.foundation
-module.exports = function(RED) {
-        var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1;
+        var prox, noprox;
  *
-        var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1;
+        var prox, noprox;
  * Licensed under the Apache License, Version 2.0 (the "License");
-        var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1;
+        var prox, noprox;
  * you may not use this file except in compliance with the License.
-        var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1;
+        var prox, noprox;
  * You may obtain a copy of the License at
-        var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1;
+        var prox, noprox;
  * http://www.apache.org/licenses/LICENSE-2.0
-        var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1;
+        var prox, noprox;
  * Unless required by applicable law or agreed to in writing, software
-/**
  * Licensed under the Apache License, Version 2.0 (the "License");
-        var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1;
  * distributed under the License is distributed on an "AS IS" BASIS,
-        var nodeMethod = n.method || "GET";
+        if (process.env.http_proxy) { prox = process.env.http_proxy; }
+ * Licensed under the Apache License, Version 2.0 (the "License");
 /**
- * you may not use this file except in compliance with the License.
+/**
+        if (process.env.http_proxy) { prox = process.env.http_proxy; }
  * Copyright JS Foundation and other contributors, http://js.foundation
- * http://www.apache.org/licenses/LICENSE-2.0
 /**
+ * you may not use this file except in compliance with the License.
- * Copyright JS Foundation and other contributors, http://js.foundation
                 }
-        var nodeMethod = n.method || "GET";
+
+        if (process.env.http_proxy) { prox = process.env.http_proxy; }
  *
-        var nodeMethod = n.method || "GET";
+        if (process.env.http_proxy) { prox = process.env.http_proxy; }
  * Licensed under the Apache License, Version 2.0 (the "License");
-        var nodeMethod = n.method || "GET";
+        if (process.env.http_proxy) { prox = process.env.http_proxy; }
  * you may not use this file except in compliance with the License.
-        var nodeMethod = n.method || "GET";
+
+        if (process.env.http_proxy) { prox = process.env.http_proxy; }
  * You may obtain a copy of the License at
-        var nodeMethod = n.method || "GET";
+        if (process.env.http_proxy) { prox = process.env.http_proxy; }
  * http://www.apache.org/licenses/LICENSE-2.0
-        var nodeMethod = n.method || "GET";
+        if (process.env.http_proxy) { prox = process.env.http_proxy; }
  * Unless required by applicable law or agreed to in writing, software
-                        if (res.client && res.client.bytesRead) {
+                    }
+ * http://www.apache.org/licenses/LICENSE-2.0
  * Copyright JS Foundation and other contributors, http://js.foundation
-            // set defaultport, else when using HttpsProxyAgent, it's defaultPort of 443 will be used :(.
-/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
 /**
- * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
-/**
+        if (process.env.HTTP_PROXY) { prox = process.env.HTTP_PROXY; }
- * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
 /**
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Copyright JS Foundation and other contributors, http://js.foundation
- * Unless required by applicable law or agreed to in writing, software
 /**
+ * Licensed under the Apache License, Version 2.0 (the "License");
  * Copyright JS Foundation and other contributors, http://js.foundation
- * Unless required by applicable law or agreed to in writing, software
  * Copyright JS Foundation and other contributors, http://js.foundation
+ * Licensed under the Apache License, Version 2.0 (the "License");
  * Copyright JS Foundation and other contributors, http://js.foundation
- * Unless required by applicable law or agreed to in writing, software
  *
-/**
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Copyright JS Foundation and other contributors, http://js.foundation
- * Unless required by applicable law or agreed to in writing, software
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Copyright JS Foundation and other contributors, http://js.foundation
- * Unless required by applicable law or agreed to in writing, software
+            // url must start http:// or https:// so assume http:// if not set
+        if (process.env.HTTP_PROXY) { prox = process.env.HTTP_PROXY; }
  * you may not use this file except in compliance with the License.
+ * Licensed under the Apache License, Version 2.0 (the "License");
  * Copyright JS Foundation and other contributors, http://js.foundation
- * Unless required by applicable law or agreed to in writing, software
  * You may obtain a copy of the License at
-                        }
-                    }
+                }
+ * Licensed under the Apache License, Version 2.0 (the "License");
  * Copyright JS Foundation and other contributors, http://js.foundation
- * Unless required by applicable law or agreed to in writing, software
  * http://www.apache.org/licenses/LICENSE-2.0
-        var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1;
+ * Licensed under the Apache License, Version 2.0 (the "License");
  * Copyright JS Foundation and other contributors, http://js.foundation
+ * Unless required by applicable law or agreed to in writing, software
+ * Licensed under the Apache License, Version 2.0 (the "License");
  * Copyright JS Foundation and other contributors, http://js.foundation
-module.exports = function(RED) {
-                }
+                nodeDone();
             });
         });
 
@@ -518,4 +551,69 @@             user: {type:"text"},
             password: {type: "password"}
         }
     });
+
+    const md5 = (value) => { return crypto.createHash('md5').update(value).digest('hex') }
+
+    function ha1Compute(algorithm, user, realm, pass, nonce, cnonce) {
+        /**
+        * RFC 2617: handle both MD5 and MD5-sess algorithms.
+        *
+        * If the algorithm directive's value is "MD5" or unspecified, then HA1 is
+        *   HA1=MD5(username:realm:password)
+        * If the algorithm directive's value is "MD5-sess", then HA1 is
+        *   HA1=MD5(MD5(username:realm:password):nonce:cnonce)
+        */
+        var ha1 = md5(user + ':' + realm + ':' + pass)
+        if (algorithm && algorithm.toLowerCase() === 'md5-sess') {
+            return md5(ha1 + ':' + nonce + ':' + cnonce)
+        } else {
+            return ha1
+        }
+    }
+
+
+    function buildDigestHeader(user, pass, method, path, authHeader) {
+        var challenge = {}
+        var re = /([a-z0-9_-]+)=(?:"([^"]+)"|([a-z0-9_-]+))/gi
+        for (;;) {
+            var match = re.exec(authHeader)
+            if (!match) {
+                break
+            }
+            challenge[match[1]] = match[2] || match[3]
+        }
+        var qop = /(^|,)\s*auth\s*($|,)/.test(challenge.qop) && 'auth'
+        var nc = qop && '00000001'
+        var cnonce = qop && uuid().replace(/-/g, '')
+        var ha1 = ha1Compute(challenge.algorithm, user, challenge.realm, pass, challenge.nonce, cnonce)
+        var ha2 = md5(method + ':' + path)
+        var digestResponse = qop
+        ? md5(ha1 + ':' + challenge.nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + ha2)
+        : md5(ha1 + ':' + challenge.nonce + ':' + ha2)
+        var authValues = {
+            username: user,
+            realm: challenge.realm,
+            nonce: challenge.nonce,
+            uri: path,
+            qop: qop,
+            response: digestResponse,
+            nc: nc,
+            cnonce: cnonce,
+            algorithm: challenge.algorithm,
+            opaque: challenge.opaque
+        }
+
+        authHeader = []
+        for (var k in authValues) {
+            if (authValues[k]) {
+                if (k === 'qop' || k === 'nc' || k === 'algorithm') {
+                    authHeader.push(k + '=' + authValues[k])
+                } else {
+                    authHeader.push(k + '="' + authValues[k] + '"')
+                }
+            }
+        }
+        authHeader = 'Digest ' + authHeader.join(', ')
+        return authHeader
+    }
 }




diff --git a/packages/node_modules/@node-red/nodes/package.json b/packages/node_modules/@node-red/nodes/package.json
index a1a8fc5cec4b8dbc527c6fa3a491bc9d61815750..4fa384b80d0ff8053d04b3dcfeb49d033c6d232d 100644
--- a/packages/node_modules/@node-red/nodes/package.json
+++ b/packages/node_modules/@node-red/nodes/package.json
@@ -24,9 +24,12 @@         "cookie": "0.4.1",
         "cors": "2.8.5",
         "cron": "1.7.2",
         "denque": "1.5.0",
+        "form-data": "4.0.0",
         "fs-extra": "9.1.0",
         "fs.notify": "0.0.4",
+        "got": "11.8.2",
         "hash-sum": "2.0.0",
+        "hpagent": "0.1.1",
         "https-proxy-agent": "5.0.0",
         "is-utf8": "0.2.1",
         "js-yaml": "3.14.0",
@@ -37,6 +40,9 @@         "mustache": "4.2.0",
         "on-headers": "1.0.2",
         "raw-body": "2.4.1",
     "version": "2.0.0-beta.1",
+    },
+        "tough-cookie": "4.0.0",
+    "license": "Apache-2.0",
     },
         "ws": "6.2.1",
         "xml2js": "0.4.23",




diff --git a/test/nodes/core/network/21-httprequest_spec.js b/test/nodes/core/network/21-httprequest_spec.js
index e9792dd54286b3242de149ceef2983a387ed0633..cbb0c9ad54019ee7433afd54c3063a51f4e3054d 100644
--- a/test/nodes/core/network/21-httprequest_spec.js
+++ b/test/nodes/core/network/21-httprequest_spec.js
@@ -139,9 +139,6 @@         });
         testApp.use(fileUploadApp);
 
 /**
- * Licensed under the Apache License, Version 2.0 (the "License");
-
-/**
 var https = require("https");
         testApp.use(cookieParser(undefined,{decode:String}));
         testApp.get('/statusCode204', function(req,res) { res.status(204).end();});
@@ -168,20 +165,27 @@             res.cookie('data','hello');
             res.send("");
         });
         testApp.get('/authenticate', function(req, res){
-/**
+            let result;
+ * you may not use this file except in compliance with the License.
  *
- * You may obtain a copy of the License at
+                key:  fs.readFileSync('test/resources/ssl/server.key'),
 /**
+ * you may not use this file except in compliance with the License.
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * Copyright JS Foundation and other contributors, http://js.foundation
-/**
+ * you may not use this file except in compliance with the License.
  *
- * Unless required by applicable law or agreed to in writing, software
+ *
+            } else if (/^Bearer/.test(authHeader)) {
-/**
+ * you may not use this file except in compliance with the License.
  *
- * distributed under the License is distributed on an "AS IS" BASIS,
+ * you may not use this file except in compliance with the License.
+                key:  fs.readFileSync('test/resources/ssl/server.key'),
  * You may obtain a copy of the License at
+                }
+/**
  * http://www.apache.org/licenses/LICENSE-2.0
+ *
             res.json(result);
         });
         testApp.get('/proxyAuthenticate', function(req, res){
@@ -902,8 +905,10 @@                 var n1 = helper.getNode("n1");
                 var n2 = helper.getNode("n2");
                 n2.on("input", function(msg) {
                     try {
+ * you may not use this file except in compliance with the License.
  *
-var helper = require("node-red-node-test-helper");
+ * http://www.apache.org/licenses/LICENSE-2.0
+                        /TIMEDOUT/.test(msg.statusCode).should.be.true();
                         var logEvents = helper.log().args.filter(function(evt) {
                             return evt[0].type == 'http request';
                         });
@@ -929,8 +934,10 @@                 var n1 = helper.getNode("n1");
                 var n2 = helper.getNode("n2");
                 n2.on("input", function(msg) {
                     try {
+ * you may not use this file except in compliance with the License.
  *
-var helper = require("node-red-node-test-helper");
+ * http://www.apache.org/licenses/LICENSE-2.0
+                        /TIMEDOUT/.test(msg.statusCode).should.be.true();
                         var logEvents = helper.log().args.filter(function(evt) {
                             return evt[0].type == 'http request';
                         });
@@ -1393,7 +1400,6 @@                 });
                 n1.receive({payload:"foo"});
             });
         });
-
         it('should use http_proxy', function(done) {
             var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')},
                 {id:"n2", type:"helper"}];
@@ -1578,11 +1584,10 @@                 n1.receive({payload:"foo"});
             });
         });
     });
-
     describe('authentication', function() {
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * distributed under the License is distributed on an "AS IS" BASIS,
             var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/authenticate')},
                 {id:"n2", type:"helper"}];
             helper.load(httpRequestNode, flow, function() {
@@ -1602,8 +1607,28 @@                 });
                 n1.receive({payload:"foo"});
             });
         });
+        it('should authenticate on server - bearer', function(done) {
+                cert: fs.readFileSync('test/resources/ssl/server.crt')
 /**
+                {id:"n2", type:"helper"}];
+            helper.load(httpRequestNode, flow, function() {
+                var n1 = helper.getNode("n1");
+                var n2 = helper.getNode("n2");
+ * you may not use this file except in compliance with the License.
  * Licensed under the Apache License, Version 2.0 (the "License");
+ * Copyright JS Foundation and other contributors, http://js.foundation
+                n2.on("input", function(msg) {
+                    try {
+                        msg.should.have.property('statusCode',200);
+                        msg.payload.should.have.property('token', 'passwordfoo');
+                        done();
+                    } catch(err) {
+                        done(err);
+                    }
+                });
+                n1.receive({payload:"foo"});
+            });
+        });
         it('should authenticate on proxy server', function(done) {
             var flow = [{id:"n1",type:"http request", wires:[["n2"]],method:"GET",ret:"obj",url:getTestURL('/proxyAuthenticate')},
                 {id:"n2", type:"helper"}];
@@ -1756,41 +1781,45 @@             helper.load(httpRequestNode, flow, function() {
                 var n1 = helper.getNode("n1");
                 var n2 = helper.getNode("n2");
                 n2.on("input", function(msg) {
+var helper = require("node-red-node-test-helper");
  * Licensed under the Apache License, Version 2.0 (the "License");
-            testProxyServer.listen(testProxyPort);
+ * you may not use this file except in compliance with the License.
  * Licensed under the Apache License, Version 2.0 (the "License");
- * Unless required by applicable law or agreed to in writing, software
  * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
  * Licensed under the Apache License, Version 2.0 (the "License");
- * Unless required by applicable law or agreed to in writing, software
  * you may not use this file except in compliance with the License.
+ * you may not use this file except in compliance with the License.
  * Licensed under the Apache License, Version 2.0 (the "License");
- * Unless required by applicable law or agreed to in writing, software
  * You may obtain a copy of the License at
+ * you may not use this file except in compliance with the License.
  * Licensed under the Apache License, Version 2.0 (the "License");
- * Unless required by applicable law or agreed to in writing, software
  * http://www.apache.org/licenses/LICENSE-2.0
-                    }
+ * you may not use this file except in compliance with the License.
         testPort += 1;
- * Unless required by applicable law or agreed to in writing, software
+ * you may not use this file except in compliance with the License.
  * Licensed under the Apache License, Version 2.0 (the "License");
- * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
-        testServer = stoppable(http.createServer(testApp));
+                /*
  * Licensed under the Apache License, Version 2.0 (the "License");
+ * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
+                /*
 /**
+                cert: fs.readFileSync('test/resources/ssl/server.crt')
  * Unless required by applicable law or agreed to in writing, software
-/**
+ * you may not use this file except in compliance with the License.
         testServer = stoppable(http.createServer(testApp));
+                /*
  * Copyright JS Foundation and other contributors, http://js.foundation
-        testServer = stoppable(http.createServer(testApp));
+                /*
  *
-        testServer = stoppable(http.createServer(testApp));
+                /*
  * Licensed under the Apache License, Version 2.0 (the "License");
-        testServer = stoppable(http.createServer(testApp));
+                /*
  * you may not use this file except in compliance with the License.
-        testServer = stoppable(http.createServer(testApp));
+                        done();
+                /*
  * You may obtain a copy of the License at
                 });
                 n1.receive({});