Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 82 additions & 53 deletions packages/node_modules/@node-red/nodes/core/network/21-httprequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,15 +143,6 @@ in your Node-RED user directory (${RED.settings.userDir}).
});
}
}
/**
* @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.count = 0;
this.on("input",function(msg,nodeSend,nodeDone) {
node.count++;
Expand Down Expand Up @@ -256,34 +247,42 @@ in your Node-RED user directory (${RED.settings.userDir}).
opts.hooks = {
beforeRequest: [
options => {
// Whilst HTTP headers are meant to be case-insensitive,
// in the real world, there are servers that aren't so compliant.
// GOT will lower case all headers given a chance, so we need
// to restore the case of any headers the user has set.
Object.keys(options.headers).forEach(h => {
if (originalHeaderMap[h] && originalHeaderMap[h] !== h) {
options.headers[originalHeaderMap[h]] = options.headers[h];
delete options.headers[h];
try {
// Whilst HTTP headers are meant to be case-insensitive,
// in the real world, there are servers that aren't so compliant.
// GOT will lower case all headers given a chance, so we need
// to restore the case of any headers the user has set.
Object.keys(options.headers).forEach(h => {
if (originalHeaderMap[h] && originalHeaderMap[h] !== h) {
options.headers[originalHeaderMap[h]] = options.headers[h];
delete options.headers[h];
}
})
if (node.insecureHTTPParser) {
// Setting the property under _unixOptions as pretty
// much the only hack available to get got to apply
// a core http option it doesn't think we should be
// allowed to set
options._unixOptions = { ...options.unixOptions, insecureHTTPParser: true }
}
})
if (node.insecureHTTPParser) {
// Setting the property under _unixOptions as pretty
// much the only hack available to get got to apply
// a core http option it doesn't think we should be
// allowed to set
options._unixOptions = { ...options.unixOptions, insecureHTTPParser: true }
} catch (err) {
node.warn("Error in beforeRequest hook: " + err.message);
}
}
],
beforeRedirect: [
(options, response) => {
let redirectInfo = {
location: response.headers.location
}
if (response.headers.hasOwnProperty('set-cookie')) {
redirectInfo.cookies = extractCookies(response.headers['set-cookie']);
try {
let redirectInfo = {
location: response.headers.location
}
if (response.headers.hasOwnProperty('set-cookie')) {
redirectInfo.cookies = extractCookies(response.headers['set-cookie']);
}
redirectList.push(redirectInfo)
} catch (err) {
node.warn("Error processing redirect: " + err.message);
}
redirectList.push(redirectInfo)
}
]
}
Expand Down Expand Up @@ -422,25 +421,30 @@ in your Node-RED user directory (${RED.settings.userDir}).
let digestCreds = this.credentials;
let sentCreds = false;
opts.hooks.afterResponse = [(response, retry) => {
if (response.statusCode === 401) {
if (sentCreds) {
return response
}
const requestUrl = new URL(response.request.requestUrl);
const options = { headers: {} }
const normalisedHeaders = {};
Object.keys(response.headers).forEach(k => {
normalisedHeaders[k.toLowerCase()] = response.headers[k]
})
if (normalisedHeaders['www-authenticate']) {
let authHeader = buildDigestHeader(digestCreds.user,digestCreds.password, response.request.options.method, requestUrl.pathname + requestUrl.search, normalisedHeaders['www-authenticate'])
options.headers.Authorization = authHeader;
try {
if (response.statusCode === 401) {
if (sentCreds) {
return response
}
const requestUrl = new URL(response.request.requestUrl);
const options = { headers: {} }
const normalisedHeaders = {};
Object.keys(response.headers).forEach(k => {
normalisedHeaders[k.toLowerCase()] = response.headers[k]
})
if (normalisedHeaders['www-authenticate']) {
let authHeader = buildDigestHeader(digestCreds.user,digestCreds.password, response.request.options.method, requestUrl.pathname + requestUrl.search, normalisedHeaders['www-authenticate'])
options.headers.Authorization = authHeader;
}
// response.request.options.merge(options)
sentCreds = true;
return retry(options);
}
// response.request.options.merge(options)
sentCreds = true;
return retry(options);
return response
} catch (err) {
node.warn("Digest authentication failed: " + err.message);
return response;
}
return response
}];
} else if (this.authType === "bearer") {
opts.headers.Authorization = `Bearer ${this.credentials.password||""}`
Expand Down Expand Up @@ -720,13 +724,29 @@ in your Node-RED user directory (${RED.settings.userDir}).

function extractCookies(setCookie) {
var cookies = {};
if (!Array.isArray(setCookie)) {
return cookies;
}
setCookie.forEach(function(c) {
var parsedCookie = cookie.parse(c);
var eq_idx = c.indexOf('=');
var key = c.substr(0, eq_idx).trim()
parsedCookie.value = parsedCookie[key];
delete parsedCookie[key];
cookies[key] = parsedCookie;
try {
if (typeof c !== 'string') {
return;
}
var parsedCookie = cookie.parse(c);
var eq_idx = c.indexOf('=');
if (eq_idx === -1) {
return;
}
var key = c.substr(0, eq_idx).trim()
if (!key) {
return;
}
parsedCookie.value = parsedCookie[key];
delete parsedCookie[key];
cookies[key] = parsedCookie;
} catch (err) {
// Skip malformed cookies
}
});
return cookies;
}
Expand Down Expand Up @@ -778,6 +798,9 @@ in your Node-RED user directory (${RED.settings.userDir}).
}

function buildDigestHeader(user, pass, method, path, authHeader) {
if (!authHeader || typeof authHeader !== 'string') {
throw new Error("Invalid or missing WWW-Authenticate header");
}
var challenge = {}
var re = /([a-z0-9_-]+)=(?:"([^"]+)"|([a-z0-9_-]+))/gi
for (;;) {
Expand All @@ -787,6 +810,12 @@ in your Node-RED user directory (${RED.settings.userDir}).
}
challenge[match[1]] = match[2] || match[3]
}
if (!challenge.nonce) {
throw new Error("Invalid digest challenge: missing nonce");
}
if (!challenge.realm) {
throw new Error("Invalid digest challenge: missing realm");
}
var qop = /(^|,)\s*auth\s*($|,)/.test(challenge.qop) && 'auth'
var nc = qop && '00000001'
var cnonce = qop && uuid().replace(/-/g, '')
Expand Down