Files
bruno/packages/bruno-cli
lohit f5e437adaf fix: enable SSL session caching and HTTP agent reuse for faster consecutive requests (#6987)
* fix: enable SSL session caching for faster consecutive requests (#6929)

* fix: enable SSL session caching for faster consecutive requests

Previously, Bruno created a new HTTPS agent for every request, which meant
SSL/TLS sessions couldn't be reused. This caused the full TLS handshake
(~450ms) to run on every request, even to the same endpoint.

Changes:
- Add agent caching based on TLS configuration (certs, proxy, SSL options)
- Reuse cached agents for requests with matching configuration
- SSL sessions are now cached and reused, significantly reducing
  response time for consecutive requests to the same host

The fix maintains backward compatibility:
- Timeline logging moved to setup phase (before agent creation)
- Proxy and SSL validation behavior unchanged
- Added clearAgentCache() for testing and configuration changes

Fixes #5574

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: address review feedback for SSL session caching

- Add passphrase to cache key to prevent incorrect agent reuse
- Add MAX_AGENT_CACHE_SIZE (100) with LRU-style eviction
- Use consistent node: prefix for crypto import

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: lohit <lohit@usebruno.com>

* feat(bruno-requests): add timeline agent for TLS event logging

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(bruno-requests): add agent cache for SSL session reuse

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test(bruno-requests): add tests for agent cache

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(bruno-requests): integrate agent cache into http-https-agents

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor(bruno-electron): use shared agent cache from bruno-requests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(bruno-cli): use agent cache for SSL session reuse

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(bruno-requests): add HTTP agent timeline support

Add createTimelineHttpAgentClass for logging HTTP connection events
including proxy usage, DNS lookups, and connection establishment.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor(bruno-requests): extract shared agent caching logic

Add getOrCreateAgentInternal helper to reduce code duplication
between getOrCreateAgent and getOrCreateHttpAgent functions.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(bruno-requests): use HTTP agent cache for connection reuse

Export getOrCreateHttpAgent and use it in http-https-agents for
HTTP requests to enable connection pooling.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(bruno-cli): improve HTTP agent handling and error logging

- Use { keepAlive: true } instead of tlsOptions for HTTP agents
- Add warning log for system proxy configuration errors
- Fix brace style consistency

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(bruno-electron): improve HTTP agent handling

- Use { keepAlive: true } instead of tlsOptions for HTTP agents
- Fix brace style consistency
- Add missing newline at EOF

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(bruno-requests): address code review findings for agent caching

- Fix Buffer hashing bug: properly handle Buffer values in hashValue()
- Add CA array support: new hashCaValue() handles string[] | Buffer[]
- Fix timeline race condition: capture timeline reference in closure
  at createConnection start to isolate concurrent requests
- Fix SSL verify message: check socket.authorized for accurate status
- Fix HTTP/HTTPS agent logic: only set httpsAgent for HTTPS requests
- Add tests for concurrent requests timeline isolation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(bruno-requests): log when reusing cached agent

- HTTPS agents: "Reusing cached agent (SSL session reuse enabled)"
- HTTP agents: "Reusing cached agent (connection reuse enabled)"

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat(preferences): add cache.httpHttpsAgents.enabled preference

* feat(agent-cache): add disableCache option to getOrCreateAgent

* feat(proxy-util): respect httpHttpsAgents cache preference

* refactor(agent-cache): use named props for getOrCreateAgent and getOrCreateHttpAgent

* feat(ipc): add renderer:clear-http-https-agent-cache handler

* feat(redux): add cache.httpHttpsAgents preferences to initial state

* feat(ui): add Cache tab to Preferences

* feat(cli): add --disable-http-https-agents-cache flag

* refactor(cache): replace window.ipcRenderer calls with redux actions

Add getCacheStats, purgeCache, and clearHttpHttpsAgentCache thunks to
the app slice. Update the Cache preferences component to dispatch these
actions instead of calling window.ipcRenderer directly.

Also move handleSave and handleSaveRef above useFormik to fix declaration
order — onSubmit closes over handleSaveRef, so the ref must be initialized
before useFormik is called.

* fix: tests

* fix(cache): thread disableCache and hostname through all agent-creation paths

- Forward disableHttpHttpsAgentsCache through getHttpHttpsAgents → createAgents
  so OAuth2 token requests and bru.sendRequest honour the CLI flag
- Add hostname to agent cache keys (getAgentCacheKey, getHttpAgentCacheKey)
  for per-host TLS session reuse; extract hostname at every call site in
  run-single-request.js, proxy-util.js, and http-https-agents.ts
- Add extractHostname helper in http-https-agents.ts to safely parse hostnames
- Add test coverage for cert, key, pfx, passphrase, and hostname cache-key
  differentiation in agent-cache.spec.ts

* refactor(cache): rename getOrCreateAgent to getOrCreateHttpsAgent

* refactor: simplify UI labels, optimize agent timeline wrapping, silence proxy errors

* fix: tests

* fix(proxy): fix proxy agent construction and CA cert handling

Three fixes:

1. Proxy agents (HttpsProxyAgent, HttpProxyAgent, SocksProxyAgent) expect
   (proxyUri, options) constructor signature, but the agent cache was packing
   proxyUri into options as a single argument. Fixed the non-timeline code
   path in getOrCreateAgentInternal.

2. HTTP requests through an HTTPS proxy need TLS options (ca certs) to
   validate the proxy's certificate. All getOrCreateHttpAgent call sites
   now pass TLS options when the proxy protocol is HTTPS.

3. Setting the `ca` option on any Node.js TLS connection replaces the
   default OpenSSL trust store entirely. CAs only in the OpenSSL default
   trust store (e.g. /etc/ssl/cert.pem) but not in tls.rootCertificates
   were lost. Fixed by converting `ca` to a secureContext via addCACert(),
   which appends custom CAs on top of the OpenSSL defaults instead of
   replacing them.

Also simplified PatchedHttpsProxyAgent to selectively forward only the
relevant TLS options (cert, key, pfx, passphrase, rejectUnauthorized,
secureContext) to the target TLS upgrade instead of blindly merging all
constructor options.

* fix(tls): load client certs into secureContext to prevent silent drop

Add Cache tab to Preferences UI

* fix(proxy): align proxy auth check to use auth.disabled field consistently

* refactor(cache): rename CLI flag to --cache-ssl-session and disable caching by default

- Rename --disable-http-https-agents-cache to --cache-ssl-session (opt-in)
- Rename disableHttpHttpsAgentsCache to cacheSslSession across CLI and bruno-requests
- Default caching to disabled in both bruno-electron and bruno-cli
- Add cacheSslSession to buildCertsAndProxyConfig for bru.sendRequest
- Update Preferences UI labels to "Cache SSL Session"

* refactor(cache): rename httpHttpsAgents to sslSession across preferences and UI

* refactor(cache): remove unused getCacheStats and purgeCache IPC actions

---------

Co-authored-by: karthik <47263234+kxbnb@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-05 18:46:20 +05:30
..
2023-02-06 02:27:22 +05:30
2024-02-06 05:00:25 +05:30
2023-04-01 13:40:23 +05:30

Bruno CLI

With Bruno CLI, you can now run your API collections with ease using simple command line commands.

This makes it easier to test your APIs in different environments, automate your testing process, and integrate your API tests with your continuous integration and deployment workflows.

For detailed documentation, visit Bruno CLI Documentation.

Installation

To install the Bruno CLI, use the node package manager of your choice, such as NPM:

npm install -g @usebruno/cli

Getting started

Navigate to the directory where your API collection resides, and then run:

bru run

This command will run all the requests in your collection. You can also run a single request by specifying its filename:

bru run request.bru

Or run all requests in a collection's subfolder:

bru run folder

If you need to use an environment, you can specify it with the --env option:

bru run folder --env Local

If you need to collect the results of your API tests, you can specify the --output option:

bru run folder --output results.json

If you need to run a set of requests that connect to peers with both publicly and privately signed certificates respectively, you can add private CA certificates via the --cacert option. By default, these certificates will be used in addition to the default truststore:

bru run folder --cacert myCustomCA.pem

If you need to limit the trusted CA to a specified set when validating the request peer, provide them via --cacert and in addition use --ignore-truststore to disable the default truststore:

bru run request.bru --cacert myCustomCA.pem --ignore-truststore

Importing Collections

You can import collections from other formats, such as OpenAPI, using the import command:

bru import openapi --source api.yml --output ~/Desktop/my-collection --collection-name "My API"

You can also use the shorter form with aliases:

bru import openapi -s api.yml -o ~/Desktop/my-collection -n "My API"

This creates a Bruno collection directory that can be opened in Bruno.

You can also import directly from a URL:

bru import openapi --source https://example.com/api-spec.json --output ~/Desktop --collection-name "Remote API"

You can also export the collection as a JSON file:

bru import openapi --source api.yml --output-file ~/Desktop/my-collection.json --collection-name "My API"

Import Options:

Option Details
--source, -s Path to the source file or URL (required)
--output, -o Path to the output directory
--output-file, -f Path to the output JSON file
--collection-name, -n Name for the imported collection
--insecure Skip SSL certificate validation when fetching from URLs

Command Line Options

Option Details
-h, --help Show help
--version Show version number
-r Indicates a recursive run (default: false)
--cacert [string] CA certificate to verify peer against
--env [string] Specify environment to run with
--env-var [string] Overwrite a single environment variable, multiple usages possible
-o, --output [string] Path to write file results to
-f, --format [string] Format of the file results; available formats are "json" (default) or "junit"
--reporter-json [string] Path to generate a JSON report
--reporter-junit [string] Path to generate a JUnit report
--reporter-html [string] Path to generate an HTML report
--insecure Allow insecure server connections
--tests-only Only run requests that have tests
--bail Stop execution after a failure of a request, test, or assertion
--csv-file-path CSV file to run the collection with
--reporter--skip-all-headers Skip all headers in the report
--reporter-skip-headers Skip specific headers in the report
--client-cert-config Client certificate configuration by passing a JSON file
--delay [number] Add delay to each request

Scripting

Bruno cli returns the following exit status codes:

  • 0 -- execution successful
  • 1 -- an assertion, test, or request in the executed collection failed
  • 2 -- the specified output directory does not exist
  • 3 -- the request chain seems to loop endlessly
  • 4 -- bru was called outside of a collection root directory
  • 5 -- the specified input file does not exist
  • 6 -- the specified environment does not exist
  • 7 -- the environment override was not a string or object
  • 8 -- an environment override is malformed
  • 9 -- an invalid output format was requested
  • 255 -- another error occurred

Demo

demo

Support

If you encounter any issues or have any feedback or suggestions, please raise them on our GitHub repository

Thank you for using Bruno CLI!

Changelog

See https://github.com/usebruno/bruno/releases

License

MIT