seda_sdk_rs/proxy_http_fetch.rs
1//! Proxy HTTP fetch action and associated types for the `seda_runtime_sdk`.
2//! These are used to make HTTP requests through the proxy nodes.
3//!
4//! Defines JSON-serializable request and the struct [`ProxyHttpFetchAction`]
5//! and provides, [`proxy_http_fetch`], [`generate_proxy_http_signing_message`]
6//! for executing proxy HTTP requests, and generating signing messages via VM FFI calls.
7
8use serde::{Deserialize, Serialize};
9
10use crate::{
11 bytes::{Bytes, ToBytes},
12 http::{HttpFetchOptions, HttpFetchResponse},
13 keccak256::keccak256,
14 promise::PromiseStatus,
15 HttpFetchMethod,
16};
17
18/// An Proxy HTTP fetch action containing the target URL, fetch options, and public key of the proxy.
19/// This action is serialized and sent to the VM for execution.
20#[derive(Serialize, Deserialize, Clone, Debug)]
21pub struct ProxyHttpFetchAction {
22 /// The URL to fetch.
23 pub url: String,
24 /// The public key of the proxy node that will handle the request.
25 pub public_key: Option<String>,
26 /// The options for the HTTP fetch request.
27 pub options: HttpFetchOptions,
28}
29
30/// Makes an HTTP request through the proxy service
31///
32/// # Panics
33///
34/// Panics if the serialization of the [`ProxyHttpFetchAction`] fails or if the deserialization of the response fails.
35/// We expect these to never happen in practice, as the SDK is designed to ensure valid inputs.
36///
37/// # Examples
38///
39/// ```no_run
40/// use seda_sdk_rs::bytes::ToBytes;
41/// use seda_sdk_rs::proxy_http_fetch::proxy_http_fetch;
42/// use seda_sdk_rs::http::{HttpFetchOptions, HttpFetchMethod};
43/// use std::collections::BTreeMap;
44///
45/// // Basic GET request
46/// let response = proxy_http_fetch("https://api.example.com/data", None, None);
47/// if response.is_ok() {
48/// println!("Status: {}", response.status);
49/// println!("Body length: {}", response.content_length);
50/// }
51///
52/// // POST request with custom options
53/// let mut headers = BTreeMap::new();
54/// headers.insert("Content-Type".to_string(), "application/json".to_string());
55///
56/// let options = HttpFetchOptions {
57/// method: HttpFetchMethod::Post,
58/// headers,
59/// body: Some(serde_json::to_vec(&serde_json::json!({"key": "value"})).unwrap().to_bytes()),
60/// timeout_ms: None,
61/// };
62///
63/// let response = proxy_http_fetch(
64/// "https://api.example.com/data",
65/// Some("02452f1c0a2753c6d2e55da4abeb1b6115c595eec9b0e237b8aaa74913ad3f1dc7".to_string()),
66/// Some(options)
67/// );
68///
69/// // Check response
70/// if response.is_ok() {
71/// // Access response data
72/// println!("Status: {}", response.status);
73/// println!("Final URL: {}", response.url);
74/// println!("Response size: {}", response.content_length);
75///
76/// // Access response headers
77/// if let Some(content_type) = response.headers.get("content-type") {
78/// println!("Content-Type: {}", content_type);
79/// }
80/// }
81/// ```
82pub fn proxy_http_fetch<URL: ToString>(
83 url: URL,
84 public_key: Option<String>,
85 options: Option<HttpFetchOptions>,
86) -> HttpFetchResponse {
87 let http_action = ProxyHttpFetchAction {
88 url: url.to_string(),
89 public_key,
90 options: options.unwrap_or_default(),
91 };
92
93 let action = serde_json::to_string(&http_action).unwrap();
94 let result_length = unsafe { super::raw::proxy_http_fetch(action.as_ptr(), action.len() as u32) };
95 let mut result_data_ptr = vec![0; result_length as usize];
96
97 unsafe {
98 super::raw::call_result_write(result_data_ptr.as_mut_ptr(), result_length);
99 }
100
101 let promise_status: PromiseStatus =
102 serde_json::from_slice(&result_data_ptr).expect("Could not deserialize proxy_http_fetch");
103
104 HttpFetchResponse::from_promise(promise_status)
105}
106
107/// Generates the message which the data proxy hashed and signed. This can be useful when you need to verify
108/// the data proxy signature in the tally phase. With this message there is no need to include the entire request
109/// and response data in the execution result.
110///
111///
112/// # Examples
113///
114/// ```no_run
115/// use seda_sdk_rs::{
116/// bytes::{Bytes, ToBytes},
117/// http::HttpFetchMethod,
118/// proxy_http_fetch::{generate_proxy_http_signing_message, proxy_http_fetch},
119/// };
120///
121/// let url = "https://api.example.com/data";
122/// let response = proxy_http_fetch(url, None, None);
123///
124/// if response.is_ok() {
125/// let proxy_message = generate_proxy_http_signing_message(
126/// url.to_string(),
127/// HttpFetchMethod::Get,
128/// Bytes::default(),
129/// response.bytes.to_bytes()
130/// );
131/// // Use proxy_message for signature verification
132/// }
133/// ```
134pub fn generate_proxy_http_signing_message(
135 url: String,
136 method: HttpFetchMethod,
137 request_body: Bytes,
138 response_body: Bytes,
139) -> Bytes {
140 let url_hash = keccak256(url.as_bytes().to_vec());
141 let method_hash = keccak256(method.as_str().as_bytes().to_vec());
142 let request_body_hash = keccak256(request_body.to_vec());
143 let response_body_hash = keccak256(response_body.to_vec());
144
145 [url_hash, method_hash, request_body_hash, response_body_hash]
146 .concat()
147 .to_bytes()
148}