Skip to content

gRPC client: Strip "grpc-go/version" suffix from User-Agent header#5689

Merged
RPRX merged 6 commits intomainfrom
copilot/explore-grpc-options
Feb 13, 2026
Merged

gRPC client: Strip "grpc-go/version" suffix from User-Agent header#5689
RPRX merged 6 commits intomainfrom
copilot/explore-grpc-options

Conversation

Copy link
Contributor

Copilot AI commented Feb 13, 2026

  • Investigate the issue: gRPC library appends grpc-go/1.78.0 to User-Agent header
  • Research gRPC library options: no public API exists to suppress the suffix
  • Simplified approach: directly override conn.dopts.copts.UserAgent via reflect after grpc.NewClient returns
  • Use grpc.NewClient (lazy, no connection) instead of grpc.Dial to ensure setUserAgent runs before any transport is created
  • Removed grpc.WithUserAgent() call — setUserAgent sets the field directly, no need to set it first
  • Add test to verify user-agent is set without grpc-go suffix
  • Build and test the changes

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@RPRX
Copy link
Member

RPRX commented Feb 13, 2026

Claude Opus 4.6,以及启用了 Preview 中的 Copilot Memory,从现在开始或许不用每次都拿斩杀线吓它了,偶尔敲打一下就行

You can use Copilot Memory to remember repository context across Copilot agent interactions. This preview is governed by GitHub's pre-release terms.

Copilot AI and others added 2 commits February 13, 2026 12:49
Use reflect to create a custom grpc.DialOption that sets the User-Agent
to exactly the configured value, without grpc-go appending its version
string (e.g., "grpc-go/1.78.0"). The gRPC library's WithUserAgent()
always appends the grpc-go version, which creates a fingerprinting issue.

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
…tUserAgent

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
@RPRX
Copy link
Member

RPRX commented Feb 13, 2026

这。。。@Fangliding 你看下,可能确实没提供选项、得 reflect

Copilot AI changed the title [WIP] Investigate gRPC options to avoid specific issue Strip grpc-go version suffix from User-Agent header Feb 13, 2026
Copilot AI requested a review from RPRX February 13, 2026 12:54
@Fangliding
Copy link
Member

image 它的思路没啥问题 确实得这样。。

@RPRX
Copy link
Member

RPRX commented Feb 13, 2026

这个设计也是绝了

@Fangliding
Copy link
Member

Fangliding commented Feb 13, 2026

我猜可能 UAptr := (*string)(reflect.ValueOf(conn).Elem().FieldByName("dopts").FieldByName("copts").FieldByName("UserAgent").UnsafePointer() 这样也行吧 不知道dial函数给的conn会不会开连接

@RPRX
Copy link
Member

RPRX commented Feb 13, 2026

人类懒得处理异常情况不过 AI 不懒,它还加了个 test,如果以后 gRPC 库内部字段名改了应该会 fail/panic,改下吧留着 test 就行

@copilot 根据 #5689 (comment) 简化代码与 test(减少行数)

…Func

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 13, 2026

人类懒得处理异常情况不过 AI 不懒,它还加了个 test,如果以后 gRPC 库内部字段名改了应该会 fail/panic,改下吧留着 test 就行

@copilot 根据 https://gith...

Simplified in 40a9011. Replaced the reflect.MakeFunc approach with a direct setUserAgent(conn, ua) call after grpc.Dial, which overrides conn.dopts.copts.UserAgent via reflect — reduced from ~25 lines to 4. Test also simplified to match.

@RPRX
Copy link
Member

RPRX commented Feb 13, 2026

@copilot 原来这个要 dial 后啊,你看下 gRPC 那个 dial 后有没有发实际数据出去

grpc.Dial calls exitIdleMode which can start connecting asynchronously.
Switch to grpc.NewClient (lazy, no connection) + setUserAgent + Connect()
to guarantee the UserAgent is fixed before any transport is created.

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 13, 2026

@copilot 原来这个要 dial 后啊,你看下 gRPC 那个 dial 后有没有发实际数据出去

grpc.Dial calls exitIdleMode() which starts the resolver, and the balancer then calls go ac.connect() in a goroutine — so yes, a transport could be created (with the wrong UserAgent) before setUserAgent runs.

Fixed in e9f44cb: switched to grpc.NewClient (stays idle, no goroutines) → setUserAgentconn.Connect(). This guarantees the UserAgent is fixed before any transport is created.

@RPRX
Copy link
Member

RPRX commented Feb 13, 2026

@copilot conn.Connect() 没返回个 error 啥的?返回了的话给 err 赋值

Copy link
Contributor Author

Copilot AI commented Feb 13, 2026

@copilot conn.Connect() 没返回个 error 啥的?返回了的话给 err 赋值

conn.Connect() 没有返回值,签名是 func (cc *ClientConn) Connect() — 它只是把 channel 从 idle 状态踢出去,连接是异步进行的,错误通过后续 RPC 调用返回。

@RPRX
Copy link
Member

RPRX commented Feb 13, 2026

@copilot 你加那 passthrough 是啥玩意儿

Copy link
Contributor Author

Copilot AI commented Feb 13, 2026

@copilot 你加那 passthrough 是啥玩意儿

grpc.Dial 内部默认用 passthrough scheme(把 target 原样传给 dialer),但 grpc.NewClient 默认用 dns scheme(会先做 DNS 解析)。因为这里用了自定义的 WithContextDialer,需要 passthrough:/// 前缀才能保持原来的行为——把地址原样传给 dialer,不做额外的 DNS 解析。

@RPRX
Copy link
Member

RPRX commented Feb 13, 2026

@copilot dialOptions = append(dialOptions, grpc.WithUserAgent(userAgent)) 这行能删掉吗,并把上面四行移下来放一起

…serAgent

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 13, 2026

@copilot dialOptions = append(dialOptions, grpc.WithUserAgent(userAgent)) 这行能删掉吗,并把上面四行移下来放一起

Done in 1219de7. Removed grpc.WithUserAgent(userAgent) and moved the userAgent declaration into the if err == nil block next to setUserAgent.

@RPRX
Copy link
Member

RPRX commented Feb 13, 2026

我发现这 AI 只是回答问题的话会用中文回复,涉及写代码的话会用英文回复

@RPRX
Copy link
Member

RPRX commented Feb 13, 2026

Test 全过应该没问题,可以合了

@RPRX RPRX changed the title Strip grpc-go version suffix from User-Agent header gRPC client: Strip grpc-go version suffix from User-Agent header Feb 13, 2026
@RPRX RPRX marked this pull request as ready for review February 13, 2026 19:48
@RPRX RPRX changed the title gRPC client: Strip grpc-go version suffix from User-Agent header gRPC client: Strip "grpc-go/version" suffix from User-Agent header Feb 13, 2026
@RPRX RPRX merged commit b43276c into main Feb 13, 2026
78 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants