use super::JsonRpcErrorData; use crate::errors::{Web3ProxyError, Web3ProxyResult}; use crate::jsonrpc::ValidatedRequest; use crate::response_cache::ForwardedResponse; use axum::body::StreamBody; use axum::response::IntoResponse; use axum::Json; use bytes::{Bytes, BytesMut}; use futures_util::stream::{self, StreamExt}; use futures_util::TryStreamExt; use serde::{de, Deserialize, Serialize}; use serde_json::value::RawValue; use std::fmt; use std::marker::PhantomData; use std::sync::Arc; pub trait JsonRpcParams = fmt::Debug + serde::Serialize + Send + Sync + 'static; pub trait JsonRpcResultData = serde::Serialize + serde::de::DeserializeOwned + fmt::Debug + Send; /// TODO: borrow values to avoid allocs if possible /// TODO: lots of overlap with `SingleForwardedResponse` #[derive(Debug, Serialize)] pub struct ParsedResponse> { pub jsonrpc: String, pub id: Box, #[serde(flatten)] pub payload: ResponsePayload, } impl ParsedResponse { pub fn from_value(value: serde_json::Value, id: Box) -> Self { let result = serde_json::value::to_raw_value(&value) .expect("this should not fail") .into(); Self::from_result(result, id) } } impl ParsedResponse> { pub fn from_response_data(data: ForwardedResponse>, id: Box) -> Self { match data { ForwardedResponse::NullResult => { let x: Box = Default::default(); // TODO: how can we make this generic if this always wants to be a Box?. Do we even want to keep NullResult? Self::from_result(Arc::from(x), id) } ForwardedResponse::RpcError { error_data, .. } => Self::from_error(error_data, id), ForwardedResponse::Result { value, .. } => Self::from_result(value, id), } } } impl ParsedResponse { pub fn from_result(result: T, id: Box) -> Self { Self { jsonrpc: "2.0".to_string(), id, payload: ResponsePayload::Success { result }, } } pub fn from_error(error: JsonRpcErrorData, id: Box) -> Self { Self { jsonrpc: "2.0".to_string(), id, payload: ResponsePayload::Error { error }, } } pub fn result(&self) -> Option<&T> { match &self.payload { ResponsePayload::Success { result } => Some(result), ResponsePayload::Error { .. } => None, } } pub fn into_result(self) -> Web3ProxyResult { match self.payload { ResponsePayload::Success { result } => Ok(result), ResponsePayload::Error { error } => Err(Web3ProxyError::JsonRpcErrorData(error)), } } } impl<'de, T> Deserialize<'de> for ParsedResponse where T: de::DeserializeOwned, { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { struct ResponseVisitor(PhantomData); impl<'de, T> de::Visitor<'de> for ResponseVisitor where T: de::DeserializeOwned, { type Value = ParsedResponse; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid jsonrpc 2.0 response object") } fn visit_map(self, mut map: A) -> Result where A: de::MapAccess<'de>, { let mut jsonrpc = None; // response & error let mut id = None; // only response let mut result = None; // only error let mut error = None; while let Some(key) = map.next_key()? { match key { "jsonrpc" => { if jsonrpc.is_some() { return Err(de::Error::duplicate_field("jsonrpc")); } let value = map.next_value()?; if value != "2.0" { return Err(de::Error::invalid_value( de::Unexpected::Str(value), &"2.0", )); } jsonrpc = Some(value); } "id" => { if id.is_some() { return Err(de::Error::duplicate_field("id")); } let value: Box = map.next_value()?; id = Some(value); } "result" => { if result.is_some() { return Err(de::Error::duplicate_field("result")); } let value: T = map.next_value()?; result = Some(value); } "error" => { if error.is_some() { return Err(de::Error::duplicate_field("Error")); } let value: JsonRpcErrorData = map.next_value()?; error = Some(value); } key => { return Err(de::Error::unknown_field( key, &["jsonrpc", "id", "result", "error"], )); } } } let id = id.unwrap_or_default(); // jsonrpc version must be present in all responses let jsonrpc = jsonrpc .ok_or_else(|| de::Error::missing_field("jsonrpc"))? .to_string(); let payload = match (result, error) { (Some(result), None) => ResponsePayload::Success { result }, (None, Some(error)) => ResponsePayload::Error { error }, _ => { return Err(de::Error::custom( "response must be either a success or error object", )) } }; Ok(ParsedResponse { jsonrpc, id, payload, }) } } deserializer.deserialize_map(ResponseVisitor(PhantomData)) } } #[derive(Debug, Deserialize, Serialize)] #[serde(untagged)] pub enum ResponsePayload { Success { result: T }, Error { error: JsonRpcErrorData }, } #[derive(Debug)] pub struct StreamResponse { _t: PhantomData, buffer: Bytes, response: reqwest::Response, web3_request: Arc, } impl StreamResponse { // TODO: error handing pub async fn read(self) -> Web3ProxyResult> where T: de::DeserializeOwned, { let mut buffer = BytesMut::with_capacity(self.buffer.len()); buffer.extend_from_slice(&self.buffer); buffer.extend_from_slice(&self.response.bytes().await?); let parsed = serde_json::from_slice(&buffer)?; Ok(parsed) } } impl IntoResponse for StreamResponse { fn into_response(self) -> axum::response::Response { let stream = stream::once(async { Ok::<_, reqwest::Error>(self.buffer) }) .chain(self.response.bytes_stream()) .map_ok(move |x| { let len = x.len(); self.web3_request.add_response(len); x }); let body = StreamBody::new(stream); body.into_response() } } #[derive(Debug)] pub enum SingleResponse> { /// TODO: save the size here so we don't have to serialize again Parsed(ParsedResponse), Stream(StreamResponse), } impl SingleResponse where T: de::DeserializeOwned + Serialize, { // TODO: threshold from configs // TODO: error handling // TODO: if a large stream's response's initial chunk "error" then we should buffer it pub async fn read_if_short( mut response: reqwest::Response, nbytes: u64, web3_request: &Arc, ) -> Web3ProxyResult> { match response.content_length() { // short Some(len) if len <= nbytes => Ok(Self::from_bytes(response.bytes().await?)?), // long Some(_) => Ok(Self::Stream(StreamResponse { _t: PhantomData::, buffer: Bytes::new(), response, web3_request: web3_request.clone(), })), // unknown length. maybe compressed. maybe streaming. maybe both None => { let mut buffer = BytesMut::new(); while (buffer.len() as u64) < nbytes { match response.chunk().await? { Some(chunk) => { buffer.extend_from_slice(&chunk); } None => { // it was short return Ok(Self::from_bytes(buffer.freeze())?); } } } // we've read nbytes of the response, but there is more to come let buffer = buffer.freeze(); Ok(Self::Stream(StreamResponse { _t: PhantomData::, buffer, response, web3_request: web3_request.clone(), })) } } } fn from_bytes(buf: Bytes) -> Result { let val = serde_json::from_slice(&buf)?; Ok(Self::Parsed(val)) } // TODO: error handling pub async fn parsed(self) -> Web3ProxyResult> { match self { Self::Parsed(resp) => Ok(resp), Self::Stream(resp) => resp.read().await, } } pub fn num_bytes(&self) -> usize { match self { Self::Parsed(response) => serde_json::to_string(response) .expect("this should always serialize") .len(), Self::Stream(response) => match response.response.content_length() { Some(len) => len as usize, None => 0, }, } } pub fn set_id(&mut self, id: Box) { match self { SingleResponse::Parsed(x) => { x.id = id; } SingleResponse::Stream(..) => { // stream responses will hopefully always have the right id already because we pass the orignal id all the way from the front to the back } } } } impl From> for SingleResponse { fn from(response: ParsedResponse) -> Self { Self::Parsed(response) } } impl IntoResponse for SingleResponse where T: Serialize, { fn into_response(self) -> axum::response::Response { match self { Self::Parsed(resp) => Json(resp).into_response(), Self::Stream(resp) => resp.into_response(), } } } #[derive(Debug)] pub enum Response> { Single(SingleResponse), Batch(Vec>), } impl Response> { pub async fn to_json_string(self) -> Web3ProxyResult { let x = match self { Self::Single(resp) => { // TODO: handle streaming differently? let parsed = resp.parsed().await?; serde_json::to_string(&parsed) } Self::Batch(resps) => serde_json::to_string(&resps), }; let x = x.expect("to_string should always work"); Ok(x) } } impl From> for Response { fn from(response: ParsedResponse) -> Self { Self::Single(SingleResponse::Parsed(response)) } } impl IntoResponse for Response where T: Serialize, { fn into_response(self) -> axum::response::Response { match self { Self::Single(resp) => resp.into_response(), Self::Batch(resps) => Json(resps).into_response(), } } }