mirror of https://github.com/fafhrd91/actix-web
Merge branch 'master' into web/feat/convert-err-extractor
This commit is contained in:
commit
04c9020969
|
@ -9,11 +9,13 @@
|
|||
- `HttpResponse` can now be used as a `Responder` with any body type. [#2567]
|
||||
- `Result` extractor wrapper can now convert error types. [#2581]
|
||||
- Associated types in `FromRequest` impl for `Option` and `Result` has changed. [#2581]
|
||||
- Maximim number of extractors has changed from 10 to 12. [#2582]
|
||||
|
||||
[#1988]: https://github.com/actix/actix-web/pull/1988
|
||||
[#2567]: https://github.com/actix/actix-web/pull/2567
|
||||
[#2569]: https://github.com/actix/actix-web/pull/2569
|
||||
[#2581] https://github.com/actix/actix-web/pull/2581
|
||||
[#2582]: https://github.com/actix/actix-web/pull/2582
|
||||
|
||||
|
||||
## 4.0.0-beta.19 - 2022-01-04
|
||||
|
|
|
@ -71,8 +71,8 @@ experimental-io-uring = ["actix-server/io-uring"]
|
|||
[dependencies]
|
||||
actix-codec = "0.4.1"
|
||||
actix-macros = "0.2.3"
|
||||
actix-rt = "2.3"
|
||||
actix-server = "2.0.0-rc.2"
|
||||
actix-rt = "2.6"
|
||||
actix-server = "2.0.0-rc.4"
|
||||
actix-service = "2.0.0"
|
||||
actix-utils = "3.0.0"
|
||||
actix-tls = { version = "3.0.0", default-features = false, optional = true }
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
# Changes
|
||||
|
||||
## Unreleased - 2021-xx-xx
|
||||
- The `prefer_utf8` option introduced in `0.4.0` is now true by default. [#2583]
|
||||
|
||||
[#2583]: https://github.com/actix/actix-web/pull/2583
|
||||
|
||||
|
||||
## 0.6.0-beta.13 - 2022-01-04
|
||||
|
|
|
@ -39,7 +39,7 @@ mime_guess = "2.0.1"
|
|||
percent-encoding = "2.1"
|
||||
pin-project-lite = "0.2.7"
|
||||
|
||||
tokio-uring = { version = "0.1", optional = true }
|
||||
tokio-uring = { version = "0.2", optional = true, features = ["bytes"] }
|
||||
|
||||
[dev-dependencies]
|
||||
actix-rt = "2.2"
|
||||
|
|
|
@ -10,6 +10,9 @@ use actix_web::{error::Error, web::Bytes};
|
|||
use futures_core::{ready, Stream};
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
#[cfg(feature = "experimental-io-uring")]
|
||||
use bytes::BytesMut;
|
||||
|
||||
use super::named::File;
|
||||
|
||||
pin_project! {
|
||||
|
@ -214,64 +217,3 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "experimental-io-uring")]
|
||||
use bytes_mut::BytesMut;
|
||||
|
||||
// TODO: remove new type and use bytes::BytesMut directly
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "experimental-io-uring")]
|
||||
mod bytes_mut {
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use tokio_uring::buf::{IoBuf, IoBufMut};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BytesMut(bytes::BytesMut);
|
||||
|
||||
impl BytesMut {
|
||||
pub(super) fn new() -> Self {
|
||||
Self(bytes::BytesMut::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for BytesMut {
|
||||
type Target = bytes::BytesMut;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for BytesMut {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl IoBuf for BytesMut {
|
||||
fn stable_ptr(&self) -> *const u8 {
|
||||
self.0.as_ptr()
|
||||
}
|
||||
|
||||
fn bytes_init(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
fn bytes_total(&self) -> usize {
|
||||
self.0.capacity()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl IoBufMut for BytesMut {
|
||||
fn stable_mut_ptr(&mut self) -> *mut u8 {
|
||||
self.0.as_mut_ptr()
|
||||
}
|
||||
|
||||
unsafe fn set_init(&mut self, init_len: usize) {
|
||||
if self.len() < init_len {
|
||||
self.0.set_len(init_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,8 +67,8 @@ mod tests {
|
|||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
use actix_service::ServiceFactory;
|
||||
use actix_web::{
|
||||
dev::ServiceFactory,
|
||||
guard,
|
||||
http::{
|
||||
header::{self, ContentDisposition, DispositionParam, DispositionType},
|
||||
|
@ -303,7 +303,7 @@ mod tests {
|
|||
let resp = file.respond_to(&req).await.unwrap();
|
||||
assert_eq!(
|
||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||
"application/javascript"
|
||||
"application/javascript; charset=utf-8"
|
||||
);
|
||||
assert_eq!(
|
||||
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
use std::{
|
||||
fmt,
|
||||
fs::Metadata,
|
||||
io,
|
||||
path::{Path, PathBuf},
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use actix_service::{Service, ServiceFactory};
|
||||
use actix_web::{
|
||||
body::{self, BoxBody, SizedStream},
|
||||
dev::{AppService, HttpServiceFactory, ResourceDef, ServiceRequest, ServiceResponse},
|
||||
dev::{
|
||||
self, AppService, HttpServiceFactory, ResourceDef, Service, ServiceFactory,
|
||||
ServiceRequest, ServiceResponse,
|
||||
},
|
||||
http::{
|
||||
header::{
|
||||
self, Charset, ContentDisposition, ContentEncoding, DispositionParam,
|
||||
|
@ -37,7 +38,7 @@ bitflags! {
|
|||
|
||||
impl Default for Flags {
|
||||
fn default() -> Self {
|
||||
Flags::from_bits_truncate(0b0000_0111)
|
||||
Flags::from_bits_truncate(0b0000_1111)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,12 +66,12 @@ impl Default for Flags {
|
|||
/// NamedFile::open_async("./static/index.html").await
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Deref, DerefMut)]
|
||||
#[derive(Debug, Deref, DerefMut)]
|
||||
pub struct NamedFile {
|
||||
path: PathBuf,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
file: File,
|
||||
path: PathBuf,
|
||||
modified: Option<SystemTime>,
|
||||
pub(crate) md: Metadata,
|
||||
pub(crate) flags: Flags,
|
||||
|
@ -80,32 +81,6 @@ pub struct NamedFile {
|
|||
pub(crate) encoding: Option<ContentEncoding>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for NamedFile {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("NamedFile")
|
||||
.field("path", &self.path)
|
||||
.field(
|
||||
"file",
|
||||
#[cfg(feature = "experimental-io-uring")]
|
||||
{
|
||||
&"tokio_uring::File"
|
||||
},
|
||||
#[cfg(not(feature = "experimental-io-uring"))]
|
||||
{
|
||||
&self.file
|
||||
},
|
||||
)
|
||||
.field("modified", &self.modified)
|
||||
.field("md", &self.md)
|
||||
.field("flags", &self.flags)
|
||||
.field("status_code", &self.status_code)
|
||||
.field("content_type", &self.content_type)
|
||||
.field("content_disposition", &self.content_disposition)
|
||||
.field("encoding", &self.encoding)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "experimental-io-uring"))]
|
||||
pub(crate) use std::fs::File;
|
||||
#[cfg(feature = "experimental-io-uring")]
|
||||
|
@ -627,7 +602,7 @@ impl Service<ServiceRequest> for NamedFileService {
|
|||
type Error = Error;
|
||||
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||
|
||||
actix_service::always_ready!();
|
||||
dev::always_ready!();
|
||||
|
||||
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||
let (req, _) = req.into_parts();
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::{fmt, io, ops::Deref, path::PathBuf, rc::Rc};
|
|||
|
||||
use actix_web::{
|
||||
body::BoxBody,
|
||||
dev::{Service, ServiceRequest, ServiceResponse},
|
||||
dev::{self, Service, ServiceRequest, ServiceResponse},
|
||||
error::Error,
|
||||
guard::Guard,
|
||||
http::{header, Method},
|
||||
|
@ -98,7 +98,7 @@ impl Service<ServiceRequest> for FilesService {
|
|||
type Error = Error;
|
||||
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||
|
||||
actix_service::always_ready!();
|
||||
dev::always_ready!();
|
||||
|
||||
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||
let is_method_valid = if let Some(guard) = &self.guards {
|
||||
|
|
|
@ -19,12 +19,12 @@ async fn test_utf8_file_contents() {
|
|||
assert_eq!(res.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
res.headers().get(header::CONTENT_TYPE),
|
||||
Some(&HeaderValue::from_static("text/plain")),
|
||||
Some(&HeaderValue::from_static("text/plain; charset=utf-8")),
|
||||
);
|
||||
|
||||
// prefer UTF-8 encoding
|
||||
// disable UTF-8 attribute
|
||||
let srv =
|
||||
test::init_service(App::new().service(Files::new("/", "./tests").prefer_utf8(true)))
|
||||
test::init_service(App::new().service(Files::new("/", "./tests").prefer_utf8(false)))
|
||||
.await;
|
||||
|
||||
let req = TestRequest::with_uri("/utf8.txt").to_request();
|
||||
|
@ -33,6 +33,6 @@ async fn test_utf8_file_contents() {
|
|||
assert_eq!(res.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
res.headers().get(header::CONTENT_TYPE),
|
||||
Some(&HeaderValue::from_static("text/plain; charset=utf-8")),
|
||||
Some(&HeaderValue::from_static("text/plain")),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -292,16 +292,6 @@ impl FromRequest for Method {
|
|||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl FromRequest for () {
|
||||
type Error = Infallible;
|
||||
type Future = Ready<Result<Self, Self::Error>>;
|
||||
|
||||
fn from_request(_: &HttpRequest, _: &mut Payload) -> Self::Future {
|
||||
ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[allow(non_snake_case)]
|
||||
mod tuple_from_req {
|
||||
|
@ -390,6 +380,15 @@ mod tuple_from_req {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromRequest for () {
|
||||
type Error = Infallible;
|
||||
type Future = Ready<Result<Self, Self::Error>>;
|
||||
|
||||
fn from_request(_: &HttpRequest, _: &mut Payload) -> Self::Future {
|
||||
ok(())
|
||||
}
|
||||
}
|
||||
|
||||
tuple_from_req! { TupleFromRequest1; A }
|
||||
tuple_from_req! { TupleFromRequest2; A, B }
|
||||
tuple_from_req! { TupleFromRequest3; A, B, C }
|
||||
|
@ -400,6 +399,8 @@ mod tuple_from_req {
|
|||
tuple_from_req! { TupleFromRequest8; A, B, C, D, E, F, G, H }
|
||||
tuple_from_req! { TupleFromRequest9; A, B, C, D, E, F, G, H, I }
|
||||
tuple_from_req! { TupleFromRequest10; A, B, C, D, E, F, G, H, I, J }
|
||||
tuple_from_req! { TupleFromRequest11; A, B, C, D, E, F, G, H, I, J, K }
|
||||
tuple_from_req! { TupleFromRequest12; A, B, C, D, E, F, G, H, I, J, K, L }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -12,17 +12,14 @@ use crate::{
|
|||
/// # What Is A Request Handler
|
||||
/// A request handler has three requirements:
|
||||
/// 1. It is an async function (or a function/closure that returns an appropriate future);
|
||||
/// 1. The function accepts zero or more parameters that implement [`FromRequest`];
|
||||
/// 1. The function parameters (up to 12) implement [`FromRequest`];
|
||||
/// 1. The async function (or future) resolves to a type that can be converted into an
|
||||
/// [`HttpResponse`] (i.e., it implements the [`Responder`] trait).
|
||||
///
|
||||
/// # Compiler Errors
|
||||
/// If you get the error `the trait Handler<_> is not implemented`, then your handler does not
|
||||
/// fulfill one or more of the above requirements.
|
||||
///
|
||||
/// Unfortunately we cannot provide a better compile error message (while keeping the trait's
|
||||
/// flexibility) unless a stable alternative to [`#[rustc_on_unimplemented]`][on_unimpl] is added
|
||||
/// to Rust.
|
||||
/// fulfill the _first_ of the above requirements. Missing other requirements manifest as errors on
|
||||
/// implementing [`FromRequest`] and [`Responder`], respectively.
|
||||
///
|
||||
/// # How Do Handlers Receive Variable Numbers Of Arguments
|
||||
/// Rest assured there is no macro magic here; it's just traits.
|
||||
|
@ -62,13 +59,15 @@ use crate::{
|
|||
/// This is the source code for the 2-parameter implementation of `Handler` to help illustrate the
|
||||
/// bounds of the handler call after argument extraction:
|
||||
/// ```ignore
|
||||
/// impl<Func, Arg1, Arg2, R> Handler<(Arg1, Arg2), R> for Func
|
||||
/// impl<Func, Arg1, Arg2, Fut> Handler<(Arg1, Arg2)> for Func
|
||||
/// where
|
||||
/// Func: Fn(Arg1, Arg2) -> R + Clone + 'static,
|
||||
/// R: Future,
|
||||
/// R::Output: Responder,
|
||||
/// Func: Fn(Arg1, Arg2) -> Fut + Clone + 'static,
|
||||
/// Fut: Future,
|
||||
/// {
|
||||
/// fn call(&self, (arg1, arg2): (Arg1, Arg2)) -> R {
|
||||
/// type Output = Fut::Output;
|
||||
/// type Future = Fut;
|
||||
///
|
||||
/// fn call(&self, (arg1, arg2): (Arg1, Arg2)) -> Self::Future {
|
||||
/// (self)(arg1, arg2)
|
||||
/// }
|
||||
/// }
|
||||
|
@ -76,7 +75,6 @@ use crate::{
|
|||
///
|
||||
/// [arity]: https://en.wikipedia.org/wiki/Arity
|
||||
/// [`from_request`]: FromRequest::from_request
|
||||
/// [on_unimpl]: https://github.com/rust-lang/rust/issues/29628
|
||||
pub trait Handler<Args>: Clone + 'static {
|
||||
type Output;
|
||||
type Future: Future<Output = Self::Output>;
|
||||
|
@ -121,8 +119,9 @@ where
|
|||
/// ```
|
||||
macro_rules! factory_tuple ({ $($param:ident)* } => {
|
||||
impl<Func, Fut, $($param,)*> Handler<($($param,)*)> for Func
|
||||
where Func: Fn($($param),*) -> Fut + Clone + 'static,
|
||||
Fut: Future,
|
||||
where
|
||||
Func: Fn($($param),*) -> Fut + Clone + 'static,
|
||||
Fut: Future,
|
||||
{
|
||||
type Output = Fut::Output;
|
||||
type Future = Fut;
|
||||
|
@ -148,3 +147,25 @@ factory_tuple! { A B C D E F G H I }
|
|||
factory_tuple! { A B C D E F G H I J }
|
||||
factory_tuple! { A B C D E F G H I J K }
|
||||
factory_tuple! { A B C D E F G H I J K L }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn assert_impl_handler<T: FromRequest>(_: impl Handler<T>) {}
|
||||
|
||||
#[test]
|
||||
fn arg_number() {
|
||||
async fn handler_min() {}
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[allow(clippy::too_many_arguments, clippy::just_underscores_and_digits)]
|
||||
async fn handler_max(
|
||||
_01: (), _02: (), _03: (), _04: (), _05: (), _06: (),
|
||||
_07: (), _08: (), _09: (), _10: (), _11: (), _12: (),
|
||||
) {}
|
||||
|
||||
assert_impl_handler(handler_min);
|
||||
assert_impl_handler(handler_max);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue