1use crate::{
6 header_names::USER_AGENT,
7 new_backend::get_backend,
8 settings::{validate_request, GLOBAL_SETTINGS},
9 Request, Response, Result,
10};
11
12#[derive(Default)]
17pub struct Client {
18 settings: ClientSettings,
19}
20
21impl Client {
22 pub fn new(mut settings: ClientSettings) -> Self {
23 settings.update_from_global_settings();
24 Self { settings }
25 }
26
27 #[cfg(feature = "ohttp")]
29 pub fn with_ohttp_channel(
30 channel: &str,
31 settings: ClientSettings,
32 ) -> Result<Self, crate::ViaductError> {
33 if !crate::ohttp::is_ohttp_channel_configured(channel) {
34 return Err(crate::ViaductError::OhttpChannelNotConfigured(
35 channel.to_string(),
36 ));
37 }
38 let mut client_settings = settings;
39 client_settings.ohttp_channel = Some(channel.to_string());
40 Ok(Self::new(client_settings))
41 }
42
43 fn set_user_agent(&self, request: &mut Request) -> Result<()> {
44 if let Some(user_agent) = &self.settings.user_agent {
45 request.headers.insert_if_missing(USER_AGENT, user_agent)?;
46 }
47 Ok(())
48 }
49
50 pub async fn send(&self, mut request: Request) -> Result<Response> {
51 validate_request(&request)?;
52 self.set_user_agent(&mut request)?;
53
54 #[cfg(feature = "ohttp")]
56 if let Some(channel) = &self.settings.ohttp_channel {
57 crate::debug!(
58 "Client configured for OHTTP channel '{}', processing request via OHTTP",
59 channel
60 );
61 return crate::ohttp::process_ohttp_request(request, channel, self.settings.clone())
62 .await;
63 }
64
65 crate::debug!("Processing request via standard backend");
67 get_backend()?
68 .send_request(request, self.settings.clone())
69 .await
70 }
71
72 pub fn send_sync(&self, request: Request) -> Result<Response> {
73 pollster::block_on(self.send(request))
74 }
75}
76
77#[derive(Debug, uniffi::Record, Clone)]
78#[repr(C)]
79pub struct ClientSettings {
80 #[uniffi(default = 0)]
82 pub timeout: u32,
83 #[uniffi(default = 10)]
85 pub redirect_limit: u32,
86 #[cfg(feature = "ohttp")]
88 pub ohttp_channel: Option<String>,
89 #[uniffi(default = None)]
94 pub user_agent: Option<String>,
95}
96
97impl ClientSettings {
98 pub fn update_from_global_settings(&mut self) {
99 let settings = GLOBAL_SETTINGS.read();
100 if self.user_agent.is_none() {
101 self.user_agent = settings.default_user_agent.clone();
102 }
103 }
104}
105
106impl Default for ClientSettings {
107 fn default() -> Self {
108 Self {
109 #[cfg(target_os = "ios")]
110 timeout: 7000,
111 #[cfg(not(target_os = "ios"))]
112 timeout: 10000,
113 redirect_limit: 10,
114 user_agent: None,
115 #[cfg(feature = "ohttp")]
116 ohttp_channel: None,
117 }
118 }
119}
120
121#[cfg(test)]
122mod test {
123 use url::Url;
124
125 use super::*;
126 use crate::settings;
127
128 #[test]
129 fn test_user_agent() {
130 let mut req = Request::get(Url::parse("http://example.com/").unwrap());
131 let client = Client::new(ClientSettings::default());
133 client.set_user_agent(&mut req).unwrap();
134 assert_eq!(req.headers.get(USER_AGENT), None);
135 settings::set_global_default_user_agent("global-user-agent".into());
137 let client = Client::new(ClientSettings::default());
138 let mut req = Request::get(Url::parse("http://example.com/").unwrap());
139 client.set_user_agent(&mut req).unwrap();
140 assert_eq!(req.headers.get(USER_AGENT), Some("global-user-agent"));
141 let client = Client::new(ClientSettings {
143 user_agent: Some("client-settings-user-agent".into()),
144 ..ClientSettings::default()
145 });
146 let mut req = Request::get(Url::parse("http://example.com/").unwrap());
147 client.set_user_agent(&mut req).unwrap();
148 assert_eq!(
149 req.headers.get(USER_AGENT),
150 Some("client-settings-user-agent")
151 );
152 let mut req = Request::get(Url::parse("http://example.com/").unwrap());
154 req.headers
155 .insert(USER_AGENT, "request-user-agent")
156 .unwrap();
157 client.set_user_agent(&mut req).unwrap();
158 assert_eq!(req.headers.get(USER_AGENT), Some("request-user-agent"));
159 }
160}