Skip to content

Commit e0ea5d0

Browse files
dbolducLucioFranco
andauthored
feat: preserve request user-agent (#2250)
Co-authored-by: Lucio Franco <luciofranco14@gmail.com>
1 parent 55e48c0 commit e0ea5d0

File tree

1 file changed

+91
-2
lines changed

1 file changed

+91
-2
lines changed

tonic/src/transport/channel/service/user_agent.rs

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,21 @@ where
3939
}
4040

4141
fn call(&mut self, mut req: Request<ReqBody>) -> Self::Future {
42-
req.headers_mut()
43-
.insert(USER_AGENT, self.user_agent.clone());
42+
if let Ok(Some(user_agent)) = req
43+
.headers_mut()
44+
.try_insert(USER_AGENT, self.user_agent.clone())
45+
{
46+
// The User-Agent header has already been set on the request. Let's
47+
// append our user agent to the end.
48+
let mut buf = Vec::new();
49+
buf.extend(user_agent.as_bytes());
50+
buf.push(b' ');
51+
buf.extend(self.user_agent.as_bytes());
52+
req.headers_mut().insert(
53+
USER_AGENT,
54+
HeaderValue::from_bytes(&buf).expect("user-agent should be valid"),
55+
);
56+
}
4457

4558
self.inner.call(req)
4659
}
@@ -67,4 +80,80 @@ mod tests {
6780
HeaderValue::from_str(&format!("Greeter 1.1 {}", TONIC_USER_AGENT)).unwrap()
6881
)
6982
}
83+
84+
struct TestSvc {
85+
pub expected_user_agent: String,
86+
}
87+
88+
impl Service<Request<()>> for TestSvc {
89+
type Response = ();
90+
type Error = ();
91+
type Future = std::future::Ready<Result<(), ()>>;
92+
93+
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
94+
Poll::Ready(Ok(()))
95+
}
96+
97+
fn call(&mut self, req: Request<()>) -> Self::Future {
98+
let user_agent = req.headers().get(USER_AGENT).unwrap().to_str().unwrap();
99+
assert_eq!(user_agent, self.expected_user_agent);
100+
std::future::ready(Ok(()))
101+
}
102+
}
103+
104+
#[tokio::test]
105+
async fn sets_default_user_agent_if_none_present() {
106+
let expected_user_agent = TONIC_USER_AGENT.to_string();
107+
let mut ua = UserAgent::new(
108+
TestSvc {
109+
expected_user_agent,
110+
},
111+
None,
112+
);
113+
let _ = ua.call(Request::default()).await;
114+
}
115+
116+
#[tokio::test]
117+
async fn sets_custom_user_agent_if_none_present() {
118+
let expected_user_agent = format!("Greeter 1.1 {}", TONIC_USER_AGENT);
119+
let mut ua = UserAgent::new(
120+
TestSvc {
121+
expected_user_agent,
122+
},
123+
Some(HeaderValue::from_static("Greeter 1.1")),
124+
);
125+
let _ = ua.call(Request::default()).await;
126+
}
127+
128+
#[tokio::test]
129+
async fn appends_default_user_agent_to_request_user_agent() {
130+
let mut req = Request::default();
131+
req.headers_mut()
132+
.insert(USER_AGENT, HeaderValue::from_static("request-ua/x.y"));
133+
134+
let expected_user_agent = format!("request-ua/x.y {}", TONIC_USER_AGENT);
135+
let mut ua = UserAgent::new(
136+
TestSvc {
137+
expected_user_agent,
138+
},
139+
None,
140+
);
141+
let _ = ua.call(req).await;
142+
}
143+
144+
#[tokio::test]
145+
async fn appends_custom_user_agent_to_request_user_agent() {
146+
let mut req = Request::default();
147+
req.headers_mut()
148+
.insert(USER_AGENT, HeaderValue::from_static("request-ua/x.y"));
149+
150+
let expected_user_agent = format!("request-ua/x.y Greeter 1.1 {}", TONIC_USER_AGENT);
151+
let mut ua = UserAgent::new(
152+
TestSvc {
153+
expected_user_agent,
154+
},
155+
Some(HeaderValue::from_static("Greeter 1.1")),
156+
);
157+
let _ = ua.call(req).await;
158+
}
70159
}

0 commit comments

Comments
 (0)