A better Rust ATProto crate
at main 6.8 kB view raw
1//! Minimal HTTP client abstraction shared across crates. 2 3use std::fmt::Display; 4use std::future::Future; 5use std::sync::Arc; 6 7/// HTTP client trait for sending raw HTTP requests. 8#[cfg_attr(not(target_arch = "wasm32"), trait_variant::make(Send))] 9pub trait HttpClient { 10 /// Error type returned by the HTTP client 11 type Error: std::error::Error + Display + Send + Sync + 'static; 12 13 /// Send an HTTP request and return the response. 14 fn send_http( 15 &self, 16 request: http::Request<Vec<u8>>, 17 ) -> impl Future<Output = core::result::Result<http::Response<Vec<u8>>, Self::Error>>; 18} 19 20#[cfg(feature = "streaming")] 21use crate::stream::{ByteStream, StreamError}; 22 23/// Extension trait for HTTP client with streaming support 24#[cfg(feature = "streaming")] 25#[cfg_attr(not(target_arch = "wasm32"), trait_variant::make(Send))] 26pub trait HttpClientExt: HttpClient { 27 /// Send HTTP request and return streaming response 28 fn send_http_streaming( 29 &self, 30 request: http::Request<Vec<u8>>, 31 ) -> impl Future<Output = Result<http::Response<ByteStream>, Self::Error>>; 32 33 /// Send HTTP request with streaming body and receive streaming response 34 #[cfg(not(target_arch = "wasm32"))] 35 fn send_http_bidirectional<S>( 36 &self, 37 parts: http::request::Parts, 38 body: S, 39 ) -> impl Future<Output = Result<http::Response<ByteStream>, Self::Error>> 40 where 41 S: n0_future::Stream<Item = Result<bytes::Bytes, StreamError>> + Send + 'static; 42 43 /// Send HTTP request with streaming body and receive streaming response (WASM) 44 #[cfg(target_arch = "wasm32")] 45 fn send_http_bidirectional<S>( 46 &self, 47 parts: http::request::Parts, 48 body: S, 49 ) -> impl Future<Output = Result<http::Response<ByteStream>, Self::Error>> 50 where 51 S: n0_future::Stream<Item = Result<bytes::Bytes, StreamError>> + 'static; 52} 53 54#[cfg(feature = "reqwest-client")] 55impl HttpClient for reqwest::Client { 56 type Error = reqwest::Error; 57 58 async fn send_http( 59 &self, 60 request: http::Request<Vec<u8>>, 61 ) -> core::result::Result<http::Response<Vec<u8>>, Self::Error> { 62 // Convert http::Request to reqwest::Request 63 let (parts, body) = request.into_parts(); 64 65 let mut req = self.request(parts.method, parts.uri.to_string()).body(body); 66 67 // Copy headers 68 for (name, value) in parts.headers.iter() { 69 req = req.header(name.as_str(), value.as_bytes()); 70 } 71 72 // Send request 73 let resp = req.send().await?; 74 75 // Convert reqwest::Response to http::Response 76 let mut builder = http::Response::builder().status(resp.status()); 77 78 // Copy headers 79 for (name, value) in resp.headers().iter() { 80 builder = builder.header(name.as_str(), value.as_bytes()); 81 } 82 83 // Read body 84 let body = resp.bytes().await?.to_vec(); 85 86 Ok(builder.body(body).expect("Failed to build response")) 87 } 88} 89 90#[cfg(not(target_arch = "wasm32"))] 91impl<T: HttpClient + Sync> HttpClient for Arc<T> { 92 type Error = T::Error; 93 94 fn send_http( 95 &self, 96 request: http::Request<Vec<u8>>, 97 ) -> impl Future<Output = core::result::Result<http::Response<Vec<u8>>, Self::Error>> + Send 98 { 99 self.as_ref().send_http(request) 100 } 101} 102 103#[cfg(target_arch = "wasm32")] 104impl<T: HttpClient> HttpClient for Arc<T> { 105 type Error = T::Error; 106 107 fn send_http( 108 &self, 109 request: http::Request<Vec<u8>>, 110 ) -> impl Future<Output = core::result::Result<http::Response<Vec<u8>>, Self::Error>> { 111 self.as_ref().send_http(request) 112 } 113} 114 115#[cfg(all(feature = "streaming", feature = "reqwest-client"))] 116impl HttpClientExt for reqwest::Client { 117 async fn send_http_streaming( 118 &self, 119 request: http::Request<Vec<u8>>, 120 ) -> Result<http::Response<ByteStream>, Self::Error> { 121 // Convert http::Request to reqwest::Request 122 let (parts, body) = request.into_parts(); 123 124 let mut req = self.request(parts.method, parts.uri.to_string()).body(body); 125 126 // Copy headers 127 for (name, value) in parts.headers.iter() { 128 req = req.header(name.as_str(), value.as_bytes()); 129 } 130 131 // Send request and get streaming response 132 let resp = req.send().await?; 133 134 // Convert reqwest::Response to http::Response with ByteStream 135 let mut builder = http::Response::builder().status(resp.status()); 136 137 // Copy headers 138 for (name, value) in resp.headers().iter() { 139 builder = builder.header(name.as_str(), value.as_bytes()); 140 } 141 142 // Convert bytes_stream to ByteStream 143 use futures::StreamExt; 144 let stream = resp 145 .bytes_stream() 146 .map(|result| result.map_err(|e| StreamError::transport(e))); 147 let byte_stream = ByteStream::new(stream); 148 149 Ok(builder.body(byte_stream).expect("Failed to build response")) 150 } 151 152 #[cfg(not(target_arch = "wasm32"))] 153 async fn send_http_bidirectional<S>( 154 &self, 155 parts: http::request::Parts, 156 body: S, 157 ) -> Result<http::Response<ByteStream>, Self::Error> 158 where 159 S: n0_future::Stream<Item = Result<bytes::Bytes, StreamError>> + Send + 'static, 160 { 161 // Convert stream to reqwest::Body 162 use futures::StreamExt; 163 let reqwest_body = reqwest::Body::wrap_stream(body); 164 165 let mut req = self 166 .request(parts.method, parts.uri.to_string()) 167 .body(reqwest_body); 168 169 // Copy headers 170 for (name, value) in parts.headers.iter() { 171 req = req.header(name.as_str(), value.as_bytes()); 172 } 173 174 // Send and convert response 175 let resp = req.send().await?; 176 177 let mut builder = http::Response::builder().status(resp.status()); 178 179 for (name, value) in resp.headers().iter() { 180 builder = builder.header(name.as_str(), value.as_bytes()); 181 } 182 183 let stream = resp 184 .bytes_stream() 185 .map(|result| result.map_err(|e| StreamError::transport(e))); 186 let byte_stream = ByteStream::new(stream); 187 188 Ok(builder.body(byte_stream).expect("Failed to build response")) 189 } 190 191 #[cfg(target_arch = "wasm32")] 192 async fn send_http_bidirectional<S>( 193 &self, 194 _parts: http::request::Parts, 195 _body: S, 196 ) -> Result<http::Response<ByteStream>, Self::Error> 197 where 198 S: n0_future::Stream<Item = Result<bytes::Bytes, StreamError>> + 'static, 199 { 200 // WASM reqwest doesn't support streaming request bodies 201 // This would require ReadableStream/WritableStream integration 202 unimplemented!("Bidirectional streaming not yet supported on WASM") 203 } 204}