mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-08 14:35:03 +00:00
Merge branch 'main' into feature/add-bru-setNextRequest
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@usebruno/cli",
|
||||
"version": "0.14.0",
|
||||
"version": "0.15.1",
|
||||
"license": "MIT",
|
||||
"main": "src/index.js",
|
||||
"bin": {
|
||||
@@ -24,8 +24,8 @@
|
||||
"package.json"
|
||||
],
|
||||
"dependencies": {
|
||||
"@usebruno/js": "0.8.0",
|
||||
"@usebruno/lang": "0.8.0",
|
||||
"@usebruno/js": "0.9.1",
|
||||
"@usebruno/lang": "0.9.0",
|
||||
"axios": "^1.5.1",
|
||||
"chai": "^4.3.7",
|
||||
"chalk": "^3.0.0",
|
||||
|
||||
@@ -162,9 +162,7 @@ const getCollectionRoot = (dir) => {
|
||||
}
|
||||
|
||||
const content = fs.readFileSync(collectionRootPath, 'utf8');
|
||||
const json = collectionBruToJson(content);
|
||||
|
||||
return json;
|
||||
return collectionBruToJson(content);
|
||||
};
|
||||
|
||||
const builder = async (yargs) => {
|
||||
|
||||
@@ -88,6 +88,13 @@ const prepareRequest = (request, collectionRoot) => {
|
||||
axiosRequest.data = request.body.xml;
|
||||
}
|
||||
|
||||
if (request.body.mode === 'sparql') {
|
||||
if (!contentTypeDefined) {
|
||||
axiosRequest.headers['content-type'] = 'application/sparql-query';
|
||||
}
|
||||
axiosRequest.data = request.body.sparql;
|
||||
}
|
||||
|
||||
if (request.body.mode === 'formUrlEncoded') {
|
||||
axiosRequest.headers['content-type'] = 'application/x-www-form-urlencoded';
|
||||
const params = {};
|
||||
|
||||
@@ -12,10 +12,10 @@ const { ScriptRuntime, TestRuntime, VarsRuntime, AssertRuntime } = require('@use
|
||||
const { stripExtension } = require('../utils/filesystem');
|
||||
const { getOptions } = require('../utils/bru');
|
||||
const https = require('https');
|
||||
const { HttpsProxyAgent } = require('https-proxy-agent');
|
||||
const { HttpProxyAgent } = require('http-proxy-agent');
|
||||
const { SocksProxyAgent } = require('socks-proxy-agent');
|
||||
const { makeAxiosInstance } = require('../utils/axios-instance');
|
||||
const { shouldUseProxy, PatchedHttpsProxyAgent } = require('../utils/proxy-util');
|
||||
|
||||
const runSingleRequest = async function (
|
||||
filename,
|
||||
@@ -48,7 +48,7 @@ const runSingleRequest = async function (
|
||||
|
||||
// run pre-request vars
|
||||
const preRequestVars = get(bruJson, 'request.vars.req');
|
||||
if (preRequestVars && preRequestVars.length) {
|
||||
if (preRequestVars?.length) {
|
||||
const varsRuntime = new VarsRuntime();
|
||||
varsRuntime.runPreRequestVars(
|
||||
preRequestVars,
|
||||
@@ -65,7 +65,7 @@ const runSingleRequest = async function (
|
||||
get(collectionRoot, 'request.script.req'),
|
||||
get(bruJson, 'request.script.req')
|
||||
]).join(os.EOL);
|
||||
if (requestScriptFile && requestScriptFile.length) {
|
||||
if (requestScriptFile?.length) {
|
||||
const scriptRuntime = new ScriptRuntime();
|
||||
const result = await scriptRuntime.runRequestScript(
|
||||
decomment(requestScriptFile),
|
||||
@@ -91,36 +91,56 @@ const runSingleRequest = async function (
|
||||
if (insecure) {
|
||||
httpsAgentRequestFields['rejectUnauthorized'] = false;
|
||||
} else {
|
||||
const cacertArray = [options['cacert'], process.env.SSL_CERT_FILE, process.env.NODE_EXTRA_CA_CERTS];
|
||||
const cacert = cacertArray.find((el) => el);
|
||||
if (cacert && cacert.length > 1) {
|
||||
const caCertArray = [options['cacert'], process.env.SSL_CERT_FILE, process.env.NODE_EXTRA_CA_CERTS];
|
||||
const caCert = caCertArray.find((el) => el);
|
||||
if (caCert && caCert.length > 1) {
|
||||
try {
|
||||
caCrt = fs.readFileSync(cacert);
|
||||
httpsAgentRequestFields['ca'] = caCrt;
|
||||
httpsAgentRequestFields['ca'] = fs.readFileSync(caCert);
|
||||
} catch (err) {
|
||||
console.log('Error reading CA cert file:' + cacert, err);
|
||||
console.log('Error reading CA cert file:' + caCert, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const interpolationOptions = {
|
||||
envVars: envVariables,
|
||||
collectionVariables,
|
||||
processEnvVars
|
||||
};
|
||||
|
||||
// client certificate config
|
||||
const clientCertConfig = get(brunoConfig, 'clientCertificates.certs', []);
|
||||
for (let clientCert of clientCertConfig) {
|
||||
const domain = interpolateString(clientCert.domain, interpolationOptions);
|
||||
const certFilePath = interpolateString(clientCert.certFilePath, interpolationOptions);
|
||||
const keyFilePath = interpolateString(clientCert.keyFilePath, interpolationOptions);
|
||||
if (domain && certFilePath && keyFilePath) {
|
||||
const hostRegex = '^https:\\/\\/' + domain.replaceAll('.', '\\.').replaceAll('*', '.*');
|
||||
|
||||
if (request.url.match(hostRegex)) {
|
||||
try {
|
||||
httpsAgentRequestFields['cert'] = fs.readFileSync(certFilePath);
|
||||
httpsAgentRequestFields['key'] = fs.readFileSync(keyFilePath);
|
||||
} catch (err) {
|
||||
console.log('Error reading cert/key file', err);
|
||||
}
|
||||
httpsAgentRequestFields['passphrase'] = interpolateString(clientCert.passphrase, interpolationOptions);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set proxy if enabled
|
||||
const proxyEnabled = get(brunoConfig, 'proxy.enabled', false);
|
||||
if (proxyEnabled) {
|
||||
let proxyUri;
|
||||
const interpolationOptions = {
|
||||
envVars: envVariables,
|
||||
collectionVariables,
|
||||
processEnvVars
|
||||
};
|
||||
|
||||
const shouldProxy = shouldUseProxy(request.url, get(brunoConfig, 'proxy.bypassProxy', ''));
|
||||
if (proxyEnabled && shouldProxy) {
|
||||
const proxyProtocol = interpolateString(get(brunoConfig, 'proxy.protocol'), interpolationOptions);
|
||||
const proxyHostname = interpolateString(get(brunoConfig, 'proxy.hostname'), interpolationOptions);
|
||||
const proxyPort = interpolateString(get(brunoConfig, 'proxy.port'), interpolationOptions);
|
||||
const proxyAuthEnabled = get(brunoConfig, 'proxy.auth.enabled', false);
|
||||
const socksEnabled = proxyProtocol.includes('socks');
|
||||
|
||||
interpolateString;
|
||||
|
||||
let proxyUri;
|
||||
if (proxyAuthEnabled) {
|
||||
const proxyAuthUsername = interpolateString(get(brunoConfig, 'proxy.auth.username'), interpolationOptions);
|
||||
const proxyAuthPassword = interpolateString(get(brunoConfig, 'proxy.auth.password'), interpolationOptions);
|
||||
@@ -132,16 +152,13 @@ const runSingleRequest = async function (
|
||||
|
||||
if (socksEnabled) {
|
||||
const socksProxyAgent = new SocksProxyAgent(proxyUri);
|
||||
|
||||
request.httpsAgent = socksProxyAgent;
|
||||
|
||||
request.httpAgent = socksProxyAgent;
|
||||
} else {
|
||||
request.httpsAgent = new HttpsProxyAgent(
|
||||
request.httpsAgent = new PatchedHttpsProxyAgent(
|
||||
proxyUri,
|
||||
Object.keys(httpsAgentRequestFields).length > 0 ? { ...httpsAgentRequestFields } : undefined
|
||||
);
|
||||
|
||||
request.httpAgent = new HttpProxyAgent(proxyUri);
|
||||
}
|
||||
} else if (Object.keys(httpsAgentRequestFields).length > 0) {
|
||||
@@ -167,7 +184,7 @@ const runSingleRequest = async function (
|
||||
responseTime = response.headers.get('request-duration');
|
||||
response.headers.delete('request-duration');
|
||||
} catch (err) {
|
||||
if (err && err.response) {
|
||||
if (err?.response) {
|
||||
response = err.response;
|
||||
|
||||
// Prevents the duration on leaking to the actual result
|
||||
@@ -204,7 +221,7 @@ const runSingleRequest = async function (
|
||||
|
||||
// run post-response vars
|
||||
const postResponseVars = get(bruJson, 'request.vars.res');
|
||||
if (postResponseVars && postResponseVars.length) {
|
||||
if (postResponseVars?.length) {
|
||||
const varsRuntime = new VarsRuntime();
|
||||
varsRuntime.runPostResponseVars(
|
||||
postResponseVars,
|
||||
@@ -222,7 +239,7 @@ const runSingleRequest = async function (
|
||||
get(collectionRoot, 'request.script.res'),
|
||||
get(bruJson, 'request.script.res')
|
||||
]).join(os.EOL);
|
||||
if (responseScriptFile && responseScriptFile.length) {
|
||||
if (responseScriptFile?.length) {
|
||||
const scriptRuntime = new ScriptRuntime();
|
||||
const result = await scriptRuntime.runResponseScript(
|
||||
decomment(responseScriptFile),
|
||||
@@ -283,7 +300,7 @@ const runSingleRequest = async function (
|
||||
testResults = get(result, 'results', []);
|
||||
}
|
||||
|
||||
if (testResults && testResults.length) {
|
||||
if (testResults?.length) {
|
||||
each(testResults, (testResult) => {
|
||||
if (testResult.status === 'pass') {
|
||||
console.log(chalk.green(` ✓ `) + chalk.dim(testResult.description));
|
||||
|
||||
@@ -4,10 +4,10 @@ const axios = require('axios');
|
||||
* Function that configures axios with timing interceptors
|
||||
* Important to note here that the timings are not completely accurate.
|
||||
* @see https://github.com/axios/axios/issues/695
|
||||
* @returns {import('axios').AxiosStatic}
|
||||
* @returns {axios.AxiosInstance}
|
||||
*/
|
||||
function makeAxiosInstance() {
|
||||
/** @type {import('axios').AxiosStatic} */
|
||||
/** @type {axios.AxiosInstance} */
|
||||
const instance = axios.create();
|
||||
|
||||
instance.interceptors.request.use((config) => {
|
||||
@@ -26,9 +26,7 @@ function makeAxiosInstance() {
|
||||
if (error.response) {
|
||||
const end = Date.now();
|
||||
const start = error.config.headers['request-start-time'];
|
||||
if (error.response) {
|
||||
error.response.headers['request-duration'] = end - start;
|
||||
}
|
||||
error.response.headers['request-duration'] = end - start;
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
85
packages/bruno-cli/src/utils/proxy-util.js
Normal file
85
packages/bruno-cli/src/utils/proxy-util.js
Normal file
@@ -0,0 +1,85 @@
|
||||
const parseUrl = require('url').parse;
|
||||
const { isEmpty } = require('lodash');
|
||||
const { HttpsProxyAgent } = require('https-proxy-agent');
|
||||
|
||||
const DEFAULT_PORTS = {
|
||||
ftp: 21,
|
||||
gopher: 70,
|
||||
http: 80,
|
||||
https: 443,
|
||||
ws: 80,
|
||||
wss: 443
|
||||
};
|
||||
/**
|
||||
* check for proxy bypass, copied form 'proxy-from-env'
|
||||
*/
|
||||
const shouldUseProxy = (url, proxyBypass) => {
|
||||
if (proxyBypass === '*') {
|
||||
return false; // Never proxy if wildcard is set.
|
||||
}
|
||||
|
||||
// use proxy if no proxyBypass is set
|
||||
if (!proxyBypass || typeof proxyBypass !== 'string' || isEmpty(proxyBypass.trim())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const parsedUrl = typeof url === 'string' ? parseUrl(url) : url || {};
|
||||
let proto = parsedUrl.protocol;
|
||||
let hostname = parsedUrl.host;
|
||||
let port = parsedUrl.port;
|
||||
if (typeof hostname !== 'string' || !hostname || typeof proto !== 'string') {
|
||||
return false; // Don't proxy URLs without a valid scheme or host.
|
||||
}
|
||||
|
||||
proto = proto.split(':', 1)[0];
|
||||
// Stripping ports in this way instead of using parsedUrl.hostname to make
|
||||
// sure that the brackets around IPv6 addresses are kept.
|
||||
hostname = hostname.replace(/:\d*$/, '');
|
||||
port = parseInt(port) || DEFAULT_PORTS[proto] || 0;
|
||||
|
||||
return proxyBypass.split(/[,;\s]/).every(function (dontProxyFor) {
|
||||
if (!dontProxyFor) {
|
||||
return true; // Skip zero-length hosts.
|
||||
}
|
||||
const parsedProxy = dontProxyFor.match(/^(.+):(\d+)$/);
|
||||
let parsedProxyHostname = parsedProxy ? parsedProxy[1] : dontProxyFor;
|
||||
const parsedProxyPort = parsedProxy ? parseInt(parsedProxy[2]) : 0;
|
||||
if (parsedProxyPort && parsedProxyPort !== port) {
|
||||
return true; // Skip if ports don't match.
|
||||
}
|
||||
|
||||
if (!/^[.*]/.test(parsedProxyHostname)) {
|
||||
// No wildcards, so stop proxying if there is an exact match.
|
||||
return hostname !== parsedProxyHostname;
|
||||
}
|
||||
|
||||
if (parsedProxyHostname.charAt(0) === '*') {
|
||||
// Remove leading wildcard.
|
||||
parsedProxyHostname = parsedProxyHostname.slice(1);
|
||||
}
|
||||
// Stop proxying if the hostname ends with the no_proxy host.
|
||||
return !hostname.endsWith(parsedProxyHostname);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Patched version of HttpsProxyAgent to get around a bug that ignores
|
||||
* options like ca and rejectUnauthorized when upgrading the socket to TLS:
|
||||
* https://github.com/TooTallNate/proxy-agents/issues/194
|
||||
*/
|
||||
class PatchedHttpsProxyAgent extends HttpsProxyAgent {
|
||||
constructor(proxy, opts) {
|
||||
super(proxy, opts);
|
||||
this.constructorOpts = opts;
|
||||
}
|
||||
|
||||
async connect(req, opts) {
|
||||
const combinedOpts = { ...this.constructorOpts, ...opts };
|
||||
return super.connect(req, combinedOpts);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
shouldUseProxy,
|
||||
PatchedHttpsProxyAgent
|
||||
};
|
||||
Reference in New Issue
Block a user