Skip to content

Commit b5ece18

Browse files
committed
Set the Content-Type header for binary content
Reverse proxies can default to different content types with UTF-8 encoding and reject/modify the body. This change sets the `Content-type` header in requests to `application/octet-stream` as the body will be a serialized protocol buffer.
1 parent 8aa5588 commit b5ece18

File tree

2 files changed

+41
-53
lines changed

2 files changed

+41
-53
lines changed

src/client.rs

Lines changed: 34 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
use ::prost::Message;
1+
use prost::Message;
22
use reqwest;
3+
use reqwest::header::CONTENT_TYPE;
34
use reqwest::Client;
5+
use std::default::Default;
46

57
use crate::error::VssError;
68
use crate::types::{
@@ -9,6 +11,8 @@ use crate::types::{
911
};
1012
use crate::util::retry::{retry, RetryPolicy};
1113

14+
const APPLICATION_OCTET_STREAM: &str = "application/octet-stream";
15+
1216
/// Thin-client to access a hosted instance of Versioned Storage Service (VSS).
1317
/// The provided [`VssClient`] API is minimalistic and is congruent to the VSS server-side API.
1418
#[derive(Clone)]
@@ -45,25 +49,15 @@ impl<R: RetryPolicy<E = VssError>> VssClient<R> {
4549
retry(
4650
|| async {
4751
let url = format!("{}/getObject", self.base_url);
48-
49-
let request_body = request.encode_to_vec();
50-
let raw_response = self.client.post(url).body(request_body).send().await?;
51-
let status = raw_response.status();
52-
let payload = raw_response.bytes().await?;
53-
54-
if status.is_success() {
55-
let response = GetObjectResponse::decode(&payload[..])?;
56-
52+
self.post_request(request, &url).await.and_then(|response: GetObjectResponse| {
5753
if response.value.is_none() {
58-
return Err(VssError::InternalServerError(
54+
Err(VssError::InternalServerError(
5955
"VSS Server API Violation, expected value in GetObjectResponse but found none".to_string(),
60-
));
56+
))
57+
} else {
58+
Ok(response)
6159
}
62-
63-
Ok(response)
64-
} else {
65-
Err(VssError::new(status, payload))
66-
}
60+
})
6761
},
6862
&self.retry_policy,
6963
)
@@ -78,18 +72,7 @@ impl<R: RetryPolicy<E = VssError>> VssClient<R> {
7872
retry(
7973
|| async {
8074
let url = format!("{}/putObjects", self.base_url);
81-
82-
let request_body = request.encode_to_vec();
83-
let response_raw = self.client.post(&url).body(request_body).send().await?;
84-
let status = response_raw.status();
85-
let payload = response_raw.bytes().await?;
86-
87-
if status.is_success() {
88-
let response = PutObjectResponse::decode(&payload[..])?;
89-
Ok(response)
90-
} else {
91-
Err(VssError::new(status, payload))
92-
}
75+
self.post_request(request, &url).await
9376
},
9477
&self.retry_policy,
9578
)
@@ -103,18 +86,7 @@ impl<R: RetryPolicy<E = VssError>> VssClient<R> {
10386
retry(
10487
|| async {
10588
let url = format!("{}/deleteObject", self.base_url);
106-
107-
let request_body = request.encode_to_vec();
108-
let response_raw = self.client.post(url).body(request_body).send().await?;
109-
let status = response_raw.status();
110-
let payload = response_raw.bytes().await?;
111-
112-
if status.is_success() {
113-
let response = DeleteObjectResponse::decode(&payload[..])?;
114-
Ok(response)
115-
} else {
116-
Err(VssError::new(status, payload))
117-
}
89+
self.post_request(request, &url).await
11890
},
11991
&self.retry_policy,
12092
)
@@ -130,21 +102,30 @@ impl<R: RetryPolicy<E = VssError>> VssClient<R> {
130102
retry(
131103
|| async {
132104
let url = format!("{}/listKeyVersions", self.base_url);
133-
134-
let request_body = request.encode_to_vec();
135-
let response_raw = self.client.post(url).body(request_body).send().await?;
136-
let status = response_raw.status();
137-
let payload = response_raw.bytes().await?;
138-
139-
if status.is_success() {
140-
let response = ListKeyVersionsResponse::decode(&payload[..])?;
141-
Ok(response)
142-
} else {
143-
Err(VssError::new(status, payload))
144-
}
105+
self.post_request(request, &url).await
145106
},
146107
&self.retry_policy,
147108
)
148109
.await
149110
}
111+
112+
async fn post_request<Rq: Message, Rs: Message + Default>(&self, request: &Rq, url: &str) -> Result<Rs, VssError> {
113+
let request_body = request.encode_to_vec();
114+
let response_raw = self
115+
.client
116+
.post(url)
117+
.header(CONTENT_TYPE, APPLICATION_OCTET_STREAM)
118+
.body(request_body)
119+
.send()
120+
.await?;
121+
let status = response_raw.status();
122+
let payload = response_raw.bytes().await?;
123+
124+
if status.is_success() {
125+
let response = Rs::decode(&payload[..])?;
126+
Ok(response)
127+
} else {
128+
Err(VssError::new(status, payload))
129+
}
130+
}
150131
}

tests/tests.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
mod tests {
33
use mockito::{self, Matcher};
44
use prost::Message;
5+
use reqwest::header::CONTENT_TYPE;
56
use std::time::Duration;
67
use vss_client::client::VssClient;
78
use vss_client::error::VssError;
@@ -12,6 +13,8 @@ mod tests {
1213
};
1314
use vss_client::util::retry::{ExponentialBackoffRetryPolicy, RetryPolicy};
1415

16+
const APPLICATION_OCTET_STREAM: &'static str = "application/octet-stream";
17+
1518
const GET_OBJECT_ENDPOINT: &'static str = "/getObject";
1619
const PUT_OBJECT_ENDPOINT: &'static str = "/putObjects";
1720
const DELETE_OBJECT_ENDPOINT: &'static str = "/deleteObject";
@@ -31,6 +34,7 @@ mod tests {
3134

3235
// Register the mock endpoint with the mockito server.
3336
let mock_server = mockito::mock("POST", GET_OBJECT_ENDPOINT)
37+
.match_header(CONTENT_TYPE.as_str(), APPLICATION_OCTET_STREAM)
3438
.match_body(get_request.encode_to_vec())
3539
.with_status(200)
3640
.with_body(mock_response.encode_to_vec())
@@ -64,6 +68,7 @@ mod tests {
6468

6569
// Register the mock endpoint with the mockito server.
6670
let mock_server = mockito::mock("POST", PUT_OBJECT_ENDPOINT)
71+
.match_header(CONTENT_TYPE.as_str(), APPLICATION_OCTET_STREAM)
6772
.match_body(request.encode_to_vec())
6873
.with_status(200)
6974
.with_body(mock_response.encode_to_vec())
@@ -94,6 +99,7 @@ mod tests {
9499

95100
// Register the mock endpoint with the mockito server.
96101
let mock_server = mockito::mock("POST", DELETE_OBJECT_ENDPOINT)
102+
.match_header(CONTENT_TYPE.as_str(), APPLICATION_OCTET_STREAM)
97103
.match_body(request.encode_to_vec())
98104
.with_status(200)
99105
.with_body(mock_response.encode_to_vec())
@@ -134,6 +140,7 @@ mod tests {
134140

135141
// Register the mock endpoint with the mockito server.
136142
let mock_server = mockito::mock("POST", LIST_KEY_VERSIONS_ENDPOINT)
143+
.match_header(CONTENT_TYPE.as_str(), APPLICATION_OCTET_STREAM)
137144
.match_body(request.encode_to_vec())
138145
.with_status(200)
139146
.with_body(mock_response.encode_to_vec())

0 commit comments

Comments
 (0)