Skip to content

Commit

Permalink
Merge pull request #2 from sleeyax/develop
Browse files Browse the repository at this point in the history
  • Loading branch information
sleeyax authored Dec 28, 2023
2 parents bc06a5d + 9cb60c6 commit e6869e1
Show file tree
Hide file tree
Showing 31 changed files with 527 additions and 125 deletions.
6 changes: 3 additions & 3 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
},
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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). 💖

<!-- sponsors --><a href="https://github.com/secretkeysio"><img src="https://github.com/secretkeysio.png" width="60px" alt="SecretKeys" /></a><!-- sponsors -->
<a href="https://github.com/secretkeysio"><img src="https://github.com/secretkeysio.png" width="60px" alt="SecretKeys" /></a>

## 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)
36 changes: 36 additions & 0 deletions examples/blocking.rs
Original file line number Diff line number Diff line change
@@ -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<http::Response<Body>, 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),
}
}
33 changes: 30 additions & 3 deletions src/buffer.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -55,15 +57,22 @@ 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<T>(&self) -> Box<T> {
unsafe {
let dataPtr: Cronet_RawDataPtr = self.data_ptr();
Box::from_raw(dataPtr as *mut T)
}
}

pub(crate) fn data_ptr(&self) -> Cronet_RawDataPtr {
unsafe { Cronet_Buffer_GetData(self.ptr) }
pub fn data_slice<T>(&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.
Expand All @@ -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<T>(&self, data: Box<T>, data_size: u64) -> Result<(), &'static str> {
#[allow(dead_code)]
pub(crate) fn write<T>(&self, data: Box<T>, 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;
Expand All @@ -90,6 +100,23 @@ impl Buffer {

Ok(())
}

pub(crate) fn write_slice<T>(&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 {
Expand Down
2 changes: 1 addition & 1 deletion src/buffer_callback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
};

static mut BUFFER_CALLBACKS: Lazy<CronetCallbacks<Cronet_BufferCallbackPtr, BufferCallbackFn>> =
Lazy::new(|| CronetCallbacks::new());
Lazy::new(CronetCallbacks::new);

#[no_mangle]
unsafe extern "C" fn cronetBufferCallbackOnDestroy(
Expand Down
26 changes: 25 additions & 1 deletion src/client/body.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -30,13 +30,26 @@ 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),
}
}

pub(crate) fn len(&self) -> Option<u64> {
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),
}
}

Expand All @@ -49,13 +62,23 @@ impl Body {
enum Kind {
Reader(Box<dyn Read + Send>, Option<u64>),
Bytes(Bytes),
BytesMut(BytesMut),
}

impl Kind {
fn try_clone(&self) -> Option<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()),
}
}
}
Expand Down Expand Up @@ -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),
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/client/body_upload_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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::<u8>(4);
assert_eq!(actual.len(), expected.len());
assert_eq!(*actual, expected.as_bytes());
assert_eq!(actual, expected.as_bytes());

buffer.destroy();
}
Expand Down
46 changes: 38 additions & 8 deletions src/client/client.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -51,12 +52,41 @@ impl Client {
self.should_redirect = should_redirect;
}

pub fn send<T>(&self, request: http::Request<Body>) -> http::Result<http::Response<Body>> {
pub fn send(&self, request: http::Request<Body>) -> Result<http::Response<Body>, 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::<Status>();
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()
}
}
38 changes: 38 additions & 0 deletions src/client/error.rs
Original file line number Diff line number Diff line change
@@ -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<CronetError> 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),
}
}
}
5 changes: 5 additions & 0 deletions src/client/mod.rs
Original file line number Diff line number Diff line change
@@ -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::*;
Loading

0 comments on commit e6869e1

Please sign in to comment.