Skip to content

Commit

Permalink
fixup! feat: Add option to set a request timeout
Browse files Browse the repository at this point in the history
  • Loading branch information
threema-donat committed Apr 30, 2024
1 parent 2a1fe70 commit 840c450
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 49 deletions.
6 changes: 5 additions & 1 deletion examples/certificate_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
};

let mut certificate = std::fs::File::open(certificate_file)?;
Ok(Client::certificate(&mut certificate, &password, endpoint)?)

// Create config with the given endpoint and default timeouts
let client_config = a2::ClientConfig::new(endpoint);

Ok(Client::certificate(&mut certificate, &password, client_config)?)
}
#[cfg(all(not(feature = "openssl"), feature = "ring"))]
{
Expand Down
9 changes: 7 additions & 2 deletions examples/token_client.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use argparse::{ArgumentParser, Store, StoreOption, StoreTrue};
use std::fs::File;

use a2::{Client, DefaultNotificationBuilder, Endpoint, NotificationBuilder, NotificationOptions};
use a2::{
client::ClientConfig, Client, DefaultNotificationBuilder, Endpoint, NotificationBuilder, NotificationOptions,
};

// An example client connectiong to APNs with a JWT token
#[tokio::main]
Expand Down Expand Up @@ -46,8 +48,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
Endpoint::Production
};

// Create config with the given endpoint and default timeouts
let client_config = ClientConfig::new(endpoint);

// Connecting to APNs
let client = Client::token(&mut private_key, key_id, team_id, endpoint).unwrap();
let client = Client::token(&mut private_key, key_id, team_id, client_config).unwrap();

