1pub mod response;
2
3pub use crate::client::response::*;
4
5use crate::message::Message;
6use reqwest::header::{AUTHORIZATION, CONTENT_LENGTH, CONTENT_TYPE, RETRY_AFTER};
7use reqwest::{Body, StatusCode};
8
9pub struct Client {
11 http_client: reqwest::Client,
12}
13
14impl Default for Client {
15 fn default() -> Self {
16 Self::new()
17 }
18}
19
20impl Client {
21 pub fn new() -> Client {
23 let http_client = reqwest::ClientBuilder::new()
24 .pool_max_idle_per_host(std::usize::MAX)
25 .build()
26 .unwrap();
27
28 Client { http_client }
29 }
30
31 pub async fn send(&self, message: Message<'_>) -> Result<FcmResponse, FcmError> {
33 let payload = serde_json::to_vec(&message.body).unwrap();
34
35 let request = self
36 .http_client
37 .post("https://fcm.googleapis.com/fcm/send")
38 .header(CONTENT_TYPE, "application/json")
39 .header(CONTENT_LENGTH, format!("{}", payload.len() as u64).as_bytes())
40 .header(AUTHORIZATION, format!("key={}", message.api_key).as_bytes())
41 .body(Body::from(payload))
42 .build()?;
43 let response = self.http_client.execute(request).await?;
44
45 let response_status = response.status();
46
47 let retry_after = response
48 .headers()
49 .get(RETRY_AFTER)
50 .and_then(|ra| ra.to_str().ok())
51 .and_then(|ra| ra.parse::<RetryAfter>().ok());
52
53 match response_status {
54 StatusCode::OK => {
55 let fcm_response: FcmResponse = response.json().await.unwrap();
56
57 match fcm_response.error {
58 Some(ErrorReason::Unavailable) => Err(response::FcmError::ServerError(retry_after)),
59 Some(ErrorReason::InternalServerError) => Err(response::FcmError::ServerError(retry_after)),
60 _ => Ok(fcm_response),
61 }
62 }
63 StatusCode::UNAUTHORIZED => Err(response::FcmError::Unauthorized),
64 StatusCode::BAD_REQUEST => Err(response::FcmError::InvalidMessage("Bad Request".to_string())),
65 status if status.is_server_error() => Err(response::FcmError::ServerError(retry_after)),
66 _ => Err(response::FcmError::InvalidMessage("Unknown Error".to_string())),
67 }
68 }
69}