1#[deprecated(
3 since = "0.11.4",
4 note = "The shielding module is not longer experimental. Please use `fastly::shielding` instead."
5)]
6pub mod shielding;
7
8use crate::{
9 abi::{self, FastlyStatus},
10 convert::{ToHeaderName, ToHeaderValue},
11 error::BufferSizeError,
12 http::{
13 header::{HeaderName, HeaderValue},
14 request::{
15 handle::{redirect_to_grip_proxy, redirect_to_websocket_proxy, RequestHandle},
16 CacheKeyGen, Request, SendError, SendErrorCause,
17 },
18 response::assert_single_downstream_response_is_sent,
19 },
20 Backend, Error,
21};
22use anyhow::anyhow;
23use bytes::Bytes;
24use fastly_sys::fastly_backend;
25use http::header::HeaderMap;
26use sha2::{Digest, Sha256};
27use std::sync::Arc;
28
29#[doc(inline)]
30pub use fastly_sys::fastly_backend::BackendHealth;
31
32pub use crate::backend::{BackendBuilder, BackendCreationError};
33pub use crate::Response;
34
35#[doc = include_str!("../docs/snippets/experimental.md")]
37#[deprecated(since = "0.10.1")]
38pub fn uap_parse(
39 user_agent: &str,
40) -> Result<(String, Option<String>, Option<String>, Option<String>), Error> {
41 let user_agent: &[u8] = user_agent.as_ref();
42 let max_length = 255;
43 let mut family = Vec::with_capacity(max_length);
44 let mut major = Vec::with_capacity(max_length);
45 let mut minor = Vec::with_capacity(max_length);
46 let mut patch = Vec::with_capacity(max_length);
47 let mut family_nwritten = 0;
48 let mut major_nwritten = 0;
49 let mut minor_nwritten = 0;
50 let mut patch_nwritten = 0;
51
52 let status = unsafe {
53 abi::fastly_uap::parse(
54 user_agent.as_ptr(),
55 user_agent.len(),
56 family.as_mut_ptr(),
57 family.capacity(),
58 &mut family_nwritten,
59 major.as_mut_ptr(),
60 major.capacity(),
61 &mut major_nwritten,
62 minor.as_mut_ptr(),
63 minor.capacity(),
64 &mut minor_nwritten,
65 patch.as_mut_ptr(),
66 patch.capacity(),
67 &mut patch_nwritten,
68 )
69 };
70 if status.is_err() {
71 return Err(Error::msg("fastly_uap::parse failed"));
72 }
73 assert!(
74 family_nwritten <= family.capacity(),
75 "fastly_uap::parse wrote too many bytes for family"
76 );
77 unsafe {
78 family.set_len(family_nwritten);
79 }
80 assert!(
81 major_nwritten <= major.capacity(),
82 "fastly_uap::parse wrote too many bytes for major"
83 );
84 unsafe {
85 major.set_len(major_nwritten);
86 }
87 assert!(
88 minor_nwritten <= minor.capacity(),
89 "fastly_uap::parse wrote too many bytes for minor"
90 );
91 unsafe {
92 minor.set_len(minor_nwritten);
93 }
94 assert!(
95 patch_nwritten <= patch.capacity(),
96 "fastly_uap::parse wrote too many bytes for patch"
97 );
98 unsafe {
99 patch.set_len(patch_nwritten);
100 }
101 Ok((
102 String::from_utf8_lossy(&family).to_string(),
103 Some(String::from_utf8_lossy(&major).to_string()),
104 Some(String::from_utf8_lossy(&minor).to_string()),
105 Some(String::from_utf8_lossy(&patch).to_string()),
106 ))
107}
108
109#[doc = include_str!("../docs/snippets/experimental.md")]
111pub trait RequestCacheKey {
112 #[doc = include_str!("../docs/snippets/experimental.md")]
114 fn set_cache_key(&mut self, key: [u8; 32]);
115 #[doc = include_str!("../docs/snippets/experimental.md")]
117 fn with_cache_key(self, key: [u8; 32]) -> Self;
118 #[doc = include_str!("../docs/snippets/experimental.md")]
120 fn set_cache_key_fn(&mut self, f: impl Fn(&Request) -> [u8; 32] + Send + Sync + 'static);
121 #[doc = include_str!("../docs/snippets/experimental.md")]
123 fn with_cache_key_fn(self, f: impl Fn(&Request) -> [u8; 32] + Send + Sync + 'static) -> Self;
124 #[doc = include_str!("../docs/snippets/experimental.md")]
126 fn set_cache_key_str(&mut self, key_str: impl AsRef<[u8]>);
127 #[doc = include_str!("../docs/snippets/experimental.md")]
129 fn with_cache_key_str(self, key_str: impl AsRef<[u8]>) -> Self;
130}
131
132impl RequestCacheKey for Request {
133 #[doc = include_str!("../docs/snippets/experimental.md")]
135 fn set_cache_key(&mut self, key: [u8; 32]) {
136 self.metadata.override_cache_key = Some(CacheKeyGen::Set(Vec::from(key).into()));
137 }
138
139 #[doc = include_str!("../docs/snippets/experimental.md")]
141 fn with_cache_key(mut self, key: [u8; 32]) -> Self {
142 self.set_cache_key(key);
143 self
144 }
145
146 #[doc = include_str!("../docs/snippets/experimental.md")]
148 fn set_cache_key_fn(&mut self, f: impl Fn(&Request) -> [u8; 32] + Send + Sync + 'static) {
149 self.metadata.override_cache_key = Some(CacheKeyGen::Lazy(Arc::new(move |req| {
150 Bytes::from(Vec::from(f(req)))
151 })));
152 }
153
154 #[doc = include_str!("../docs/snippets/experimental.md")]
156 fn with_cache_key_fn(
157 mut self,
158 f: impl Fn(&Request) -> [u8; 32] + Send + Sync + 'static,
159 ) -> Self {
160 self.set_cache_key_fn(f);
161 self
162 }
163
164 #[doc = include_str!("../docs/snippets/experimental.md")]
170 fn set_cache_key_str(&mut self, key_str: impl AsRef<[u8]>) {
171 let mut sha = Sha256::new();
172 sha.update(key_str);
173 sha.update(b"\x00\xf0\x9f\xa7\x82\x00"); let finalized: [u8; 32] = *sha.finalize().as_ref();
175 self.set_cache_key(finalized)
176 }
177
178 #[doc = include_str!("../docs/snippets/experimental.md")]
180 fn with_cache_key_str(mut self, key_str: impl AsRef<[u8]>) -> Self {
181 self.set_cache_key_str(key_str);
182 self
183 }
184}
185
186#[doc = include_str!("../docs/snippets/experimental.md")]
189pub trait RequestHandleCacheKey {
190 #[doc = include_str!("../docs/snippets/experimental.md")]
192 fn set_cache_key(&mut self, key: impl AsRef<[u8]>);
193}
194
195impl RequestHandleCacheKey for RequestHandle {
196 #[doc = include_str!("../docs/snippets/experimental.md")]
198 fn set_cache_key(&mut self, key: impl AsRef<[u8]>) {
199 let key = key.as_ref();
200 const DIGITS: &[u8; 16] = b"0123456789ABCDEF";
201 let mut hex = Vec::with_capacity(key.len() * 2);
202 for b in key.iter() {
203 hex.push(DIGITS[(b >> 4) as usize]);
204 hex.push(DIGITS[(b & 0xf) as usize]);
205 }
206
207 self.insert_header(
208 &HeaderName::from_static("fastly-xqd-cache-key"),
209 &HeaderValue::from_bytes(&hex).unwrap(),
210 )
211 }
212}
213
214pub trait RequestUpgradeWebsocket {
216 #[deprecated(
218 since = "0.10.0",
219 note = "The RequestUpgradeWebsocket::handoff_websocket() trait method is now part of Request."
220 )]
221 fn handoff_websocket(self, backend: &str) -> Result<(), SendError>;
222
223 #[deprecated(
225 since = "0.10.0",
226 note = "The RequestUpgradeWebsocket::handoff_fanout() trait method is now part of Request."
227 )]
228 fn handoff_fanout(self, backend: &str) -> Result<(), SendError>;
229}
230impl RequestUpgradeWebsocket for Request {
231 fn handoff_websocket(self, backend: &str) -> Result<(), SendError> {
240 assert_single_downstream_response_is_sent(true);
241 let cloned = self.clone_without_body();
242 let (req_handle, _) = self.into_handles();
243 let status = redirect_to_websocket_proxy(req_handle, backend);
244 if status.is_err() {
245 Err(SendError::new(
246 backend,
247 cloned,
248 SendErrorCause::status(status),
249 ))
250 } else {
251 Ok(())
252 }
253 }
254
255 fn handoff_fanout(self, backend: &str) -> Result<(), SendError> {
263 assert_single_downstream_response_is_sent(true);
264 let cloned = self.clone_without_body();
265 let (req_handle, _) = self.into_handles();
266 let status = redirect_to_grip_proxy(req_handle, backend);
267 if status.is_err() {
268 Err(SendError::new(
269 backend,
270 cloned,
271 SendErrorCause::status(status),
272 ))
273 } else {
274 Ok(())
275 }
276 }
277}
278
279pub trait RequestHandleUpgradeWebsocket {
282 #[deprecated(
284 since = "0.10.0",
285 note = "The RequestHandleUpgradeWebsocket::handoff_websocket() trait method is now part of RequestHandle."
286 )]
287 fn handoff_websocket(&mut self, backend: &str) -> Result<(), SendErrorCause>;
288
289 #[deprecated(
291 since = "0.10.0",
292 note = "The RequestHandleUpgradeWebsocket::handoff_fanout() trait method is now part of RequestHandle."
293 )]
294 fn handoff_fanout(&mut self, backend: &str) -> Result<(), SendErrorCause>;
295}
296
297impl RequestHandleUpgradeWebsocket for RequestHandle {
298 fn handoff_websocket(&mut self, backend: &str) -> Result<(), SendErrorCause> {
307 match unsafe {
308 abi::fastly_http_req::redirect_to_websocket_proxy_v2(
309 self.as_u32(),
310 backend.as_ptr(),
311 backend.len(),
312 )
313 } {
314 FastlyStatus::OK => Ok(()),
315 status => Err(SendErrorCause::status(status)),
316 }
317 }
318
319 fn handoff_fanout(&mut self, backend: &str) -> Result<(), SendErrorCause> {
327 match unsafe {
328 abi::fastly_http_req::redirect_to_grip_proxy_v2(
329 self.as_u32(),
330 backend.as_ptr(),
331 backend.len(),
332 )
333 } {
334 FastlyStatus::OK => Ok(()),
335 status => Err(SendErrorCause::status(status)),
336 }
337 }
338}
339
340pub trait BackendExt {
342 fn is_healthy(&self) -> Result<BackendHealth, Error>;
346}
347
348impl BackendExt for Backend {
349 fn is_healthy(&self) -> Result<BackendHealth, Error> {
350 let mut backend_health_out = BackendHealth::Unknown;
351 unsafe {
352 fastly_backend::is_healthy(
353 self.name().as_ptr(),
354 self.name().len(),
355 &mut backend_health_out,
356 )
357 }
358 .result()
359 .map_err(|e| match e {
360 FastlyStatus::NONE => anyhow!("backend not found"),
361 _ => anyhow!("backend healthcheck error: {:?}", e),
362 })?;
363 Ok(backend_health_out)
364 }
365}
366
367pub trait GrpcBackend {
369 #[doc = include_str!("../docs/snippets/experimental.md")]
375 fn for_grpc(self, value: bool) -> Self;
376}
377
378#[derive(thiserror::Error, Debug)]
380pub enum BodyHandleError {
381 #[error("trailers not yet ready")]
383 TrailersNotReady,
384}
385
386pub trait BodyHandleExt {
388 #[doc = include_str!("../docs/snippets/trailers.md")]
395 #[doc = include_str!("../docs/snippets/experimental.md")]
396 fn get_trailer_names<'a>(
397 &'a self,
398 buf_size: usize,
399 ) -> Result<Box<dyn Iterator<Item = Result<HeaderName, BufferSizeError>> + 'a>, BodyHandleError>;
400
401 #[doc = include_str!("../docs/snippets/trailers.md")]
417 #[doc = include_str!("../docs/snippets/experimental.md")]
418 fn get_trailer_value(
419 &self,
420 name: &HeaderName,
421 max_len: usize,
422 ) -> Result<Result<Option<HeaderValue>, BufferSizeError>, BodyHandleError>;
423
424 #[doc = include_str!("../docs/snippets/trailers.md")]
429 #[doc = include_str!("../docs/snippets/experimental.md")]
430 fn get_trailer_values<'a>(
431 &'a self,
432 name: &'a HeaderName,
433 max_len: usize,
434 ) -> Result<Box<dyn Iterator<Item = Result<HeaderValue, BufferSizeError>> + 'a>, BodyHandleError>;
435}
436
437pub trait BodyExt {
439 #[doc = include_str!("../docs/snippets/experimental.md")]
445 fn append_trailer(&mut self, name: impl ToHeaderName, value: impl ToHeaderValue);
446
447 #[doc = include_str!("../docs/snippets/trailers.md")]
456 #[doc = include_str!("../docs/snippets/experimental.md")]
457 fn get_trailers(&mut self) -> Result<HeaderMap, Error>;
458}
459
460pub trait StreamingBodyExt {
462 #[doc = include_str!("../docs/snippets/experimental.md")]
468 fn append_trailer(&mut self, name: impl ToHeaderName, value: impl ToHeaderValue);
469
470 #[doc = include_str!("../docs/snippets/trailers.md")]
479 #[doc = include_str!("../docs/snippets/experimental.md")]
480 fn finish_with_trailers(self, trailers: &HeaderMap) -> Result<(), std::io::Error>;
481}