This commit is contained in:
Nikolay Kim 2020-10-05 12:08:26 +06:00
parent 3dd7dc68bd
commit 8bb852aa36
9 changed files with 225 additions and 667 deletions

View file

@ -1,11 +1,9 @@
# Cors Middleware for actix web framework [![Build Status](https://travis-ci.org/actix/actix-web.svg?branch=master)](https://travis-ci.org/actix/actix-web) [![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) [![crates.io](https://meritbadge.herokuapp.com/actix-cors)](https://crates.io/crates/actix-cors) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) # Cors Middleware for ntex framework [![Build Status](https://travis-ci.org/actix/actix-web.svg?branch=master)](https://travis-ci.org/actix/actix-web) [![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) [![crates.io](https://meritbadge.herokuapp.com/actix-cors)](https://crates.io/crates/actix-cors) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
**This crate moved to https://github.com/actix/actix-extras.** **This crate moved to https://github.com/ntex/ntex-extras.**
## Documentation & community resources ## Documentation & community resources
* [User Guide](https://actix.rs/docs/) * [API Documentation](https://docs.rs/ntex-cors/)
* [API Documentation](https://docs.rs/actix-cors/) * Cargo package: [ntex-cors](https://crates.io/crates/ntex-cors)
* [Chat on gitter](https://gitter.im/actix/actix) * Minimum supported Rust version: 1.42 or later
* Cargo package: [actix-cors](https://crates.io/crates/actix-cors)
* Minimum supported Rust version: 1.34 or later

View file

@ -1,8 +1,4 @@
#![allow( #![allow(type_alias_bounds, clippy::borrow_interior_mutable_const, clippy::type_complexity)]
type_alias_bounds,
clippy::borrow_interior_mutable_const,
clippy::type_complexity
)]
//! Static files support //! Static files support
use std::fmt::Write; use std::fmt::Write;
@ -40,8 +36,7 @@ use self::error::{FilesError, UriSegmentError};
pub use crate::named::NamedFile; pub use crate::named::NamedFile;
pub use crate::range::HttpRange; pub use crate::range::HttpRange;
type HttpService<Err: ErrorRenderer> = type HttpService<Err: ErrorRenderer> = BoxService<WebRequest<Err>, WebResponse, Err::Container>;
BoxService<WebRequest<Err>, WebResponse, Err::Container>;
type HttpServiceFactory<Err: ErrorRenderer> = type HttpServiceFactory<Err: ErrorRenderer> =
BoxServiceFactory<(), WebRequest<Err>, WebResponse, Err::Container, ()>; BoxServiceFactory<(), WebRequest<Err>, WebResponse, Err::Container, ()>;
@ -60,18 +55,14 @@ pub struct ChunkedReadFile {
size: u64, size: u64,
offset: u64, offset: u64,
file: Option<File>, file: Option<File>,
fut: fut: Option<LocalBoxFuture<'static, Result<(File, Bytes), BlockingError<io::Error>>>>,
Option<LocalBoxFuture<'static, Result<(File, Bytes), BlockingError<io::Error>>>>,
counter: u64, counter: u64,
} }
impl Stream for ChunkedReadFile { impl Stream for ChunkedReadFile {
type Item = Result<Bytes, std::io::Error>; type Item = Result<Bytes, std::io::Error>;
fn poll_next( fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
mut self: Pin<&mut Self>,
cx: &mut Context,
) -> Poll<Option<Self::Item>> {
if let Some(ref mut fut) = self.fut { if let Some(ref mut fut) = self.fut {
return match Pin::new(fut).poll(cx) { return match Pin::new(fut).poll(cx) {
Poll::Ready(Ok((file, bytes))) => { Poll::Ready(Ok((file, bytes))) => {
@ -108,8 +99,7 @@ impl Stream for ChunkedReadFile {
max_bytes = cmp::min(size.saturating_sub(counter), 65_536) as usize; max_bytes = cmp::min(size.saturating_sub(counter), 65_536) as usize;
let mut buf = Vec::with_capacity(max_bytes); let mut buf = Vec::with_capacity(max_bytes);
file.seek(io::SeekFrom::Start(offset))?; file.seek(io::SeekFrom::Start(offset))?;
let nbytes = let nbytes = file.by_ref().take(max_bytes as u64).read_to_end(&mut buf)?;
file.by_ref().take(max_bytes as u64).read_to_end(&mut buf)?;
if nbytes == 0 { if nbytes == 0 {
return Err(io::ErrorKind::UnexpectedEof.into()); return Err(io::ErrorKind::UnexpectedEof.into());
} }
@ -122,8 +112,7 @@ impl Stream for ChunkedReadFile {
} }
} }
type DirectoryRenderer = type DirectoryRenderer = dyn Fn(&Directory, &HttpRequest) -> Result<WebResponse, io::Error>;
dyn Fn(&Directory, &HttpRequest) -> Result<WebResponse, io::Error>;
/// A directory; responds with the generated directory listing. /// A directory; responds with the generated directory listing.
#[derive(Debug)] #[derive(Debug)]
@ -171,10 +160,7 @@ macro_rules! encode_file_name {
}; };
} }
fn directory_listing( fn directory_listing(dir: &Directory, req: &HttpRequest) -> Result<WebResponse, io::Error> {
dir: &Directory,
req: &HttpRequest,
) -> Result<WebResponse, io::Error> {
let index_of = format!("Index of {}", req.path()); let index_of = format!("Index of {}", req.path());
let mut body = String::new(); let mut body = String::new();
let base = Path::new(req.path()); let base = Path::new(req.path());
@ -183,9 +169,7 @@ fn directory_listing(
if dir.is_visible(&entry) { if dir.is_visible(&entry) {
let entry = entry.unwrap(); let entry = entry.unwrap();
let p = match entry.path().strip_prefix(&dir.path) { let p = match entry.path().strip_prefix(&dir.path) {
Ok(p) if cfg!(windows) => { Ok(p) if cfg!(windows) => base.join(p).to_string_lossy().replace("\\", "/"),
base.join(p).to_string_lossy().replace("\\", "/")
}
Ok(p) => base.join(p).to_string_lossy().into_owned(), Ok(p) => base.join(p).to_string_lossy().into_owned(),
Err(_) => continue, Err(_) => continue,
}; };
@ -223,9 +207,7 @@ fn directory_listing(
index_of, index_of, body index_of, index_of, body
); );
Ok(WebResponse::new( Ok(WebResponse::new(
HttpResponse::Ok() HttpResponse::Ok().content_type("text/html; charset=utf-8").body(html),
.content_type("text/html; charset=utf-8")
.body(html),
req.clone(), req.clone(),
)) ))
} }
@ -324,8 +306,8 @@ impl<Err: ErrorRenderer> Files<Err> {
/// Set custom directory renderer /// Set custom directory renderer
pub fn files_listing_renderer<F>(mut self, f: F) -> Self pub fn files_listing_renderer<F>(mut self, f: F) -> Self
where where
for<'r, 's> F: Fn(&'r Directory, &'s HttpRequest) -> Result<WebResponse, io::Error> for<'r, 's> F:
+ 'static, Fn(&'r Directory, &'s HttpRequest) -> Result<WebResponse, io::Error> + 'static,
{ {
self.renderer = Rc::new(f); self.renderer = Rc::new(f);
self self
@ -397,9 +379,7 @@ impl<Err: ErrorRenderer> Files<Err> {
> + 'static, > + 'static,
{ {
// create and configure default resource // create and configure default resource
self.default = Some(Rc::new(boxed::factory( self.default = Some(Rc::new(boxed::factory(f.into_factory().map_init_err(|_| ()))));
f.into_factory().map_init_err(|_| ()),
)));
self self
} }
@ -567,10 +547,7 @@ where
named_file.flags = self.file_flags; named_file.flags = self.file_flags;
let (req, _) = req.into_parts(); let (req, _) = req.into_parts();
Either::Left(ok(WebResponse::new( Either::Left(ok(WebResponse::new(named_file.into_response(&req), req)))
named_file.into_response(&req),
req,
)))
} }
Err(e) => self.handle_io_error(e, req), Err(e) => self.handle_io_error(e, req),
} }
@ -595,17 +572,13 @@ where
match NamedFile::open(path) { match NamedFile::open(path) {
Ok(mut named_file) => { Ok(mut named_file) => {
if let Some(ref mime_override) = self.mime_override { if let Some(ref mime_override) = self.mime_override {
let new_disposition = let new_disposition = mime_override(&named_file.content_type.type_());
mime_override(&named_file.content_type.type_());
named_file.content_disposition.disposition = new_disposition; named_file.content_disposition.disposition = new_disposition;
} }
named_file.flags = self.file_flags; named_file.flags = self.file_flags;
let (req, _) = req.into_parts(); let (req, _) = req.into_parts();
Either::Left(ok(WebResponse::new( Either::Left(ok(WebResponse::new(named_file.into_response(&req), req)))
named_file.into_response(&req),
req,
)))
} }
Err(e) => self.handle_io_error(e, req), Err(e) => self.handle_io_error(e, req),
} }
@ -682,9 +655,8 @@ mod tests {
#[ntex::test] #[ntex::test]
async fn test_if_modified_since_without_if_none_match() { async fn test_if_modified_since_without_if_none_match() {
let file = NamedFile::open("Cargo.toml").unwrap(); let file = NamedFile::open("Cargo.toml").unwrap();
let since = hyperx::header::HttpDate::from( let since =
SystemTime::now().add(Duration::from_secs(60)), hyperx::header::HttpDate::from(SystemTime::now().add(Duration::from_secs(60)));
);
let req = TestRequest::default() let req = TestRequest::default()
.header(http::header::IF_MODIFIED_SINCE, since.to_string()) .header(http::header::IF_MODIFIED_SINCE, since.to_string())
@ -696,9 +668,8 @@ mod tests {
#[ntex::test] #[ntex::test]
async fn test_if_modified_since_with_if_none_match() { async fn test_if_modified_since_with_if_none_match() {
let file = NamedFile::open("Cargo.toml").unwrap(); let file = NamedFile::open("Cargo.toml").unwrap();
let since = hyperx::header::HttpDate::from( let since =
SystemTime::now().add(Duration::from_secs(60)), hyperx::header::HttpDate::from(SystemTime::now().add(Duration::from_secs(60)));
);
let req = TestRequest::default() let req = TestRequest::default()
.header(http::header::IF_NONE_MATCH, "miss_etag") .header(http::header::IF_NONE_MATCH, "miss_etag")
@ -722,14 +693,9 @@ mod tests {
let req = TestRequest::default().to_http_request(); let req = TestRequest::default().to_http_request();
let resp = test::respond_to(file, &req).await; let resp = test::respond_to(file, &req).await;
assert_eq!(resp.headers().get(http::header::CONTENT_TYPE).unwrap(), "text/x-toml");
assert_eq!( assert_eq!(
resp.headers().get(http::header::CONTENT_TYPE).unwrap(), resp.headers().get(http::header::CONTENT_DISPOSITION).unwrap(),
"text/x-toml"
);
assert_eq!(
resp.headers()
.get(http::header::CONTENT_DISPOSITION)
.unwrap(),
"inline; filename=\"Cargo.toml\"" "inline; filename=\"Cargo.toml\""
); );
} }
@ -749,21 +715,14 @@ mod tests {
let req = TestRequest::default().to_http_request(); let req = TestRequest::default().to_http_request();
let resp = test::respond_to(file, &req).await; let resp = test::respond_to(file, &req).await;
assert_eq!( assert_eq!(
resp.headers() resp.headers().get(http::header::CONTENT_DISPOSITION).unwrap(),
.get(http::header::CONTENT_DISPOSITION)
.unwrap(),
"inline; filename=\"Cargo.toml\"" "inline; filename=\"Cargo.toml\""
); );
let file = NamedFile::open("Cargo.toml") let file = NamedFile::open("Cargo.toml").unwrap().disable_content_disposition();
.unwrap()
.disable_content_disposition();
let req = TestRequest::default().to_http_request(); let req = TestRequest::default().to_http_request();
let resp = test::respond_to(file, &req).await; let resp = test::respond_to(file, &req).await;
assert!(resp assert!(resp.headers().get(http::header::CONTENT_DISPOSITION).is_none());
.headers()
.get(http::header::CONTENT_DISPOSITION)
.is_none());
} }
// #[ntex::test] // #[ntex::test]
@ -795,9 +754,7 @@ mod tests {
#[ntex::test] #[ntex::test]
async fn test_named_file_set_content_type() { async fn test_named_file_set_content_type() {
let mut file = NamedFile::open("Cargo.toml") let mut file = NamedFile::open("Cargo.toml").unwrap().set_content_type(mime::TEXT_XML);
.unwrap()
.set_content_type(mime::TEXT_XML);
{ {
file.file(); file.file();
let _f: &File = &file; let _f: &File = &file;
@ -808,14 +765,9 @@ mod tests {
let req = TestRequest::default().to_http_request(); let req = TestRequest::default().to_http_request();
let resp = test::respond_to(file, &req).await; let resp = test::respond_to(file, &req).await;
assert_eq!(resp.headers().get(http::header::CONTENT_TYPE).unwrap(), "text/xml");
assert_eq!( assert_eq!(
resp.headers().get(http::header::CONTENT_TYPE).unwrap(), resp.headers().get(http::header::CONTENT_DISPOSITION).unwrap(),
"text/xml"
);
assert_eq!(
resp.headers()
.get(http::header::CONTENT_DISPOSITION)
.unwrap(),
"inline; filename=\"Cargo.toml\"" "inline; filename=\"Cargo.toml\""
); );
} }
@ -833,23 +785,16 @@ mod tests {
let req = TestRequest::default().to_http_request(); let req = TestRequest::default().to_http_request();
let resp = test::respond_to(file, &req).await; let resp = test::respond_to(file, &req).await;
assert_eq!(resp.headers().get(http::header::CONTENT_TYPE).unwrap(), "image/png");
assert_eq!( assert_eq!(
resp.headers().get(http::header::CONTENT_TYPE).unwrap(), resp.headers().get(http::header::CONTENT_DISPOSITION).unwrap(),
"image/png"
);
assert_eq!(
resp.headers()
.get(http::header::CONTENT_DISPOSITION)
.unwrap(),
"inline; filename=\"test.png\"" "inline; filename=\"test.png\""
); );
} }
#[ntex::test] #[ntex::test]
async fn test_named_file_image_attachment() { async fn test_named_file_image_attachment() {
use hyperx::header::{ use hyperx::header::{Charset, ContentDisposition, DispositionParam, DispositionType};
Charset, ContentDisposition, DispositionParam, DispositionType,
};
let cd = ContentDisposition { let cd = ContentDisposition {
disposition: DispositionType::Attachment, disposition: DispositionType::Attachment,
@ -859,9 +804,7 @@ mod tests {
"test.png".to_string().into_bytes(), "test.png".to_string().into_bytes(),
)], )],
}; };
let mut file = NamedFile::open("tests/test.png") let mut file = NamedFile::open("tests/test.png").unwrap().set_content_disposition(cd);
.unwrap()
.set_content_disposition(cd);
{ {
file.file(); file.file();
let _f: &File = &file; let _f: &File = &file;
@ -872,14 +815,9 @@ mod tests {
let req = TestRequest::default().to_http_request(); let req = TestRequest::default().to_http_request();
let resp = test::respond_to(file, &req).await; let resp = test::respond_to(file, &req).await;
assert_eq!(resp.headers().get(http::header::CONTENT_TYPE).unwrap(), "image/png");
assert_eq!( assert_eq!(
resp.headers().get(http::header::CONTENT_TYPE).unwrap(), resp.headers().get(http::header::CONTENT_DISPOSITION).unwrap(),
"image/png"
);
assert_eq!(
resp.headers()
.get(http::header::CONTENT_DISPOSITION)
.unwrap(),
"attachment; filename=\"test.png\"" "attachment; filename=\"test.png\""
); );
} }
@ -902,18 +840,15 @@ mod tests {
"application/octet-stream" "application/octet-stream"
); );
assert_eq!( assert_eq!(
resp.headers() resp.headers().get(http::header::CONTENT_DISPOSITION).unwrap(),
.get(http::header::CONTENT_DISPOSITION)
.unwrap(),
"attachment; filename=\"test.binary\"" "attachment; filename=\"test.binary\""
); );
} }
#[ntex::test] #[ntex::test]
async fn test_named_file_status_code_text() { async fn test_named_file_status_code_text() {
let mut file = NamedFile::open("Cargo.toml") let mut file =
.unwrap() NamedFile::open("Cargo.toml").unwrap().set_status_code(StatusCode::NOT_FOUND);
.set_status_code(StatusCode::NOT_FOUND);
{ {
file.file(); file.file();
let _f: &File = &file; let _f: &File = &file;
@ -924,14 +859,9 @@ mod tests {
let req = TestRequest::default().to_http_request(); let req = TestRequest::default().to_http_request();
let resp = test::respond_to(file, &req).await; let resp = test::respond_to(file, &req).await;
assert_eq!(resp.headers().get(http::header::CONTENT_TYPE).unwrap(), "text/x-toml");
assert_eq!( assert_eq!(
resp.headers().get(http::header::CONTENT_TYPE).unwrap(), resp.headers().get(http::header::CONTENT_DISPOSITION).unwrap(),
"text/x-toml"
);
assert_eq!(
resp.headers()
.get(http::header::CONTENT_DISPOSITION)
.unwrap(),
"inline; filename=\"Cargo.toml\"" "inline; filename=\"Cargo.toml\""
); );
assert_eq!(resp.status(), StatusCode::NOT_FOUND); assert_eq!(resp.status(), StatusCode::NOT_FOUND);
@ -943,13 +873,9 @@ mod tests {
DispositionType::Attachment DispositionType::Attachment
} }
let mut srv = test::init_service( let mut srv = test::init_service(App::new().service(
App::new().service( Files::new("/", ".").mime_override(all_attachment).index_file("Cargo.toml"),
Files::new("/", ".") ))
.mime_override(all_attachment)
.index_file("Cargo.toml"),
),
)
.await; .await;
let request = TestRequest::get().uri("/").to_request(); let request = TestRequest::get().uri("/").to_request();
@ -960,9 +886,8 @@ mod tests {
.headers() .headers()
.get(http::header::CONTENT_DISPOSITION) .get(http::header::CONTENT_DISPOSITION)
.expect("To have CONTENT_DISPOSITION"); .expect("To have CONTENT_DISPOSITION");
let content_disposition = content_disposition let content_disposition =
.to_str() content_disposition.to_str().expect("Convert CONTENT_DISPOSITION to str");
.expect("Convert CONTENT_DISPOSITION to str");
assert_eq!(content_disposition, "attachment; filename=\"Cargo.toml\""); assert_eq!(content_disposition, "attachment; filename=\"Cargo.toml\"");
} }
@ -1005,12 +930,8 @@ mod tests {
.to_request(); .to_request();
let response = test::call_service(&mut srv, request).await; let response = test::call_service(&mut srv, request).await;
let contentrange = response let contentrange =
.headers() response.headers().get(http::header::CONTENT_RANGE).unwrap().to_str().unwrap();
.get(http::header::CONTENT_RANGE)
.unwrap()
.to_str()
.unwrap();
assert_eq!(contentrange, "bytes 10-20/100"); assert_eq!(contentrange, "bytes 10-20/100");
@ -1021,12 +942,8 @@ mod tests {
.to_request(); .to_request();
let response = test::call_service(&mut srv, request).await; let response = test::call_service(&mut srv, request).await;
let contentrange = response let contentrange =
.headers() response.headers().get(http::header::CONTENT_RANGE).unwrap().to_str().unwrap();
.get(http::header::CONTENT_RANGE)
.unwrap()
.to_str()
.unwrap();
assert_eq!(contentrange, "bytes */100"); assert_eq!(contentrange, "bytes */100");
} }
@ -1077,9 +994,7 @@ mod tests {
// assert_eq!(contentlength, "100"); // assert_eq!(contentlength, "100");
// chunked // chunked
let request = TestRequest::get() let request = TestRequest::get().uri("/t%65st/tests/test.binary").to_request();
.uri("/t%65st/tests/test.binary")
.to_request();
let response = test::call_service(&mut srv, request).await; let response = test::call_service(&mut srv, request).await;
// with enabled compression // with enabled compression
@ -1128,9 +1043,7 @@ mod tests {
App::new().service(Files::new("/", ".").index_file("Cargo.toml")), App::new().service(Files::new("/", ".").index_file("Cargo.toml")),
) )
.await; .await;
let request = TestRequest::get() let request = TestRequest::get().uri("/tests/test%20space.binary").to_request();
.uri("/tests/test%20space.binary")
.to_request();
let response = test::call_service(&srv, request).await; let response = test::call_service(&srv, request).await;
assert_eq!(response.status(), StatusCode::OK); assert_eq!(response.status(), StatusCode::OK);
@ -1143,19 +1056,13 @@ mod tests {
async fn test_files_not_allowed() { async fn test_files_not_allowed() {
let mut srv = test::init_service(App::new().service(Files::new("/", "."))).await; let mut srv = test::init_service(App::new().service(Files::new("/", "."))).await;
let req = TestRequest::default() let req = TestRequest::default().uri("/Cargo.toml").method(Method::POST).to_request();
.uri("/Cargo.toml")
.method(Method::POST)
.to_request();
let resp = test::call_service(&mut srv, req).await; let resp = test::call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
let mut srv = test::init_service(App::new().service(Files::new("/", "."))).await; let mut srv = test::init_service(App::new().service(Files::new("/", "."))).await;
let req = TestRequest::default() let req = TestRequest::default().method(Method::PUT).uri("/Cargo.toml").to_request();
.method(Method::PUT)
.uri("/Cargo.toml")
.to_request();
let resp = test::call_service(&mut srv, req).await; let resp = test::call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
} }
@ -1167,10 +1074,7 @@ mod tests {
) )
.await; .await;
let req = TestRequest::default() let req = TestRequest::default().uri("/Cargo.toml").method(Method::POST).to_request();
.uri("/Cargo.toml")
.method(Method::POST)
.to_request();
let resp = test::call_service(&mut srv, req).await; let resp = test::call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
@ -1214,11 +1118,7 @@ mod tests {
let res = test::call_service(&mut srv, request).await; let res = test::call_service(&mut srv, request).await;
assert_eq!(res.status(), StatusCode::OK); assert_eq!(res.status(), StatusCode::OK);
assert_eq!( assert_eq!(
res.headers() res.headers().get(http::header::CONTENT_ENCODING).unwrap().to_str().unwrap(),
.get(http::header::CONTENT_ENCODING)
.unwrap()
.to_str()
.unwrap(),
"gzip" "gzip"
); );
} }
@ -1233,10 +1133,9 @@ mod tests {
#[ntex::test] #[ntex::test]
async fn test_static_files() { async fn test_static_files() {
let mut srv = test::init_service( let mut srv =
App::new().service(Files::new("/", ".").show_files_listing()), test::init_service(App::new().service(Files::new("/", ".").show_files_listing()))
) .await;
.await;
let req = TestRequest::with_uri("/missing").to_request(); let req = TestRequest::with_uri("/missing").to_request();
let resp = test::call_service(&mut srv, req).await; let resp = test::call_service(&mut srv, req).await;
@ -1248,10 +1147,9 @@ mod tests {
let resp = test::call_service(&mut srv, req).await; let resp = test::call_service(&mut srv, req).await;
assert_eq!(resp.status(), StatusCode::NOT_FOUND); assert_eq!(resp.status(), StatusCode::NOT_FOUND);
let mut srv = test::init_service( let mut srv =
App::new().service(Files::new("/", ".").show_files_listing()), test::init_service(App::new().service(Files::new("/", ".").show_files_listing()))
) .await;
.await;
let req = TestRequest::with_uri("/tests").to_request(); let req = TestRequest::with_uri("/tests").to_request();
let resp = test::call_service(&mut srv, req).await; let resp = test::call_service(&mut srv, req).await;
assert_eq!( assert_eq!(
@ -1275,13 +1173,9 @@ mod tests {
assert_eq!(resp.status(), StatusCode::NOT_FOUND); assert_eq!(resp.status(), StatusCode::NOT_FOUND);
// should redirect if index present // should redirect if index present
let mut srv = test::init_service( let mut srv = test::init_service(App::new().service(
App::new().service( Files::new("/", ".").index_file("test.png").redirect_to_slash_directory(),
Files::new("/", ".") ))
.index_file("test.png")
.redirect_to_slash_directory(),
),
)
.await; .await;
let req = TestRequest::with_uri("/tests").to_request(); let req = TestRequest::with_uri("/tests").to_request();
let resp = test::call_service(&mut srv, req).await; let resp = test::call_service(&mut srv, req).await;

View file

@ -100,10 +100,7 @@ impl NamedFile {
None, None,
filename.into_owned().into_bytes(), filename.into_owned().into_bytes(),
)]; )];
let cd = ContentDisposition { let cd = ContentDisposition { disposition, parameters };
disposition,
parameters,
};
(ct, cd) (ct, cd)
}; };
@ -256,13 +253,15 @@ impl NamedFile {
pub fn into_response(self, req: &HttpRequest) -> HttpResponse { pub fn into_response(self, req: &HttpRequest) -> HttpResponse {
if self.status_code != StatusCode::OK { if self.status_code != StatusCode::OK {
let mut resp = HttpResponse::build(self.status_code); let mut resp = HttpResponse::build(self.status_code);
resp.header(http::header::CONTENT_TYPE, self.content_type.to_string()) resp.header(http::header::CONTENT_TYPE, self.content_type.to_string()).if_true(
.if_true(self.flags.contains(Flags::CONTENT_DISPOSITION), |res| { self.flags.contains(Flags::CONTENT_DISPOSITION),
|res| {
res.header( res.header(
http::header::CONTENT_DISPOSITION, http::header::CONTENT_DISPOSITION,
self.content_disposition.to_string(), self.content_disposition.to_string(),
); );
}); },
);
if let Some(current_encoding) = self.encoding { if let Some(current_encoding) = self.encoding {
resp.encoding(current_encoding); resp.encoding(current_encoding);
} }
@ -276,16 +275,9 @@ impl NamedFile {
return resp.streaming(reader); return resp.streaming(reader);
} }
let etag = if self.flags.contains(Flags::ETAG) { let etag = if self.flags.contains(Flags::ETAG) { self.etag() } else { None };
self.etag() let last_modified =
} else { if self.flags.contains(Flags::LAST_MD) { self.last_modified() } else { None };
None
};
let last_modified = if self.flags.contains(Flags::LAST_MD) {
self.last_modified()
} else {
None
};
// check preconditions // check preconditions
let precondition_failed = if !any_match(etag.as_ref(), req) { let precondition_failed = if !any_match(etag.as_ref(), req) {
@ -293,9 +285,9 @@ impl NamedFile {
} else if let (Some(ref m), Some(header::IfUnmodifiedSince(ref since))) = { } else if let (Some(ref m), Some(header::IfUnmodifiedSince(ref since))) = {
let mut header = None; let mut header = None;
for hdr in req.headers().get_all(http::header::IF_UNMODIFIED_SINCE) { for hdr in req.headers().get_all(http::header::IF_UNMODIFIED_SINCE) {
if let Ok(v) = header::IfUnmodifiedSince::parse_header( if let Ok(v) =
&header::Raw::from(hdr.as_bytes()), header::IfUnmodifiedSince::parse_header(&header::Raw::from(hdr.as_bytes()))
) { {
header = Some(v); header = Some(v);
break; break;
} }
@ -321,9 +313,9 @@ impl NamedFile {
} else if let (Some(ref m), Some(header::IfModifiedSince(ref since))) = { } else if let (Some(ref m), Some(header::IfModifiedSince(ref since))) = {
let mut header = None; let mut header = None;
for hdr in req.headers().get_all(http::header::IF_MODIFIED_SINCE) { for hdr in req.headers().get_all(http::header::IF_MODIFIED_SINCE) {
if let Ok(v) = header::IfModifiedSince::parse_header(&header::Raw::from( if let Ok(v) =
hdr.as_bytes(), header::IfModifiedSince::parse_header(&header::Raw::from(hdr.as_bytes()))
)) { {
header = Some(v); header = Some(v);
break; break;
} }
@ -341,23 +333,22 @@ impl NamedFile {
}; };
let mut resp = HttpResponse::build(self.status_code); let mut resp = HttpResponse::build(self.status_code);
resp.header(http::header::CONTENT_TYPE, self.content_type.to_string()) resp.header(http::header::CONTENT_TYPE, self.content_type.to_string()).if_true(
.if_true(self.flags.contains(Flags::CONTENT_DISPOSITION), |res| { self.flags.contains(Flags::CONTENT_DISPOSITION),
|res| {
res.header( res.header(
http::header::CONTENT_DISPOSITION, http::header::CONTENT_DISPOSITION,
self.content_disposition.to_string(), self.content_disposition.to_string(),
); );
}); },
);
// default compressing // default compressing
if let Some(current_encoding) = self.encoding { if let Some(current_encoding) = self.encoding {
resp.encoding(current_encoding); resp.encoding(current_encoding);
} }
resp.if_some(last_modified, |lm, resp| { resp.if_some(last_modified, |lm, resp| {
resp.header( resp.header(http::header::LAST_MODIFIED, header::LastModified(lm).to_string());
http::header::LAST_MODIFIED,
header::LastModified(lm).to_string(),
);
}) })
.if_some(etag, |etag, resp| { .if_some(etag, |etag, resp| {
resp.header(http::header::ETAG, header::ETag(etag).to_string()); resp.header(http::header::ETAG, header::ETag(etag).to_string());
@ -377,18 +368,10 @@ impl NamedFile {
resp.encoding(ContentEncoding::Identity); resp.encoding(ContentEncoding::Identity);
resp.header( resp.header(
http::header::CONTENT_RANGE, http::header::CONTENT_RANGE,
format!( format!("bytes {}-{}/{}", offset, offset + length - 1, self.md.len()),
"bytes {}-{}/{}",
offset,
offset + length - 1,
self.md.len()
),
); );
} else { } else {
resp.header( resp.header(http::header::CONTENT_RANGE, format!("bytes */{}", length));
http::header::CONTENT_RANGE,
format!("bytes */{}", length),
);
return resp.status(StatusCode::RANGE_NOT_SATISFIABLE).finish(); return resp.status(StatusCode::RANGE_NOT_SATISFIABLE).finish();
}; };
} else { } else {

View file

@ -75,10 +75,7 @@ impl HttpRange {
end - start + 1 end - start + 1
}; };
Ok(Some(HttpRange { Ok(Some(HttpRange { start: start as u64, length: length as u64 }))
start: start as u64,
length: length as u64,
}))
} }
}) })
.collect::<Result<_, _>>()?; .collect::<Result<_, _>>()?;
@ -122,180 +119,55 @@ mod tests {
T("bytes=0x01-0x02", 10, vec![]), T("bytes=0x01-0x02", 10, vec![]),
T("bytes= ", 10, vec![]), T("bytes= ", 10, vec![]),
T("bytes= , , , ", 10, vec![]), T("bytes= , , , ", 10, vec![]),
T( T("bytes=0-9", 10, vec![HttpRange { start: 0, length: 10 }]),
"bytes=0-9", T("bytes=0-", 10, vec![HttpRange { start: 0, length: 10 }]),
10, T("bytes=5-", 10, vec![HttpRange { start: 5, length: 5 }]),
vec![HttpRange { T("bytes=0-20", 10, vec![HttpRange { start: 0, length: 10 }]),
start: 0, T("bytes=15-,0-5", 10, vec![HttpRange { start: 0, length: 6 }]),
length: 10,
}],
),
T(
"bytes=0-",
10,
vec![HttpRange {
start: 0,
length: 10,
}],
),
T(
"bytes=5-",
10,
vec![HttpRange {
start: 5,
length: 5,
}],
),
T(
"bytes=0-20",
10,
vec![HttpRange {
start: 0,
length: 10,
}],
),
T(
"bytes=15-,0-5",
10,
vec![HttpRange {
start: 0,
length: 6,
}],
),
T( T(
"bytes=1-2,5-", "bytes=1-2,5-",
10, 10,
vec![ vec![HttpRange { start: 1, length: 2 }, HttpRange { start: 5, length: 5 }],
HttpRange {
start: 1,
length: 2,
},
HttpRange {
start: 5,
length: 5,
},
],
), ),
T( T(
"bytes=-2 , 7-", "bytes=-2 , 7-",
11, 11,
vec![ vec![HttpRange { start: 9, length: 2 }, HttpRange { start: 7, length: 4 }],
HttpRange {
start: 9,
length: 2,
},
HttpRange {
start: 7,
length: 4,
},
],
), ),
T( T(
"bytes=0-0 ,2-2, 7-", "bytes=0-0 ,2-2, 7-",
11, 11,
vec![ vec![
HttpRange { HttpRange { start: 0, length: 1 },
start: 0, HttpRange { start: 2, length: 1 },
length: 1, HttpRange { start: 7, length: 4 },
},
HttpRange {
start: 2,
length: 1,
},
HttpRange {
start: 7,
length: 4,
},
], ],
), ),
T( T("bytes=-5", 10, vec![HttpRange { start: 5, length: 5 }]),
"bytes=-5", T("bytes=-15", 10, vec![HttpRange { start: 0, length: 10 }]),
10, T("bytes=0-499", 10000, vec![HttpRange { start: 0, length: 500 }]),
vec![HttpRange { T("bytes=500-999", 10000, vec![HttpRange { start: 500, length: 500 }]),
start: 5, T("bytes=-500", 10000, vec![HttpRange { start: 9500, length: 500 }]),
length: 5, T("bytes=9500-", 10000, vec![HttpRange { start: 9500, length: 500 }]),
}],
),
T(
"bytes=-15",
10,
vec![HttpRange {
start: 0,
length: 10,
}],
),
T(
"bytes=0-499",
10000,
vec![HttpRange {
start: 0,
length: 500,
}],
),
T(
"bytes=500-999",
10000,
vec![HttpRange {
start: 500,
length: 500,
}],
),
T(
"bytes=-500",
10000,
vec![HttpRange {
start: 9500,
length: 500,
}],
),
T(
"bytes=9500-",
10000,
vec![HttpRange {
start: 9500,
length: 500,
}],
),
T( T(
"bytes=0-0,-1", "bytes=0-0,-1",
10000, 10000,
vec![ vec![HttpRange { start: 0, length: 1 }, HttpRange { start: 9999, length: 1 }],
HttpRange {
start: 0,
length: 1,
},
HttpRange {
start: 9999,
length: 1,
},
],
), ),
T( T(
"bytes=500-600,601-999", "bytes=500-600,601-999",
10000, 10000,
vec![ vec![
HttpRange { HttpRange { start: 500, length: 101 },
start: 500, HttpRange { start: 601, length: 399 },
length: 101,
},
HttpRange {
start: 601,
length: 399,
},
], ],
), ),
T( T(
"bytes=500-700,601-999", "bytes=500-700,601-999",
10000, 10000,
vec![ vec![
HttpRange { HttpRange { start: 500, length: 201 },
start: 500, HttpRange { start: 601, length: 399 },
length: 201,
},
HttpRange {
start: 601,
length: 399,
},
], ],
), ),
// Match Apache laxity: // Match Apache laxity:
@ -303,18 +175,9 @@ mod tests {
"bytes= 1 -2 , 4- 5, 7 - 8 , ,,", "bytes= 1 -2 , 4- 5, 7 - 8 , ,,",
11, 11,
vec![ vec![
HttpRange { HttpRange { start: 1, length: 2 },
start: 1, HttpRange { start: 4, length: 2 },
length: 2, HttpRange { start: 7, length: 2 },
},
HttpRange {
start: 4,
length: 2,
},
HttpRange {
start: 7,
length: 2,
},
], ],
), ),
]; ];

View file

@ -62,9 +62,7 @@ use ntex::http::header::{self, HeaderValue};
use ntex::http::{Extensions, HttpMessage, Payload}; use ntex::http::{Extensions, HttpMessage, Payload};
use ntex::service::{Service, Transform}; use ntex::service::{Service, Transform};
use ntex::web::dev::{WebRequest, WebResponse}; use ntex::web::dev::{WebRequest, WebResponse};
use ntex::web::{ use ntex::web::{DefaultError, ErrorRenderer, FromRequest, HttpRequest, WebResponseError};
DefaultError, ErrorRenderer, FromRequest, HttpRequest, WebResponseError,
};
/// The extractor type to obtain your identity from a request. /// The extractor type to obtain your identity from a request.
/// ///
@ -219,10 +217,7 @@ pub struct IdentityService<T, Err> {
impl<T, Err> IdentityService<T, Err> { impl<T, Err> IdentityService<T, Err> {
/// Create new identity service with specified backend. /// Create new identity service with specified backend.
pub fn new(backend: T) -> Self { pub fn new(backend: T) -> Self {
IdentityService { IdentityService { backend: Rc::new(backend), _t: PhantomData }
backend: Rc::new(backend),
_t: PhantomData,
}
} }
} }
@ -260,11 +255,7 @@ pub struct IdentityServiceMiddleware<S, T, Err> {
impl<S, T, Err> Clone for IdentityServiceMiddleware<S, T, Err> { impl<S, T, Err> Clone for IdentityServiceMiddleware<S, T, Err> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self { backend: self.backend.clone(), service: self.service.clone(), _t: PhantomData }
backend: self.backend.clone(),
service: self.service.clone(),
_t: PhantomData,
}
} }
} }
@ -298,8 +289,7 @@ where
async move { async move {
match fut.await { match fut.await {
Ok(id) => { Ok(id) => {
req.extensions_mut() req.extensions_mut().insert(IdentityItem { id, changed: false });
.insert(IdentityItem { id, changed: false });
// https://github.com/actix/actix-web/issues/1263 // https://github.com/actix/actix-web/issues/1263
let fut = { srv.call(req) }; let fut = { srv.call(req) };
@ -400,11 +390,7 @@ impl<Err: ErrorRenderer> CookieIdentityInner<Err> {
} }
let mut jar = CookieJar::new(); let mut jar = CookieJar::new();
let key = if self.legacy_supported() { let key = if self.legacy_supported() { &self.key } else { &self.key_v2 };
&self.key
} else {
&self.key_v2
};
if add_cookie { if add_cookie {
jar.private(&key).add(cookie); jar.private(&key).add(cookie);
} else { } else {
@ -412,8 +398,7 @@ impl<Err: ErrorRenderer> CookieIdentityInner<Err> {
jar.private(&key).remove(cookie); jar.private(&key).remove(cookie);
} }
for cookie in jar.delta() { for cookie in jar.delta() {
let val = let val = HeaderValue::from_str(&cookie.to_string()).map_err(HttpError::from)?;
HeaderValue::from_str(&cookie.to_string()).map_err(HttpError::from)?;
resp.headers_mut().append(header::SET_COOKIE, val); resp.headers_mut().append(header::SET_COOKIE, val);
} }
Ok(()) Ok(())
@ -432,11 +417,7 @@ impl<Err: ErrorRenderer> CookieIdentityInner<Err> {
} else { } else {
None None
}; };
res.or_else(|| { res.or_else(|| jar.private(&self.key_v2).get(&self.name).and_then(|c| self.parse(c)))
jar.private(&self.key_v2)
.get(&self.name)
.and_then(|c| self.parse(c))
})
} }
fn parse(&self, cookie: Cookie) -> Option<CookieValue> { fn parse(&self, cookie: Cookie) -> Option<CookieValue> {
@ -575,19 +556,12 @@ impl<Err: ErrorRenderer> IdentityPolicy<Err> for CookieIdentityPolicy<Err> {
type ResponseFuture = Ready<Result<(), CookieIdentityPolicyError>>; type ResponseFuture = Ready<Result<(), CookieIdentityPolicyError>>;
fn from_request(&self, req: &mut WebRequest<Err>) -> Self::Future { fn from_request(&self, req: &mut WebRequest<Err>) -> Self::Future {
ok(self.0.load(req).map( ok(self.0.load(req).map(|CookieValue { identity, login_timestamp, .. }| {
|CookieValue { if self.0.requires_oob_data() {
identity, req.extensions_mut().insert(CookieIdentityExtention { login_timestamp });
login_timestamp, }
.. identity
}| { }))
if self.0.requires_oob_data() {
req.extensions_mut()
.insert(CookieIdentityExtention { login_timestamp });
}
identity
},
))
} }
fn to_response( fn to_response(
@ -609,9 +583,8 @@ impl<Err: ErrorRenderer> IdentityPolicy<Err> for CookieIdentityPolicy<Err> {
} else if self.0.always_update_cookie() && id.is_some() { } else if self.0.always_update_cookie() && id.is_some() {
let visit_timestamp = SystemTime::now(); let visit_timestamp = SystemTime::now();
let login_timestamp = if self.0.requires_oob_data() { let login_timestamp = if self.0.requires_oob_data() {
let CookieIdentityExtention { let CookieIdentityExtention { login_timestamp: lt } =
login_timestamp: lt, res.request().extensions_mut().remove().unwrap();
} = res.request().extensions_mut().remove().unwrap();
lt lt
} else { } else {
None None
@ -678,30 +651,24 @@ mod tests {
) )
.await; .await;
let resp = let resp =
test::call_service(&mut srv, TestRequest::with_uri("/index").to_request()) test::call_service(&mut srv, TestRequest::with_uri("/index").to_request()).await;
.await;
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
let resp = let resp =
test::call_service(&mut srv, TestRequest::with_uri("/login").to_request()) test::call_service(&mut srv, TestRequest::with_uri("/login").to_request()).await;
.await;
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
let c = resp.response().cookies().next().unwrap().to_owned(); let c = resp.response().cookies().next().unwrap().to_owned();
let resp = test::call_service( let resp = test::call_service(
&mut srv, &mut srv,
TestRequest::with_uri("/index") TestRequest::with_uri("/index").cookie(c.clone()).to_request(),
.cookie(c.clone())
.to_request(),
) )
.await; .await;
assert_eq!(resp.status(), StatusCode::CREATED); assert_eq!(resp.status(), StatusCode::CREATED);
let resp = test::call_service( let resp = test::call_service(
&mut srv, &mut srv,
TestRequest::with_uri("/logout") TestRequest::with_uri("/logout").cookie(c.clone()).to_request(),
.cookie(c.clone())
.to_request(),
) )
.await; .await;
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
@ -728,8 +695,7 @@ mod tests {
) )
.await; .await;
let resp = let resp =
test::call_service(&mut srv, TestRequest::with_uri("/login").to_request()) test::call_service(&mut srv, TestRequest::with_uri("/login").to_request()).await;
.await;
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert!(resp.headers().contains_key(header::SET_COOKIE)); assert!(resp.headers().contains_key(header::SET_COOKIE));
let c = resp.response().cookies().next().unwrap().to_owned(); let c = resp.response().cookies().next().unwrap().to_owned();
@ -756,8 +722,7 @@ mod tests {
) )
.await; .await;
let resp = let resp =
test::call_service(&mut srv, TestRequest::with_uri("/login").to_request()) test::call_service(&mut srv, TestRequest::with_uri("/login").to_request()).await;
.await;
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);
assert!(resp.headers().contains_key(header::SET_COOKIE)); assert!(resp.headers().contains_key(header::SET_COOKIE));
let c = resp.response().cookies().next().unwrap().to_owned(); let c = resp.response().cookies().next().unwrap().to_owned();
@ -779,11 +744,9 @@ mod tests {
> { > {
test::init_service( test::init_service(
App::new() App::new()
.wrap(IdentityService::new(f(CookieIdentityPolicy::new( .wrap(IdentityService::new(f(CookieIdentityPolicy::new(&COOKIE_KEY_MASTER)
&COOKIE_KEY_MASTER, .secure(false)
) .name(COOKIE_NAME))))
.secure(false)
.name(COOKIE_NAME))))
.service(web::resource("/").to(|id: Identity| async move { .service(web::resource("/").to(|id: Identity| async move {
let identity = id.identity(); let identity = id.identity();
if identity.is_none() { if identity.is_none() {
@ -808,11 +771,8 @@ mod tests {
visit_timestamp: Option<SystemTime>, visit_timestamp: Option<SystemTime>,
) -> Cookie<'static> { ) -> Cookie<'static> {
let mut jar = CookieJar::new(); let mut jar = CookieJar::new();
let key: Vec<u8> = COOKIE_KEY_MASTER let key: Vec<u8> =
.iter() COOKIE_KEY_MASTER.iter().chain([1, 0, 0, 0].iter()).map(|e| *e).collect();
.chain([1, 0, 0, 0].iter())
.map(|e| *e)
.collect();
jar.private(&Key::derive_from(&key)).add(Cookie::new( jar.private(&Key::derive_from(&key)).add(Cookie::new(
COOKIE_NAME, COOKIE_NAME,
serde_json::to_string(&CookieValue { serde_json::to_string(&CookieValue {
@ -836,10 +796,8 @@ mod tests {
for cookie in response.headers().get_all(header::SET_COOKIE) { for cookie in response.headers().get_all(header::SET_COOKIE) {
cookies.add(Cookie::parse(cookie.to_str().unwrap().to_string()).unwrap()); cookies.add(Cookie::parse(cookie.to_str().unwrap().to_string()).unwrap());
} }
let cookie = cookies let cookie =
.private(&Key::derive_from(&COOKIE_KEY_MASTER)) cookies.private(&Key::derive_from(&COOKIE_KEY_MASTER)).get(COOKIE_NAME).unwrap();
.get(COOKIE_NAME)
.unwrap();
assert_eq!(cookie.value(), identity); assert_eq!(cookie.value(), identity);
} }
@ -864,15 +822,9 @@ mod tests {
for cookie in response.headers().get_all(header::SET_COOKIE) { for cookie in response.headers().get_all(header::SET_COOKIE) {
cookies.add(Cookie::parse(cookie.to_str().unwrap().to_string()).unwrap()); cookies.add(Cookie::parse(cookie.to_str().unwrap().to_string()).unwrap());
} }
let key: Vec<u8> = COOKIE_KEY_MASTER let key: Vec<u8> =
.iter() COOKIE_KEY_MASTER.iter().chain([1, 0, 0, 0].iter()).map(|e| *e).collect();
.chain([1, 0, 0, 0].iter()) let cookie = cookies.private(&Key::derive_from(&key)).get(COOKIE_NAME).unwrap();
.map(|e| *e)
.collect();
let cookie = cookies
.private(&Key::derive_from(&key))
.get(COOKIE_NAME)
.unwrap();
let cv: CookieValue = serde_json::from_str(cookie.value()).unwrap(); let cv: CookieValue = serde_json::from_str(cookie.value()).unwrap();
assert_eq!(cv.identity, identity); assert_eq!(cv.identity, identity);
let now = SystemTime::now(); let now = SystemTime::now();
@ -880,8 +832,7 @@ mod tests {
match login_timestamp { match login_timestamp {
LoginTimestampCheck::NoTimestamp => assert_eq!(cv.login_timestamp, None), LoginTimestampCheck::NoTimestamp => assert_eq!(cv.login_timestamp, None),
LoginTimestampCheck::NewTimestamp => assert!( LoginTimestampCheck::NewTimestamp => assert!(
t30sec_ago <= cv.login_timestamp.unwrap() t30sec_ago <= cv.login_timestamp.unwrap() && cv.login_timestamp.unwrap() <= now
&& cv.login_timestamp.unwrap() <= now
), ),
LoginTimestampCheck::OldTimestamp(old_timestamp) => { LoginTimestampCheck::OldTimestamp(old_timestamp) => {
assert_eq!(cv.login_timestamp, Some(old_timestamp)) assert_eq!(cv.login_timestamp, Some(old_timestamp))
@ -890,8 +841,7 @@ mod tests {
match visit_timestamp { match visit_timestamp {
VisitTimeStampCheck::NoTimestamp => assert_eq!(cv.visit_timestamp, None), VisitTimeStampCheck::NoTimestamp => assert_eq!(cv.visit_timestamp, None),
VisitTimeStampCheck::NewTimestamp => assert!( VisitTimeStampCheck::NewTimestamp => assert!(
t30sec_ago <= cv.visit_timestamp.unwrap() t30sec_ago <= cv.visit_timestamp.unwrap() && cv.visit_timestamp.unwrap() <= now
&& cv.visit_timestamp.unwrap() <= now
), ),
} }
} }
@ -919,9 +869,7 @@ mod tests {
let cookie = legacy_login_cookie(COOKIE_LOGIN); let cookie = legacy_login_cookie(COOKIE_LOGIN);
let mut resp = test::call_service( let mut resp = test::call_service(
&mut srv, &mut srv,
TestRequest::with_uri("/") TestRequest::with_uri("/").cookie(cookie.clone()).to_request(),
.cookie(cookie.clone())
.to_request(),
) )
.await; .await;
assert_no_login_cookie(&mut resp); assert_no_login_cookie(&mut resp);
@ -930,14 +878,11 @@ mod tests {
#[ntex::test] #[ntex::test]
async fn test_identity_legacy_cookie_rejected_if_visit_timestamp_needed() { async fn test_identity_legacy_cookie_rejected_if_visit_timestamp_needed() {
let mut srv = let mut srv = create_identity_server(|c| c.visit_deadline(Duration::days(90))).await;
create_identity_server(|c| c.visit_deadline(Duration::days(90))).await;
let cookie = legacy_login_cookie(COOKIE_LOGIN); let cookie = legacy_login_cookie(COOKIE_LOGIN);
let mut resp = test::call_service( let mut resp = test::call_service(
&mut srv, &mut srv,
TestRequest::with_uri("/") TestRequest::with_uri("/").cookie(cookie.clone()).to_request(),
.cookie(cookie.clone())
.to_request(),
) )
.await; .await;
assert_login_cookie( assert_login_cookie(
@ -951,14 +896,11 @@ mod tests {
#[ntex::test] #[ntex::test]
async fn test_identity_legacy_cookie_rejected_if_login_timestamp_needed() { async fn test_identity_legacy_cookie_rejected_if_login_timestamp_needed() {
let mut srv = let mut srv = create_identity_server(|c| c.login_deadline(Duration::days(90))).await;
create_identity_server(|c| c.login_deadline(Duration::days(90))).await;
let cookie = legacy_login_cookie(COOKIE_LOGIN); let cookie = legacy_login_cookie(COOKIE_LOGIN);
let mut resp = test::call_service( let mut resp = test::call_service(
&mut srv, &mut srv,
TestRequest::with_uri("/") TestRequest::with_uri("/").cookie(cookie.clone()).to_request(),
.cookie(cookie.clone())
.to_request(),
) )
.await; .await;
assert_login_cookie( assert_login_cookie(
@ -972,14 +914,11 @@ mod tests {
#[ntex::test] #[ntex::test]
async fn test_identity_cookie_rejected_if_login_timestamp_needed() { async fn test_identity_cookie_rejected_if_login_timestamp_needed() {
let mut srv = let mut srv = create_identity_server(|c| c.login_deadline(Duration::days(90))).await;
create_identity_server(|c| c.login_deadline(Duration::days(90))).await;
let cookie = login_cookie(COOKIE_LOGIN, None, Some(SystemTime::now())); let cookie = login_cookie(COOKIE_LOGIN, None, Some(SystemTime::now()));
let mut resp = test::call_service( let mut resp = test::call_service(
&mut srv, &mut srv,
TestRequest::with_uri("/") TestRequest::with_uri("/").cookie(cookie.clone()).to_request(),
.cookie(cookie.clone())
.to_request(),
) )
.await; .await;
assert_login_cookie( assert_login_cookie(
@ -993,14 +932,11 @@ mod tests {
#[ntex::test] #[ntex::test]
async fn test_identity_cookie_rejected_if_visit_timestamp_needed() { async fn test_identity_cookie_rejected_if_visit_timestamp_needed() {
let mut srv = let mut srv = create_identity_server(|c| c.visit_deadline(Duration::days(90))).await;
create_identity_server(|c| c.visit_deadline(Duration::days(90))).await;
let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now()), None); let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now()), None);
let mut resp = test::call_service( let mut resp = test::call_service(
&mut srv, &mut srv,
TestRequest::with_uri("/") TestRequest::with_uri("/").cookie(cookie.clone()).to_request(),
.cookie(cookie.clone())
.to_request(),
) )
.await; .await;
assert_login_cookie( assert_login_cookie(
@ -1014,18 +950,12 @@ mod tests {
#[ntex::test] #[ntex::test]
async fn test_identity_cookie_rejected_if_login_timestamp_too_old() { async fn test_identity_cookie_rejected_if_login_timestamp_too_old() {
let mut srv = let mut srv = create_identity_server(|c| c.login_deadline(Duration::days(90))).await;
create_identity_server(|c| c.login_deadline(Duration::days(90))).await; let cookie =
let cookie = login_cookie( login_cookie(COOKIE_LOGIN, Some(SystemTime::now() - Duration::days(180)), None);
COOKIE_LOGIN,
Some(SystemTime::now() - Duration::days(180)),
None,
);
let mut resp = test::call_service( let mut resp = test::call_service(
&mut srv, &mut srv,
TestRequest::with_uri("/") TestRequest::with_uri("/").cookie(cookie.clone()).to_request(),
.cookie(cookie.clone())
.to_request(),
) )
.await; .await;
assert_login_cookie( assert_login_cookie(
@ -1039,18 +969,12 @@ mod tests {
#[ntex::test] #[ntex::test]
async fn test_identity_cookie_rejected_if_visit_timestamp_too_old() { async fn test_identity_cookie_rejected_if_visit_timestamp_too_old() {
let mut srv = let mut srv = create_identity_server(|c| c.visit_deadline(Duration::days(90))).await;
create_identity_server(|c| c.visit_deadline(Duration::days(90))).await; let cookie =
let cookie = login_cookie( login_cookie(COOKIE_LOGIN, None, Some(SystemTime::now() - Duration::days(180)));
COOKIE_LOGIN,
None,
Some(SystemTime::now() - Duration::days(180)),
);
let mut resp = test::call_service( let mut resp = test::call_service(
&mut srv, &mut srv,
TestRequest::with_uri("/") TestRequest::with_uri("/").cookie(cookie.clone()).to_request(),
.cookie(cookie.clone())
.to_request(),
) )
.await; .await;
assert_login_cookie( assert_login_cookie(
@ -1064,14 +988,11 @@ mod tests {
#[ntex::test] #[ntex::test]
async fn test_identity_cookie_not_updated_on_login_deadline() { async fn test_identity_cookie_not_updated_on_login_deadline() {
let mut srv = let mut srv = create_identity_server(|c| c.login_deadline(Duration::days(90))).await;
create_identity_server(|c| c.login_deadline(Duration::days(90))).await;
let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now()), None); let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now()), None);
let mut resp = test::call_service( let mut resp = test::call_service(
&mut srv, &mut srv,
TestRequest::with_uri("/") TestRequest::with_uri("/").cookie(cookie.clone()).to_request(),
.cookie(cookie.clone())
.to_request(),
) )
.await; .await;
assert_no_login_cookie(&mut resp); assert_no_login_cookie(&mut resp);
@ -1082,17 +1003,14 @@ mod tests {
#[ntex::test] #[ntex::test]
async fn test_identity_cookie_updated_on_visit_deadline() { async fn test_identity_cookie_updated_on_visit_deadline() {
let mut srv = create_identity_server(|c| { let mut srv = create_identity_server(|c| {
c.visit_deadline(Duration::days(90)) c.visit_deadline(Duration::days(90)).login_deadline(Duration::days(90))
.login_deadline(Duration::days(90))
}) })
.await; .await;
let timestamp = SystemTime::now() - Duration::days(1); let timestamp = SystemTime::now() - Duration::days(1);
let cookie = login_cookie(COOKIE_LOGIN, Some(timestamp), Some(timestamp)); let cookie = login_cookie(COOKIE_LOGIN, Some(timestamp), Some(timestamp));
let mut resp = test::call_service( let mut resp = test::call_service(
&mut srv, &mut srv,
TestRequest::with_uri("/") TestRequest::with_uri("/").cookie(cookie.clone()).to_request(),
.cookie(cookie.clone())
.to_request(),
) )
.await; .await;
assert_login_cookie( assert_login_cookie(

View file

@ -71,11 +71,7 @@ impl Multipart {
item: InnerMultipartItem::None, item: InnerMultipartItem::None,
}))), }))),
}, },
Err(err) => Multipart { Err(err) => Multipart { error: Some(err), safety: Safety::new(), inner: None },
error: Some(err),
safety: Safety::new(),
inner: None,
},
} }
} }
@ -104,10 +100,7 @@ impl Multipart {
impl Stream for Multipart { impl Stream for Multipart {
type Item = Result<Field, MultipartError>; type Item = Result<Field, MultipartError>;
fn poll_next( fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
mut self: Pin<&mut Self>,
cx: &mut Context,
) -> Poll<Option<Self::Item>> {
if let Some(err) = self.error.take() { if let Some(err) = self.error.take() {
Poll::Ready(Some(Err(err))) Poll::Ready(Some(Err(err)))
} else if self.safety.current() { } else if self.safety.current() {
@ -126,9 +119,7 @@ impl Stream for Multipart {
} }
impl InnerMultipart { impl InnerMultipart {
fn read_headers( fn read_headers(payload: &mut PayloadBuffer) -> Result<Option<HeaderMap>, MultipartError> {
payload: &mut PayloadBuffer,
) -> Result<Option<HeaderMap>, MultipartError> {
match payload.read_until(b"\r\n\r\n")? { match payload.read_until(b"\r\n\r\n")? {
None => { None => {
if payload.eof { if payload.eof {
@ -210,8 +201,7 @@ impl InnerMultipart {
if chunk.len() < boundary.len() { if chunk.len() < boundary.len() {
continue; continue;
} }
if &chunk[..2] == b"--" if &chunk[..2] == b"--" && &chunk[2..chunk.len() - 2] == boundary.as_bytes()
&& &chunk[2..chunk.len() - 2] == boundary.as_bytes()
{ {
break; break;
} else { } else {
@ -257,9 +247,7 @@ impl InnerMultipart {
match field.borrow_mut().poll(safety) { match field.borrow_mut().poll(safety) {
Poll::Pending => return Poll::Pending, Poll::Pending => return Poll::Pending,
Poll::Ready(Some(Ok(_))) => continue, Poll::Ready(Some(Ok(_))) => continue,
Poll::Ready(Some(Err(e))) => { Poll::Ready(Some(Err(e))) => return Poll::Ready(Some(Err(e))),
return Poll::Ready(Some(Err(e)))
}
Poll::Ready(None) => true, Poll::Ready(None) => true,
} }
} }
@ -295,10 +283,7 @@ impl InnerMultipart {
} }
// read boundary // read boundary
InnerState::Boundary => { InnerState::Boundary => {
match InnerMultipart::read_boundary( match InnerMultipart::read_boundary(&mut *payload, &self.boundary)? {
&mut *payload,
&self.boundary,
)? {
None => return Poll::Pending, None => return Poll::Pending,
Some(eof) => { Some(eof) => {
if eof { if eof {
@ -380,12 +365,7 @@ impl Field {
ct: mime::Mime, ct: mime::Mime,
inner: Rc<RefCell<InnerField>>, inner: Rc<RefCell<InnerField>>,
) -> Self { ) -> Self {
Field { Field { ct, headers, inner, safety }
ct,
headers,
inner,
safety,
}
} }
/// Get a map of headers /// Get a map of headers
@ -405,9 +385,7 @@ impl Stream for Field {
fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
if self.safety.current() { if self.safety.current() {
let mut inner = self.inner.borrow_mut(); let mut inner = self.inner.borrow_mut();
if let Some(mut payload) = if let Some(mut payload) = inner.payload.as_ref().unwrap().get_mut(&self.safety) {
inner.payload.as_ref().unwrap().get_mut(&self.safety)
{
payload.poll_stream(cx)?; payload.poll_stream(cx)?;
} }
inner.poll(&self.safety) inner.poll(&self.safety)
@ -458,12 +436,7 @@ impl InnerField {
None None
}; };
Ok(InnerField { Ok(InnerField { boundary, payload: Some(payload), eof: false, length: len })
boundary,
payload: Some(payload),
eof: false,
length: len,
})
} }
/// Reads body part content chunk of the specified size. /// Reads body part content chunk of the specified size.
@ -576,8 +549,7 @@ impl InnerField {
return Poll::Ready(None); return Poll::Ready(None);
} }
let result = if let Some(mut payload) = self.payload.as_ref().unwrap().get_mut(s) let result = if let Some(mut payload) = self.payload.as_ref().unwrap().get_mut(s) {
{
if !self.eof { if !self.eof {
let res = if let Some(ref mut len) = self.length { let res = if let Some(ref mut len) = self.length {
InnerField::read_len(&mut *payload, len) InnerField::read_len(&mut *payload, len)
@ -597,7 +569,9 @@ impl InnerField {
Ok(None) => Poll::Pending, Ok(None) => Poll::Pending,
Ok(Some(line)) => { Ok(Some(line)) => {
if line.as_ref() != b"\r\n" { if line.as_ref() != b"\r\n" {
log::warn!("multipart field did not read all the data or it is malformed"); log::warn!(
"multipart field did not read all the data or it is malformed"
);
} }
Poll::Ready(None) Poll::Ready(None)
} }
@ -620,9 +594,7 @@ struct PayloadRef {
impl PayloadRef { impl PayloadRef {
fn new(payload: PayloadBuffer) -> PayloadRef { fn new(payload: PayloadBuffer) -> PayloadRef {
PayloadRef { PayloadRef { payload: Rc::new(payload.into()) }
payload: Rc::new(payload.into()),
}
} }
fn get_mut<'a, 'b>(&'a self, s: &'b Safety) -> Option<RefMut<'a, PayloadBuffer>> fn get_mut<'a, 'b>(&'a self, s: &'b Safety) -> Option<RefMut<'a, PayloadBuffer>>
@ -639,9 +611,7 @@ impl PayloadRef {
impl Clone for PayloadRef { impl Clone for PayloadRef {
fn clone(&self) -> PayloadRef { fn clone(&self) -> PayloadRef {
PayloadRef { PayloadRef { payload: Rc::clone(&self.payload) }
payload: Rc::clone(&self.payload),
}
} }
} }
@ -713,11 +683,7 @@ impl PayloadBuffer {
where where
S: Stream<Item = Result<Bytes, PayloadError>> + 'static, S: Stream<Item = Result<Bytes, PayloadError>> + 'static,
{ {
PayloadBuffer { PayloadBuffer { eof: false, buf: BytesMut::new(), stream: stream.boxed_local() }
eof: false,
buf: BytesMut::new(),
stream: stream.boxed_local(),
}
} }
fn poll_stream(&mut self, cx: &mut Context) -> Result<(), PayloadError> { fn poll_stream(&mut self, cx: &mut Context) -> Result<(), PayloadError> {
@ -775,9 +741,7 @@ impl PayloadBuffer {
/// Read bytes until new line delimiter or eof /// Read bytes until new line delimiter or eof
pub fn readline_or_eof(&mut self) -> Result<Option<Bytes>, MultipartError> { pub fn readline_or_eof(&mut self) -> Result<Option<Bytes>, MultipartError> {
match self.readline() { match self.readline() {
Err(MultipartError::Incomplete) if self.eof => { Err(MultipartError::Incomplete) if self.eof => Ok(Some(self.buf.split().freeze())),
Ok(Some(self.buf.split().freeze()))
}
line => line, line => line,
} }
} }
@ -808,10 +772,7 @@ mod tests {
} }
let mut headers = HeaderMap::new(); let mut headers = HeaderMap::new();
headers.insert( headers.insert(header::CONTENT_TYPE, header::HeaderValue::from_static("test"));
header::CONTENT_TYPE,
header::HeaderValue::from_static("test"),
);
match Multipart::boundary(&headers) { match Multipart::boundary(&headers) {
Err(MultipartError::ParseContentType) => (), Err(MultipartError::ParseContentType) => (),
@ -819,10 +780,8 @@ mod tests {
} }
let mut headers = HeaderMap::new(); let mut headers = HeaderMap::new();
headers.insert( headers
header::CONTENT_TYPE, .insert(header::CONTENT_TYPE, header::HeaderValue::from_static("multipart/mixed"));
header::HeaderValue::from_static("multipart/mixed"),
);
match Multipart::boundary(&headers) { match Multipart::boundary(&headers) {
Err(MultipartError::Boundary) => (), Err(MultipartError::Boundary) => (),
_ => unreachable!("should not happen"), _ => unreachable!("should not happen"),
@ -836,10 +795,7 @@ mod tests {
), ),
); );
assert_eq!( assert_eq!(Multipart::boundary(&headers).unwrap(), "5c02368e880e436dab70ed54e1c58209");
Multipart::boundary(&headers).unwrap(),
"5c02368e880e436dab70ed54e1c58209"
);
} }
fn create_stream() -> ( fn create_stream() -> (
@ -859,21 +815,14 @@ mod tests {
impl SlowStream { impl SlowStream {
fn new(bytes: Bytes) -> SlowStream { fn new(bytes: Bytes) -> SlowStream {
return SlowStream { return SlowStream { bytes, pos: 0, ready: false };
bytes,
pos: 0,
ready: false,
};
} }
} }
impl Stream for SlowStream { impl Stream for SlowStream {
type Item = Result<Bytes, PayloadError>; type Item = Result<Bytes, PayloadError>;
fn poll_next( fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
self: Pin<&mut Self>,
cx: &mut Context,
) -> Poll<Option<Self::Item>> {
let this = self.get_mut(); let this = self.get_mut();
if !this.ready { if !this.ready {
this.ready = true; this.ready = true;
@ -1112,16 +1061,10 @@ mod tests {
sender.feed_data(Bytes::from("line2")); sender.feed_data(Bytes::from("line2"));
lazy(|cx| payload.poll_stream(cx)).await.unwrap(); lazy(|cx| payload.poll_stream(cx)).await.unwrap();
assert_eq!( assert_eq!(Some(Bytes::from("line")), payload.read_until(b"ne").unwrap());
Some(Bytes::from("line")),
payload.read_until(b"ne").unwrap()
);
assert_eq!(payload.buf.len(), 6); assert_eq!(payload.buf.len(), 6);
assert_eq!( assert_eq!(Some(Bytes::from("1line2")), payload.read_until(b"2").unwrap());
Some(Bytes::from("1line2")),
payload.read_until(b"2").unwrap()
);
assert_eq!(payload.buf.len(), 0); assert_eq!(payload.buf.len(), 0);
} }
} }

View file

@ -89,8 +89,7 @@ impl<Err> CookieSessionInner<Err> {
state: impl Iterator<Item = (String, String)>, state: impl Iterator<Item = (String, String)>,
) -> Result<(), CookieSessionError> { ) -> Result<(), CookieSessionError> {
let state: HashMap<String, String> = state.collect(); let state: HashMap<String, String> = state.collect();
let value = let value = serde_json::to_string(&state).map_err(CookieSessionError::Serialize)?;
serde_json::to_string(&state).map_err(CookieSessionError::Serialize)?;
if value.len() > 4064 { if value.len() > 4064 {
return Err(CookieSessionError::Overflow); return Err(CookieSessionError::Overflow);
} }
@ -153,9 +152,7 @@ impl<Err> CookieSessionInner<Err> {
let cookie_opt = match self.security { let cookie_opt = match self.security {
CookieSecurity::Signed => jar.signed(&self.key).get(&self.name), CookieSecurity::Signed => jar.signed(&self.key).get(&self.name),
CookieSecurity::Private => { CookieSecurity::Private => jar.private(&self.key).get(&self.name),
jar.private(&self.key).get(&self.name)
}
}; };
if let Some(cookie) = cookie_opt { if let Some(cookie) = cookie_opt {
if let Ok(val) = serde_json::from_str(cookie.value()) { if let Ok(val) = serde_json::from_str(cookie.value()) {
@ -216,20 +213,14 @@ impl<Err> CookieSession<Err> {
/// ///
/// Panics if key length is less than 32 bytes. /// Panics if key length is less than 32 bytes.
pub fn signed(key: &[u8]) -> Self { pub fn signed(key: &[u8]) -> Self {
CookieSession(Rc::new(CookieSessionInner::new( CookieSession(Rc::new(CookieSessionInner::new(key, CookieSecurity::Signed)))
key,
CookieSecurity::Signed,
)))
} }
/// Construct new *private* `CookieSessionBackend` instance. /// Construct new *private* `CookieSessionBackend` instance.
/// ///
/// Panics if key length is less than 32 bytes. /// Panics if key length is less than 32 bytes.
pub fn private(key: &[u8]) -> Self { pub fn private(key: &[u8]) -> Self {
CookieSession(Rc::new(CookieSessionInner::new( CookieSession(Rc::new(CookieSessionInner::new(key, CookieSecurity::Private)))
key,
CookieSecurity::Private,
)))
} }
/// Sets the `path` field in the session cookie being built. /// Sets the `path` field in the session cookie being built.
@ -310,10 +301,7 @@ where
type Future = Ready<Result<Self::Transform, Self::InitError>>; type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future { fn new_transform(&self, service: S) -> Self::Future {
ok(CookieSessionMiddleware { ok(CookieSessionMiddleware { service, inner: self.0.clone() })
service,
inner: self.0.clone(),
})
} }
} }
@ -400,64 +388,52 @@ mod tests {
#[ntex::test] #[ntex::test]
async fn cookie_session() { async fn cookie_session() {
let app = test::init_service( let app = test::init_service(
App::new() App::new().wrap(CookieSession::signed(&[0; 32]).secure(false)).service(
.wrap(CookieSession::signed(&[0; 32]).secure(false)) web::resource("/").to(|ses: Session| async move {
.service(web::resource("/").to(|ses: Session| async move {
let _ = ses.set("counter", 100); let _ = ses.set("counter", 100);
"test" "test"
})), }),
),
) )
.await; .await;
let request = test::TestRequest::get().to_request(); let request = test::TestRequest::get().to_request();
let response = app.call(request).await.unwrap(); let response = app.call(request).await.unwrap();
assert!(response assert!(response.response().cookies().find(|c| c.name() == "ntex-session").is_some());
.response()
.cookies()
.find(|c| c.name() == "ntex-session")
.is_some());
} }
#[ntex::test] #[ntex::test]
async fn private_cookie() { async fn private_cookie() {
let app = test::init_service( let app = test::init_service(
App::new() App::new().wrap(CookieSession::private(&[0; 32]).secure(false)).service(
.wrap(CookieSession::private(&[0; 32]).secure(false)) web::resource("/").to(|ses: Session| async move {
.service(web::resource("/").to(|ses: Session| async move {
let _ = ses.set("counter", 100); let _ = ses.set("counter", 100);
"test" "test"
})), }),
),
) )
.await; .await;
let request = test::TestRequest::get().to_request(); let request = test::TestRequest::get().to_request();
let response = app.call(request).await.unwrap(); let response = app.call(request).await.unwrap();
assert!(response assert!(response.response().cookies().find(|c| c.name() == "ntex-session").is_some());
.response()
.cookies()
.find(|c| c.name() == "ntex-session")
.is_some());
} }
#[ntex::test] #[ntex::test]
async fn cookie_session_extractor() { async fn cookie_session_extractor() {
let app = test::init_service( let app = test::init_service(
App::new() App::new().wrap(CookieSession::signed(&[0; 32]).secure(false)).service(
.wrap(CookieSession::signed(&[0; 32]).secure(false)) web::resource("/").to(|ses: Session| async move {
.service(web::resource("/").to(|ses: Session| async move {
let _ = ses.set("counter", 100); let _ = ses.set("counter", 100);
"test" "test"
})), }),
),
) )
.await; .await;
let request = test::TestRequest::get().to_request(); let request = test::TestRequest::get().to_request();
let response = app.call(request).await.unwrap(); let response = app.call(request).await.unwrap();
assert!(response assert!(response.response().cookies().find(|c| c.name() == "ntex-session").is_some());
.response()
.cookies()
.find(|c| c.name() == "ntex-session")
.is_some());
} }
#[ntex::test] #[ntex::test]
@ -486,17 +462,11 @@ mod tests {
let request = test::TestRequest::get().to_request(); let request = test::TestRequest::get().to_request();
let response = app.call(request).await.unwrap(); let response = app.call(request).await.unwrap();
let cookie = response let cookie =
.response() response.response().cookies().find(|c| c.name() == "ntex-test").unwrap().clone();
.cookies()
.find(|c| c.name() == "ntex-test")
.unwrap()
.clone();
assert_eq!(cookie.path().unwrap(), "/test/"); assert_eq!(cookie.path().unwrap(), "/test/");
let request = test::TestRequest::with_uri("/test/") let request = test::TestRequest::with_uri("/test/").cookie(cookie).to_request();
.cookie(cookie)
.to_request();
let body = test::read_response(&app, request).await; let body = test::read_response(&app, request).await;
assert_eq!(body, Bytes::from_static(b"counter: 100")); assert_eq!(body, Bytes::from_static(b"counter: 100"));
} }
@ -510,10 +480,7 @@ mod tests {
let _ = ses.set("counter", 100); let _ = ses.set("counter", 100);
"test" "test"
})) }))
.service( .service(web::resource("/test/").to(|| async move { "no-changes-in-session" })),
web::resource("/test/")
.to(|| async move { "no-changes-in-session" }),
),
) )
.await; .await;

View file

@ -139,9 +139,7 @@ impl Session {
let mut inner = self.0.borrow_mut(); let mut inner = self.0.borrow_mut();
if inner.status != SessionStatus::Purged { if inner.status != SessionStatus::Purged {
inner.status = SessionStatus::Changed; inner.status = SessionStatus::Changed;
inner inner.state.insert(key.to_owned(), serde_json::to_string(&value)?);
.state
.insert(key.to_owned(), serde_json::to_string(&value)?);
} }
Ok(()) Ok(())
} }
@ -190,17 +188,9 @@ impl Session {
pub fn get_changes( pub fn get_changes(
res: &mut WebResponse, res: &mut WebResponse,
) -> ( ) -> (SessionStatus, Option<impl Iterator<Item = (String, String)>>) {
SessionStatus, if let Some(s_impl) = res.request().extensions().get::<Rc<RefCell<SessionInner>>>() {
Option<impl Iterator<Item = (String, String)>>, let state = std::mem::replace(&mut s_impl.borrow_mut().state, HashMap::new());
) {
if let Some(s_impl) = res
.request()
.extensions()
.get::<Rc<RefCell<SessionInner>>>()
{
let state =
std::mem::replace(&mut s_impl.borrow_mut().state, HashMap::new());
(s_impl.borrow().status.clone(), Some(state.into_iter())) (s_impl.borrow().status.clone(), Some(state.into_iter()))
} else { } else {
(SessionStatus::Unchanged, None) (SessionStatus::Unchanged, None)

View file

@ -1,2 +1,4 @@
max_width = 89 max_width = 96
reorder_imports = true reorder_imports = true
use_small_heuristics = "max"
use_field_init_shorthand = true