let options = NotificationOptions {
apns_topic: topic.as_deref(),
Expand Down
97 changes: 58 additions & 39 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,64 +59,78 @@ pub struct Client {
http_client: HttpClient<HyperConnector, BoxBody<Bytes, Infallible>>,
}

/// Uses [`Endpoint::Production`] by default.
#[derive(Debug, Clone)]
pub struct ClientBuilder {
/// The default implementation uses [`Endpoint::Production`] and can be created
/// trough calling [`ClientConfig::default`].
pub struct ClientConfig {
/// The endpoint where the requests are sent to
pub endpoint: Endpoint,
/// The timeout of the HTTP requests
pub request_timeout_secs: Option<u64>,
/// The timeout for idle sockets being kept alive
pub pool_idle_timeout_secs: Option<u64>,
/// The endpoint where the requests are sent to
pub endpoint: Endpoint,
/// See [`crate::signer::Signer`]
pub signer: Option<Signer>,
/// The HTTPS connector used to connect to APNs
pub connector: Option<HyperConnector>,
}

impl Default for ClientBuilder {
impl Default for ClientConfig {
fn default() -> Self {
Self {
pool_idle_timeout_secs: Some(600),
request_timeout_secs: Some(DEFAULT_REQUEST_TIMEOUT_SECS),
endpoint: Endpoint::Production,
request_timeout_secs: Some(DEFAULT_REQUEST_TIMEOUT_SECS),
pool_idle_timeout_secs: Some(600),
}
}
}

impl ClientConfig {
pub fn new(endpoint: Endpoint) -> Self {
ClientConfig {
endpoint,
..Default::default()
}
}
}

#[derive(Debug, Clone)]
struct ClientBuilder {
config: ClientConfig,
signer: Option<Signer>,
connector: Option<HyperConnector>,
}

impl Default for ClientBuilder {
fn default() -> Self {
Self {
config: Default::default(),
signer: None,
connector: Some(default_connector()),
}
}
}

impl ClientBuilder {
pub fn connector(mut self, connector: HyperConnector) -> Self {
fn connector(mut self, connector: HyperConnector) -> Self {
self.connector = Some(connector);
self
}

pub fn signer(mut self, signer: Signer) -> Self {
fn signer(mut self, signer: Signer) -> Self {
self.signer = Some(signer);
self
}

pub fn request_timeout(mut self, seconds: u64) -> Self {
self.request_timeout_secs = Some(seconds);
fn config(mut self, config: ClientConfig) -> Self {
self.config = config;
self
}

pub fn pool_idle_timeout(mut self, seconds: u64) -> Self {
self.pool_idle_timeout_secs = Some(seconds);
self
}

pub fn endpoint(mut self, endpoint: Endpoint) -> Self {
self.endpoint = endpoint;
self
}

pub fn build(self) -> Client {
fn build(self) -> Client {
let ClientBuilder {
request_timeout_secs,
pool_idle_timeout_secs,
endpoint,
config:
ClientConfig {
endpoint,
request_timeout_secs,
pool_idle_timeout_secs,
},
signer,
connector,
} = self;
Expand Down Expand Up @@ -163,7 +177,7 @@ impl Client {
///
/// Only works with the `openssl` feature.
#[cfg(feature = "openssl")]
pub fn certificate<R>(certificate: &mut R, password: &str, endpoint: Endpoint) -> Result<Client, Error>
pub fn certificate<R>(certificate: &mut R, password: &str, config: ClientConfig) -> Result<Client, Error>
where
R: Read,
{
Expand All @@ -176,23 +190,23 @@ impl Client {
};
let connector = client_cert_connector(&cert.to_pem()?, &pkey.private_key_to_pem_pkcs8()?)?;

Ok(Self::builder().connector(connector).endpoint(endpoint).build())
Ok(Self::builder().connector(connector).config(config).build())
}

/// Create a connection to APNs using the raw PEM-formatted certificate and
/// key, extracted from the provider client certificate you obtain from your
/// [Apple developer account](https://developer.apple.com/account/)
pub fn certificate_parts(cert_pem: &[u8], key_pem: &[u8], endpoint: Endpoint) -> Result<Client, Error> {
pub fn certificate_parts(cert_pem: &[u8], key_pem: &[u8], config: ClientConfig) -> Result<Client, Error> {
let connector = client_cert_connector(cert_pem, key_pem)?;

Ok(Self::builder().endpoint(endpoint).connector(connector).build())
Ok(Self::builder().config(config).connector(connector).build())
}

/// Create a connection to APNs using system certificates, signing every
/// request with a signature using a private key, key id and team id
/// provisioned from your [Apple developer
/// account](https://developer.apple.com/account/).
pub fn token<S, T, R>(pkcs8_pem: R, key_id: S, team_id: T, endpoint: Endpoint) -> Result<Client, Error>
pub fn token<S, T, R>(pkcs8_pem: R, key_id: S, team_id: T, config: ClientConfig) -> Result<Client, Error>
where
S: Into<String>,
T: Into<String>,
Expand All @@ -201,7 +215,7 @@ impl Client {
let signature_ttl = Duration::from_secs(60 * 55);
let signer = Signer::new(pkcs8_pem, key_id, team_id, signature_ttl)?;

Ok(Self::builder().endpoint(endpoint).signer(signer).build())
Ok(Self::builder().config(config).signer(signer).build())
}

/// Send a notification payload.
Expand Down Expand Up @@ -349,7 +363,12 @@ jDwmlD1Gg0yJt1e38djFwsxsfr5q2hv0Rj9fTEqAPr8H7mGm0wKxZ7iQ
fn test_sandbox_request_uri() {
let builder = DefaultNotificationBuilder::new();
let payload = builder.build("a_test_id", Default::default());
let client = Client::builder().endpoint(Endpoint::Sandbox).build();
let client = Client::builder()
.config(ClientConfig {
endpoint: Endpoint::Sandbox,
..Default::default()
})
.build();
let request = client.build_request(payload).unwrap();
let uri = format!("{}", request.uri());

Expand All @@ -370,7 +389,7 @@ jDwmlD1Gg0yJt1e38djFwsxsfr5q2hv0Rj9fTEqAPr8H7mGm0wKxZ7iQ
fn test_request_invalid() {
let builder = DefaultNotificationBuilder::new();
let payload = builder.build("\r\n", Default::default());
let client = Client::builder().endpoint(Endpoint::Production).build();
let client = Client::builder().build();
let request = client.build_request(payload);

assert!(matches!(request, Err(Error::BuildRequestError(_))));
Expand Down Expand Up @@ -420,7 +439,7 @@ jDwmlD1Gg0yJt1e38djFwsxsfr5q2hv0Rj9fTEqAPr8H7mGm0wKxZ7iQ

let builder = DefaultNotificationBuilder::new();
let payload = builder.build("a_test_id", Default::default());
let client = Client::builder().endpoint(Endpoint::Production).signer(signer).build();
let client = Client::builder().signer(signer).build();
let request = client.build_request(payload).unwrap();

assert_ne!(None, request.headers().get(AUTHORIZATION));
Expand Down Expand Up @@ -639,7 +658,7 @@ jDwmlD1Gg0yJt1e38djFwsxsfr5q2hv0Rj9fTEqAPr8H7mGm0wKxZ7iQ
let key: Vec<u8> = include_str!("../test_cert/test.key").bytes().collect();
let cert: Vec<u8> = include_str!("../test_cert/test.crt").bytes().collect();

let c = Client::certificate_parts(&cert, &key, Endpoint::Sandbox)?;
let c = Client::certificate_parts(&cert, &key, ClientConfig::default())?;
assert!(c.options.signer.is_none());
Ok(())
}
Expand Down
10 changes: 5 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
//! ## Example sending a plain notification using token authentication:
//!
//! ```no_run
//! # use a2::{DefaultNotificationBuilder, NotificationBuilder, Client, Endpoint};
//! # use a2::{DefaultNotificationBuilder, NotificationBuilder, Client, ClientConfig, Endpoint};
//! # use std::fs::File;
//! # #[tokio::main]
//! # async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
Expand All @@ -48,7 +48,7 @@
//! &mut file,
//! "KEY_ID",
//! "TEAM_ID",
//! Endpoint::Production).unwrap();
//! ClientConfig::default()).unwrap();
//!
//! let response = client.send(payload).await?;
//! println!("Sent: {:?}", response);
Expand All @@ -64,7 +64,7 @@
//! # {
//!
//! use a2::{
//! Client, Endpoint, DefaultNotificationBuilder, NotificationBuilder, NotificationOptions,
//! Client, ClientConfig, Endpoint, DefaultNotificationBuilder, NotificationBuilder, NotificationOptions,
//! Priority,
//! };
//! use std::fs::File;
Expand Down Expand Up @@ -97,7 +97,7 @@
//! let client = Client::certificate(
//! &mut file,
//! "Correct Horse Battery Stable",
//! Endpoint::Production)?;
//! ClientConfig::default())?;
//!
//! let response = client.send(payload).await?;
//! println!("Sent: {:?}", response);
Expand Down Expand Up @@ -131,6 +131,6 @@ pub use crate::request::notification::{

pub use crate::response::{ErrorBody, ErrorReason, Response};

pub use crate::client::{Client, Endpoint};
pub use crate::client::{Client, ClientConfig, Endpoint};

pub use crate::error::Error;
4 changes: 2 additions & 2 deletions src/request/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub struct Payload<'a> {
/// use a2::client::Endpoint;
/// use a2::request::notification::{NotificationBuilder, NotificationOptions};
/// use a2::request::payload::{PayloadLike, APS};
/// use a2::Client;
/// use a2::{Client, ClientConfig};
/// use a2::DefaultNotificationBuilder;
/// use serde::Serialize;
/// use std::fs::File;
Expand All @@ -45,7 +45,7 @@ pub struct Payload<'a> {
/// let payload = builder.build("device-token-from-the-user", Default::default());
/// let mut file = File::open("/path/to/private_key.p8")?;
///
/// let client = Client::token(&mut file, "KEY_ID", "TEAM_ID", Endpoint::Production).unwrap();
/// let client = Client::token(&mut file, "KEY_ID", "TEAM_ID", ClientConfig::default()).unwrap();
///
/// let response = client.send(payload).await?;
/// println!("Sent: {:?}", response);
Expand Down

0 comments on commit 840c450

Please sign in to comment.