Merge branch 'master' into remove-del-cookie

This commit is contained in:
Rob Ede 2022-01-21 17:17:51 +00:00 committed by GitHub
commit 8cdef80bb5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 193 additions and 91 deletions

View File

@ -2,7 +2,7 @@
//! //!
//! Provides a non-blocking service for serving static files from disk. //! Provides a non-blocking service for serving static files from disk.
//! //!
//! # Example //! # Examples
//! ``` //! ```
//! use actix_web::App; //! use actix_web::App;
//! use actix_files::Files; //! use actix_files::Files;

View File

@ -39,13 +39,18 @@
//! ``` //! ```
//! # use actix_web::HttpResponse; //! # use actix_web::HttpResponse;
//! # use actix_web_codegen::route; //! # use actix_web_codegen::route;
//! #[route("/test", method="GET", method="HEAD")] //! #[route("/test", method = "GET", method = "HEAD")]
//! async fn get_and_head_handler() -> HttpResponse { //! async fn get_and_head_handler() -> HttpResponse {
//! HttpResponse::Ok().finish() //! HttpResponse::Ok().finish()
//! } //! }
//! ``` //! ```
//! //!
//! [actix-web attributes docs]: https://docs.rs/actix-web/*/actix_web/#attributes //! # Multiple Path Handlers
//! There are no macros to generate multi-path handlers. Let us know in [this issue].
//!
//! [this issue]: https://github.com/actix/actix-web/issues/1709
//!
//! [actix-web attributes docs]: https://docs.rs/actix-web/latest/actix_web/#attributes
//! [GET]: macro@get //! [GET]: macro@get
//! [POST]: macro@post //! [POST]: macro@post
//! [PUT]: macro@put //! [PUT]: macro@put
@ -73,22 +78,23 @@ mod route;
/// ``` /// ```
/// ///
/// # Attributes /// # Attributes
/// - `"path"` - Raw literal string with path for which to register handler. /// - `"path"`: Raw literal string with path for which to register handler.
/// - `name="resource_name"` - Specifies resource name for the handler. If not set, the function name of handler is used. /// - `name = "resource_name"`: Specifies resource name for the handler. If not set, the function
/// - `method="HTTP_METHOD"` - Registers HTTP method to provide guard for. Upper-case string, "GET", "POST" for example. /// name of handler is used.
/// - `guard="function_name"` - Registers function as guard using `actix_web::guard::fn_guard` /// - `method = "HTTP_METHOD"`: Registers HTTP method to provide guard for. Upper-case string,
/// - `wrap="Middleware"` - Registers a resource middleware. /// "GET", "POST" for example.
/// - `guard = "function_name"`: Registers function as guard using `actix_web::guard::fn_guard`.
/// - `wrap = "Middleware"`: Registers a resource middleware.
/// ///
/// # Notes /// # Notes
/// Function name can be specified as any expression that is going to be accessible to the generate /// Function name can be specified as any expression that is going to be accessible to the generate
/// code, e.g `my_guard` or `my_module::my_guard`. /// code, e.g `my_guard` or `my_module::my_guard`.
/// ///
/// # Example /// # Examples
///
/// ``` /// ```
/// # use actix_web::HttpResponse; /// # use actix_web::HttpResponse;
/// # use actix_web_codegen::route; /// # use actix_web_codegen::route;
/// #[route("/test", method="GET", method="HEAD")] /// #[route("/test", method = "GET", method = "HEAD")]
/// async fn example() -> HttpResponse { /// async fn example() -> HttpResponse {
/// HttpResponse::Ok().finish() /// HttpResponse::Ok().finish()
/// } /// }
@ -98,69 +104,54 @@ pub fn route(args: TokenStream, input: TokenStream) -> TokenStream {
route::with_method(None, args, input) route::with_method(None, args, input)
} }
macro_rules! doc_comment {
($x:expr; $($tt:tt)*) => {
#[doc = $x]
$($tt)*
};
}
macro_rules! method_macro { macro_rules! method_macro {
( ($variant:ident, $method:ident) => {
$($variant:ident, $method:ident,)+ #[doc = concat!("Creates route handler with `actix_web::guard::", stringify!($variant), "`.")]
) => { ///
$(doc_comment! { /// # Syntax
concat!(" /// ```plain
Creates route handler with `actix_web::guard::", stringify!($variant), "`. #[doc = concat!("#[", stringify!($method), r#"("path"[, attributes])]"#)]
/// ```
# Syntax ///
```plain /// # Attributes
#[", stringify!($method), r#"("path"[, attributes])] /// - `"path"`: Raw literal string with path for which to register handler.
``` /// - `name = "resource_name"`: Specifies resource name for the handler. If not set, the function
/// name of handler is used.
# Attributes /// - `guard = "function_name"`: Registers function as guard using `actix_web::guard::fn_guard`.
- `"path"` - Raw literal string with path for which to register handler. /// - `wrap = "Middleware"`: Registers a resource middleware.
- `name="resource_name"` - Specifies resource name for the handler. If not set, the function name of handler is used. ///
- `guard="function_name"` - Registers function as guard using `actix_web::guard::fn_guard`. /// # Notes
- `wrap="Middleware"` - Registers a resource middleware. /// Function name can be specified as any expression that is going to be accessible to the
/// generate code, e.g `my_guard` or `my_module::my_guard`.
# Notes ///
Function name can be specified as any expression that is going to be accessible to the generate /// # Examples
code, e.g `my_guard` or `my_module::my_guard`. /// ```
/// # use actix_web::HttpResponse;
# Example #[doc = concat!("# use actix_web_codegen::", stringify!($method), ";")]
#[doc = concat!("#[", stringify!($method), r#"("/")]"#)]
``` /// async fn example() -> HttpResponse {
# use actix_web::HttpResponse; /// HttpResponse::Ok().finish()
# use actix_web_codegen::"#, stringify!($method), "; /// }
#[", stringify!($method), r#"("/")] /// ```
async fn example() -> HttpResponse { #[proc_macro_attribute]
HttpResponse::Ok().finish() pub fn $method(args: TokenStream, input: TokenStream) -> TokenStream {
route::with_method(Some(route::MethodType::$variant), args, input)
} }
```
"#);
#[proc_macro_attribute]
pub fn $method(args: TokenStream, input: TokenStream) -> TokenStream {
route::with_method(Some(route::MethodType::$variant), args, input)
}
})+
}; };
} }
method_macro! { method_macro!(Get, get);
Get, get, method_macro!(Post, post);
Post, post, method_macro!(Put, put);
Put, put, method_macro!(Delete, delete);
Delete, delete, method_macro!(Head, head);
Head, head, method_macro!(Connect, connect);
Connect, connect, method_macro!(Options, options);
Options, options, method_macro!(Trace, trace);
Trace, trace, method_macro!(Patch, patch);
Patch, patch,
}
/// Marks async main function as the actix system entry-point.
/// Marks async main function as the Actix Web system entry-point.
///
/// # Examples /// # Examples
/// ``` /// ```
/// #[actix_web::main] /// #[actix_web::main]

View File

@ -2,7 +2,7 @@
//! //!
//! Type definitions required to use [`awc::Client`](super::Client) as a WebSocket client. //! Type definitions required to use [`awc::Client`](super::Client) as a WebSocket client.
//! //!
//! # Example //! # Examples
//! //!
//! ```no_run //! ```no_run
//! use awc::{Client, ws}; //! use awc::{Client, ws};

View File

@ -24,7 +24,7 @@ async fn main() -> std::io::Result<()> {
App::new() App::new()
.wrap(middleware::DefaultHeaders::new().add(("X-Version", "0.2"))) .wrap(middleware::DefaultHeaders::new().add(("X-Version", "0.2")))
.wrap(middleware::Compress::default()) .wrap(middleware::Compress::default())
.wrap(middleware::Logger::default()) .wrap(middleware::Logger::default().log_target("1234"))
.service(index) .service(index)
.service(no_params) .service(no_params)
.service( .service(

View File

@ -16,7 +16,7 @@ crate::http::header::common_header! {
/// # Example Values /// # Example Values
/// * `Tue, 15 Nov 1994 08:12:31 GMT` /// * `Tue, 15 Nov 1994 08:12:31 GMT`
/// ///
/// # Example /// # Examples
/// ///
/// ``` /// ```
/// use std::time::SystemTime; /// use std::time::SystemTime;

View File

@ -19,7 +19,7 @@ crate::http::header::common_header! {
/// # Example Values /// # Example Values
/// * `Thu, 01 Dec 1994 16:00:00 GMT` /// * `Thu, 01 Dec 1994 16:00:00 GMT`
/// ///
/// # Example /// # Examples
/// ///
/// ``` /// ```
/// use std::time::{SystemTime, Duration}; /// use std::time::{SystemTime, Duration};

View File

@ -18,7 +18,7 @@ crate::http::header::common_header! {
/// # Example Values /// # Example Values
/// * `Sat, 29 Oct 1994 19:43:31 GMT` /// * `Sat, 29 Oct 1994 19:43:31 GMT`
/// ///
/// # Example /// # Examples
/// ///
/// ``` /// ```
/// use std::time::{SystemTime, Duration}; /// use std::time::{SystemTime, Duration};

View File

@ -18,7 +18,7 @@ crate::http::header::common_header! {
/// # Example Values /// # Example Values
/// * `Sat, 29 Oct 1994 19:43:31 GMT` /// * `Sat, 29 Oct 1994 19:43:31 GMT`
/// ///
/// # Example /// # Examples
/// ///
/// ``` /// ```
/// use std::time::{SystemTime, Duration}; /// use std::time::{SystemTime, Duration};

View File

@ -17,7 +17,7 @@ crate::http::header::common_header! {
/// # Example Values /// # Example Values
/// * `Sat, 29 Oct 1994 19:43:31 GMT` /// * `Sat, 29 Oct 1994 19:43:31 GMT`
/// ///
/// # Example /// # Examples
/// ///
/// ``` /// ```
/// use std::time::{SystemTime, Duration}; /// use std::time::{SystemTime, Duration};

View File

@ -124,7 +124,7 @@ impl Logger {
/// ///
/// It is convention to print "-" to indicate no output instead of an empty string. /// It is convention to print "-" to indicate no output instead of an empty string.
/// ///
/// # Example /// # Examples
/// ``` /// ```
/// # use actix_web::http::{header::HeaderValue}; /// # use actix_web::http::{header::HeaderValue};
/// # use actix_web::middleware::Logger; /// # use actix_web::middleware::Logger;
@ -700,7 +700,6 @@ mod tests {
Ok(()) Ok(())
}; };
let s = format!("{}", FormatDisplay(&render)); let s = format!("{}", FormatDisplay(&render));
println!("{}", s);
assert!(s.contains("/test/route/yeah")); assert!(s.contains("/test/route/yeah"));
} }
@ -794,7 +793,6 @@ mod tests {
Ok(()) Ok(())
}; };
let s = format!("{}", FormatDisplay(&render)); let s = format!("{}", FormatDisplay(&render));
println!("{}", s);
assert!(s.contains("192.0.2.60")); assert!(s.contains("192.0.2.60"));
} }

View File

@ -508,10 +508,12 @@ mod tests {
use bytes::Bytes; use bytes::Bytes;
use super::*; use super::*;
use crate::dev::{ResourceDef, ResourceMap}; use crate::{
use crate::http::{header, StatusCode}; dev::{ResourceDef, ResourceMap},
use crate::test::{call_service, init_service, read_body, TestRequest}; http::{header, StatusCode},
use crate::{web, App, HttpResponse}; test::{self, call_service, init_service, read_body, TestRequest},
web, App, HttpResponse,
};
#[test] #[test]
fn test_debug() { fn test_debug() {
@ -865,4 +867,47 @@ mod tests {
let res = call_service(&srv, req).await; let res = call_service(&srv, req).await;
assert_eq!(res.status(), StatusCode::OK); assert_eq!(res.status(), StatusCode::OK);
} }
#[actix_rt::test]
async fn url_for_closest_named_resource() {
// we mount the route named 'nested' on 2 different scopes, 'a' and 'b'
let srv = test::init_service(
App::new()
.service(
web::scope("/foo")
.service(web::resource("/nested").name("nested").route(web::get().to(
|req: HttpRequest| {
HttpResponse::Ok()
.body(format!("{}", req.url_for_static("nested").unwrap()))
},
)))
.service(web::scope("/baz").service(web::resource("deep")))
.service(web::resource("{foo_param}")),
)
.service(web::scope("/bar").service(
web::resource("/nested").name("nested").route(web::get().to(
|req: HttpRequest| {
HttpResponse::Ok()
.body(format!("{}", req.url_for_static("nested").unwrap()))
},
)),
)),
)
.await;
let foo_resp =
test::call_service(&srv, TestRequest::with_uri("/foo/nested").to_request()).await;
assert_eq!(foo_resp.status(), StatusCode::OK);
let body = read_body(foo_resp).await;
// `body` equals http://localhost:8080/bar/nested
// because nested from /bar overrides /foo's
// to do this any other way would require something like a custom tree search
assert_eq!(body, "http://localhost:8080/bar/nested");
let bar_resp =
test::call_service(&srv, TestRequest::with_uri("/bar/nested").to_request()).await;
assert_eq!(bar_resp.status(), StatusCode::OK);
let body = read_body(bar_resp).await;
assert_eq!(body, "http://localhost:8080/bar/nested");
}
} }

View File

@ -24,7 +24,7 @@ use crate::{
/// re-insert the cloned data back into the extensions map. A `DerefMut` impl is intentionally not /// re-insert the cloned data back into the extensions map. A `DerefMut` impl is intentionally not
/// provided to make this potential foot-gun more obvious. /// provided to make this potential foot-gun more obvious.
/// ///
/// # Example /// # Examples
/// ```no_run /// ```no_run
/// # use actix_web::{web, HttpResponse, HttpRequest, Responder, HttpMessage as _}; /// # use actix_web::{web, HttpResponse, HttpRequest, Responder, HttpMessage as _};
/// ///

View File

@ -1,6 +1,7 @@
use std::{ use std::{
borrow::Cow, borrow::Cow,
cell::RefCell, cell::RefCell,
fmt::Write as _,
rc::{Rc, Weak}, rc::{Rc, Weak},
}; };
@ -10,12 +11,14 @@ use url::Url;
use crate::{error::UrlGenerationError, request::HttpRequest}; use crate::{error::UrlGenerationError, request::HttpRequest};
const AVG_PATH_LEN: usize = 24;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ResourceMap { pub struct ResourceMap {
pattern: ResourceDef, pattern: ResourceDef,
/// Named resources within the tree or, for external resources, /// Named resources within the tree or, for external resources, it points to isolated nodes
/// it points to isolated nodes outside the tree. /// outside the tree.
named: AHashMap<String, Rc<ResourceMap>>, named: AHashMap<String, Rc<ResourceMap>>,
parent: RefCell<Weak<ResourceMap>>, parent: RefCell<Weak<ResourceMap>>,
@ -35,6 +38,35 @@ impl ResourceMap {
} }
} }
/// Format resource map as tree structure (unfinished).
#[allow(dead_code)]
pub(crate) fn tree(&self) -> String {
let mut buf = String::new();
self._tree(&mut buf, 0);
buf
}
pub(crate) fn _tree(&self, buf: &mut String, level: usize) {
if let Some(children) = &self.nodes {
for child in children {
writeln!(
buf,
"{}{} {}",
"--".repeat(level),
child.pattern.pattern().unwrap(),
child
.pattern
.name()
.map(|name| format!("({})", name))
.unwrap_or_else(|| "".to_owned())
)
.unwrap();
ResourceMap::_tree(child, buf, level + 1);
}
}
}
/// Adds a (possibly nested) resource. /// Adds a (possibly nested) resource.
/// ///
/// To add a non-prefix pattern, `nested` must be `None`. /// To add a non-prefix pattern, `nested` must be `None`.
@ -44,7 +76,11 @@ impl ResourceMap {
pattern.set_id(self.nodes.as_ref().unwrap().len() as u16); pattern.set_id(self.nodes.as_ref().unwrap().len() as u16);
if let Some(new_node) = nested { if let Some(new_node) = nested {
assert_eq!(&new_node.pattern, pattern, "`patern` and `nested` mismatch"); debug_assert_eq!(
&new_node.pattern, pattern,
"`pattern` and `nested` mismatch"
);
// parents absorb references to the named resources of children
self.named.extend(new_node.named.clone().into_iter()); self.named.extend(new_node.named.clone().into_iter());
self.nodes.as_mut().unwrap().push(new_node); self.nodes.as_mut().unwrap().push(new_node);
} else { } else {
@ -64,7 +100,7 @@ impl ResourceMap {
None => false, None => false,
}; };
// Don't add external resources to the tree // don't add external resources to the tree
if !is_external { if !is_external {
self.nodes.as_mut().unwrap().push(new_node); self.nodes.as_mut().unwrap().push(new_node);
} }
@ -78,7 +114,7 @@ impl ResourceMap {
} }
} }
/// Generate url for named resource /// Generate URL for named resource.
/// ///
/// Check [`HttpRequest::url_for`] for detailed information. /// Check [`HttpRequest::url_for`] for detailed information.
pub fn url_for<U, I>( pub fn url_for<U, I>(
@ -97,7 +133,7 @@ impl ResourceMap {
.named .named
.get(name) .get(name)
.ok_or(UrlGenerationError::ResourceNotFound)? .ok_or(UrlGenerationError::ResourceNotFound)?
.root_rmap_fn(String::with_capacity(24), |mut acc, node| { .root_rmap_fn(String::with_capacity(AVG_PATH_LEN), |mut acc, node| {
node.pattern node.pattern
.resource_path_from_iter(&mut acc, &mut elements) .resource_path_from_iter(&mut acc, &mut elements)
.then(|| acc) .then(|| acc)
@ -128,6 +164,7 @@ impl ResourceMap {
Ok(url) Ok(url)
} }
/// Returns true if there is a resource that would match `path`.
pub fn has_resource(&self, path: &str) -> bool { pub fn has_resource(&self, path: &str) -> bool {
self.find_matching_node(path).is_some() self.find_matching_node(path).is_some()
} }
@ -142,9 +179,10 @@ impl ResourceMap {
/// is possible. /// is possible.
pub fn match_pattern(&self, path: &str) -> Option<String> { pub fn match_pattern(&self, path: &str) -> Option<String> {
self.find_matching_node(path)?.root_rmap_fn( self.find_matching_node(path)?.root_rmap_fn(
String::with_capacity(24), String::with_capacity(AVG_PATH_LEN),
|mut acc, node| { |mut acc, node| {
acc.push_str(node.pattern.pattern()?); let pattern = node.pattern.pattern()?;
acc.push_str(pattern);
Some(acc) Some(acc)
}, },
) )
@ -490,4 +528,33 @@ mod tests {
"https://duck.com/abcd" "https://duck.com/abcd"
); );
} }
#[test]
fn url_for_override_within_map() {
let mut root = ResourceMap::new(ResourceDef::prefix(""));
let mut foo_rdef = ResourceDef::prefix("/foo");
let mut foo_map = ResourceMap::new(foo_rdef.clone());
let mut nested_rdef = ResourceDef::new("/nested");
nested_rdef.set_name("nested");
foo_map.add(&mut nested_rdef, None);
root.add(&mut foo_rdef, Some(Rc::new(foo_map)));
let mut foo_rdef = ResourceDef::prefix("/bar");
let mut foo_map = ResourceMap::new(foo_rdef.clone());
let mut nested_rdef = ResourceDef::new("/nested");
nested_rdef.set_name("nested");
foo_map.add(&mut nested_rdef, None);
root.add(&mut foo_rdef, Some(Rc::new(foo_map)));
let rmap = Rc::new(root);
ResourceMap::finish(&rmap);
let req = crate::test::TestRequest::default().to_http_request();
let url = rmap.url_for(&req, "nested", &[""; 0]).unwrap().to_string();
assert_eq!(url, "http://localhost:8080/bar/nested");
assert!(rmap.url_for(&req, "missing", &["u123"]).is_err());
}
} }

View File

@ -831,6 +831,7 @@ mod tests {
let req = ServiceRequest::from_parts(req, pl); let req = ServiceRequest::from_parts(req, pl);
svc.call(req) svc.call(req)
}) })
.route("/", web::get().to(|| async { "" }))
.service( .service(
web::resource("/resource1/{name}/index.html").route(web::get().to(index)), web::resource("/resource1/{name}/index.html").route(web::get().to(index)),
), ),