Rocket/core/lib/src/local/request.rs

387 lines
12 KiB
Rust

macro_rules! pub_request_impl {
($import:literal $($prefix:tt $suffix:tt)?) =>
{
/// Borrows the inner `Request` as seen by Rocket.
///
/// Note that no routing has occurred and that there is no remote
/// address unless one has been explicitly set with
/// [`set_remote()`](Request::set_remote()).
///
/// # Example
///
/// ```rust
#[doc = $import]
///
/// # Client::_test(|_, request, _| {
/// let request: LocalRequest = request;
/// let inner: &rocket::Request = request.inner();
/// # });
/// ```
#[inline(always)]
pub fn inner(&self) -> &Request<'c> {
self._request()
}
/// Mutably borrows the inner `Request` as seen by Rocket.
///
/// Note that no routing has occurred and that there is no remote
/// address unless one has been explicitly set with
/// [`set_remote()`](Request::set_remote()).
///
/// # Example
///
/// ```rust
#[doc = $import]
///
/// # Client::_test(|_, request, _| {
/// let mut request: LocalRequest = request;
/// let inner: &mut rocket::Request = request.inner_mut();
/// # });
/// ```
#[inline(always)]
pub fn inner_mut(&mut self) -> &mut Request<'c> {
self._request_mut()
}
/// Add a header to this request.
///
/// Any type that implements `Into<Header>` can be used here. Among
/// others, this includes [`ContentType`] and [`Accept`].
///
/// [`ContentType`]: crate::http::ContentType
/// [`Accept`]: crate::http::Accept
///
/// # Examples
///
/// Add the Content-Type header:
///
/// ```rust
#[doc = $import]
/// use rocket::http::Header;
/// use rocket::http::ContentType;
///
/// # Client::_test(|_, request, _| {
/// let request: LocalRequest = request;
/// let req = request
/// .header(ContentType::JSON)
/// .header(Header::new("X-Custom", "custom-value"));
/// # });
/// ```
#[inline]
pub fn header<H>(mut self, header: H) -> Self
where H: Into<crate::http::Header<'static>>
{
self._request_mut().add_header(header.into());
self
}
/// Adds a header to this request without consuming `self`.
///
/// # Examples
///
/// Add the Content-Type header:
///
/// ```rust
#[doc = $import]
/// use rocket::http::ContentType;
///
/// # Client::_test(|_, mut request, _| {
/// let mut request: LocalRequest = request;
/// request.add_header(ContentType::JSON);
/// # });
/// ```
#[inline]
pub fn add_header<H>(&mut self, header: H)
where H: Into<crate::http::Header<'static>>
{
self._request_mut().add_header(header.into());
}
/// Set the remote address of this request.
///
/// # Examples
///
/// Set the remote address to "8.8.8.8:80":
///
/// ```rust
#[doc = $import]
///
/// # Client::_test(|_, request, _| {
/// let request: LocalRequest = request;
/// let address = "8.8.8.8:80".parse().unwrap();
/// let req = request.remote(address);
/// # });
/// ```
#[inline]
pub fn remote(mut self, address: std::net::SocketAddr) -> Self {
self.set_remote(address);
self
}
/// Add a cookie to this request.
///
/// # Examples
///
/// Add `user_id` cookie:
///
/// ```rust
#[doc = $import]
/// use rocket::http::Cookie;
///
/// # Client::_test(|_, request, _| {
/// let request: LocalRequest = request;
/// let req = request
/// .cookie(("username", "sb"))
/// .cookie(("user_id", "12"));
/// # });
/// ```
#[inline]
pub fn cookie<'a, C>(mut self, cookie: C) -> Self
where C: Into<crate::http::Cookie<'a>>
{
self._request_mut().cookies_mut().add_original(cookie.into().into_owned());
self
}
/// Add all of the cookies in `cookies` to this request.
///
/// # Example
///
/// ```rust
#[doc = $import]
/// use rocket::http::Cookie;
///
/// # Client::_test(|_, request, _| {
/// let request: LocalRequest = request;
/// let cookies = vec![("a", "b"), ("c", "d")];
/// let req = request.cookies(cookies);
/// # });
/// ```
#[inline]
pub fn cookies<'a, C, I>(mut self, cookies: I) -> Self
where C: Into<crate::http::Cookie<'a>>,
I: IntoIterator<Item = C>
{
for cookie in cookies {
let cookie: crate::http::Cookie<'_> = cookie.into();
self._request_mut().cookies_mut().add_original(cookie.into_owned());
}
self
}
/// Add a [private cookie] to this request.
///
/// [private cookie]: crate::http::CookieJar::add_private()
///
/// # Examples
///
/// Add `user_id` as a private cookie:
///
/// ```rust
#[doc = $import]
/// use rocket::http::Cookie;
///
/// # Client::_test(|_, request, _| {
/// let request: LocalRequest = request;
/// let req = request.private_cookie(("user_id", "sb"));
/// # });
/// ```
#[cfg(feature = "secrets")]
#[cfg_attr(nightly, doc(cfg(feature = "secrets")))]
#[inline]
pub fn private_cookie<C>(mut self, cookie: C) -> Self
where C: Into<crate::http::Cookie<'static>>
{
self._request_mut().cookies_mut().add_original_private(cookie.into());
self
}
/// Set mTLS client certificates to send along with the request.
///
/// If the request already contained certificates, they are replaced with
/// those in `reader.`
///
/// `reader` is expected to be PEM-formatted and contain X509 certificates.
/// If it contains more than one certificate, the entire chain is set on the
/// request. If it contains items other than certificates, the certificate
/// chain up to the first non-certificate item is set on the request. If
/// `reader` is syntactically invalid PEM, certificates are cleared on the
/// request.
///
/// The type `C` can be anything that implements [`std::io::Read`]. This
/// includes: `&[u8]`, `File`, `&File`, `Stdin`, and so on. To read a file
/// in at compile-time, use [`include_bytes!()`].
///
/// ```rust
/// use std::fs::File;
///
#[doc = $import]
/// use rocket::fs::relative;
///
/// # Client::_test(|_, request, _| {
/// let request: LocalRequest = request;
/// let path = relative!("../../examples/tls/private/ed25519_cert.pem");
/// let req = request.identity(File::open(path).unwrap());
/// # });
/// ```
#[cfg(feature = "mtls")]
#[cfg_attr(nightly, doc(cfg(feature = "mtls")))]
pub fn identity<C: std::io::Read>(mut self, reader: C) -> Self {
use crate::http::{tls::util::load_cert_chain, private::Certificates};
let mut reader = std::io::BufReader::new(reader);
let certs = load_cert_chain(&mut reader).map(Certificates::from);
self._request_mut().connection.client_certificates = certs.ok();
self
}
/// Sets the body data of the request.
///core/lib/src/local/request.rs
/// # Examples
///
/// ```rust
#[doc = $import]
/// use rocket::http::ContentType;
///
/// # Client::_test(|_, request, _| {
/// let request: LocalRequest = request;
/// let req = request
/// .header(ContentType::Text)
/// .body("Hello, world!");
/// # });
/// ```
#[inline]
pub fn body<S: AsRef<[u8]>>(mut self, body: S) -> Self {
// TODO: For CGI, we want to be able to set the body to be stdin
// without actually reading everything into a vector. Can we allow
// that here while keeping the simplicity? Looks like it would
// require us to reintroduce a NetStream::Local(Box<Read>) or
// something like that.
*self._body_mut() = body.as_ref().into();
self
}
/// Sets the body to `value` serialized as JSON with `Content-Type`
/// [`ContentType::JSON`](crate::http::ContentType::JSON).
///
/// If `value` fails to serialize, the body is set to empty. The
/// `Content-Type` header is _always_ set.
///
/// # Examples
///
/// ```rust
#[doc = $import]
/// use rocket::serde::Serialize;
/// use rocket::http::ContentType;
///
/// #[derive(Serialize)]
/// struct Task {
/// id: usize,
/// complete: bool,
/// }
///
/// # Client::_test(|_, request, _| {
/// let task = Task { id: 10, complete: false };
///
/// let request: LocalRequest = request;
/// let req = request.json(&task);
/// assert_eq!(req.content_type(), Some(&ContentType::JSON));
/// # });
/// ```
#[cfg(feature = "json")]
#[cfg_attr(nightly, doc(cfg(feature = "json")))]
pub fn json<T: crate::serde::Serialize>(self, value: &T) -> Self {
let json = serde_json::to_vec(&value).unwrap_or_default();
self.header(crate::http::ContentType::JSON).body(json)
}
/// Sets the body to `value` serialized as MessagePack with `Content-Type`
/// [`ContentType::MsgPack`](crate::http::ContentType::MsgPack).
///
/// If `value` fails to serialize, the body is set to empty. The
/// `Content-Type` header is _always_ set.
///
/// # Examples
///
/// ```rust
#[doc = $import]
/// use rocket::serde::Serialize;
/// use rocket::http::ContentType;
///
/// #[derive(Serialize)]
/// struct Task {
/// id: usize,
/// complete: bool,
/// }
///
/// # Client::_test(|_, request, _| {
/// let task = Task { id: 10, complete: false };
///
/// let request: LocalRequest = request;
/// let req = request.msgpack(&task);
/// assert_eq!(req.content_type(), Some(&ContentType::MsgPack));
/// # });
/// ```
#[cfg(feature = "msgpack")]
#[cfg_attr(nightly, doc(cfg(feature = "msgpack")))]
pub fn msgpack<T: crate::serde::Serialize>(self, value: &T) -> Self {
let msgpack = rmp_serde::to_vec(value).unwrap_or_default();
self.header(crate::http::ContentType::MsgPack).body(msgpack)
}
/// Set the body (data) of the request without consuming `self`.
///
/// # Examples
///
/// Set the body to be a JSON structure; also sets the Content-Type.
///
/// ```rust
#[doc = $import]
/// use rocket::http::ContentType;
///
/// # Client::_test(|_, request, _| {
/// let request: LocalRequest = request;
/// let mut request = request.header(ContentType::JSON);
/// request.set_body(r#"{ "key": "value", "array": [1, 2, 3] }"#);
/// # });
/// ```
#[inline]
pub fn set_body<S: AsRef<[u8]>>(&mut self, body: S) {
*self._body_mut() = body.as_ref().into();
}
/// Dispatches the request, returning the response.
///
/// This method consumes `self` and is the preferred mechanism for
/// dispatching.
///
/// # Example
///
/// ```rust
#[doc = $import]
///
/// # Client::_test(|_, request, _| {
/// let request: LocalRequest = request;
/// let response = request.dispatch();
/// # });
/// ```
#[inline(always)]
pub $($prefix)? fn dispatch(self) -> LocalResponse<'c> {
self._dispatch()$(.$suffix)?
}
#[cfg(test)]
#[allow(dead_code)]
fn _ensure_impls_exist() {
fn is_clone_debug<T: Clone + std::fmt::Debug>() {}
is_clone_debug::<Self>();
fn is_deref_req<'a, T: std::ops::Deref<Target = Request<'a>>>() {}
is_deref_req::<Self>();
fn is_deref_mut_req<'a, T: std::ops::DerefMut<Target = Request<'a>>>() {}
is_deref_mut_req::<Self>();
}
}}