pub use fastly_shared::CacheOverride;
use crate::abi::{self, MultiValueHostcallError};
use crate::backend::{Backend, BackendError};
use crate::body::{Body, BodyHandle, StreamingBody};
use crate::error::{ensure, BufferSizeError, Error};
use crate::response::{handles_to_response, FastlyResponseMetadata};
use http::header::HeaderValue;
use http::{Extensions, Request, Response};
use lazy_static::lazy_static;
use std::convert::TryInto;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use thiserror::Error;
pub use handle::{downstream_request_and_body_handles, RequestHandle};
pub use pending::{
select, select_handles, PendingRequest, PendingRequestHandle, PollHandleResult, PollResult,
};
#[macro_use]
mod macros;
mod handle;
mod pending;
pub fn downstream_request() -> Request<Body> {
let (req_handle, body_handle) = downstream_request_and_body_handles();
let mut req = Request::builder()
.version(req_handle.get_version())
.method(
req_handle
.get_method(crate::METHOD_MAX_LEN)
.expect("downstream request HTTP method too large"),
)
.uri(
req_handle
.get_uri(crate::URI_MAX_LEN)
.expect("downstream request URI too large"),
);
for name in req_handle.get_header_names(crate::HEADER_NAME_MAX_LEN) {
let name = name.expect("request header names too large");
for value in req_handle.get_header_values(&name, crate::HEADER_VALUE_MAX_LEN) {
let value = value.expect("downstream request header values too large");
req = req.header(&name, value);
}
}
req.body(body_handle.into())
.expect("downstream request must be valid")
}
pub fn downstream_client_ip_addr() -> Option<IpAddr> {
let mut octets = [0; 16];
let mut nwritten = 0;
let status = unsafe {
abi::fastly_http_req::downstream_client_ip_addr(octets.as_mut_ptr(), &mut nwritten)
};
if status.is_err() {
panic!("downstream_client_ip_addr failed");
}
match nwritten {
0 => None,
4 => {
let octets: [u8; 4] = octets[0..4]
.try_into()
.expect("octets is at least 4 bytes long");
let addr: Ipv4Addr = octets.into();
Some(addr.into())
}
16 => {
let addr: Ipv6Addr = octets.into();
Some(addr.into())
}
_ => panic!("downstream_client_ip_addr wrote an unexpected number of bytes"),
}
}
pub fn downstream_tls_client_hello() -> Result<Vec<u8>, Error> {
let mut ch_size = 0;
let status = unsafe {
abi::fastly_http_req::downstream_tls_client_hello(std::ptr::null_mut(), 0, &mut ch_size)
};
if status.is_err() && ch_size == 0 {
panic!("couldn't get the downstream TLS client hello");
}
let mut buf = vec![0; ch_size];
let status = unsafe {
abi::fastly_http_req::downstream_tls_client_hello(buf.as_mut_ptr(), buf.len(), &mut ch_size)
};
if status.is_err() {
panic!("couldn't get the downstream TLS cipher protocol");
}
Ok(buf)
}
pub fn downstream_tls_cipher_openssl_name() -> &'static str {
lazy_static! {
static ref OPENSSL_NAME: String = {
let mut buf = vec![0; 128];
let mut nwritten = 0;
let status = unsafe {
abi::fastly_http_req::downstream_tls_cipher_openssl_name(
buf.as_mut_ptr(),
buf.len(),
&mut nwritten,
)
};
if status.is_err() {
panic!("couldn't get the downstream TLS cipher's OpenSSL name");
}
buf.truncate(nwritten);
String::from_utf8(buf).expect("TLS cipher OpenSSL name must be valid UTF-8")
};
}
OPENSSL_NAME.as_str()
}
pub fn downstream_tls_protocol() -> &'static str {
lazy_static! {
static ref PROTOCOL: String = {
let mut buf = vec![0; 32];
let mut nwritten = 0;
let status = unsafe {
abi::fastly_http_req::downstream_tls_protocol(
buf.as_mut_ptr(),
buf.len(),
&mut nwritten,
)
};
if status.is_err() {
panic!("couldn't get the downstream TLS cipher protocol");
}
buf.truncate(nwritten);
String::from_utf8(buf).expect("TLS protocol must be valid UTF-8")
};
}
PROTOCOL.as_str()
}
pub fn downstream_original_header_names_with_len(
buf_size: usize,
) -> impl Iterator<Item = Result<String, BufferSizeError>> {
abi::MultiValueHostcall::new(
b'\0',
buf_size,
move |buf, buf_size, cursor, ending_cursor, nwritten| unsafe {
abi::fastly_http_req::original_header_names_get(
buf,
buf_size,
cursor,
ending_cursor,
nwritten,
)
},
)
.map(move |res| {
use MultiValueHostcallError::{BufferTooSmall, ClosureError};
match res {
Ok(name_bytes) => Ok(String::from_utf8(name_bytes.to_vec()).unwrap()),
Err(BufferTooSmall) => Err(BufferSizeError::new(buf_size)),
Err(ClosureError(e)) => {
panic!("fastly_http_req::header_values_get returned error: {:?}", e)
}
}
})
}
pub fn downstream_original_header_names() -> impl Iterator<Item = Result<String, BufferSizeError>> {
downstream_original_header_names_with_len(crate::HEADER_NAME_MAX_LEN)
}
pub fn downstream_original_header_count() -> u32 {
let mut count = 0;
let status = unsafe { abi::fastly_http_req::original_header_count(&mut count) };
if status.is_err() || count == 0 {
panic!("downstream_original_header_count failed")
}
count
}
#[derive(Debug, Error)]
#[error("error sending request: {error} to backend {backend}")]
pub struct SendError {
backend: String,
sent_req: Request<()>,
#[source]
error: Error,
}
impl SendError {
fn new<T>(backend: impl Into<String>, req: Request<T>, error: impl Into<Error>) -> Self {
SendError {
backend: backend.into(),
sent_req: req.map(|_| ()),
error: error.into(),
}
}
fn from_resp_metadata(mut metadata: FastlyResponseMetadata, error: impl Into<Error>) -> Self {
let sent_req = metadata.take_sent_req().expect("sent_req must be present");
let backend_name = metadata.backend().expect("backend must be present").name();
Self::new(backend_name, sent_req, error)
}
fn from_pending_req(pending_req: PendingRequest, error: impl Into<Error>) -> Self {
Self::from_resp_metadata(pending_req.metadata, error)
}
pub fn backend_name(&self) -> &str {
self.backend.as_str()
}
pub fn into_sent_req(self) -> Request<()> {
self.sent_req
}
}
pub trait RequestExt: Sized {
fn send(self, backend: &str) -> Result<Response<Body>, SendError> {
self.inner_to_body().send(backend)
}
fn send_async(self, backend: &str) -> Result<PendingRequest, SendError> {
self.inner_to_body().send_async(backend)
}
fn send_async_streaming(
self,
backend: &str,
) -> Result<(StreamingBody, PendingRequest), SendError> {
self.inner_to_body().send_async_streaming(backend)
}
fn inner_to_body(self) -> Request<Body>;
fn inner_to_bytes(self) -> Result<Request<Vec<u8>>, Error>;
fn fastly_metadata(&self) -> &FastlyRequestMetadata;
fn fastly_metadata_mut(&mut self) -> &mut FastlyRequestMetadata;
fn cache_override_mut(&mut self) -> &mut CacheOverride {
&mut self.fastly_metadata_mut().cache_override
}
fn cache_override(&self) -> &CacheOverride {
&self.fastly_metadata().cache_override
}
fn set_pass(&mut self) {
self.cache_override_mut().set_pass();
}
fn set_ttl(&mut self, ttl: u32) {
self.cache_override_mut().set_ttl(ttl);
}
fn set_stale_while_revalidate(&mut self, swr: u32) {
self.cache_override_mut().set_stale_while_revalidate(swr);
}
fn set_pci(&mut self, pci: bool) {
self.cache_override_mut().set_pci(pci);
}
fn set_surrogate_key(&mut self, surrogate_key: HeaderValue) {
self.cache_override_mut().set_surrogate_key(surrogate_key);
}
}
fn prepare_handles(
backend: &str,
req: Request<Body>,
) -> Result<(RequestHandle, BodyHandle, Request<()>), SendError> {
if let Err(e) = validate_request(&req) {
return Err(SendError::new(backend, req, e));
}
let req_handle = {
let mut req_handle = RequestHandle::new();
req_handle.set_version(req.version());
req_handle.set_method(req.method());
req_handle.set_uri(req.uri());
req_handle.set_cache_override(req.cache_override());
for name in req.headers().keys() {
req_handle.set_header_values(name, req.headers().get_all(name));
}
req_handle
};
let (body_handle, sent_req) = {
let (parts, body) = req.into_parts();
let body_handle = body.into_handle();
let sent_req = Request::from_parts(parts, ());
(body_handle, sent_req)
};
Ok((req_handle, body_handle, sent_req))
}
fn validate_request(req: &Request<Body>) -> Result<(), Error> {
ensure!(
req.uri().scheme().is_some() && req.uri().authority().is_some(),
"request URIs must have a scheme (http/https) and an authority (host)"
);
Ok(())
}
fn get_or_default_metadata(exts: &Extensions) -> &FastlyRequestMetadata {
static DEFAULT: FastlyRequestMetadata = FastlyRequestMetadata::default();
if let Some(md) = exts.get::<FastlyRequestMetadata>() {
md
} else {
&DEFAULT
}
}
fn get_or_insert_metadata(exts: &mut Extensions) -> &mut FastlyRequestMetadata {
if exts.get::<FastlyRequestMetadata>().is_none() {
exts.insert(FastlyRequestMetadata::default());
}
exts.get_mut::<FastlyRequestMetadata>().unwrap()
}
impl RequestExt for Request<Body> {
fn send(self, backend: &str) -> Result<Response<Body>, SendError> {
let backend = try_with_req!(backend, self, backend.parse::<Backend>());
let (req_handle, req_body_handle, sent_req) = prepare_handles(backend.name(), self)?;
let (resp_handle, resp_body_handle) = try_with_req!(
backend.name(),
sent_req,
req_handle.send(req_body_handle, backend.name())
);
Ok(handles_to_response(
resp_handle,
resp_body_handle,
FastlyResponseMetadata::new(backend, sent_req),
))
}
fn send_async(self, backend: &str) -> Result<PendingRequest, SendError> {
let backend = try_with_req!(backend, self, backend.parse::<Backend>());
let (req_handle, req_body_handle, sent_req) = prepare_handles(backend.name(), self)?;
let pending_req_handle = try_with_req!(
backend.name(),
sent_req,
req_handle.send_async(req_body_handle, backend.name())
);
let pending_req = PendingRequest::new(
pending_req_handle,
FastlyResponseMetadata::new(backend, sent_req),
);
Ok(pending_req)
}
fn send_async_streaming(
self,
backend: &str,
) -> Result<(StreamingBody, PendingRequest), SendError> {
let backend = try_with_req!(backend, self, backend.parse::<Backend>());
let (req_handle, req_body_handle, sent_req) = prepare_handles(backend.name(), self)?;
let (streaming_body_handle, pending_req_handle) = try_with_req!(
backend.name(),
sent_req,
req_handle.send_async_streaming(req_body_handle, backend.name())
);
let pending_req = PendingRequest::new(
pending_req_handle,
FastlyResponseMetadata::new(backend, sent_req),
);
Ok((streaming_body_handle.into(), pending_req))
}
fn inner_to_body(self) -> Request<Body> {
self
}
fn inner_to_bytes(self) -> Result<Request<Vec<u8>>, Error> {
let (parts, body) = self.into_parts();
Ok(Request::from_parts(parts, body.into_bytes()))
}
fn fastly_metadata(&self) -> &FastlyRequestMetadata {
get_or_default_metadata(self.extensions())
}
fn fastly_metadata_mut(&mut self) -> &mut FastlyRequestMetadata {
get_or_insert_metadata(self.extensions_mut())
}
}
impl RequestExt for Request<&[u8]> {
fn inner_to_body(self) -> Request<Body> {
self.map(Body::from)
}
fn inner_to_bytes(self) -> Result<Request<Vec<u8>>, Error> {
Ok(self.map(|b| b.to_vec()))
}
fn fastly_metadata(&self) -> &FastlyRequestMetadata {
get_or_default_metadata(self.extensions())
}
fn fastly_metadata_mut(&mut self) -> &mut FastlyRequestMetadata {
get_or_insert_metadata(self.extensions_mut())
}
}
impl RequestExt for Request<Vec<u8>> {
fn inner_to_body(self) -> Request<Body> {
self.map(Body::from)
}
fn inner_to_bytes(self) -> Result<Request<Vec<u8>>, Error> {
Ok(self)
}
fn fastly_metadata(&self) -> &FastlyRequestMetadata {
get_or_default_metadata(self.extensions())
}
fn fastly_metadata_mut(&mut self) -> &mut FastlyRequestMetadata {
get_or_insert_metadata(self.extensions_mut())
}
}
impl RequestExt for Request<&str> {
fn inner_to_body(self) -> Request<Body> {
self.map(Body::from)
}
fn inner_to_bytes(self) -> Result<Request<Vec<u8>>, Error> {
Ok(self.map(|b| b.as_bytes().to_vec()))
}
fn fastly_metadata(&self) -> &FastlyRequestMetadata {
get_or_default_metadata(self.extensions())
}
fn fastly_metadata_mut(&mut self) -> &mut FastlyRequestMetadata {
get_or_insert_metadata(self.extensions_mut())
}
}
impl RequestExt for Request<String> {
fn inner_to_body(self) -> Request<Body> {
self.map(Body::from)
}
fn inner_to_bytes(self) -> Result<Request<Vec<u8>>, Error> {
Ok(self.map(|b| b.into_bytes()))
}
fn fastly_metadata(&self) -> &FastlyRequestMetadata {
get_or_default_metadata(self.extensions())
}
fn fastly_metadata_mut(&mut self) -> &mut FastlyRequestMetadata {
get_or_insert_metadata(self.extensions_mut())
}
}
impl RequestExt for Request<()> {
fn inner_to_body(self) -> Request<Body> {
self.map(|_| Body::new())
}
fn inner_to_bytes(self) -> Result<Request<Vec<u8>>, Error> {
Ok(self.map(|_| vec![]))
}
fn fastly_metadata(&self) -> &FastlyRequestMetadata {
get_or_default_metadata(self.extensions())
}
fn fastly_metadata_mut(&mut self) -> &mut FastlyRequestMetadata {
get_or_insert_metadata(self.extensions_mut())
}
}
#[derive(Debug)]
pub struct FastlyRequestMetadata {
backend: Option<Backend>,
cache_override: CacheOverride,
}
impl FastlyRequestMetadata {
pub const fn default() -> Self {
Self {
backend: None,
cache_override: CacheOverride::default(),
}
}
pub fn backend(&self) -> &Option<Backend> {
&self.backend
}
pub fn backend_mut(&mut self) -> &mut Option<Backend> {
&mut self.backend
}
pub fn cache_override(&self) -> &CacheOverride {
&self.cache_override
}
pub fn cache_override_mut(&mut self) -> &mut CacheOverride {
&mut self.cache_override
}
}
pub trait RequestBuilderExt: Sized {
fn fastly_metadata_ref(&self) -> Option<&FastlyRequestMetadata>;
fn fastly_metadata_mut(&mut self) -> Option<&mut FastlyRequestMetadata>;
fn backend(mut self, backend: impl AsRef<str>) -> Result<Self, BackendError> {
let backend = backend.as_ref().parse()?;
self.fastly_metadata_mut()
.map(|md| md.backend = Some(backend));
Ok(self)
}
fn backend_ref(&self) -> Option<&Backend> {
self.fastly_metadata_ref()
.and_then(|md| md.backend.as_ref())
}
fn backend_mut(&mut self) -> Option<&mut Backend> {
self.fastly_metadata_mut()
.and_then(|md| md.backend.as_mut())
}
fn cache_override(mut self, cache_override: CacheOverride) -> Self {
self.fastly_metadata_mut()
.map(|md| md.cache_override = cache_override);
self
}
fn cache_override_ref(&self) -> Option<&CacheOverride> {
self.fastly_metadata_ref().map(|md| &md.cache_override)
}
fn cache_override_mut(&mut self) -> Option<&mut CacheOverride> {
self.fastly_metadata_mut().map(|md| &mut md.cache_override)
}
fn pass(mut self) -> Self {
self.cache_override_mut().map(|co| co.set_pass());
self
}
fn ttl(mut self, ttl: u32) -> Self {
self.cache_override_mut().map(|co| co.set_ttl(ttl));
self
}
fn stale_while_revalidate(mut self, swr: u32) -> Self {
self.cache_override_mut()
.map(|co| co.set_stale_while_revalidate(swr));
self
}
fn pci(mut self, pci: bool) -> Self {
self.cache_override_mut().map(|co| co.set_pci(pci));
self
}
fn surrogate_key(mut self, surrogate_key: HeaderValue) -> Self {
self.cache_override_mut()
.map(|co| co.set_surrogate_key(surrogate_key));
self
}
}
impl RequestBuilderExt for http::request::Builder {
fn fastly_metadata_ref(&self) -> Option<&FastlyRequestMetadata> {
self.extensions_ref().map(get_or_default_metadata)
}
fn fastly_metadata_mut(&mut self) -> Option<&mut FastlyRequestMetadata> {
self.extensions_mut().map(get_or_insert_metadata)
}
}