Skip to content

Commit

Permalink
Merge pull request usebruno#2820 from matthewdickinson/feature/cli-co…
Browse files Browse the repository at this point in the history
…okies

Added cookie support to CLI requests
  • Loading branch information
lohxt1 authored Nov 21, 2024
2 parents 1238bf7 + 6d8cc38 commit 1fb4298
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/bruno-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"lodash": "^4.17.21",
"qs": "^6.11.0",
"socks-proxy-agent": "^8.0.2",
"tough-cookie": "^4.1.3",
"@usebruno/vm2": "^3.9.13",
"xmlbuilder": "^15.1.1",
"yargs": "^17.6.2"
Expand Down
9 changes: 9 additions & 0 deletions packages/bruno-cli/src/commands/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,11 @@ const builder = async (yargs) => {
description:
'The specified custom CA certificate (--cacert) will be used exclusively and the default truststore is ignored, if this option is specified. Evaluated in combination with "--cacert" only.'
})
.option('disable-cookies', {
type: 'boolean',
default: false,
description: 'Automatically save and sent cookies with requests'
})
.option('env', {
describe: 'Environment variables',
type: 'string'
Expand Down Expand Up @@ -301,6 +306,7 @@ const handler = async function (argv) {
filename,
cacert,
ignoreTruststore,
disableCookies,
env,
envVar,
insecure,
Expand Down Expand Up @@ -392,6 +398,9 @@ const handler = async function (argv) {
if (insecure) {
options['insecure'] = true;
}
if (disableCookies) {
options['disableCookies'] = true;
}
if (cacert && cacert.length) {
if (insecure) {
console.error(chalk.red(`Ignoring the cacert option since insecure connections are enabled`));
Expand Down
14 changes: 14 additions & 0 deletions packages/bruno-cli/src/runner/run-single-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const { addAwsV4Interceptor, resolveAwsV4Credentials } = require('./awsv4auth-he
const { shouldUseProxy, PatchedHttpsProxyAgent } = require('../utils/proxy-util');
const path = require('path');
const { createFormData } = require('../utils/common');
const { getCookieStringForUrl, saveCookies, shouldUseCookies } = require('../utils/cookies');
const protocolRegex = /^([-+\w]{1,25})(:?\/\/|:)/;

const onConsoleLog = (type, args) => {
Expand Down Expand Up @@ -178,6 +179,14 @@ const runSingleRequest = async function (
});
}

//set cookies if enabled
if (!options.disableCookies) {
const cookieString = getCookieStringForUrl(request.url);
if (cookieString && typeof cookieString === 'string' && cookieString.length) {
request.headers['cookie'] = cookieString;
}
}

// stringify the request url encoded params
if (request.headers['content-type'] === 'application/x-www-form-urlencoded') {
request.data = qs.stringify(request.data);
Expand Down Expand Up @@ -220,6 +229,11 @@ const runSingleRequest = async function (
// Prevents the duration on leaking to the actual result
responseTime = response.headers.get('request-duration');
response.headers.delete('request-duration');

//save cookies if enabled
if (!options.disableCookies) {
saveCookies(request.url, response.headers);
}
} catch (err) {
if (err?.response) {
response = err.response;
Expand Down
100 changes: 100 additions & 0 deletions packages/bruno-cli/src/utils/cookies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
const { Cookie, CookieJar } = require('tough-cookie');
const each = require('lodash/each');

const cookieJar = new CookieJar();

const addCookieToJar = (setCookieHeader, requestUrl) => {
const cookie = Cookie.parse(setCookieHeader, { loose: true });
cookieJar.setCookieSync(cookie, requestUrl, {
ignoreError: true // silently ignore things like parse errors and invalid domains
});
};

const getCookiesForUrl = (url) => {
return cookieJar.getCookiesSync(url);
};

const getCookieStringForUrl = (url) => {
const cookies = getCookiesForUrl(url);

if (!Array.isArray(cookies) || !cookies.length) {
return '';
}

const validCookies = cookies.filter((cookie) => !cookie.expires || cookie.expires > Date.now());

return validCookies.map((cookie) => cookie.cookieString()).join('; ');
};

const getDomainsWithCookies = () => {
return new Promise((resolve, reject) => {
const domainCookieMap = {};

cookieJar.store.getAllCookies((err, cookies) => {
if (err) {
return reject(err);
}

cookies.forEach((cookie) => {
if (!domainCookieMap[cookie.domain]) {
domainCookieMap[cookie.domain] = [cookie];
} else {
domainCookieMap[cookie.domain].push(cookie);
}
});

const domains = Object.keys(domainCookieMap);
const domainsWithCookies = [];

each(domains, (domain) => {
const cookies = domainCookieMap[domain];
const validCookies = cookies.filter((cookie) => !cookie.expires || cookie.expires > Date.now());

if (validCookies.length) {
domainsWithCookies.push({
domain,
cookies: validCookies,
cookieString: validCookies.map((cookie) => cookie.cookieString()).join('; ')
});
}
});

resolve(domainsWithCookies);
});
});
};

const deleteCookiesForDomain = (domain) => {
return new Promise((resolve, reject) => {
cookieJar.store.removeCookies(domain, null, (err) => {
if (err) {
return reject(err);
}

return resolve();
});
});
};

const saveCookies = (url, headers) => {
let setCookieHeaders = [];
if (headers['set-cookie']) {
setCookieHeaders = Array.isArray(headers['set-cookie'])
? headers['set-cookie']
: [headers['set-cookie']];
for (let setCookieHeader of setCookieHeaders) {
if (typeof setCookieHeader === 'string' && setCookieHeader.length) {
addCookieToJar(setCookieHeader, url);
}
}
}
}

module.exports = {
addCookieToJar,
getCookiesForUrl,
getCookieStringForUrl,
getDomainsWithCookies,
deleteCookiesForDomain,
saveCookies
};

0 comments on commit 1fb4298

Please sign in to comment.