1#[cfg(feature = "timezone")]
2use crate::Result;
3
4use serde::de::DeserializeOwned;
5use wasm_bindgen::JsCast;
6use worker_sys::BotManagement;
7
8#[derive(Debug, Clone)]
12pub struct Cf {
13 inner: worker_sys::IncomingRequestCfProperties,
14}
15
16unsafe impl Send for Cf {}
17unsafe impl Sync for Cf {}
18
19impl Cf {
20 #[cfg(feature = "http")]
21 pub(crate) fn new(inner: worker_sys::IncomingRequestCfProperties) -> Self {
22 Self { inner }
23 }
24
25 #[cfg(feature = "http")]
26 pub(crate) fn inner(&self) -> &worker_sys::IncomingRequestCfProperties {
27 &self.inner
28 }
29
30 pub fn bot_management(&self) -> Option<BotManagement> {
33 self.inner.bot_management()
34 }
35
36 pub fn verified_bot_category(&self) -> Option<String> {
38 self.inner.verified_bot_category()
39 }
40
41 pub fn colo(&self) -> String {
44 self.inner.colo().unwrap()
45 }
46
47 pub fn asn(&self) -> Option<u32> {
49 self.inner.asn().unwrap()
50 }
51
52 pub fn as_organization(&self) -> Option<String> {
54 self.inner.as_organization().unwrap()
55 }
56
57 pub fn country(&self) -> Option<String> {
60 self.inner.country().unwrap()
61 }
62
63 pub fn http_protocol(&self) -> String {
65 self.inner.http_protocol().unwrap()
66 }
67
68 pub fn request_priority(&self) -> Option<RequestPriority> {
72 if let Some(priority) = self.inner.request_priority().unwrap() {
73 let mut weight = 1;
74 let mut exclusive = false;
75 let mut group = 0;
76 let mut group_weight = 0;
77
78 priority
79 .as_str()
80 .split(';')
81 .map(|key_value_pair| {
82 let mut iter = key_value_pair.split('=');
83
84 let key = iter.next().unwrap(); let value = iter.next().unwrap(); (key, value)
89 })
90 .for_each(|(key, value)| match key {
91 "weight" => weight = value.parse().unwrap(),
92 "exclusive" => exclusive = value == "1",
93 "group" => group = value.parse().unwrap(),
94 "group-weight" => group_weight = value.parse().unwrap(),
95 _ => unreachable!(),
96 });
97
98 Some(RequestPriority {
99 weight,
100 exclusive,
101 group,
102 group_weight,
103 })
104 } else {
105 None
106 }
107 }
108
109 pub fn tls_cipher(&self) -> String {
111 self.inner.tls_cipher().unwrap()
112 }
113
114 pub fn tls_client_auth(&self) -> Option<TlsClientAuth> {
117 self.inner.tls_client_auth().unwrap().map(Into::into)
118 }
119
120 pub fn tls_version(&self) -> String {
122 self.inner.tls_version().unwrap()
124 }
125
126 pub fn city(&self) -> Option<String> {
128 self.inner.city().unwrap()
129 }
130
131 pub fn continent(&self) -> Option<String> {
133 self.inner.continent().unwrap()
134 }
135
136 pub fn coordinates(&self) -> Option<(f32, f32)> {
138 let lat_opt = self.inner.latitude().unwrap();
139 let lon_opt = self.inner.longitude().unwrap();
140 match (lat_opt, lon_opt) {
141 (Some(lat_str), Some(lon_str)) => {
142 let lat = lat_str.parse().unwrap();
144 let lon = lon_str.parse().unwrap();
145 Some((lat, lon))
146 }
147 _ => None,
148 }
149 }
150
151 pub fn postal_code(&self) -> Option<String> {
153 self.inner.postal_code().unwrap()
154 }
155
156 pub fn metro_code(&self) -> Option<String> {
158 self.inner.metro_code().unwrap()
159 }
160
161 pub fn region(&self) -> Option<String> {
163 self.inner.region().unwrap()
164 }
165
166 pub fn region_code(&self) -> Option<String> {
168 self.inner.region_code().unwrap()
169 }
170
171 #[cfg(feature = "timezone")]
173 pub fn timezone(&self) -> Result<impl chrono::TimeZone> {
174 let tz = self.inner.timezone()?;
175 Ok(tz.parse::<chrono_tz::Tz>()?)
176 }
177
178 pub fn timezone_name(&self) -> String {
180 self.inner.timezone().unwrap()
181 }
182
183 pub fn is_eu_country(&self) -> bool {
185 self.inner.is_eu_country().unwrap() == Some("1".to_string())
186 }
187
188 pub fn host_metadata<T: serde::de::DeserializeOwned>(&self) -> crate::Result<Option<T>> {
189 let host_metadata = self.inner.host_metadata()?;
190 if host_metadata.is_undefined() {
191 Ok(None)
192 } else {
193 serde_wasm_bindgen::from_value(host_metadata)
194 .map(Some)
195 .map_err(|e| wasm_bindgen::JsValue::from(e.to_string()))
196 }
197 .map_err(crate::Error::from)
198 }
199}
200
201#[derive(Debug, Clone, Copy)]
203pub struct RequestPriority {
204 pub weight: usize,
206
207 pub exclusive: bool,
209
210 pub group: usize,
212
213 pub group_weight: usize,
215}
216
217impl From<worker_sys::IncomingRequestCfProperties> for Cf {
218 fn from(inner: worker_sys::IncomingRequestCfProperties) -> Self {
219 Self { inner }
220 }
221}
222
223#[derive(Debug)]
225pub struct TlsClientAuth {
226 inner: worker_sys::TlsClientAuth,
227}
228
229impl TlsClientAuth {
230 pub fn cert_issuer_dn_legacy(&self) -> String {
231 self.inner.cert_issuer_dn_legacy().unwrap()
232 }
233
234 pub fn cert_issuer_dn(&self) -> String {
235 self.inner.cert_issuer_dn().unwrap()
236 }
237
238 pub fn cert_issuer_dn_rfc2253(&self) -> String {
239 self.inner.cert_issuer_dn_rfc2253().unwrap()
240 }
241
242 pub fn cert_subject_dn_legacy(&self) -> String {
243 self.inner.cert_subject_dn_legacy().unwrap()
244 }
245
246 pub fn cert_verified(&self) -> String {
247 self.inner.cert_verified().unwrap()
248 }
249
250 pub fn cert_not_after(&self) -> String {
251 self.inner.cert_not_after().unwrap()
252 }
253
254 pub fn cert_subject_dn(&self) -> String {
255 self.inner.cert_subject_dn().unwrap()
256 }
257
258 pub fn cert_fingerprint_sha1(&self) -> String {
259 self.inner.cert_fingerprint_sha1().unwrap()
260 }
261
262 pub fn cert_fingerprint_sha256(&self) -> String {
263 self.inner.cert_fingerprint_sha256().unwrap()
264 }
265
266 pub fn cert_not_before(&self) -> String {
267 self.inner.cert_not_before().unwrap()
268 }
269
270 pub fn cert_serial(&self) -> String {
271 self.inner.cert_serial().unwrap()
272 }
273
274 pub fn cert_presented(&self) -> String {
275 self.inner.cert_presented().unwrap()
276 }
277
278 pub fn cert_subject_dn_rfc2253(&self) -> String {
279 self.inner.cert_subject_dn_rfc2253().unwrap()
280 }
281}
282
283impl From<worker_sys::TlsClientAuth> for TlsClientAuth {
284 fn from(inner: worker_sys::TlsClientAuth) -> Self {
285 Self { inner }
286 }
287}
288
289#[derive(Clone, Debug)]
290pub struct CfResponseProperties(pub(crate) js_sys::Object);
291
292impl CfResponseProperties {
293 pub fn into_raw(self) -> js_sys::Object {
294 self.0
295 }
296
297 pub fn try_into<T: DeserializeOwned>(self) -> crate::Result<T> {
298 Ok(serde_wasm_bindgen::from_value(self.0.unchecked_into())?)
299 }
300}
301
302unsafe impl Send for CfResponseProperties {}
303unsafe impl Sync for CfResponseProperties {}