use self::handle::RequestHandle;
use super::body::{self, Body, StreamingBody};
use super::response::{handles_to_response, FastlyResponseMetadata, Response};
use crate::backend::Backend;
use crate::convert::{Borrowable, ToBackend, ToHeaderName, ToHeaderValue, ToMethod, ToUrl};
use crate::error::{ensure, BufferSizeError, Error};
use crate::handle::BodyHandle;
use crate::limits::{self, RequestLimits};
use fastly_shared::CacheOverride;
use http::header::{HeaderName, HeaderValue};
use http::{HeaderMap, Method, Version};
use mime::Mime;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::io::BufRead;
use std::net::IpAddr;
use thiserror::Error;
use url::Url;
pub use pending::{select, PendingRequest, PollResult};
#[macro_use]
mod macros;
pub(crate) mod handle;
pub(crate) mod pending;
#[derive(Debug)]
pub struct Request {
version: Version,
method: Method,
url: Url,
headers: HeaderMap,
body: Option<Body>,
cache_override: CacheOverride,
is_from_client: bool,
}
impl Request {
pub fn from_client() -> Request {
Request::try_from_client()
.unwrap_or_else(|_| panic_with_status!(crate::http::StatusCode::BAD_REQUEST))
}
pub fn try_from_client() -> Result<Request, BufferSizeError> {
let (req_handle, body_handle) = self::handle::client_request_and_body();
Request::from_handles(req_handle, Some(body_handle))
}
pub fn is_from_client(&self) -> bool {
self.is_from_client
}
pub fn new(method: impl ToMethod, url: impl ToUrl) -> Self {
Self {
version: Version::HTTP_11,
method: method.into_owned(),
url: url.into_owned(),
headers: HeaderMap::new(),
body: None,
cache_override: CacheOverride::default(),
is_from_client: false,
}
}
pub fn clone_without_body(&self) -> Request {
Self {
version: self.version,
method: self.method.clone(),
url: self.url.clone(),
headers: self.headers.clone(),
body: None,
cache_override: self.cache_override.clone(),
is_from_client: self.is_from_client,
}
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/clones-body.md")
)]
pub fn clone_with_body(&mut self) -> Request {
let mut new_req = self.clone_without_body();
if self.has_body() {
for chunk in self.take_body().read_chunks(4096) {
let chunk = chunk.expect("can read body chunk");
new_req.get_body_mut().write_bytes(&chunk);
self.get_body_mut().write_bytes(&chunk);
}
}
new_req
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/url-argument.md")
)]
pub fn get(url: impl ToUrl) -> Self {
Self::new(Method::GET, url)
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/url-argument.md")
)]
pub fn head(url: impl ToUrl) -> Self {
Self::new(Method::HEAD, url)
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/url-argument.md")
)]
pub fn post(url: impl ToUrl) -> Self {
Self::new(Method::POST, url)
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/url-argument.md")
)]
pub fn put(url: impl ToUrl) -> Self {
Self::new(Method::PUT, url)
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/url-argument.md")
)]
pub fn delete(url: impl ToUrl) -> Self {
Self::new(Method::DELETE, url)
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/url-argument.md")
)]
pub fn connect(url: impl ToUrl) -> Self {
Self::new(Method::CONNECT, url)
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/url-argument.md")
)]
pub fn options(url: impl ToUrl) -> Self {
Self::new(Method::OPTIONS, url)
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/url-argument.md")
)]
pub fn trace(url: impl ToUrl) -> Self {
Self::new(Method::TRACE, url)
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/url-argument.md")
)]
pub fn patch(url: impl ToUrl) -> Self {
Self::new(Method::PATCH, url)
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/backend-argument.md")
)]
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/panics-responselimits.md")
)]
pub fn send(self, backend: impl ToBackend) -> Result<Response, SendError> {
let backend = backend.into_owned();
let (req_handle, req_body_handle, sent_req) = self.prepare_handles(&backend)?;
let (resp_handle, resp_body_handle) = try_with_req!(
backend.name(),
sent_req,
req_handle.send(req_body_handle, backend.name())
);
handles_to_response(
resp_handle,
resp_body_handle,
FastlyResponseMetadata::new(backend, sent_req),
)
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/backend-argument.md")
)]
pub fn send_async(self, backend: impl ToBackend) -> Result<PendingRequest, SendError> {
let backend = backend.into_owned();
let (req_handle, req_body_handle, sent_req) = self.prepare_handles(&backend)?;
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)
}
pub fn send_async_streaming(
self,
backend: impl ToBackend,
) -> Result<(StreamingBody, PendingRequest), SendError> {
let backend = backend.into_owned();
let (req_handle, req_body_handle, sent_req) = self.prepare_handles(&backend)?;
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 prepare_handles(
mut self,
backend: impl ToBackend,
) -> Result<(RequestHandle, BodyHandle, Self), SendError> {
if let Err(e) = validate_request(&self) {
return Err(SendError::new(
backend.into_borrowable().as_ref().name(),
self,
e,
));
}
let (req_handle, body_handle) = self.to_handles();
Ok((
req_handle,
body_handle.unwrap_or_else(|| BodyHandle::new()),
self,
))
}
pub fn with_body(mut self, body: impl Into<Body>) -> Self {
self.set_body(body);
self
}
pub fn has_body(&self) -> bool {
self.body.is_some()
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/creates-empty-body.md")
)]
pub fn get_body_mut(&mut self) -> &mut Body {
self.body.get_or_insert_with(|| Body::new())
}
pub fn try_get_body_mut(&mut self) -> Option<&mut Body> {
self.body.as_mut()
}
pub fn get_body_prefix_mut(&mut self, length: usize) -> body::Prefix {
self.get_body_mut().get_prefix_mut(length)
}
pub fn get_body_prefix_str_mut(&mut self, length: usize) -> body::PrefixString {
self.get_body_mut().get_prefix_str_mut(length)
}
pub fn try_get_body_prefix_str_mut(
&mut self,
length: usize,
) -> Result<body::PrefixString, std::str::Utf8Error> {
self.get_body_mut().try_get_prefix_str_mut(length)
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/body-argument.md")
)]
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/discards-body.md")
)]
pub fn set_body(&mut self, body: impl Into<Body>) {
self.body = Some(body.into());
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/creates-empty-body.md")
)]
pub fn take_body(&mut self) -> Body {
self.body.take().unwrap_or_else(|| Body::new())
}
pub fn try_take_body(&mut self) -> Option<Body> {
self.body.take()
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/body-append-constant-time.md")
)]
pub fn append_body(&mut self, other: Body) {
if let Some(ref mut body) = &mut self.body {
body.append(other);
} else {
self.body = Some(other);
}
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/buffers-body-reqresp.md")
)]
pub fn into_body_bytes(mut self) -> Vec<u8> {
self.take_body_bytes()
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/buffers-body-reqresp.md")
)]
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/panics-reqresp-intobody-utf8.md")
)]
pub fn into_body_str(mut self) -> String {
self.take_body_str()
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/creates-empty-body.md")
)]
pub fn into_body(self) -> Body {
self.body.unwrap_or_else(|| Body::new())
}
pub fn try_into_body(self) -> Option<Body> {
self.body
}
pub fn with_body_str(mut self, body: &str) -> Self {
self.set_body_str(body);
self
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/discards-body.md")
)]
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/sets-text-plain.md")
)]
pub fn set_body_str(&mut self, body: &str) {
self.body = Some(Body::from(body));
self.set_content_type(mime::TEXT_PLAIN_UTF_8);
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/buffers-body-reqresp.md")
)]
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/panics-reqresp-takebody-utf8.md")
)]
pub fn take_body_str(&mut self) -> String {
if let Some(body) = self.try_take_body() {
body.into_string()
} else {
String::new()
}
}
pub fn read_body_lines(&mut self) -> std::io::Lines<&mut Body> {
self.get_body_mut().lines()
}
pub fn with_body_bytes(mut self, body: &[u8]) -> Self {
self.set_body_bytes(body);
self
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/discards-body.md")
)]
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/sets-app-octet-stream.md")
)]
pub fn set_body_bytes(&mut self, body: &[u8]) {
self.body = Some(Body::from(body));
self.set_content_type(mime::APPLICATION_OCTET_STREAM);
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/buffers-body-reqresp.md")
)]
pub fn take_body_bytes(&mut self) -> Vec<u8> {
if let Some(body) = self.try_take_body() {
body.into_bytes()
} else {
Vec::new()
}
}
pub fn read_body_chunks<'a>(
&'a mut self,
chunk_size: usize,
) -> impl Iterator<Item = Result<Vec<u8>, std::io::Error>> + 'a {
self.get_body_mut().read_chunks(chunk_size)
}
pub fn with_body_json(mut self, value: &impl Serialize) -> Result<Self, serde_json::Error> {
self.set_body_json(value)?;
Ok(self)
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/discards-body.md")
)]
pub fn set_body_json(&mut self, value: &impl Serialize) -> Result<(), serde_json::Error> {
self.body = Some(Body::new());
serde_json::to_writer(self.get_body_mut(), value)?;
self.set_content_type(mime::APPLICATION_JSON);
Ok(())
}
pub fn take_body_json<T: DeserializeOwned>(&mut self) -> Result<T, serde_json::Error> {
if let Some(body) = self.try_take_body() {
serde_json::from_reader(body)
} else {
serde_json::from_reader(std::io::empty())
}
}
pub fn with_body_form(
mut self,
value: &impl Serialize,
) -> Result<Self, serde_urlencoded::ser::Error> {
self.set_body_form(value)?;
Ok(self)
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/discards-body.md")
)]
pub fn set_body_form(
&mut self,
value: &impl Serialize,
) -> Result<(), serde_urlencoded::ser::Error> {
self.body = Some(Body::new());
let s = serde_urlencoded::to_string(value)?;
self.set_body(s);
self.set_content_type(mime::APPLICATION_WWW_FORM_URLENCODED);
Ok(())
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/returns-deserializeowned.md")
)]
pub fn take_body_form<T: DeserializeOwned>(
&mut self,
) -> Result<T, serde_urlencoded::de::Error> {
if let Some(body) = self.try_take_body() {
serde_urlencoded::from_reader(body)
} else {
serde_urlencoded::from_reader(std::io::empty())
}
}
pub fn get_content_type(&self) -> Option<Mime> {
self.get_header_str(http::header::CONTENT_TYPE).map(|v| {
v.parse()
.unwrap_or_else(|_| panic!("invalid MIME type in Content-Type header: {}", v))
})
}
pub fn with_content_type(mut self, mime: Mime) -> Self {
self.set_content_type(mime);
self
}
pub fn set_content_type(&mut self, mime: Mime) {
self.set_header(http::header::CONTENT_TYPE, mime.as_ref())
}
pub fn get_content_length(&self) -> Option<usize> {
self.get_header(http::header::CONTENT_LENGTH)
.and_then(|v| v.to_str().ok())
.and_then(|v| v.parse().ok())
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/header-name-argument.md")
)]
pub fn contains_header(&self, name: impl ToHeaderName) -> bool {
self.headers.contains_key(name.into_borrowable().as_ref())
}
pub fn with_header(mut self, name: impl ToHeaderName, value: impl ToHeaderValue) -> Self {
self.set_header(name, value);
self
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/header-name-argument.md")
)]
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/panics-reqresp-header-utf8.md")
)]
pub fn get_header_str(&self, name: impl ToHeaderName) -> Option<&str> {
let name = name.into_borrowable();
if let Some(hdr) = self.get_header(name.as_ref()) {
Some(
hdr.to_str()
.unwrap_or_else(|_| panic!("non-UTF-8 HTTP header value for header: {}", name)),
)
} else {
None
}
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/header-name-argument.md")
)]
pub fn get_header(&self, name: impl ToHeaderName) -> Option<&HeaderValue> {
self.headers.get(name.into_borrowable().as_ref())
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/header-name-argument.md")
)]
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/panics-reqresp-headers-utf8.md")
)]
pub fn get_header_all_str(&self, name: impl ToHeaderName) -> Vec<&str> {
let name = name.into_borrowable();
self.get_header_all(name.as_ref())
.map(|v| {
v.to_str()
.unwrap_or_else(|_| panic!("non-UTF-8 HTTP header value for header: {}", name))
})
.collect()
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/header-name-argument.md")
)]
pub fn get_header_all(&self, name: impl ToHeaderName) -> impl Iterator<Item = &HeaderValue> {
self.headers.get_all(name.into_borrowable().as_ref()).iter()
}
pub fn get_header_names_str(&self) -> Vec<&str> {
self.get_header_names().map(|n| n.as_str()).collect()
}
pub fn get_header_names(&self) -> impl Iterator<Item = &HeaderName> {
self.headers.keys()
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/header-name-value-argument.md")
)]
pub fn set_header(&mut self, name: impl ToHeaderName, value: impl ToHeaderValue) {
self.headers.insert(name.into_owned(), value.into_owned());
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/header-name-value-argument.md")
)]
pub fn append_header(&mut self, name: impl ToHeaderName, value: impl ToHeaderValue) {
self.headers.append(name.into_owned(), value.into_owned());
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/removes-one-header.md")
)]
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/header-name-argument.md")
)]
pub fn remove_header(&mut self, name: impl ToHeaderName) -> Option<HeaderValue> {
self.headers.remove(name.into_borrowable().as_ref())
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/removes-one-header.md")
)]
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/header-name-argument.md")
)]
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/panics-reqresp-remove-header-utf8.md")
)]
pub fn remove_header_str(&mut self, name: impl ToHeaderName) -> Option<String> {
let name = name.into_borrowable();
if let Some(hdr) = self.remove_header(name.as_ref()) {
Some(
hdr.to_str()
.map(|s| s.to_owned())
.unwrap_or_else(|_| panic!("non-UTF-8 HTTP header value for header: {}", name)),
)
} else {
None
}
}
pub fn with_method(mut self, method: impl ToMethod) -> Self {
self.set_method(method);
self
}
pub fn get_method_str(&self) -> &str {
self.get_method().as_str()
}
pub fn get_method(&self) -> &Method {
&self.method
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/method-argument.md")
)]
pub fn set_method<'a>(&mut self, method: impl ToMethod) {
self.method = method.into_owned();
}
pub fn with_url(mut self, url: impl ToUrl) -> Self {
self.set_url(url);
self
}
pub fn get_url_str(&self) -> &str {
self.get_url().as_str()
}
pub fn get_url(&self) -> &Url {
&self.url
}
pub fn get_url_mut(&mut self) -> &mut Url {
&mut self.url
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/url-argument.md")
)]
pub fn set_url(&mut self, url: impl ToUrl) {
self.url = url.into_owned();
}
pub fn get_path(&self) -> &str {
self.get_url().path()
}
pub fn with_path(mut self, path: &str) -> Self {
self.set_path(path);
self
}
pub fn set_path(&mut self, path: &str) {
self.get_url_mut().set_path(path);
}
pub fn get_query_str(&self) -> Option<&str> {
self.get_url().query()
}
#[cfg_attr(
feature = "unstable-doc",
doc(include = "../../docs/snippets/returns-deserializeowned.md")
)]
pub fn get_query<T: DeserializeOwned>(&self) -> Result<T, serde_urlencoded::de::Error> {
serde_urlencoded::from_str(self.url.query().unwrap_or(""))
}
pub fn with_query_str(mut self, query: impl AsRef<str>) -> Self {
self.set_query_str(query);
self
}
pub fn set_query_str(&mut self, query: impl AsRef<str>) {
self.get_url_mut().set_query(Some(query.as_ref()))
}
pub fn with_query(
mut self,
query: &impl Serialize,
) -> Result<Self, serde_urlencoded::ser::Error> {
self.set_query(query)?;
Ok(self)
}
pub fn set_query(
&mut self,
query: &impl Serialize,
) -> Result<(), serde_urlencoded::ser::Error> {
let s = serde_urlencoded::to_string(query)?;
self.get_url_mut().set_query(Some(&s));
Ok(())
}
pub fn remove_query(&mut self) {
self.get_url_mut().set_query(None);
}
pub fn with_version(mut self, version: Version) -> Self {
self.set_version(version);
self
}
pub fn get_version(&self) -> Version {
self.version
}
pub fn set_version(&mut self, version: Version) {
self.version = version;
}
pub fn with_pass(mut self, pass: bool) -> Self {
self.set_pass(pass);
self
}
pub fn set_pass(&mut self, pass: bool) {
self.cache_override.set_pass(pass);
}
pub fn with_ttl(mut self, ttl: u32) -> Self {
self.set_ttl(ttl);
self
}
pub fn set_ttl(&mut self, ttl: u32) {
self.cache_override.set_ttl(ttl);
}
pub fn with_stale_while_revalidate(mut self, swr: u32) -> Self {
self.set_stale_while_revalidate(swr);
self
}
pub fn set_stale_while_revalidate(&mut self, swr: u32) {
self.cache_override.set_stale_while_revalidate(swr);
}
pub fn with_pci(mut self, pci: bool) -> Self {
self.set_pci(pci);
self
}
pub fn set_pci(&mut self, pci: bool) {
self.cache_override.set_pci(pci);
}
pub fn with_surrogate_key(mut self, sk: HeaderValue) -> Self {
self.set_surrogate_key(sk);
self
}
pub fn set_surrogate_key(&mut self, sk: HeaderValue) {
self.cache_override.set_surrogate_key(sk);
}
pub fn get_client_ip_addr(&self) -> Option<IpAddr> {
if !self.is_from_client() {
return None;
}
self::handle::client_ip_addr()
}
pub fn get_original_header_names(&self) -> Option<impl Iterator<Item = String>> {
if !self.is_from_client() {
return None;
} else {
Some(
self::handle::client_original_header_names_impl(
limits::INITIAL_HEADER_NAME_BUF_SIZE,
RequestLimits::get_max_header_name_bytes(),
)
.map(|res| res.expect("original request header name too large")),
)
}
}
pub fn get_original_header_count(&self) -> Option<u32> {
if !self.is_from_client() {
return None;
}
Some(self::handle::client_original_header_count())
}
pub fn get_tls_client_hello(&self) -> Option<&[u8]> {
if !self.is_from_client() {
return None;
}
self::handle::client_tls_client_hello()
}
pub fn get_tls_cipher_openssl_name(&self) -> Option<&'static str> {
if !self.is_from_client() {
return None;
}
self::handle::client_tls_cipher_openssl_name()
}
pub fn get_tls_protocol(&self) -> Option<&'static str> {
if !self.is_from_client() {
return None;
}
self::handle::client_tls_protocol()
}
pub fn from_handles(
req_handle: RequestHandle,
body_handle: Option<BodyHandle>,
) -> Result<Self, BufferSizeError> {
let req_limits = limits::REQUEST_LIMITS.read().unwrap();
let method = req_handle
.get_method_impl(limits::INITIAL_METHOD_BUF_SIZE, req_limits.max_method_bytes)?;
let url =
req_handle.get_url_impl(limits::INITIAL_URL_BUF_SIZE, req_limits.max_url_bytes)?;
let mut req = Request::new(method, url).with_version(req_handle.get_version());
req.is_from_client = true;
for name in req_handle.get_header_names_impl(
limits::INITIAL_HEADER_NAME_BUF_SIZE,
req_limits.max_header_name_bytes,
) {
let name = name?;
for value in req_handle.get_header_values_impl(
&name,
limits::INITIAL_HEADER_VALUE_BUF_SIZE,
req_limits.max_header_value_bytes,
) {
let value = value?;
req.append_header(&name, value);
}
}
if let Some(body) = body_handle {
req.set_body(body);
}
Ok(req)
}
pub fn into_handles(mut self) -> (RequestHandle, Option<BodyHandle>) {
self.to_handles()
}
fn to_handles(&mut self) -> (RequestHandle, Option<BodyHandle>) {
let req_handle = {
let mut req_handle = RequestHandle::new();
req_handle.set_version(self.version);
req_handle.set_method(&self.method);
req_handle.set_url(&self.url);
req_handle.set_cache_override(&self.cache_override);
for name in self.headers.keys() {
req_handle.set_header_values(name, self.headers.get_all(name));
}
req_handle
};
let body_handle = if let Some(body) = self.try_take_body() {
Some(body.into_handle())
} else {
None
};
(req_handle, body_handle)
}
}
#[derive(Debug)]
struct FastlyExts {
cache_override: CacheOverride,
is_from_client: bool,
}
impl Into<http::Request<Body>> for Request {
fn into(self) -> http::Request<Body> {
let mut req = http::Request::new(self.body.unwrap_or_else(|| Body::new()));
req.extensions_mut().insert(FastlyExts {
cache_override: self.cache_override,
is_from_client: self.is_from_client,
});
*req.headers_mut() = self.headers;
*req.method_mut() = self.method;
*req.uri_mut() = self
.url
.into_string()
.parse()
.expect("Url to Uri conversion shouldn't fail, but did");
*req.version_mut() = self.version;
req
}
}
impl From<http::Request<Body>> for Request {
fn from(from: http::Request<Body>) -> Self {
let (mut parts, body) = from.into_parts();
let (cache_override, is_from_client) = parts
.extensions
.remove()
.map(|exts: FastlyExts| (exts.cache_override, exts.is_from_client))
.unwrap_or_else(|| (CacheOverride::default(), false));
Request {
version: parts.version,
method: parts.method,
url: Url::parse(&parts.uri.to_string())
.expect("Uri to Url conversion shouldn't fail, but did"),
headers: parts.headers,
body: Some(body),
cache_override,
is_from_client,
}
}
}
#[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(backend: impl Into<String>, sent_req: Request, error: impl Into<Error>) -> Self {
SendError {
backend: backend.into(),
sent_req,
error: error.into(),
}
}
pub(crate) 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
}
}
fn validate_request(req: &Request) -> Result<(), Error> {
let scheme_ok = req.url.scheme().eq_ignore_ascii_case("http")
|| req.url.scheme().eq_ignore_ascii_case("https");
ensure!(
scheme_ok && req.url.has_authority(),
"request URIs must have a scheme (http/https) and an authority (host)"
);
Ok(())
}
#[derive(Debug)]
pub(crate) struct FastlyRequestMetadata {
backend: Option<Backend>,
cache_override: CacheOverride,
}