diff --git a/.vscode/launch.json b/.vscode/launch.json index f42a57f..a28e4ef 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -29,15 +29,15 @@ { "type": "lldb", "request": "launch", - "name": "Debug example 'buffer'", + "name": "Debug example 'blocking'", "cargo": { "args": [ "build", - "--example=buffer", + "--example=blocking", "--package=cronet-rs" ], "filter": { - "name": "buffer", + "name": "blocking", "kind": "example" } }, diff --git a/README.md b/README.md index fb96c9f..e19f4a6 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ $ cd cronet-rs Then, follow the steps to build the project: -1. Get the latest cronet binaries: [build from source](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/components/cronet/build_instructions.md) or get prebuilt binaries from somewhere (if you know a reputable source, [let me know](https://github.com/sleeyax/cronet-rs/issues/new)!). +1. Get the latest cronet binaries: [build from source](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/components/cronet/build_instructions.md) or download prebuilt binaries from [here](https://github.com/sleeyax/cronet-binaries/releases). 2. Place all `.h` header files in `src` and all binaries (`.so`, `.dll`, `.dylib`) in `bin`. 3. Run `cargo build`. This should trigger `bindgen` to (re)generate the bindings. @@ -24,12 +24,12 @@ Then, follow the steps to build the project: Maintenance of this project is made possible by all the contributors and sponsors. If you'd like to sponsor this project and have your avatar or company logo appear below click [here](https://github.com/sponsors/sleeyax). 💖 -SecretKeys +SecretKeys ## Related projects Other projects that are related to this project and might interest you: -- [Cronet in C#](https://github.com/sleeyax/CronetSharp) -- [Cronet in go](https://github.com/SagerNet/cronet-go) +- [Cronet bindings for C#](https://github.com/sleeyax/CronetSharp) +- [Cronet bindings for go](https://github.com/sleeyax/cronet-go) - [NaïveProxy](https://github.com/klzgrad/naiveproxy) diff --git a/examples/blocking.rs b/examples/blocking.rs new file mode 100644 index 0000000..7df6a02 --- /dev/null +++ b/examples/blocking.rs @@ -0,0 +1,36 @@ +use cronet_rs::client::{Body, Client}; + +fn main() { + let client = Client::new(); + + println!("sending GET request..."); + let request = http::Request::builder() + .method("GET") + .uri("https://httpbin.org/anything") + .body(Body::default()) + .unwrap(); + let result = client.send(request); + print_result(result); + + println!("sending POST request..."); + let request = http::Request::builder() + .method("POST") + .uri("https://httpbin.org/anything") + .header("content-type", "application/x-www-form-urlencoded") + .body(Body::from("Hello, world")) + .unwrap(); + let result = client.send(request); + print_result(result); +} + +fn print_result(result: Result, cronet_rs::client::ClientError>) { + match result { + Ok(response) => { + println!("Status: {}", response.status()); + println!("Headers: {:#?}", response.headers()); + let body = response.body().as_bytes().unwrap(); + println!("Body: {}", String::from_utf8_lossy(body)); + } + Err(error) => println!("Error: {}", error), + } +} diff --git a/src/buffer.rs b/src/buffer.rs index 24373ef..b37c3a5 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -1,3 +1,5 @@ +use std::slice; + use crate::{ BufferCallback, Cronet_BufferPtr, Cronet_Buffer_Create, Cronet_Buffer_Destroy, Cronet_Buffer_GetData, Cronet_Buffer_GetSize, Cronet_Buffer_InitWithAlloc, @@ -55,6 +57,10 @@ impl Buffer { unsafe { Cronet_Buffer_GetSize(self.ptr) } } + pub(crate) fn data_ptr(&self) -> Cronet_RawDataPtr { + unsafe { Cronet_Buffer_GetData(self.ptr) } + } + pub fn data(&self) -> Box { unsafe { let dataPtr: Cronet_RawDataPtr = self.data_ptr(); @@ -62,8 +68,11 @@ impl Buffer { } } - pub(crate) fn data_ptr(&self) -> Cronet_RawDataPtr { - unsafe { Cronet_Buffer_GetData(self.ptr) } + pub fn data_slice(&self, size: usize) -> &[T] { + unsafe { + let slice = slice::from_raw_parts(self.data_ptr() as *mut T, size); + slice + } } /// Write arbitrary data to the buffer. @@ -74,7 +83,8 @@ impl Buffer { /// /// * `data` - The data to write to the buffer. /// * `data_size` - The size of the data to write to the buffer. Must be less than or equal to the buffer's size. - pub fn write(&self, data: Box, data_size: u64) -> Result<(), &'static str> { + #[allow(dead_code)] + pub(crate) fn write(&self, data: Box, data_size: u64) -> Result<(), &'static str> { let src = Box::into_raw(data); let src_size = data_size; let dst = self.data_ptr() as *mut T; @@ -90,6 +100,23 @@ impl Buffer { Ok(()) } + + pub(crate) fn write_slice(&self, data: &[T], data_size: u64) -> Result<(), &'static str> { + let src = data.as_ptr(); + let src_size = data_size; + let dst = self.data_ptr() as *mut T; + let dst_size = self.size(); + + if dst_size < src_size { + return Err("Buffer is too small to hold the specified data"); + } + + unsafe { + std::ptr::copy_nonoverlapping(src, dst, src_size as usize); + } + + Ok(()) + } } impl Destroy for Buffer { diff --git a/src/buffer_callback.rs b/src/buffer_callback.rs index 13cc888..78c00ca 100644 --- a/src/buffer_callback.rs +++ b/src/buffer_callback.rs @@ -6,7 +6,7 @@ use crate::{ }; static mut BUFFER_CALLBACKS: Lazy> = - Lazy::new(|| CronetCallbacks::new()); + Lazy::new(CronetCallbacks::new); #[no_mangle] unsafe extern "C" fn cronetBufferCallbackOnDestroy( diff --git a/src/client/body.rs b/src/client/body.rs index 31f0121..0c3de43 100644 --- a/src/client/body.rs +++ b/src/client/body.rs @@ -1,7 +1,7 @@ // This implementation is inspired by: https://github.com/seanmonstar/reqwest/blob/6792f697fcdb27c47dcbf7bd05f23368d1d4ac80/src/blocking/body.rs // License: https://github.com/seanmonstar/reqwest/blob/master/LICENSE-MIT -use bytes::Bytes; +use bytes::{Bytes, BytesMut}; use std::fmt; use std::fs::File; use std::io::Read; @@ -30,6 +30,18 @@ impl Body { match self.kind { Kind::Reader(_, _) => None, Kind::Bytes(ref bytes) => Some(bytes.as_ref()), + Kind::BytesMut(ref bytes) => Some(bytes.as_ref()), + } + } + + /// Returns the body as a mutable byte slice. + /// Useful to build the body of a HTTP response. + /// For HTTP requests this method returns `None`. + pub fn as_bytes_mut(&mut self) -> Option<&mut BytesMut> { + match self.kind { + Kind::Reader(_, _) => None, + Kind::Bytes(_) => None, + Kind::BytesMut(ref mut bytes) => Some(bytes), } } @@ -37,6 +49,7 @@ impl Body { match self.kind { Kind::Reader(_, len) => len, Kind::Bytes(ref bytes) => Some(bytes.len() as u64), + Kind::BytesMut(ref bytes) => Some(bytes.len() as u64), } } @@ -49,6 +62,7 @@ impl Body { enum Kind { Reader(Box, Option), Bytes(Bytes), + BytesMut(BytesMut), } impl Kind { @@ -56,6 +70,15 @@ impl Kind { match self { Kind::Reader(..) => None, Kind::Bytes(v) => Some(Kind::Bytes(v.clone())), + Kind::BytesMut(v) => Some(Kind::BytesMut(v.clone())), + } + } +} + +impl Default for Body { + fn default() -> Body { + Body { + kind: Kind::BytesMut(BytesMut::new()), } } } @@ -118,6 +141,7 @@ impl fmt::Debug for Kind { .field("length", &DebugLength(v)) .finish(), Kind::Bytes(ref v) => fmt::Debug::fmt(v, f), + Kind::BytesMut(ref v) => fmt::Debug::fmt(v, f), } } } diff --git a/src/client/body_upload_provider.rs b/src/client/body_upload_provider.rs index c7de2de..b448f92 100644 --- a/src/client/body_upload_provider.rs +++ b/src/client/body_upload_provider.rs @@ -29,9 +29,9 @@ impl<'a> UploadDataProviderHandler for BodyUploadDataProvider<'a> { return; } - match buffer.write(Box::new(bytes), len) { + match buffer.write_slice(bytes, len) { Ok(_) => { - sink.on_read_succeeded(len, true); // TODO: implement chunked reads + sink.on_read_succeeded(len, false); // TODO: implement chunked reads } Err(err) => { sink.on_read_error(err); @@ -100,9 +100,9 @@ mod tests { // Read the modified buffer again by its pointer. let buffer = Buffer { ptr }; - let actual = buffer.data::<&[u8]>(); + let actual = buffer.data_slice::(4); assert_eq!(actual.len(), expected.len()); - assert_eq!(*actual, expected.as_bytes()); + assert_eq!(actual, expected.as_bytes()); buffer.destroy(); } diff --git a/src/client/client.rs b/src/client/client.rs index fa0c22d..5e942cb 100644 --- a/src/client/client.rs +++ b/src/client/client.rs @@ -1,10 +1,11 @@ -use std::thread; +use std::{sync::mpsc, thread}; -use crate::{Destroy, Engine, EngineParams, Executor, UrlRequestParams}; +use crate::{ + client::ClientError, Destroy, Engine, EngineParams, EngineResult, Executor, UrlRequest, + UrlRequestCallback, UrlRequestParams, +}; -use super::Body; - -type ShouldRedirectFn = fn(new_location_url: &str) -> bool; +use super::{Body, ResponseHandler, ShouldRedirectFn, Status}; pub struct Client { pub should_redirect: ShouldRedirectFn, @@ -51,12 +52,41 @@ impl Client { self.should_redirect = should_redirect; } - pub fn send(&self, request: http::Request) -> http::Result> { + pub fn send(&self, request: http::Request) -> Result, ClientError> { + let uri = request.uri().to_string(); + let request_parameters = UrlRequestParams::from(request); request_parameters.set_upload_data_executor(&self.executor); - // TODO: implement response handler + let (tx, rx) = mpsc::channel::(); + let response_handler = ResponseHandler::new(self.should_redirect, tx); + let callback = UrlRequestCallback::new(response_handler); + let url_request = UrlRequest::new(); + url_request.init_with_params( + &self.engine, + uri.as_str(), + &request_parameters, + &callback, + &self.executor, + ); + // request_parameters.destroy(); + let result = url_request.start(); + if result != EngineResult::Success { + return Result::Err(ClientError::EngineError(result)); + } + + let status = rx.recv().unwrap(); + + match status { + Status::Success(res) => Result::Ok(res), + Status::Canceled => Result::Err(ClientError::CancellationError), + Status::Error(e) => Result::Err(ClientError::CronetError(e)), + } + } +} - http::Result::Ok(http::Response::new(Body::from(""))) +impl Default for Client { + fn default() -> Self { + Self::new() } } diff --git a/src/client/error.rs b/src/client/error.rs new file mode 100644 index 0000000..cdbf132 --- /dev/null +++ b/src/client/error.rs @@ -0,0 +1,38 @@ +use core::fmt; + +use crate::{CronetError, EngineResult}; + +pub enum ClientError { + /// Internal cronet error. + CronetError(CronetError), + /// The request was cancelled. + CancellationError, + /// Unexpected cronet engine result. + EngineError(EngineResult), +} + +impl From for ClientError { + fn from(error: CronetError) -> Self { + Self::CronetError(error) + } +} + +impl fmt::Display for ClientError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::CronetError(error) => write!(f, "{}", error), + Self::CancellationError => write!(f, "Request was cancelled"), + Self::EngineError(error) => write!(f, "Unexpected engine result: {:?}", error), + } + } +} + +impl fmt::Debug for ClientError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::CronetError(error) => write!(f, "{}", error), + Self::CancellationError => write!(f, "Request was cancelled"), + Self::EngineError(error) => write!(f, "Unexpected engine result: {:?}", error), + } + } +} diff --git a/src/client/mod.rs b/src/client/mod.rs index f7a4312..c0d9d4a 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,7 +1,12 @@ mod body; mod body_upload_provider; +#[allow(clippy::module_inception)] mod client; +mod error; +mod response_handler; pub use body::*; pub use body_upload_provider::*; pub use client::*; +pub use error::*; +pub use response_handler::*; diff --git a/src/client/response_handler.rs b/src/client/response_handler.rs new file mode 100644 index 0000000..f4aa55e --- /dev/null +++ b/src/client/response_handler.rs @@ -0,0 +1,124 @@ +use std::{mem, sync::mpsc::Sender}; + +use bytes::BufMut; +use http::Response; + +use crate::{ + Buffer, CronetError, Destroy, UrlRequest, UrlRequestCallback, UrlRequestCallbackHandler, + UrlResponseInfo, +}; + +use super::Body; + +pub type ShouldRedirectFn = fn(new_location_url: &str) -> bool; + +#[derive(Debug)] +pub enum Status { + Success(Response), + Canceled, + Error(CronetError), +} + +pub struct ResponseHandler { + should_redirect: ShouldRedirectFn, + response: Response, + tx: Sender, + buffer: Option, + buffer_size: u64, +} + +impl ResponseHandler { + pub fn new(should_redirect: ShouldRedirectFn, tx: Sender) -> Self { + Self { + should_redirect, + response: Response::default(), + tx, + buffer: None, + buffer_size: 512, + } + } + + /// Sets the buffer size for reading the response body. + /// The default is `512` bytes. + pub fn set_buffer_size(&mut self, buffer_size: u64) { + self.buffer_size = buffer_size; + } + + /// Continues reading the response body. + fn read(&mut self, req: UrlRequest) { + if let Some(old_buffer) = &self.buffer { + old_buffer.destroy(); + } + + let buffer = Buffer::new_with_size(self.buffer_size); + self.buffer = Some(Buffer { ptr: buffer.ptr }); + + req.read(buffer); + } +} + +impl UrlRequestCallbackHandler for ResponseHandler { + fn on_redirect_received( + &mut self, + _: UrlRequestCallback, + request: UrlRequest, + info: UrlResponseInfo, + new_location_url: &str, + ) { + if (self.should_redirect)(new_location_url) { + request.follow_redirect(); + } else { + self.response = info.into(); + } + } + + fn on_response_started( + &mut self, + _: UrlRequestCallback, + req: UrlRequest, + info: UrlResponseInfo, + ) { + self.response = info.into(); + self.read(req); + } + + fn on_read_completed( + &mut self, + _: UrlRequestCallback, + req: UrlRequest, + _: UrlResponseInfo, + buffer: Buffer, + bytes_read: u64, + ) { + if bytes_read == 0 { + return; + } + + let data = buffer.data_slice::(bytes_read as usize); + self.response.body_mut().as_bytes_mut().unwrap().put(data); + + self.read(req); + } + + fn on_succeeded(&mut self, _: UrlRequestCallback, req: UrlRequest, _: UrlResponseInfo) { + req.destroy(); + let response = mem::take(&mut self.response); + self.tx.send(Status::Success(response)).unwrap(); + } + + fn on_failed( + &mut self, + _: UrlRequestCallback, + req: UrlRequest, + _: UrlResponseInfo, + error: CronetError, + ) { + req.destroy(); + self.tx.send(Status::Error(error)).unwrap(); + } + + fn on_canceled(&mut self, _: UrlRequestCallback, req: UrlRequest, _: UrlResponseInfo) { + req.destroy(); + self.tx.send(Status::Canceled).unwrap(); + } +} diff --git a/src/date_time.rs b/src/date_time.rs index f3bfc57..ada7bea 100644 --- a/src/date_time.rs +++ b/src/date_time.rs @@ -48,6 +48,12 @@ impl Destroy for DateTime { } } +impl Default for DateTime { + fn default() -> Self { + Self::new() + } +} + #[cfg(test)] mod tests { use std::time::UNIX_EPOCH; diff --git a/src/engine.rs b/src/engine.rs index c598370..b10bb4c 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -75,21 +75,21 @@ impl Engine { } /// A human-readable version string of the engine. - pub fn version(&self) -> String { + pub fn version(&self) -> &str { unsafe { let version = Cronet_Engine_GetVersionString(self.ptr); let version = CStr::from_ptr(version); - version.to_string_lossy().into_owned() + version.to_str().unwrap() } } /// Returns the default value of the `User-Agent` header. /// Can be accessed before `StartWithParams()` is called. - pub fn default_user_agent(&self) -> String { + pub fn default_user_agent(&self) -> &str { unsafe { let version = Cronet_Engine_GetDefaultUserAgent(self.ptr); let version = CStr::from_ptr(version); - version.to_string_lossy().into_owned() + version.to_str().unwrap() } } @@ -110,6 +110,12 @@ impl Destroy for Engine { } } +impl Default for Engine { + fn default() -> Self { + Self::new() + } +} + #[cfg(test)] mod tests { use crate::Destroy; diff --git a/src/engine_params.rs b/src/engine_params.rs index 857e387..5863d84 100644 --- a/src/engine_params.rs +++ b/src/engine_params.rs @@ -57,12 +57,12 @@ impl EngineParams { } /// Returns the `User-Agent` header value. - pub fn user_agent(&self) -> String { + pub fn user_agent(&self) -> &str { unsafe { let c_str = Cronet_EngineParams_user_agent_get(self.ptr); let c_str = CStr::from_ptr(c_str); let str_slice = c_str.to_str().unwrap(); - str_slice.to_owned() + str_slice } } @@ -75,12 +75,12 @@ impl EngineParams { } /// Returns the `Accept-Language` header value. - pub fn accept_language(&self) -> String { + pub fn accept_language(&self) -> &str { unsafe { let c_str = Cronet_EngineParams_accept_language_get(self.ptr); let c_str = CStr::from_ptr(c_str); let str_slice = c_str.to_str().unwrap(); - str_slice.to_owned() + str_slice } } @@ -94,12 +94,12 @@ impl EngineParams { } /// Returns the directory for HTTP Cache and Prefs Storage. - pub fn storage_path(&self) -> String { + pub fn storage_path(&self) -> &str { unsafe { let c_str = Cronet_EngineParams_storage_path_get(self.ptr); let c_str = CStr::from_ptr(c_str); let str_slice = c_str.to_str().unwrap(); - str_slice.to_owned() + str_slice } } @@ -257,12 +257,12 @@ impl EngineParams { } } - pub fn experimental_options(&self) -> String { + pub fn experimental_options(&self) -> &str { unsafe { let c_str = Cronet_EngineParams_experimental_options_get(self.ptr); let c_str = CStr::from_ptr(c_str); let str_slice = c_str.to_str().unwrap(); - str_slice.to_owned() + str_slice } } } @@ -273,6 +273,12 @@ impl Destroy for EngineParams { } } +impl Default for EngineParams { + fn default() -> Self { + Self::new() + } +} + #[derive(Debug, PartialEq)] pub enum HttpCacheMode { /// Disable HTTP cache. diff --git a/src/error.rs b/src/error.rs index b50a132..630b4b4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -49,10 +49,10 @@ impl CronetError { } /// Get the error message. - pub fn message(&self) -> String { + pub fn message(&self) -> &str { unsafe { let c_message = Cronet_Error_message_get(self.ptr); - let message = CStr::from_ptr(c_message).to_string_lossy().into_owned(); + let message = CStr::from_ptr(c_message).to_str().unwrap(); message } } @@ -116,6 +116,12 @@ impl Destroy for CronetError { } } +impl Default for CronetError { + fn default() -> Self { + Self::new() + } +} + impl fmt::Display for CronetError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( diff --git a/src/executor.rs b/src/executor.rs index 8eaf4ee..1fe9983 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -6,7 +6,7 @@ use crate::{ }; static mut EXECUTOR_CALLBACKS: Lazy> = - Lazy::new(|| CronetCallbacks::new()); + Lazy::new(CronetCallbacks::new); #[no_mangle] unsafe extern "C" fn cronetExecutorOnExecute( diff --git a/src/http_header.rs b/src/http_header.rs index 01d94de..9c4b6ae 100644 --- a/src/http_header.rs +++ b/src/http_header.rs @@ -20,12 +20,10 @@ impl HttpHeader { } /// Get the name of this header. - pub fn name(&self) -> String { + pub fn name(&self) -> &'static str { unsafe { let c_name = Cronet_HttpHeader_name_get(self.ptr); - let name = std::ffi::CStr::from_ptr(c_name) - .to_string_lossy() - .into_owned(); + let name = std::ffi::CStr::from_ptr(c_name).to_str().unwrap(); name } } @@ -38,10 +36,10 @@ impl HttpHeader { } } - pub fn value(&self) -> String { + pub fn value(&self) -> &'static str { unsafe { let c_value = Cronet_HttpHeader_value_get(self.ptr); - let value = CStr::from_ptr(c_value).to_string_lossy().into_owned(); + let value = CStr::from_ptr(c_value).to_str().unwrap(); value } } @@ -60,6 +58,12 @@ impl Destroy for HttpHeader { } } +impl Default for HttpHeader { + fn default() -> Self { + Self::new() + } +} + #[cfg(test)] mod tests { use crate::Destroy; diff --git a/src/lib.rs b/src/lib.rs index 6417583..2e51046 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ mod annotation; mod buffer; mod buffer_callback; #[cfg(feature = "client")] -mod client; +pub mod client; mod date_time; mod destroy; mod engine; diff --git a/src/metrics.rs b/src/metrics.rs index 155fa48..3da3ed8 100644 --- a/src/metrics.rs +++ b/src/metrics.rs @@ -161,24 +161,15 @@ impl Metrics { } pub fn socket_reused(&self) -> bool { - unsafe { - let reused = Cronet_Metrics_socket_reused_get(self.ptr); - reused - } + unsafe { Cronet_Metrics_socket_reused_get(self.ptr) } } pub fn sent_byte_count(&self) -> i64 { - unsafe { - let count = Cronet_Metrics_sent_byte_count_get(self.ptr); - count - } + unsafe { Cronet_Metrics_sent_byte_count_get(self.ptr) } } pub fn received_byte_count(&self) -> i64 { - unsafe { - let count = Cronet_Metrics_received_byte_count_get(self.ptr); - count - } + unsafe { Cronet_Metrics_received_byte_count_get(self.ptr) } } pub fn set_request_start(&self, datetime: DateTime) { @@ -284,6 +275,12 @@ impl Destroy for Metrics { } } +impl Default for Metrics { + fn default() -> Self { + Self::new() + } +} + #[cfg(test)] mod tests { use std::time::SystemTime; diff --git a/src/public_key_pins.rs b/src/public_key_pins.rs index 2a2f537..6c26210 100644 --- a/src/public_key_pins.rs +++ b/src/public_key_pins.rs @@ -33,10 +33,10 @@ impl PublicKeyPins { } } - pub fn host(&self) -> String { + pub fn host(&self) -> &str { unsafe { let c_str = Cronet_PublicKeyPins_host_get(self.ptr); - let host = CStr::from_ptr(c_str).to_string_lossy().into_owned(); + let host = CStr::from_ptr(c_str).to_str().unwrap(); host } } @@ -58,10 +58,10 @@ impl PublicKeyPins { unsafe { Cronet_PublicKeyPins_pins_sha256_size(self.ptr) } } - pub fn at(&self, index: u32) -> String { + pub fn at(&self, index: u32) -> &str { unsafe { let c_str = Cronet_PublicKeyPins_pins_sha256_at(self.ptr, index); - CStr::from_ptr(c_str).to_string_lossy().into_owned() + CStr::from_ptr(c_str).to_str().unwrap() } } @@ -111,6 +111,12 @@ impl Destroy for PublicKeyPins { } } +impl Default for PublicKeyPins { + fn default() -> Self { + Self::new() + } +} + #[cfg(test)] mod tests { use std::time::UNIX_EPOCH; diff --git a/src/quic_hint.rs b/src/quic_hint.rs index 53c52ad..a021548 100644 --- a/src/quic_hint.rs +++ b/src/quic_hint.rs @@ -28,10 +28,10 @@ impl QuicHint { } } - pub fn host(&self) -> String { + pub fn host(&self) -> &str { unsafe { let c_str = Cronet_QuicHint_host_get(self.ptr); - let host = CStr::from_ptr(c_str).to_string_lossy().into_owned(); + let host = CStr::from_ptr(c_str).to_str().unwrap(); host } } @@ -65,6 +65,12 @@ impl Destroy for QuicHint { } } +impl Default for QuicHint { + fn default() -> Self { + Self::new() + } +} + #[cfg(test)] mod tests { use crate::Destroy; diff --git a/src/request_finished_info.rs b/src/request_finished_info.rs index 8d3ddcf..3cc3123 100644 --- a/src/request_finished_info.rs +++ b/src/request_finished_info.rs @@ -87,6 +87,12 @@ impl Destroy for RequestFinishedInfo { } } +impl Default for RequestFinishedInfo { + fn default() -> Self { + Self::new() + } +} + /// Enum representing the reason why the request finished. #[derive(Debug, PartialEq)] pub enum RequestFinishedInfoReason { diff --git a/src/request_finished_info_listener.rs b/src/request_finished_info_listener.rs index e036e79..1f38676 100644 --- a/src/request_finished_info_listener.rs +++ b/src/request_finished_info_listener.rs @@ -10,7 +10,7 @@ use crate::{ static mut REQUEST_FINISHED_INFO_LISTENER_CALLBACKS: Lazy< CronetCallbacks, -> = Lazy::new(|| CronetCallbacks::new()); +> = Lazy::new(CronetCallbacks::new); #[no_mangle] unsafe extern "C" fn cronetOnRequestFinished( @@ -50,10 +50,9 @@ impl RequestFinishedInfoListener { } } - pub fn set_client_context(&self, raw_data: Cronet_RawDataPtr) { - unsafe { - Cronet_RequestFinishedInfoListener_SetClientContext(self.ptr, raw_data); - } + #[allow(clippy::missing_safety_doc)] + pub unsafe fn set_client_context(&self, raw_data: Cronet_RawDataPtr) { + Cronet_RequestFinishedInfoListener_SetClientContext(self.ptr, raw_data); } } @@ -84,7 +83,9 @@ mod tests { #[test] fn it_sets_client_context() { let listener = super::RequestFinishedInfoListener::new(|_, _, _, _| {}); - listener.set_client_context(std::ptr::null_mut()); + unsafe { + listener.set_client_context(std::ptr::null_mut()); + } listener.destroy(); } } diff --git a/src/runnable.rs b/src/runnable.rs index bbe5e96..3a9e1d7 100644 --- a/src/runnable.rs +++ b/src/runnable.rs @@ -6,7 +6,7 @@ use crate::{ }; static mut RUNNABLE_CALLBACKS: Lazy> = - Lazy::new(|| CronetCallbacks::new()); + Lazy::new(CronetCallbacks::new); #[no_mangle] unsafe extern "C" fn cronetRunnableOnRun(selfPtr: Cronet_RunnablePtr) { diff --git a/src/upload_data_provider.rs b/src/upload_data_provider.rs index e632305..159276e 100644 --- a/src/upload_data_provider.rs +++ b/src/upload_data_provider.rs @@ -11,7 +11,7 @@ use crate::{ static mut UPLOAD_DATA_PROVIDER_CALLBACKS: Lazy< CronetCallbacks>, -> = Lazy::new(|| CronetCallbacks::new()); +> = Lazy::new(CronetCallbacks::new); #[no_mangle] unsafe extern "C" fn cronetUploadDataProviderGetLength( @@ -19,7 +19,7 @@ unsafe extern "C" fn cronetUploadDataProviderGetLength( ) -> i64 { let lockedMap = UPLOAD_DATA_PROVIDER_CALLBACKS.map().lock().unwrap(); let callback = lockedMap.get(&selfPtr).unwrap(); - return callback.length(UploadDataProvider { ptr: selfPtr }); + callback.length(UploadDataProvider { ptr: selfPtr }) } #[no_mangle] @@ -105,8 +105,9 @@ impl UploadDataProvider { } } - pub fn set_client_context(&self, client_context: Cronet_ClientContext) { - unsafe { Cronet_UploadDataProvider_SetClientContext(self.ptr, client_context) } + #[allow(clippy::missing_safety_doc)] + pub unsafe fn set_client_context(&self, client_context: Cronet_ClientContext) { + Cronet_UploadDataProvider_SetClientContext(self.ptr, client_context) } pub fn client_context(&self) -> Cronet_ClientContext { @@ -177,7 +178,7 @@ mod tests { fn read(&self, _: UploadDataProvider, sink: UploadDataSink, buffer: Buffer) { let size = buffer.size(); - sink.on_read_succeeded(size, true); + sink.on_read_succeeded(size, false); } fn rewind(&mut self, _: UploadDataProvider, sink: UploadDataSink) { diff --git a/src/upload_data_sink.rs b/src/upload_data_sink.rs index da90899..e8194f5 100644 --- a/src/upload_data_sink.rs +++ b/src/upload_data_sink.rs @@ -11,7 +11,7 @@ use crate::{ static mut UPLOAD_DATA_SINK_CALLBACKS: Lazy< CronetCallbacks, -> = Lazy::new(|| CronetCallbacks::new()); +> = Lazy::new(CronetCallbacks::new); #[no_mangle] unsafe extern "C" fn cronetUploadDataSinkOnReadSucceeded( @@ -163,7 +163,7 @@ mod tests { on_rewind_succeeded: |_| println!("on_rewind_succeeded"), on_rewind_error: |_, _| println!("on_rewind_error"), }); - upload_data_sink.on_read_succeeded(10, true); + upload_data_sink.on_read_succeeded(10, false); upload_data_sink.on_rewind_succeeded(); upload_data_sink.on_read_error("error"); upload_data_sink.on_rewind_error("error"); diff --git a/src/url_request.rs b/src/url_request.rs index 5544c6c..fd7bdf2 100644 --- a/src/url_request.rs +++ b/src/url_request.rs @@ -36,11 +36,11 @@ impl UrlRequest { /// * `executor` - Executor on which all callbacks will be invoked. pub fn init_with_params( &self, - engine: Engine, + engine: &Engine, url: &str, - params: UrlRequestParams, - callback: UrlRequestCallback, - executor: Executor, + params: &UrlRequestParams, + callback: &UrlRequestCallback, + executor: &Executor, ) -> EngineResult { unsafe { let c_str = CString::new(url).unwrap(); @@ -134,3 +134,9 @@ impl Destroy for UrlRequest { } } } + +impl Default for UrlRequest { + fn default() -> Self { + Self::new() + } +} diff --git a/src/url_request_callback.rs b/src/url_request_callback.rs index 846220f..0170164 100644 --- a/src/url_request_callback.rs +++ b/src/url_request_callback.rs @@ -11,7 +11,7 @@ use crate::{ static mut URL_REQUEST_CALLBACK_CALLBACKS: Lazy< CronetCallbacks>, -> = Lazy::new(|| CronetCallbacks::new()); +> = Lazy::new(CronetCallbacks::new); #[no_mangle] unsafe extern "C" fn cronetUrlRequestCallbackOnRedirectReceived( @@ -20,8 +20,8 @@ unsafe extern "C" fn cronetUrlRequestCallbackOnRedirectReceived( info_ptr: Cronet_UrlResponseInfoPtr, new_location_url: Cronet_String, ) { - let lockedMap = URL_REQUEST_CALLBACK_CALLBACKS.map().lock().unwrap(); - let callback = lockedMap.get(&self_ptr).unwrap(); + let mut lockedMap = URL_REQUEST_CALLBACK_CALLBACKS.map().lock().unwrap(); + let callback = lockedMap.get_mut(&self_ptr).unwrap(); let c_str = CStr::from_ptr(new_location_url); let new_location_url = c_str.to_str().unwrap(); callback.on_redirect_received( @@ -38,8 +38,8 @@ unsafe extern "C" fn cronetUrlRequestCallbackOnResponseStarted( request_ptr: Cronet_UrlRequestPtr, info_ptr: Cronet_UrlResponseInfoPtr, ) { - let lockedMap = URL_REQUEST_CALLBACK_CALLBACKS.map().lock().unwrap(); - let callback = lockedMap.get(&self_ptr).unwrap(); + let mut lockedMap = URL_REQUEST_CALLBACK_CALLBACKS.map().lock().unwrap(); + let callback = lockedMap.get_mut(&self_ptr).unwrap(); callback.on_response_started( UrlRequestCallback { ptr: self_ptr }, UrlRequest { ptr: request_ptr }, @@ -55,8 +55,8 @@ unsafe extern "C" fn cronetUrlRequestCallbackOnReadCompleted( buffer_ptr: Cronet_BufferPtr, bytes_read: u64, ) { - let lockedMap = URL_REQUEST_CALLBACK_CALLBACKS.map().lock().unwrap(); - let callback = lockedMap.get(&self_ptr).unwrap(); + let mut lockedMap = URL_REQUEST_CALLBACK_CALLBACKS.map().lock().unwrap(); + let callback = lockedMap.get_mut(&self_ptr).unwrap(); callback.on_read_completed( UrlRequestCallback { ptr: self_ptr }, UrlRequest { ptr: request_ptr }, @@ -72,8 +72,8 @@ unsafe extern "C" fn cronetUrlRequestCallbackOnSucceeded( request_ptr: Cronet_UrlRequestPtr, info_ptr: Cronet_UrlResponseInfoPtr, ) { - let lockedMap = URL_REQUEST_CALLBACK_CALLBACKS.map().lock().unwrap(); - let callback = lockedMap.get(&self_ptr).unwrap(); + let mut lockedMap = URL_REQUEST_CALLBACK_CALLBACKS.map().lock().unwrap(); + let callback = lockedMap.get_mut(&self_ptr).unwrap(); callback.on_succeeded( UrlRequestCallback { ptr: self_ptr }, UrlRequest { ptr: request_ptr }, @@ -88,8 +88,8 @@ unsafe extern "C" fn cronetUrlRequestCallbackOnFailed( info_ptr: Cronet_UrlResponseInfoPtr, error_ptr: Cronet_ErrorPtr, ) { - let lockedMap = URL_REQUEST_CALLBACK_CALLBACKS.map().lock().unwrap(); - let callback = lockedMap.get(&self_ptr).unwrap(); + let mut lockedMap = URL_REQUEST_CALLBACK_CALLBACKS.map().lock().unwrap(); + let callback = lockedMap.get_mut(&self_ptr).unwrap(); callback.on_failed( UrlRequestCallback { ptr: self_ptr }, UrlRequest { ptr: request_ptr }, @@ -104,8 +104,8 @@ unsafe extern "C" fn cronetUrlRequestCallbackOnCanceled( request_ptr: Cronet_UrlRequestPtr, info_ptr: Cronet_UrlResponseInfoPtr, ) { - let lockedMap = URL_REQUEST_CALLBACK_CALLBACKS.map().lock().unwrap(); - let callback = lockedMap.get(&self_ptr).unwrap(); + let mut lockedMap = URL_REQUEST_CALLBACK_CALLBACKS.map().lock().unwrap(); + let callback = lockedMap.get_mut(&self_ptr).unwrap(); callback.on_canceled( UrlRequestCallback { ptr: self_ptr }, UrlRequest { ptr: request_ptr }, @@ -168,7 +168,7 @@ pub trait UrlRequestCallbackHandler { /// * `info`: Response information. /// * `newLocationUrl`: Location where the request is redirected. fn on_redirect_received( - &self, + &mut self, url_request_callback: UrlRequestCallback, request: UrlRequest, info: UrlResponseInfo, @@ -189,7 +189,7 @@ pub trait UrlRequestCallbackHandler { /// * `request`: Request that started to get the response. /// * `info`: Response information. fn on_response_started( - &self, + &mut self, url_request_callback: UrlRequestCallback, request: UrlRequest, info: UrlResponseInfo, @@ -214,7 +214,7 @@ pub trait UrlRequestCallbackHandler { /// containing the received data. /// * `bytesRead`: The number of bytes read into the `buffer`. fn on_read_completed( - &self, + &mut self, url_request_callback: UrlRequestCallback, request: UrlRequest, info: UrlResponseInfo, @@ -233,7 +233,7 @@ pub trait UrlRequestCallbackHandler { /// * `request`: Request that succeeded. /// * `info`: Response information. NOTE: this is owned by the request. fn on_succeeded( - &self, + &mut self, url_request_callback: UrlRequestCallback, request: UrlRequest, info: UrlResponseInfo, @@ -252,7 +252,7 @@ pub trait UrlRequestCallbackHandler { /// * `info`: Response information. May be `None` if no response was received. NOTE: this is owned by the request. /// * `error`: Information about the error. NOTE: this is owned by the request. fn on_failed( - &self, + &mut self, url_request_callback: UrlRequestCallback, request: UrlRequest, info: UrlResponseInfo, @@ -270,7 +270,7 @@ pub trait UrlRequestCallbackHandler { /// * `request`: Request that was canceled. /// * `info`: Response information. May be `None` if no response was received. NOTE: this is owned by the request. fn on_canceled( - &self, + &mut self, url_request_callback: UrlRequestCallback, request: UrlRequest, info: UrlResponseInfo, @@ -287,7 +287,7 @@ mod tests { impl UrlRequestCallbackHandler for TestUrlRequestCallbackHandler { fn on_redirect_received( - &self, + &mut self, _: UrlRequestCallback, _: UrlRequest, _: UrlResponseInfo, @@ -296,10 +296,17 @@ mod tests { println!("on_redirect_received"); } - fn on_response_started(&self, _: UrlRequestCallback, _: UrlRequest, _: UrlResponseInfo) {} + fn on_response_started( + &mut self, + _: UrlRequestCallback, + _: UrlRequest, + _: UrlResponseInfo, + ) { + println!("on_response_started") + } fn on_read_completed( - &self, + &mut self, _: UrlRequestCallback, _: UrlRequest, _: UrlResponseInfo, @@ -309,10 +316,12 @@ mod tests { println!("on_read_completed"); } - fn on_succeeded(&self, _: UrlRequestCallback, _: UrlRequest, _: UrlResponseInfo) {} + fn on_succeeded(&mut self, _: UrlRequestCallback, _: UrlRequest, _: UrlResponseInfo) { + println!("on_succeeded"); + } fn on_failed( - &self, + &mut self, _: UrlRequestCallback, _: UrlRequest, _: UrlResponseInfo, @@ -321,7 +330,7 @@ mod tests { println!("on_failed"); } - fn on_canceled(&self, _: UrlRequestCallback, _: UrlRequest, _: UrlResponseInfo) { + fn on_canceled(&mut self, _: UrlRequestCallback, _: UrlRequest, _: UrlResponseInfo) { println!("on_canceled"); } } diff --git a/src/url_request_params.rs b/src/url_request_params.rs index f972b24..da8304d 100644 --- a/src/url_request_params.rs +++ b/src/url_request_params.rs @@ -53,12 +53,12 @@ impl UrlRequestParams { } } - pub fn method(&self) -> String { + pub fn method(&self) -> &str { unsafe { let c_str = Cronet_UrlRequestParams_http_method_get(self.ptr); let c_str = CStr::from_ptr(c_str); let str_slice = c_str.to_str().unwrap(); - str_slice.to_owned() + str_slice } } @@ -270,6 +270,12 @@ impl Destroy for UrlRequestParams { } } +impl Default for UrlRequestParams { + fn default() -> Self { + Self::new() + } +} + #[cfg(feature = "client")] impl From> for UrlRequestParams where diff --git a/src/url_request_status_listener.rs b/src/url_request_status_listener.rs index 3eae5d5..9dd3097 100644 --- a/src/url_request_status_listener.rs +++ b/src/url_request_status_listener.rs @@ -8,7 +8,7 @@ use crate::{ static mut URL_REQUEST_STATUS_LISTENER_CALLBACKS: Lazy< CronetCallbacks, -> = Lazy::new(|| CronetCallbacks::new()); +> = Lazy::new(CronetCallbacks::new); #[no_mangle] unsafe extern "C" fn cronetUrlRequestStatusListenerOnStatus( @@ -53,6 +53,12 @@ impl Destroy for UrlRequestStatusListener { } } +impl Default for UrlRequestStatusListener { + fn default() -> Self { + Self::new() + } +} + /// Enum representing the status of a [crate::UrlRequest]. #[derive(Debug, PartialEq)] pub enum UrlRequestStatus { diff --git a/src/url_response_info.rs b/src/url_response_info.rs index 4037722..ccff671 100644 --- a/src/url_response_info.rs +++ b/src/url_response_info.rs @@ -1,5 +1,7 @@ use std::ffi::{CStr, CString}; +use http::{HeaderValue, Response, StatusCode, Version}; + use crate::{ Cronet_UrlResponseInfoPtr, Cronet_UrlResponseInfo_Create, Cronet_UrlResponseInfo_Destroy, Cronet_UrlResponseInfo_all_headers_list_add, Cronet_UrlResponseInfo_all_headers_list_at, @@ -31,11 +33,11 @@ impl UrlResponseInfo { /// The URL the response is for. /// This is the URL after following redirects, so it may not be the originally requested URL - pub fn url(&self) -> String { + pub fn url(&self) -> &str { unsafe { let url = Cronet_UrlResponseInfo_url_get(self.ptr); let url = CStr::from_ptr(url); - url.to_string_lossy().into_owned() + url.to_str().unwrap() } } @@ -54,11 +56,11 @@ impl UrlResponseInfo { /// The URL at the given index in the chain. /// The first entry is the originally requested URL; the following entries are redirects followed. - pub fn url_chain_at(&self, index: u32) -> String { + pub fn url_chain_at(&self, index: u32) -> &str { unsafe { let url = Cronet_UrlResponseInfo_url_chain_at(self.ptr, index); let url = CStr::from_ptr(url); - url.to_string_lossy().into_owned() + url.to_str().unwrap() } } @@ -91,11 +93,11 @@ impl UrlResponseInfo { /// The HTTP status text of the status line. /// For example, if the request received a "HTTP/1.1 200 OK" response, this method returns "OK". - pub fn status_text(&self) -> String { + pub fn status_text(&self) -> &str { unsafe { let text = Cronet_UrlResponseInfo_http_status_text_get(self.ptr); let text = CStr::from_ptr(text); - text.to_string_lossy().into_owned() + text.to_str().unwrap() } } @@ -107,10 +109,7 @@ impl UrlResponseInfo { } pub fn header_size(&self) -> u32 { - unsafe { - let size = Cronet_UrlResponseInfo_all_headers_list_size(self.ptr); - size - } + unsafe { Cronet_UrlResponseInfo_all_headers_list_size(self.ptr) } } pub fn header_at(&self, index: u32) -> HttpHeader { @@ -146,11 +145,12 @@ impl UrlResponseInfo { /// The protocol (for example 'quic/1+spdy/3') negotiated with the server. /// An empty string if no protocol was negotiated, the protocol is /// not known, or when using plain HTTP or HTTPS. - pub fn negotiated_protocol(&self) -> String { + pub fn negotiated_protocol(&self) -> &'static str { unsafe { let protocol = Cronet_UrlResponseInfo_negotiated_protocol_get(self.ptr); let protocol = CStr::from_ptr(protocol); - protocol.to_string_lossy().into_owned() + let protocol = protocol.to_str().unwrap(); + protocol } } @@ -162,11 +162,11 @@ impl UrlResponseInfo { } /// The proxy server that was used for the request. - pub fn proxy_server(&self) -> String { + pub fn proxy_server(&self) -> &'static str { unsafe { let server = Cronet_UrlResponseInfo_proxy_server_get(self.ptr); - let server = CStr::from_ptr(server); - server.to_string_lossy().into_owned() + let server = CStr::from_ptr(server).to_str().unwrap(); + server } } @@ -197,6 +197,52 @@ impl Destroy for UrlResponseInfo { } } +impl Default for UrlResponseInfo { + fn default() -> Self { + Self::new() + } +} + +#[cfg(feature = "client")] +#[allow(clippy::from_over_into)] +impl Into> for UrlResponseInfo +where + T: Default, +{ + fn into(self) -> Response { + let mut response = Response::default(); + + // Set HTTP version + let version = match self.negotiated_protocol() { + "http/0.9" => Version::HTTP_09, + "http/1.0" => Version::HTTP_10, + "http/1.1" => Version::HTTP_11, + "h2" => Version::HTTP_2, + "h3" => Version::HTTP_3, + "" => Version::HTTP_11, + version => panic!("Server responded with unknown HTTP version '{}'", version), + }; + *response.version_mut() = version; + + // Set status code + let status_code = self.status_code(); + *response.status_mut() = StatusCode::from_u16(status_code as u16).unwrap(); + + // Set headers + let header_size = self.header_size(); + for i in 0..header_size { + let header = self.header_at(i); + let name = header.name(); + let value = header.value(); + response + .headers_mut() + .insert(name, HeaderValue::from_static(value)); + } + + response + } +} + #[cfg(test)] mod tests { use crate::Destroy;