Skip to content

Commit

Permalink
Refactor clients to use a trait, rather than overwriting the same str…
Browse files Browse the repository at this point in the history
…uct name.

This is a large braking change, but the old system did not allow for enabling multiple clients at the same time.
  • Loading branch information
andyblarblar committed Aug 21, 2023
1 parent 8d1844e commit a49cdc6
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 22 deletions.
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ categories = ["web-programming", "asynchronous"]
edition = "2021"

[features]
default = ["isahc", "futures-lite/futures-io"] #futures are only used for read_to_end() in isach client.
default = ["isahc-client"]
isahc-client = ["isahc", "futures-lite/futures-io"] #futures are only used for read_to_end() in isach client.
hyper-client = ["hyper", "hyper-tls"] #use features = ["hyper-client"], default-features = false for about 300kb size decrease.

[dependencies]
Expand All @@ -32,6 +33,7 @@ sec1_decode = "^0.1.0"
base64 = "^0.13"
chrono = "^0.4"
log = "^0.4"
async-trait = "0.1.73"

[dev-dependencies]
argparse = "^0.2"
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Example

```rust
use web_push::*;
use web_push::clients::isahc_client::IsahcWebPushClient;
use std::fs::File;

#[tokio::main]
Expand All @@ -49,7 +50,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>
builder.set_payload(ContentEncoding::Aes128Gcm, content);
builder.set_vapid_signature(sig_builder);

let client = WebPushClient::new()?;
let client = IsahcWebPushClient::new()?;

//Finally, send the notification!
client.send(builder.build()?).await?;
Expand Down
2 changes: 1 addition & 1 deletion examples/simple_send.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>
builder.set_payload(ContentEncoding::Aes128Gcm, "test".as_bytes());
};

let client = WebPushClient::new()?;
let client = IsahcWebPushClient::new()?;

let response = client.send(builder.build()?).await?;
println!("Sent: {:?}", response);
Expand Down
18 changes: 11 additions & 7 deletions src/clients/hyper_client.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use async_trait::async_trait;
use std::convert::Infallible;

use http::header::{CONTENT_LENGTH, RETRY_AFTER};
use hyper::{body::HttpBody, client::HttpConnector, Body, Client, Request as HttpRequest};
use hyper_tls::HttpsConnector;

use crate::clients::request_builder;
use crate::clients::{request_builder, WebPushClient};
use crate::error::{RetryAfter, WebPushError};
use crate::message::WebPushMessage;

Expand All @@ -15,27 +16,30 @@ use crate::message::WebPushMessage;
///
/// This client is [`hyper`](https://crates.io/crates/hyper) based, and will only work in Tokio contexts.
#[derive(Clone)]
pub struct WebPushClient {
pub struct HyperWebPushClient {
client: Client<HttpsConnector<HttpConnector>>,
}

impl Default for WebPushClient {
impl Default for HyperWebPushClient {
fn default() -> Self {
Self::new().unwrap()
}
}

impl WebPushClient {
#[async_trait]
impl WebPushClient for HyperWebPushClient {
type CreationError = Infallible;

/// Creates a new client.
pub fn new() -> Result<WebPushClient, Infallible> {
fn new() -> Result<Self, Self::CreationError> {
//This method can never fail, but returns error to match API of the isahc client.
Ok(WebPushClient {
Ok(Self {
client: Client::builder().build(HttpsConnector::new()),
})
}

/// Sends a notification. Never times out.
pub async fn send(&self, message: WebPushMessage) -> Result<(), WebPushError> {
async fn send(&self, message: WebPushMessage) -> Result<(), WebPushError> {
trace!("Message: {:?}", message);

let request: HttpRequest<Body> = request_builder::build_request(message);
Expand Down
17 changes: 11 additions & 6 deletions src/clients/isahc_client.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use async_trait::async_trait;
use futures_lite::AsyncReadExt;
use http::header::{CONTENT_LENGTH, RETRY_AFTER};
use isahc::HttpClient;

use crate::clients::request_builder;
use crate::clients::WebPushClient;
use crate::error::{RetryAfter, WebPushError};
use crate::message::WebPushMessage;

Expand All @@ -14,26 +16,29 @@ use crate::message::WebPushMessage;
///
/// This client is built on [`isahc`](https://crates.io/crates/isahc), and will therefore work on any async executor.
#[derive(Clone)]
pub struct WebPushClient {
pub struct IsahcWebPushClient {
client: HttpClient,
}

impl Default for WebPushClient {
impl Default for IsahcWebPushClient {
fn default() -> Self {
Self::new().unwrap()
}
}

impl WebPushClient {
#[async_trait]
impl WebPushClient for IsahcWebPushClient {
type CreationError = WebPushError;

/// Creates a new client. Can fail under resource depletion.
pub fn new() -> Result<WebPushClient, WebPushError> {
Ok(WebPushClient {
fn new() -> Result<Self, Self::CreationError> {
Ok(Self {
client: HttpClient::new()?,
})
}

/// Sends a notification. Never times out.
pub async fn send(&self, message: WebPushMessage) -> Result<(), WebPushError> {
async fn send(&self, message: WebPushMessage) -> Result<(), WebPushError> {
trace!("Message: {:?}", message);

let request = request_builder::build_request::<isahc::AsyncBody>(message);
Expand Down
22 changes: 21 additions & 1 deletion src/clients/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,30 @@
//! [`request_builder`] contains the functions used to send and consume push http messages.
//! This module should be consumed by each client, by using [`http`]'s flexible api.

use crate::{WebPushError, WebPushMessage};
use async_trait::async_trait;

pub mod request_builder;

#[cfg(feature = "hyper-client")]
pub mod hyper_client;

#[cfg(not(feature = "hyper-client"))]
#[cfg(feature = "isahc-client")]
pub mod isahc_client;

/// An async client for sending the notification payload.
/// Other features, such as thread safety, may vary by implementation.
#[async_trait]
pub trait WebPushClient
where
Self: Sized,
{
/// Errors that can occur when creating a client.
type CreationError;

/// Creates a new client.
fn new() -> Result<Self, Self::CreationError>;

/// Sends a notification. Never times out.
async fn send(&self, message: WebPushMessage) -> Result<(), WebPushError>;
}
13 changes: 8 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
//! builder.set_payload(ContentEncoding::Aes128Gcm, content);
//! builder.set_vapid_signature(sig_builder);
//!
//! let client = WebPushClient::new()?;
//! let client = IsahcWebPushClient::new()?;
//!
//! //Finally, send the notification!
//! client.send(builder.build()?).await?;
Expand All @@ -47,11 +47,14 @@ extern crate log;
#[macro_use]
extern crate serde_derive;

#[cfg(feature = "hyper-client")]
pub use crate::clients::hyper_client::WebPushClient;
#[cfg(not(feature = "hyper-client"))]
pub use crate::clients::isahc_client::WebPushClient;
pub use crate::clients::request_builder;
pub use crate::clients::WebPushClient;

#[cfg(feature = "hyper-client")]
pub use crate::clients::hyper_client::HyperWebPushClient;
#[cfg(feature = "isahc-client")]
pub use crate::clients::isahc_client::IsahcWebPushClient;

pub use crate::error::WebPushError;
pub use crate::http_ece::ContentEncoding;
pub use crate::message::{
Expand Down

0 comments on commit a49cdc6

Please sign in to comment.