feat: add user-agent support in gRPC client channel options#6808
feat: add user-agent support in gRPC client channel options#6808bijin-bruno merged 4 commits intousebruno:mainfrom
Conversation
- Extracted user-agent from request headers and set it as grpc.primary_user_agent channel option. - Updated client instantiation to merge user-agent with existing channel options for enhanced request handling.
WalkthroughGrpcClient now extracts the User-Agent header case-insensitively, merges it into gRPC channel options as Changes
Sequence Diagram(s)sequenceDiagram
participant Request as Request (headers)
participant GrpcClient as GrpcClient
participant ReflectionClient as ReflectionClient
participant Server as gRPC Server
Request->>GrpcClient: provide headers + channelOptions
GrpcClient->>GrpcClient: extract User-Agent (case-insensitive)
GrpcClient->>GrpcClient: build mergedChannelOptions (include grpc.primary_user_agent)
GrpcClient->>ReflectionClient: create reflection client with mergedChannelOptions
ReflectionClient->>GrpcClient: return service & method descriptors
GrpcClient->>Server: instantiate runtime gRPC Client with mergedChannelOptions
GrpcClient->>Server: make RPC calls using mergedChannelOptions
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧹 Recent nitpick comments
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
🧰 Additional context used📓 Path-based instructions (1)**/*.{js,jsx,ts,tsx}📄 CodeRabbit inference engine (CODING_STANDARDS.md)
Files:
🧠 Learnings (2)📚 Learning: 2025-12-02T07:24:50.311ZApplied to files:
📚 Learning: 2025-12-17T21:41:24.730ZApplied to files:
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
🔇 Additional comments (3)
✏️ Tip: You can disable this entire section by setting Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/bruno-requests/src/grpc/grpc-client.js (1)
563-565: Remove the user-agent header from the metadata object to prevent duplication.The code extracts the user-agent from
request.headersand correctly sets it asgrpc.primary_user_agentin the channel options (lines 530–539), which is how@grpc/grpc-jsmanages the User-Agent header. However, the same header is then added again to the metadata object (lines 563–565) via theObject.entriesloop, causing duplication. Filter it out when populating metadata:Suggested fix
Object.entries(request.headers).forEach(([name, value]) => { if (name.toLowerCase() !== 'user-agent') { metadata.add(name, value); } });
🧹 Nitpick comments (2)
packages/bruno-requests/src/grpc/grpc-client.js (2)
531-543: User-agent extraction logic is correct but duplicated.The case-insensitive extraction and channel option assignment work as intended. However, this same logic appears again in
loadMethodsFromReflection(lines 631-637). Consider extracting to a helper function to avoid duplication.Also, note that the spread order
{ 'grpc.primary_user_agent': userAgentValue, ...channelOptions }means an explicitgrpc.primary_user_agentinchannelOptionswill override the header-derived value. If you intend for the header to always take precedence, reverse the spread order.♻️ Suggested helper extraction
+/** + * Extracts user-agent from headers (case-insensitive) and merges into channel options + * `@param` {Object} headers - Request headers + * `@param` {Object} channelOptions - Existing channel options + * `@returns` {Object} Merged channel options with grpc.primary_user_agent if present + */ +const mergeUserAgentIntoChannelOptions = (headers, channelOptions = {}) => { + const userAgentKey = Object.keys(headers || {}).find( + (key) => key.toLowerCase() === 'user-agent' + ); + const userAgentValue = userAgentKey ? headers[userAgentKey] : null; + return userAgentValue + ? { 'grpc.primary_user_agent': userAgentValue, ...channelOptions } + : channelOptions; +};Then use it in both locations:
const mergedChannelOptions = mergeUserAgentIntoChannelOptions(request.headers, channelOptions);
626-637: Inconsistent default forchannelOptionsparameter.In
startConnection,channelOptionsdefaults to{}(line 478), but here inloadMethodsFromReflectionthere's no default value. IfchannelOptionsisundefined, the spread will work but the inconsistency is confusing.♻️ Add default value for consistency
verifyOptions, sendEvent, - channelOptions + channelOptions = {} }) {
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/bruno-requests/src/grpc/grpc-client.js
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation. No tabs, just spaces
Stick to single quotes for strings. For JSX/TSX attributes, use double quotes (e.g., )
Always add semicolons at the end of statements
No trailing commas
Always use parentheses around parameters in arrow functions, even for single params
For multiline constructs, put opening braces on the same line, and ensure consistency. Minimum 2 elements for multiline
No newlines inside function parentheses
Space before and after the arrow in arrow functions.() => {}is good
No space between function name and parentheses.func()notfunc ()
Semicolons go at the end of the line, not on a new line
Names for functions need to be concise and descriptive
Add in JSDoc comments to add more details to the abstractions if needed
Add in meaningful comments instead of obvious ones where complex code flow is explained properly
Files:
packages/bruno-requests/src/grpc/grpc-client.js
🧠 Learnings (2)
📚 Learning: 2025-12-02T07:24:50.311Z
Learnt from: bijin-bruno
Repo: usebruno/bruno PR: 6263
File: packages/bruno-requests/src/auth/oauth2-helper.ts:249-249
Timestamp: 2025-12-02T07:24:50.311Z
Learning: In OAuth2 Basic Auth headers for Bruno, clientSecret is optional and can be omitted. When constructing the Authorization header in `packages/bruno-requests/src/auth/oauth2-helper.ts`, use `clientSecret || ''` instead of `clientSecret!` to properly handle cases where only clientId is provided, per community requests.
Applied to files:
packages/bruno-requests/src/grpc/grpc-client.js
📚 Learning: 2025-12-17T21:41:24.730Z
Learnt from: naman-bruno
Repo: usebruno/bruno PR: 6407
File: packages/bruno-app/src/components/Environments/ConfirmCloseEnvironment/index.js:5-41
Timestamp: 2025-12-17T21:41:24.730Z
Learning: Do not suggest PropTypes validation for React components in the Bruno codebase. The project does not use PropTypes, so reviews should avoid proposing PropTypes and rely on the existing typing/validation approach (e.g., TypeScript or alternative runtime checks) if applicable. This guideline applies broadly to all JavaScript/JSX components in the repo.
Applied to files:
packages/bruno-requests/src/grpc/grpc-client.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: SSL Tests - Windows
- GitHub Check: SSL Tests - Linux
- GitHub Check: SSL Tests - macOS
- GitHub Check: Playwright E2E Tests
- GitHub Check: CLI Tests
- GitHub Check: Unit Tests
🔇 Additional comments (1)
packages/bruno-requests/src/grpc/grpc-client.js (1)
654-654: LGTM!The
mergedChannelOptionsis correctly passed to#getReflectionClient, ensuring the user-agent is applied during reflection calls as well.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
|
@sanish-bruno Please see if a test (unit / playwright) can be added to assert this behaviour |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@packages/bruno-requests/src/grpc/grpc-client.spec.js`:
- Around line 38-49: The on method inside createMockRpc is defined as an arrow
function and uses `return this`, which binds to the outer scope instead of the
mock RPC object; change the implementation of createMockRpc so that on is a
normal function (or make it return the mock object explicitly) and return the
mock RPC object from createMockRpc; specifically update createMockRpc and its on
handler so calling mockRpc.on(...) returns the mockRpc instance (refer to
createMockRpc and the on method).
🧹 Nitpick comments (1)
packages/bruno-requests/src/grpc/grpc-client.spec.js (1)
188-204: Key ordering assertion is implementation-sensitive.This test relies on
Object.keys()returning keys in insertion order. While ES2015+ guarantees this for string keys, asserting on key position couples the test to how the production code constructs the object. If someone refactors the merging logic (e.g., usingObject.assign()differently), this test could break even if the functional behavior is correct.Consider whether this ordering is a requirement worth testing, or if testing presence/values is sufficient.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/bruno-requests/src/grpc/grpc-client.spec.js
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CODING_STANDARDS.md)
**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation. No tabs, just spaces
Stick to single quotes for strings. For JSX/TSX attributes, use double quotes (e.g., )
Always add semicolons at the end of statements
No trailing commas
Always use parentheses around parameters in arrow functions, even for single params
For multiline constructs, put opening braces on the same line, and ensure consistency. Minimum 2 elements for multiline
No newlines inside function parentheses
Space before and after the arrow in arrow functions.() => {}is good
No space between function name and parentheses.func()notfunc ()
Semicolons go at the end of the line, not on a new line
Names for functions need to be concise and descriptive
Add in JSDoc comments to add more details to the abstractions if needed
Add in meaningful comments instead of obvious ones where complex code flow is explained properly
Files:
packages/bruno-requests/src/grpc/grpc-client.spec.js
🧠 Learnings (2)
📚 Learning: 2025-12-05T20:31:33.005Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-05T20:31:33.005Z
Learning: Applies to **/*.test.{js,jsx,ts,tsx} : Add tests for any new functionality or meaningful changes. If code is added, removed, or significantly modified, corresponding tests should be updated or created
Applied to files:
packages/bruno-requests/src/grpc/grpc-client.spec.js
📚 Learning: 2025-12-17T21:41:24.730Z
Learnt from: naman-bruno
Repo: usebruno/bruno PR: 6407
File: packages/bruno-app/src/components/Environments/ConfirmCloseEnvironment/index.js:5-41
Timestamp: 2025-12-17T21:41:24.730Z
Learning: Do not suggest PropTypes validation for React components in the Bruno codebase. The project does not use PropTypes, so reviews should avoid proposing PropTypes and rely on the existing typing/validation approach (e.g., TypeScript or alternative runtime checks) if applicable. This guideline applies broadly to all JavaScript/JSX components in the repo.
Applied to files:
packages/bruno-requests/src/grpc/grpc-client.spec.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: CLI Tests
- GitHub Check: Playwright E2E Tests
- GitHub Check: Unit Tests
- GitHub Check: SSL Tests - macOS
- GitHub Check: SSL Tests - Windows
- GitHub Check: SSL Tests - Linux
🔇 Additional comments (2)
packages/bruno-requests/src/grpc/grpc-client.spec.js (2)
276-285: Good test setup for method pre-registration.Pre-registering the method in
grpcClient.methodscorrectly simulates the state afterloadMethodsFromReflectionhas been called. The serializers are minimal but sufficient for the channel options tests.
86-95: Well-structured test suite with good isolation.Test setup properly clears mocks and resets state between tests. The coverage addresses the PR objective of verifying User-Agent extraction and channel options merging for both reflection and connection flows. Based on learnings, this satisfies the requirement to add tests for new functionality.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
…#6808) * feat: add user-agent support in gRPC client channel options - Extracted user-agent from request headers and set it as grpc.primary_user_agent channel option. - Updated client instantiation to merge user-agent with existing channel options for enhanced request handling. * test: add unit tests for GrpcClient user-agent handling * test: enhance GrpcClient user-agent tests with edge case handling * test: enhance GrpcClient channelOptions handling with override capability
Jira
Fix: Allow custom User-Agent for gRPC requests
Fixes #6800
Description
This PR enables users to set a custom
User-Agentheader for gRPC requests via the metadata configuration in Bruno.Problem
When users set a
User-Agentin their gRPC metadata headers, it was being ignored. The server would always receive the defaultgrpc-node-js/X.X.Xuser-agent from the@grpc/grpc-jslibrary.Solution
The custom
User-Agentheader is now extracted from the request headers and passed as thegrpc.primary_user_agentchannel option to@grpc/grpc-js. This prepends the custom user-agent to the default one.Example:
User-Agent: my-custom-agentmy-custom-agent grpc-node-js/1.13.3Why can't we completely replace the user-agent?
The
@grpc/grpc-jslibrary does not support completely replacing the user-agent. The library always appends its own identifier (grpc-node-js/VERSION) to the user-agent string. This is by design and is a known limitation:The only options provided by
@grpc/grpc-jsare:grpc.primary_user_agent- prepends to the default user-agentgrpc.secondary_user_agent- appends to the default user-agentChanges
startConnection: ExtractsUser-Agentfrom request headers and adds it asgrpc.primary_user_agentchannel optionloadMethodsFromReflection: Same handling for reflection callsContribution Checklist:
Note: Keeping the PR small and focused helps make it easier to review and merge. If you have multiple changes you want to make, please consider submitting them as separate pull requests.
Publishing to New Package Managers
Please see here for more information.
Summary by CodeRabbit
Bug Fixes
Tests
✏️ Tip: You can customize this high-level summary in your review settings.