Skip to content

Commit 86fb8e8

Browse files
committed
feat(rad): stop using surf library for retrievals
1 parent 93097c3 commit 86fb8e8

File tree

5 files changed

+102
-97
lines changed

5 files changed

+102
-97
lines changed

data_structures/Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ name = "witnet_data_structures"
66
version = "1.6.7"
77
workspace = ".."
88

9+
[features]
10+
rocksdb-backend = ["witnet_storage/rocksdb-backend"]
11+
912
[dependencies]
1013
bls-signatures-rs = "0.1.0"
1114
bech32 = "0.7.2"
@@ -35,7 +38,7 @@ vrf = "0.2.3"
3538
witnet_crypto = { path = "../crypto" }
3639
witnet_reputation = { path = "../reputation", features = ["serde"] }
3740
witnet_protected = { path = "../protected", features = ["serde"] }
38-
witnet_storage = { path = "../storage", features = ["rocksdb-backend"] }
41+
witnet_storage = { path = "../storage" }
3942
witnet_util = { path = "../util" }
4043

4144
[build-dependencies]

net/src/client/http/mod.rs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use isahc::prelude::*;
55
use async_trait::async_trait;
66
use failure::Fail;
77
use isahc::config::RedirectPolicy;
8+
use isahc::http;
9+
use isahc::http::request::Builder;
810

911
/// Maximum number of HTTP redirects to follow
1012
const MAX_REDIRECTS: u32 = 4;
@@ -15,6 +17,21 @@ pub struct WitnetHttpClient {
1517
client: isahc::HttpClient,
1618
}
1719

20+
impl WitnetHttpClient {
21+
/// Simple wrapper around `isahc::HttpClient::send_async`.
22+
pub async fn send(
23+
&self,
24+
request: WitnetHttpRequest,
25+
) -> Result<WitnetHttpResponse, WitnetHttpError> {
26+
Ok(WitnetHttpResponse::from(
27+
self.client
28+
.send_async(request.req)
29+
.await
30+
.map_err(|e| WitnetHttpError::HttpRequestError { msg: e.to_string() })?,
31+
))
32+
}
33+
}
34+
1835
/// Errors for WitnetHttpClient and other auxiliary structures in this module.
1936
#[derive(Clone, Debug, Eq, Fail, PartialEq)]
2037
pub enum WitnetHttpError {
@@ -106,9 +123,27 @@ impl WitnetHttpClient {
106123
}
107124
}
108125

126+
/// Alias for the specific type of body that we use.
127+
pub type WitnetHttpBody = isahc::AsyncBody;
128+
/// Alias for our request builder.
129+
pub type WitnetHttpRequestBuilder = http::request::Builder;
130+
type Request = http::Request<WitnetHttpBody>;
131+
109132
/// Enables interoperability between `isahc::Request` and `surf::http::Request`.
110133
pub struct WitnetHttpRequest {
111-
req: isahc::Request<isahc::AsyncBody>,
134+
req: isahc::Request<WitnetHttpBody>,
135+
}
136+
137+
impl WitnetHttpRequest {
138+
/// Allows creating a `WitnetHttpRequest` using the same API from `http::request::Builder`.
139+
pub fn build<F, E>(mut f: F) -> Result<Self, E>
140+
where
141+
F: FnMut(WitnetHttpRequestBuilder) -> Result<Request, E>,
142+
{
143+
Ok(Self {
144+
req: f(Builder::new())?,
145+
})
146+
}
112147
}
113148

