mirror of https://github.com/fafhrd91/actix-web
Compare commits
6 Commits
b01b009136
...
1e38ce07f1
Author | SHA1 | Date |
---|---|---|
|
1e38ce07f1 | |
|
9fb6c13a1a | |
|
05cfef7f4b | |
|
8f3eb32a32 | |
|
ddd16ec9db | |
|
f7778df597 |
|
@ -3,6 +3,6 @@ disallowed-names = [
|
|||
"e", # no single letter error bindings
|
||||
]
|
||||
disallowed-methods = [
|
||||
{ path = "std::cell::RefCell::default()", reason = "prefer explicit inner type default" },
|
||||
{ path = "std::rc::Rc::default()", reason = "prefer explicit inner type default" },
|
||||
{ path = "std::cell::RefCell::default()", reason = "prefer explicit inner type default (remove allow-invalid when rust-lang/rust-clippy/#8581 is fixed)", allow-invalid = true },
|
||||
{ path = "std::rc::Rc::default()", reason = "prefer explicit inner type default (remove allow-invalid when rust-lang/rust-clippy/#8581 is fixed)", allow-invalid = true },
|
||||
]
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,6 +2,7 @@
|
|||
|
||||
## Unreleased
|
||||
|
||||
- Opt-In filesize threshold for faster synchronus reads that allow for 20x better performance.
|
||||
- Minimum supported Rust version (MSRV) is now 1.75.
|
||||
|
||||
## 0.6.6
|
||||
|
|
|
@ -24,6 +24,7 @@ pin_project! {
|
|||
state: ChunkedReadFileState<Fut>,
|
||||
counter: u64,
|
||||
callback: F,
|
||||
read_sync: bool,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,6 +58,7 @@ pub(crate) fn new_chunked_read(
|
|||
size: u64,
|
||||
offset: u64,
|
||||
file: File,
|
||||
size_threshold: u64,
|
||||
) -> impl Stream<Item = Result<Bytes, Error>> {
|
||||
ChunkedReadFile {
|
||||
size,
|
||||
|
@ -69,31 +71,45 @@ pub(crate) fn new_chunked_read(
|
|||
},
|
||||
counter: 0,
|
||||
callback: chunked_read_file_callback,
|
||||
read_sync: size < size_threshold,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "experimental-io-uring"))]
|
||||
async fn chunked_read_file_callback(
|
||||
fn chunked_read_file_callback_sync(
|
||||
mut file: File,
|
||||
offset: u64,
|
||||
max_bytes: usize,
|
||||
) -> Result<(File, Bytes), Error> {
|
||||
) -> Result<(File, Bytes), io::Error> {
|
||||
use io::{Read as _, Seek as _};
|
||||
|
||||
let res = actix_web::web::block(move || {
|
||||
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 n_bytes = file.by_ref().take(max_bytes as u64).read_to_end(&mut buf)?;
|
||||
let n_bytes = file.by_ref().take(max_bytes as u64).read_to_end(&mut buf)?;
|
||||
|
||||
if n_bytes == 0 {
|
||||
Err(io::Error::from(io::ErrorKind::UnexpectedEof))
|
||||
} else {
|
||||
Ok((file, Bytes::from(buf)))
|
||||
}
|
||||
})
|
||||
.await??;
|
||||
if n_bytes == 0 {
|
||||
Err(io::Error::from(io::ErrorKind::UnexpectedEof))
|
||||
} else {
|
||||
Ok((file, Bytes::from(buf)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "experimental-io-uring"))]
|
||||
#[inline]
|
||||
async fn chunked_read_file_callback(
|
||||
file: File,
|
||||
offset: u64,
|
||||
max_bytes: usize,
|
||||
read_sync: bool,
|
||||
) -> Result<(File, Bytes), Error> {
|
||||
let res = if read_sync {
|
||||
chunked_read_file_callback_sync(file, offset, max_bytes)?
|
||||
} else {
|
||||
actix_web::web::block(move || chunked_read_file_callback_sync(file, offset, max_bytes))
|
||||
.await??
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
@ -171,7 +187,7 @@ where
|
|||
#[cfg(not(feature = "experimental-io-uring"))]
|
||||
impl<F, Fut> Stream for ChunkedReadFile<F, Fut>
|
||||
where
|
||||
F: Fn(File, u64, usize) -> Fut,
|
||||
F: Fn(File, u64, usize, bool) -> Fut,
|
||||
Fut: Future<Output = Result<(File, Bytes), Error>>,
|
||||
{
|
||||
type Item = Result<Bytes, Error>;
|
||||
|
@ -193,7 +209,7 @@ where
|
|||
.take()
|
||||
.expect("ChunkedReadFile polled after completion");
|
||||
|
||||
let fut = (this.callback)(file, offset, max_bytes);
|
||||
let fut = (this.callback)(file, offset, max_bytes, *this.read_sync);
|
||||
|
||||
this.state
|
||||
.project_replace(ChunkedReadFileState::Future { fut });
|
||||
|
|
|
@ -49,6 +49,7 @@ pub struct Files {
|
|||
use_guards: Option<Rc<dyn Guard>>,
|
||||
guards: Vec<Rc<dyn Guard>>,
|
||||
hidden_files: bool,
|
||||
size_threshold: u64,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Files {
|
||||
|
@ -73,6 +74,7 @@ impl Clone for Files {
|
|||
use_guards: self.use_guards.clone(),
|
||||
guards: self.guards.clone(),
|
||||
hidden_files: self.hidden_files,
|
||||
size_threshold: self.size_threshold,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -119,6 +121,7 @@ impl Files {
|
|||
use_guards: None,
|
||||
guards: Vec::new(),
|
||||
hidden_files: false,
|
||||
size_threshold: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,6 +207,18 @@ impl Files {
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the async file-size threshold.
|
||||
///
|
||||
/// When a file is larger than the threshold, the reader
|
||||
/// will switch from faster blocking file-reads to slower async reads
|
||||
/// to avoid blocking the main-thread when processing large files.
|
||||
///
|
||||
/// Default is 0, meaning all files are read asyncly.
|
||||
pub fn set_size_threshold(mut self, size: u64) -> Self {
|
||||
self.size_threshold = size;
|
||||
self
|
||||
}
|
||||
|
||||
/// Specifies whether to use ETag or not.
|
||||
///
|
||||
/// Default is true.
|
||||
|
@ -367,6 +382,7 @@ impl ServiceFactory<ServiceRequest> for Files {
|
|||
file_flags: self.file_flags,
|
||||
guards: self.use_guards.clone(),
|
||||
hidden_files: self.hidden_files,
|
||||
size_threshold: self.size_threshold,
|
||||
};
|
||||
|
||||
if let Some(ref default) = *self.default.borrow() {
|
||||
|
|
|
@ -80,6 +80,7 @@ pub struct NamedFile {
|
|||
pub(crate) content_type: Mime,
|
||||
pub(crate) content_disposition: ContentDisposition,
|
||||
pub(crate) encoding: Option<ContentEncoding>,
|
||||
pub(crate) size_threshold: u64,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "experimental-io-uring"))]
|
||||
|
@ -200,6 +201,7 @@ impl NamedFile {
|
|||
encoding,
|
||||
status_code: StatusCode::OK,
|
||||
flags: Flags::default(),
|
||||
size_threshold: 0,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -353,6 +355,18 @@ impl NamedFile {
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the async file-size threshold.
|
||||
///
|
||||
/// When a file is larger than the threshold, the reader
|
||||
/// will switch from faster blocking file-reads to slower async reads
|
||||
/// to avoid blocking the main-thread when processing large files.
|
||||
///
|
||||
/// Default is 0, meaning all files are read asyncly.
|
||||
pub fn set_size_threshold(mut self, size: u64) -> Self {
|
||||
self.size_threshold = size;
|
||||
self
|
||||
}
|
||||
|
||||
/// Specifies whether to return `ETag` header in response.
|
||||
///
|
||||
/// Default is true.
|
||||
|
@ -440,7 +454,8 @@ impl NamedFile {
|
|||
res.insert_header((header::CONTENT_ENCODING, current_encoding.as_str()));
|
||||
}
|
||||
|
||||
let reader = chunked::new_chunked_read(self.md.len(), 0, self.file);
|
||||
let reader =
|
||||
chunked::new_chunked_read(self.md.len(), 0, self.file, self.size_threshold);
|
||||
|
||||
return res.streaming(reader);
|
||||
}
|
||||
|
@ -577,7 +592,7 @@ impl NamedFile {
|
|||
.map_into_boxed_body();
|
||||
}
|
||||
|
||||
let reader = chunked::new_chunked_read(length, offset, self.file);
|
||||
let reader = chunked::new_chunked_read(length, offset, self.file, self.size_threshold);
|
||||
|
||||
if offset != 0 || length != self.md.len() {
|
||||
res.status(StatusCode::PARTIAL_CONTENT);
|
||||
|
|
|
@ -39,6 +39,7 @@ pub struct FilesServiceInner {
|
|||
pub(crate) file_flags: named::Flags,
|
||||
pub(crate) guards: Option<Rc<dyn Guard>>,
|
||||
pub(crate) hidden_files: bool,
|
||||
pub(crate) size_threshold: u64,
|
||||
}
|
||||
|
||||
impl fmt::Debug for FilesServiceInner {
|
||||
|
@ -70,7 +71,9 @@ impl FilesService {
|
|||
named_file.flags = self.file_flags;
|
||||
|
||||
let (req, _) = req.into_parts();
|
||||
let res = named_file.into_response(&req);
|
||||
let res = named_file
|
||||
.set_size_threshold(self.size_threshold)
|
||||
.into_response(&req);
|
||||
ServiceResponse::new(req, res)
|
||||
}
|
||||
|
||||
|
@ -169,17 +172,7 @@ impl Service<ServiceRequest> for FilesService {
|
|||
}
|
||||
} else {
|
||||
match NamedFile::open_async(&path).await {
|
||||
Ok(mut named_file) => {
|
||||
if let Some(ref mime_override) = this.mime_override {
|
||||
let new_disposition = mime_override(&named_file.content_type.type_());
|
||||
named_file.content_disposition.disposition = new_disposition;
|
||||
}
|
||||
named_file.flags = this.file_flags;
|
||||
|
||||
let (req, _) = req.into_parts();
|
||||
let res = named_file.into_response(&req);
|
||||
Ok(ServiceResponse::new(req, res))
|
||||
}
|
||||
Ok(named_file) => Ok(this.serve_named_file(req, named_file)),
|
||||
Err(err) => this.handle_err(err, req).await,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,11 @@
|
|||
|
||||
## Unreleased
|
||||
|
||||
- Update `TestRequest::set_payload` to generate "Content-Length" header
|
||||
- Malformed websocket frames are now gracefully rejected.
|
||||
## 3.11.1
|
||||
|
||||
- Prevent more hangs after client disconnects.
|
||||
- More malformed WebSocket frames are now gracefully rejected.
|
||||
- Using `TestRequest::set_payload()` now sets a Content-Length header.
|
||||
|
||||
## 3.11.0
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "actix-http"
|
||||
version = "3.11.0"
|
||||
version = "3.11.1"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>", "Rob Ede <robjtede@icloud.com>"]
|
||||
description = "HTTP types and services for the Actix ecosystem"
|
||||
keywords = ["actix", "http", "framework", "async", "futures"]
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
<!-- prettier-ignore-start -->
|
||||
|
||||
[](https://crates.io/crates/actix-http)
|
||||
[](https://docs.rs/actix-http/3.11.0)
|
||||
[](https://docs.rs/actix-http/3.11.1)
|
||||

|
||||

|
||||
<br />
|
||||
[](https://deps.rs/crate/actix-http/3.11.0)
|
||||
[](https://deps.rs/crate/actix-http/3.11.1)
|
||||
[](https://crates.io/crates/actix-http)
|
||||
[](https://discord.gg/NWpN5mmg3x)
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ macro_rules! register {
|
|||
register!(finish => "(.*)", "(.*)", "(.*)", "(.*)")
|
||||
}};
|
||||
(finish => $p1:literal, $p2:literal, $p3:literal, $p4:literal) => {{
|
||||
#[expect(clippy::useless_concat)]
|
||||
let arr = [
|
||||
concat!("/authorizations"),
|
||||
concat!("/authorizations/", $p1),
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
- The return type for `ServiceRequest::app_data::<T>()` was changed from returning a `Data<T>` to simply a `T`. To access a `Data<T>` use `ServiceRequest::app_data::<Data<T>>()`.
|
||||
|
||||
- Cookie handling has been offloaded to the `cookie` crate:
|
||||
|
||||
- `USERINFO_ENCODE_SET` is no longer exposed. Percent-encoding is still supported; check docs.
|
||||
- Some types now require lifetime parameters.
|
||||
|
||||
|
|
8
justfile
8
justfile
|
@ -13,6 +13,8 @@ fmt:
|
|||
[private]
|
||||
downgrade-for-msrv:
|
||||
cargo {{ toolchain }} update -p=divan --precise=0.1.15 # next ver: 1.80.0
|
||||
cargo {{ toolchain }} update -p=rayon --precise=1.10.0 # next ver: 1.80.0
|
||||
cargo {{ toolchain }} update -p=rayon-core --precise=1.12.1 # next ver: 1.80.0
|
||||
cargo {{ toolchain }} update -p=half --precise=2.4.1 # next ver: 1.81.0
|
||||
cargo {{ toolchain }} update -p=idna_adapter --precise=1.2.0 # next ver: 1.82.0
|
||||
cargo {{ toolchain }} update -p=litemap --precise=0.7.4 # next ver: 1.81.0
|
||||
|
@ -50,8 +52,7 @@ clippy:
|
|||
cargo {{ toolchain }} clippy --workspace --all-targets {{ all_crate_features }}
|
||||
|
||||
# Run Clippy over workspace using MSRV.
|
||||
clippy-msrv:
|
||||
@just toolchain={{ msrv_rustup }} downgrade-for-msrv
|
||||
clippy-msrv: downgrade-for-msrv
|
||||
@just toolchain={{ msrv_rustup }} clippy
|
||||
|
||||
# Test workspace code.
|
||||
|
@ -62,8 +63,7 @@ test:
|
|||
cargo {{ toolchain }} nextest run --no-tests=warn --workspace --exclude=actix-web-codegen --exclude=actix-multipart-derive {{ all_crate_features }} --filter-expr="not test(test_reading_deflate_encoding_large_random_rustls)"
|
||||
|
||||
# Test workspace using MSRV.
|
||||
test-msrv:
|
||||
@just toolchain={{ msrv_rustup }} downgrade-for-msrv
|
||||
test-msrv: downgrade-for-msrv
|
||||
@just toolchain={{ msrv_rustup }} test
|
||||
|
||||
# Test workspace docs.
|
||||
|
|
Loading…
Reference in New Issue