Skip to content

Commit ba08553

Browse files
authored
feat: user-agent over gRPC (#2840)
1 parent a90f54a commit ba08553

File tree

2 files changed

+84
-1
lines changed

2 files changed

+84
-1
lines changed

src/gax-internal/src/grpc.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ impl Client {
7272
Request: prost::Message + 'static + Clone,
7373
Response: prost::Message + Default + 'static,
7474
{
75-
let headers = Self::make_headers(api_client_header, request_params).await?;
75+
let headers = Self::make_headers(api_client_header, request_params, &options).await?;
7676
match self.get_retry_policy(&options) {
7777
None => {
7878
self.request_attempt::<Request, Response>(
@@ -204,8 +204,15 @@ impl Client {
204204
async fn make_headers(
205205
api_client_header: &'static str,
206206
request_params: &str,
207+
options: &gax::options::RequestOptions,
207208
) -> Result<http::header::HeaderMap> {
208209
let mut headers = HeaderMap::new();
210+
if let Some(user_agent) = options.user_agent() {
211+
headers.append(
212+
http::header::USER_AGENT,
213+
http::header::HeaderValue::from_str(user_agent).map_err(Error::ser)?,
214+
);
215+
}
209216
headers.append(
210217
http::header::HeaderName::from_static("x-goog-api-client"),
211218
http::header::HeaderValue::from_static(api_client_header),
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#[cfg(all(test, feature = "_internal-grpc-client"))]
16+
mod test {
17+
use gax::options::*;
18+
use google_cloud_gax_internal::grpc;
19+
use grpc_server::{builder, google, start_echo_server};
20+
21+
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
22+
async fn test_user_agent() -> anyhow::Result<()> {
23+
let (endpoint, _server) = start_echo_server().await?;
24+
let client = builder(endpoint)
25+
.with_credentials(auth::credentials::testing::test_credentials())
26+
.build()
27+
.await?;
28+
29+
let options = {
30+
let mut o = RequestOptions::default();
31+
o.set_user_agent("custom-user-agent/v1.2.3");
32+
o
33+
};
34+
let response = send_request(client, options).await?;
35+
let user_agent = response
36+
.metadata
37+
.get(http::header::USER_AGENT.as_str())
38+
.map(String::as_str)
39+
.expect("There should be a User-Agent header");
40+
let components: Vec<&str> = user_agent.split(' ').collect();
41+
assert!(
42+
components.contains(&"custom-user-agent/v1.2.3"),
43+
"User-Agent: {user_agent}"
44+
);
45+
Ok(())
46+
}
47+
48+
async fn send_request(
49+
client: grpc::Client,
50+
options: gax::options::RequestOptions,
51+
) -> gax::Result<google::test::v1::EchoResponse> {
52+
let extensions = {
53+
let mut e = tonic::Extensions::new();
54+
e.insert(tonic::GrpcMethod::new(
55+
"google.test.v1.EchoServices",
56+
"Echo",
57+
));
58+
e
59+
};
60+
let request = google::test::v1::EchoRequest {
61+
message: "message".into(),
62+
..google::test::v1::EchoRequest::default()
63+
};
64+
client
65+
.execute(
66+
extensions,
67+
http::uri::PathAndQuery::from_static("/google.test.v1.EchoService/Echo"),
68+
request,
69+
options,
70+
"test-only-api-client/1.0",
71+
"",
72+
)
73+
.await
74+
.map(tonic::Response::into_inner)
75+
}
76+
}

0 commit comments

Comments
 (0)