mirror of
https://github.com/ntex-rs/ntex-extras.git
synced 2025-04-03 21:07:40 +03:00
fmt
This commit is contained in:
parent
3dd7dc68bd
commit
8bb852aa36
9 changed files with 225 additions and 667 deletions
|
@ -1,11 +1,9 @@
|
||||||
# Cors Middleware for actix web framework [](https://travis-ci.org/actix/actix-web) [](https://codecov.io/gh/actix/actix-web) [](https://crates.io/crates/actix-cors) [](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
# Cors Middleware for ntex framework [](https://travis-ci.org/actix/actix-web) [](https://codecov.io/gh/actix/actix-web) [](https://crates.io/crates/actix-cors) [](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
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue