offchain_utils/
fetcher.rs1use scale_info::prelude::string::{String, ToString};
2use scale_info::prelude::vec::Vec;
3use sp_runtime::offchain::{http, Duration};
4
5pub struct HttpRequest {
7 pub url: String,
8 pub headers: Vec<(String, String)>,
9}
10
11impl HttpRequest {
12 pub fn new(url: &str) -> Self {
14 Self {
15 url: url.to_string(),
16 headers: Vec::new(),
17 }
18 }
19
20 pub fn add_header(mut self, key: &str, value: &str) -> Self {
22 self.headers.push((key.to_string(), value.to_string()));
23 self
24 }
25}
26
27pub trait OffchainFetcher {
29 fn fetch(request: HttpRequest) -> Result<Vec<u8>, &'static str> {
31 let mut request_builder = http::Request::get(&request.url);
32
33 for (key, value) in request.headers.iter() {
35 request_builder = request_builder.add_header(key, value);
36 }
37 let deadline = sp_io::offchain::timestamp().add(Duration::from_millis(2_000));
38 let pending = request_builder
40 .deadline(deadline) .send()
42 .map_err(|_| "Failed to send HTTP request")?;
43
44 let response = pending.wait().map_err(|_| "No response received")?;
45
46 if response.code != 200 {
47 return Err("Non-200 response code received");
48 }
49
50 Ok(response.body().collect::<Vec<u8>>())
51 }
52
53 fn fetch_string(request: HttpRequest) -> Result<String, &'static str> {
55 let bytes = Self::fetch(request)?;
56 String::from_utf8(bytes).map_err(|_| "Failed to parse response as UTF-8")
57 }
58}
59
60pub struct DefaultOffchainFetcher;
62
63impl OffchainFetcher for DefaultOffchainFetcher {}