rocket/local/asynchronous/request.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
use std::fmt;
use crate::{Request, Data};
use crate::http::{Status, Method};
use crate::http::uri::Origin;
use super::{Client, LocalResponse};
/// An `async` local request as returned by [`Client`](super::Client).
///
/// For details, see [the top-level documentation](../index.html#localrequest).
///
/// ## Example
///
/// The following snippet uses the available builder methods to construct and
/// dispatch a `POST` request to `/` with a JSON body:
///
/// ```rust,no_run
/// use rocket::local::asynchronous::{Client, LocalRequest};
/// use rocket::http::{ContentType, Cookie};
///
/// # rocket::async_test(async {
/// let client = Client::tracked(rocket::build()).await.expect("valid rocket");
/// let req = client.post("/")
/// .header(ContentType::JSON)
/// .remote("127.0.0.1:8000".parse().unwrap())
/// .cookie(("name", "value"))
/// .body(r#"{ "value": 42 }"#);
///
/// let response = req.dispatch().await;
/// # });
/// ```
pub struct LocalRequest<'c> {
pub(in super) client: &'c Client,
pub(in super) request: Request<'c>,
data: Vec<u8>,
// The `Origin` on the right is INVALID! It should _not_ be used!
uri: Result<Origin<'c>, Origin<'static>>,
}
impl<'c> LocalRequest<'c> {
pub(crate) fn new<'u: 'c, U>(client: &'c Client, method: Method, uri: U) -> Self
where U: TryInto<Origin<'u>> + fmt::Display
{
// Try to parse `uri` into an `Origin`, storing whether it's good.
let uri_str = uri.to_string();
let try_origin = uri.try_into().map_err(|_| Origin::path_only(uri_str));
// Create a request. We'll handle bad URIs later, in `_dispatch`.
let origin = try_origin.clone().unwrap_or_else(|bad| bad);
let mut request = Request::new(client.rocket(), method, origin);
// Add any cookies we know about.
if client.tracked {
client._with_raw_cookies(|jar| {
for cookie in jar.iter() {
request.cookies_mut().add_original(cookie.clone());
}
})
}
LocalRequest { client, request, uri: try_origin, data: vec![] }
}
pub(crate) fn _request(&self) -> &Request<'c> {
&self.request
}
pub(crate) fn _request_mut(&mut self) -> &mut Request<'c> {
&mut self.request
}
pub(crate) fn _body_mut(&mut self) -> &mut Vec<u8> {
&mut self.data
}
// Performs the actual dispatch.
async fn _dispatch(mut self) -> LocalResponse<'c> {
// First, revalidate the URI, returning an error response (generated
// from an error catcher) immediately if it's invalid. If it's valid,
// then `request` already contains a correct URI.
let rocket = self.client.rocket();
if let Err(ref invalid) = self.uri {
// The user may have changed the URI in the request in which case we
// _shouldn't_ error. Check that now and error only if not.
if self.inner().uri() == invalid {
error!("invalid request URI: {:?}", invalid.path());
return LocalResponse::new(self.request, move |req| {
rocket.handle_error(Status::BadRequest, req)
}).await
}
}
// Actually dispatch the request.
let mut data = Data::local(self.data);
let token = rocket.preprocess_request(&mut self.request, &mut data).await;
let response = LocalResponse::new(self.request, move |req| {
rocket.dispatch(token, req, data)
}).await;
// If the client is tracking cookies, updates the internal cookie jar
// with the changes reflected by `response`.
if self.client.tracked {
self.client._with_raw_cookies_mut(|jar| {
let current_time = time::OffsetDateTime::now_utc();
for cookie in response.cookies().iter() {
if let Some(expires) = cookie.expires_datetime() {
if expires <= current_time {
jar.force_remove(cookie.name());
continue;
}
}
jar.add_original(cookie.clone());
}
})
}
response
}
pub_request_impl!("# use rocket::local::asynchronous::Client;\n\
use rocket::local::asynchronous::LocalRequest;" async await);
}
impl<'c> Clone for LocalRequest<'c> {
fn clone(&self) -> Self {
LocalRequest {
client: self.client,
request: self.request.clone(),
data: self.data.clone(),
uri: self.uri.clone(),
}
}
}
impl std::fmt::Debug for LocalRequest<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self._request().fmt(f)
}
}
impl<'c> std::ops::Deref for LocalRequest<'c> {
type Target = Request<'c>;
fn deref(&self) -> &Self::Target {
self.inner()
}
}
impl<'c> std::ops::DerefMut for LocalRequest<'c> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner_mut()
}
}