114149
impl From<isahc::Request<isahc::AsyncBody>> for WitnetHttpRequest {
@@ -169,6 +204,13 @@ pub struct WitnetHttpResponse {
169204
res: isahc::Response<isahc::AsyncBody>,
170205
}
171206

207+
impl WitnetHttpResponse {
208+
/// Simple wrapper around `isahc::Response::status`.
209+
pub fn inner(self) -> isahc::Response<isahc::AsyncBody> {
210+
self.res
211+
}
212+
}
213+
172214
impl From<isahc::Response<isahc::AsyncBody>> for WitnetHttpResponse {
173215
#[inline]
174216
fn from(res: isahc::Response<isahc::AsyncBody>) -> Self {

rad/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ edition = "2021"
66
workspace = ".."
77
description = "RAD component"
88

9+
[features]
10+
rocksdb-backend = ["witnet_data_structures/rocksdb-backend"]
11+
912
[dependencies]
1013
cbor-codec = { git = "https://github.com/witnet/cbor-codec.git", branch = "feat/ldexpf-shim" }
1114
failure = "0.1.8"

rad/src/lib.rs

Lines changed: 50 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22
33
extern crate witnet_data_structures;
44

5-
use std::str::FromStr;
6-
7-
use futures::{executor::block_on, future::join_all};
5+
use futures::{AsyncReadExt, executor::block_on, future::join_all};
86
use serde::Serialize;
97
pub use serde_cbor::{to_vec as cbor_to_vec, Value as CborValue};
108
#[cfg(test)]
@@ -18,13 +16,7 @@ use witnet_data_structures::{
1816
witnessing::WitnessingConfig,
1917
};
2018
pub use witnet_net::Uri;
21-
use witnet_net::{
22-
client::http::WitnetHttpClient,
23-
surf::{
24-
http::headers::{HeaderName, HeaderValues, ToHeaderValues},
25-
RequestBuilder,
26-
},
27-
};
19+
use witnet_net::client::http::WitnetHttpClient;
2820

2921
use crate::{
3022
conditions::{evaluate_tally_precondition_clause, TallyPreconditionClauseResult},
@@ -37,6 +29,9 @@ use crate::{
3729
user_agents::UserAgent,
3830
};
3931
use core::convert::From;
32+
use witnet_net::client::http::{
33+
WitnetHttpBody, WitnetHttpRequest,
34+
};
4035

4136
pub mod conditions;
4237
pub mod error;
@@ -250,38 +245,61 @@ async fn http_response(
250245
message: err.to_string(),
251246
})?
252247
}
253-
}
254-
.as_surf_client();
248+
};
255249

256-
let request = match retrieve.kind {
257-
RADType::HttpGet => client.get(&retrieve.url),
258-
RADType::HttpPost => {
259-
// The call to `.body` sets the content type header to `application/octet-stream`
260-
client.post(&retrieve.url).body(retrieve.body.clone())
250+
let request = WitnetHttpRequest::build(|builder| {
251+
// Populate the builder and generate the body for different types of retrievals
252+
let (builder, body) = match retrieve.kind {
253+
RADType::HttpGet => (
254+
builder.method("GET").uri(&retrieve.url),
255+
WitnetHttpBody::empty(),
256+
),
257+
RADType::HttpPost => {
258+
// Using `Vec<u8>` as the body sets the content type header to `application/octet-stream`
259+
(
260+
builder.method("POST").uri(&retrieve.url),
261+
WitnetHttpBody::from(retrieve.body.clone()),
262+
)
263+
}
264+
_ => panic!(
265+
"Called http_response with invalid retrieval kind {:?}",
266+
retrieve.kind
267+
),
268+
};
269+
270+
// Add random user agent
271+
let mut builder = builder.header("User-Agent", UserAgent::random());
272+
273+
// Add extra_headers from retrieve.headers
274+
for (name, value) in &retrieve.headers {
275+
builder = builder.header(name, value);
261276
}
262-
_ => panic!(
263-
"Called http_response with invalid retrieval kind {:?}",
264-
retrieve.kind
265-
),
266-
};
267-
let request = add_http_headers(request, retrieve, context)?;
268-
let mut response = request.await.map_err(|x| RadError::HttpOther {
269-
message: x.to_string(),
277+
278+
// Finally attach the body to complete building the HTTP request
279+
builder
280+
.body(body)
281+
.map_err(|e| RadError::HttpOther { message: e.to_string() })
270282
})?;
271283

284+
let response = client
285+
.send(request)
286+
.await
287+
.map_err(|x| RadError::HttpOther {
288+
message: x.to_string(),
289+
})?
290+
.inner();
291+
272292
if !response.status().is_success() {
273293
return Err(RadError::HttpStatus {
274294
status_code: response.status().into(),
275295
});
276296
}
277297

278-
let response_string = response
279-
// TODO: replace with .body_bytes() and let RADON handle the encoding?
280-
.body_string()
281-
.await
282-
.map_err(|x| RadError::HttpOther {
283-
message: x.to_string(),
284-
})?;
298+
let (_parts, mut body) = response.into_parts();
299+
let mut response_string= String::default();
300+
body.read_to_string(&mut response_string).await.map_err(|x| RadError::HttpOther {
301+
message: x.to_string(),
302+
})?;
285303

286304
let result = run_retrieval_with_data_report(retrieve, &response_string, context, settings);
287305

@@ -320,67 +338,6 @@ async fn rng_response(
320338
Ok(RadonReport::from_result(Ok(random_bytes), context))
321339
}
322340

323-
/// Add HTTP headers from `retrieve.headers` to surf request.
324-
///
325-
/// Some notes:
326-
///
327-
/// * Overwriting the User-Agent header is allowed.
328-
/// * Multiple headers with the same key but different value are not supported, the current
329-
/// implementation will only use the last header.
330-
/// * All the non-standard headers will be converted to lowercase. Standard headers will use their
331-
/// standard capitalization, for example: `Content-Type`.
332-
/// * The HTTP client may reorder the headers: the order can change between consecutive invocations
333-
/// of the same request
334-
fn add_http_headers(
335-
mut request: RequestBuilder,
336-
retrieve: &RADRetrieve,
337-
_context: &mut ReportContext<RadonTypes>,
338-
) -> Result<RequestBuilder> {
339-
// Add random user agent
340-
request = request.header("User-Agent", UserAgent::random());
341-
342-
// Add extra_headers from retrieve.headers
343-
for (name, value) in &retrieve.headers {
344-
// Additional validation because surf does not validate some cases such as:
345-
// * header name that contains `:`
346-
// * header value that contains `\n`
347-
let _name = http::header::HeaderName::from_str(name.as_str()).map_err(|e| {
348-
RadError::InvalidHttpHeader {
349-
name: name.to_string(),
350-
value: value.to_string(),
351-
error: e.to_string(),
352-
}
353-
})?;
354-
let _value = http::header::HeaderValue::from_str(value.as_str()).map_err(|e| {
355-
RadError::InvalidHttpHeader {
356-
name: name.to_string(),
357-
value: value.to_string(),
358-
error: e.to_string(),
359-
}
360-
})?;
361-
362-
// Validate headers using surf to avoid panics
363-
let name: HeaderName =
364-
HeaderName::from_str(name).map_err(|e| RadError::InvalidHttpHeader {
365-
name: name.to_string(),
366-
value: value.to_string(),
367-
error: e.to_string(),
368-
})?;
369-
let values: HeaderValues = value
370-
.to_header_values()
371-
.map_err(|e| RadError::InvalidHttpHeader {
372-
name: name.to_string(),
373-
value: value.to_string(),
374-
error: e.to_string(),
375-
})?
376-
.collect();
377-
378-
request = request.header(name, &values);
379-
}
380-
381-
Ok(request)
382-
}
383-
384341
/// Run retrieval stage of a data request, return `Result<RadonReport>`.
385342
pub async fn run_retrieval_report(
386343
retrieve: &RADRetrieve,

toolkit/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ hex = "0.4.3"
1212
regex = "1.5.5"
1313
serde_json = "1.0.66"
1414
structopt = "0.3.22"
15-
witnet_data_structures = { path = "../data_structures" }
16-
witnet_rad = { path = "../rad" }
15+
witnet_data_structures = { path = "../data_structures", default-features = false }
16+
witnet_rad = { path = "../rad", default-features = false }

0 commit comments

Comments
 (0)