mirror of https://github.com/fafhrd91/actix-web
Compare commits
11 Commits
4d0d3e31eb
...
6a6cae078d
| Author | SHA1 | Date |
|---|---|---|
|
|
6a6cae078d | |
|
|
fe890a73b2 | |
|
|
d119500f93 | |
|
|
a3f95ee1ef | |
|
|
e1da110e92 | |
|
|
219b988300 | |
|
|
9cf3521390 | |
|
|
1931388bf2 | |
|
|
8d6c23038a | |
|
|
6df38522eb | |
|
|
ae2ee7fff1 |
|
|
@ -49,7 +49,7 @@ jobs:
|
|||
toolchain: ${{ matrix.version.version }}
|
||||
|
||||
- name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean
|
||||
uses: taiki-e/install-action@f535147c22906d77695e11cb199e764aa610a4fc # v2.62.46
|
||||
uses: taiki-e/install-action@0be4756f42223b67aa4b7df5effad59010cbf4b9 # v2.62.51
|
||||
with:
|
||||
tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean
|
||||
|
||||
|
|
@ -83,7 +83,7 @@ jobs:
|
|||
uses: actions-rust-lang/setup-rust-toolchain@1780873c7b576612439a134613cc4cc74ce5538c # v1.15.2
|
||||
|
||||
- name: Install just, cargo-hack
|
||||
uses: taiki-e/install-action@f535147c22906d77695e11cb199e764aa610a4fc # v2.62.46
|
||||
uses: taiki-e/install-action@0be4756f42223b67aa4b7df5effad59010cbf4b9 # v2.62.51
|
||||
with:
|
||||
tool: just,cargo-hack
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ jobs:
|
|||
toolchain: ${{ matrix.version.version }}
|
||||
|
||||
- name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean
|
||||
uses: taiki-e/install-action@f535147c22906d77695e11cb199e764aa610a4fc # v2.62.46
|
||||
uses: taiki-e/install-action@0be4756f42223b67aa4b7df5effad59010cbf4b9 # v2.62.51
|
||||
with:
|
||||
tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ jobs:
|
|||
toolchain: nightly
|
||||
|
||||
- name: Install just
|
||||
uses: taiki-e/install-action@f535147c22906d77695e11cb199e764aa610a4fc # v2.62.46
|
||||
uses: taiki-e/install-action@0be4756f42223b67aa4b7df5effad59010cbf4b9 # v2.62.51
|
||||
with:
|
||||
tool: just
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ jobs:
|
|||
components: llvm-tools
|
||||
|
||||
- name: Install just, cargo-llvm-cov, cargo-nextest
|
||||
uses: taiki-e/install-action@f535147c22906d77695e11cb199e764aa610a4fc # v2.62.46
|
||||
uses: taiki-e/install-action@0be4756f42223b67aa4b7df5effad59010cbf4b9 # v2.62.51
|
||||
with:
|
||||
tool: just,cargo-llvm-cov,cargo-nextest
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ jobs:
|
|||
toolchain: ${{ vars.RUST_VERSION_EXTERNAL_TYPES }}
|
||||
|
||||
- name: Install just
|
||||
uses: taiki-e/install-action@f535147c22906d77695e11cb199e764aa610a4fc # v2.62.46
|
||||
uses: taiki-e/install-action@0be4756f42223b67aa4b7df5effad59010cbf4b9 # v2.62.51
|
||||
with:
|
||||
tool: just
|
||||
|
||||
|
|
|
|||
|
|
@ -345,7 +345,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "actix-web"
|
||||
version = "4.11.0"
|
||||
version = "4.12.0"
|
||||
dependencies = [
|
||||
"actix-codec",
|
||||
"actix-files",
|
||||
|
|
@ -2377,9 +2377,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.41"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ macro_rules! unsupported_type {
|
|||
|
||||
macro_rules! parse_single_value {
|
||||
($trait_fn:ident) => {
|
||||
parse_single_value!($trait_fn, $trait_fn);
|
||||
};
|
||||
($trait_fn:ident, $visit_fn:ident) => {
|
||||
fn $trait_fn<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
|
|
@ -43,7 +46,7 @@ macro_rules! parse_single_value {
|
|||
Value {
|
||||
value: &self.path[0],
|
||||
}
|
||||
.$trait_fn(visitor)
|
||||
.$visit_fn(visitor)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -205,11 +208,11 @@ impl<'de, T: ResourcePath + 'de> Deserializer<'de> for PathDeserializer<'de, T>
|
|||
})
|
||||
}
|
||||
|
||||
unsupported_type!(deserialize_any, "'any'");
|
||||
unsupported_type!(deserialize_option, "Option<T>");
|
||||
unsupported_type!(deserialize_identifier, "identifier");
|
||||
unsupported_type!(deserialize_ignored_any, "ignored_any");
|
||||
|
||||
parse_single_value!(deserialize_any, deserialize_str);
|
||||
parse_single_value!(deserialize_bool);
|
||||
parse_single_value!(deserialize_i8);
|
||||
parse_single_value!(deserialize_i16);
|
||||
|
|
@ -427,7 +430,13 @@ impl<'de> Deserializer<'de> for Value<'de> {
|
|||
Err(de::value::Error::custom("unsupported type: tuple struct"))
|
||||
}
|
||||
|
||||
unsupported_type!(deserialize_any, "any");
|
||||
fn deserialize_any<V>(self, v: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_str(v)
|
||||
}
|
||||
|
||||
unsupported_type!(deserialize_seq, "seq");
|
||||
unsupported_type!(deserialize_map, "map");
|
||||
unsupported_type!(deserialize_identifier, "identifier");
|
||||
|
|
@ -704,6 +713,104 @@ mod tests {
|
|||
assert_eq!(vals.value, "/");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_path_decode_any() {
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum AnyEnumCustom {
|
||||
String(String),
|
||||
Int(u32),
|
||||
Other,
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for AnyEnumCustom {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
struct Vis;
|
||||
impl<'de> Visitor<'de> for Vis {
|
||||
type Value = AnyEnumCustom;
|
||||
|
||||
fn expecting<'a>(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter<'a>,
|
||||
) -> std::fmt::Result {
|
||||
write!(f, "my thing")
|
||||
}
|
||||
|
||||
fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
Ok(AnyEnumCustom::Int(v))
|
||||
}
|
||||
|
||||
fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
|
||||
v.parse().map(AnyEnumCustom::Int).or_else(|_| {
|
||||
Ok(match v {
|
||||
"other" => AnyEnumCustom::Other,
|
||||
_ => AnyEnumCustom::String(format!("some str: {v}")),
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(Vis)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
#[serde(untagged)]
|
||||
pub enum AnyEnumDerive {
|
||||
String(String),
|
||||
Int(u32),
|
||||
Other,
|
||||
}
|
||||
|
||||
// single
|
||||
let rdef = ResourceDef::new("/{key}");
|
||||
|
||||
let mut path = Path::new("/%25");
|
||||
rdef.capture_match_info(&mut path);
|
||||
let de = PathDeserializer::new(&path);
|
||||
let segment: AnyEnumCustom = serde::Deserialize::deserialize(de).unwrap();
|
||||
assert_eq!(segment, AnyEnumCustom::String("some str: %".to_string()));
|
||||
|
||||
let mut path = Path::new("/%25");
|
||||
rdef.capture_match_info(&mut path);
|
||||
let de = PathDeserializer::new(&path);
|
||||
let segment: AnyEnumDerive = serde::Deserialize::deserialize(de).unwrap();
|
||||
assert_eq!(segment, AnyEnumDerive::String("%".to_string()));
|
||||
|
||||
// seq
|
||||
let rdef = ResourceDef::new("/{key}/{value}");
|
||||
|
||||
let mut path = Path::new("/other/123");
|
||||
rdef.capture_match_info(&mut path);
|
||||
let de = PathDeserializer::new(&path);
|
||||
let segment: (AnyEnumCustom, AnyEnumDerive) =
|
||||
serde::Deserialize::deserialize(de).unwrap();
|
||||
assert_eq!(segment.0, AnyEnumCustom::Other);
|
||||
// Deserializes to `String` instead of `Int` because deserializing defaults to `str` and serde doesn't automatically implement parsing numbers from `&str`s
|
||||
assert_eq!(segment.1, AnyEnumDerive::String("123".to_string()));
|
||||
|
||||
// map
|
||||
#[derive(Deserialize)]
|
||||
struct Vals {
|
||||
key: AnyEnumCustom,
|
||||
value: AnyEnumDerive,
|
||||
}
|
||||
|
||||
let rdef = ResourceDef::new("/{key}/{value}");
|
||||
|
||||
let mut path = Path::new("/123/%2F");
|
||||
rdef.capture_match_info(&mut path);
|
||||
let de = PathDeserializer::new(&path);
|
||||
let vals: Vals = serde::Deserialize::deserialize(de).unwrap();
|
||||
assert_eq!(vals.key, AnyEnumCustom::Int(123));
|
||||
assert_eq!(vals.value, AnyEnumDerive::String("/".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_borrowed() {
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
|
|
|||
|
|
@ -2,9 +2,12 @@
|
|||
|
||||
## Unreleased
|
||||
|
||||
## 4.12.0
|
||||
|
||||
- `actix_web::response::builder::HttpResponseBuilder::streaming()` now sets `Content-Type` to `application/octet-stream` if `Content-Type` does not exist.
|
||||
- `actix_web::response::builder::HttpResponseBuilder::streaming()` now calls `actix_web::response::builder::HttpResponseBuilder::no_chunking()` if `Content-Length` is set by user.
|
||||
- `actix_web::response::builder::HttpResponseBuilder::streaming()` now calls `actix_web::response::builder::HttpResponseBuilder::no_chunking()` and returns `SizedStream` if `Content-Length` is set by user.
|
||||
- Add `ws` crate feature (on-by-default) which forwards to `actix-http` and guards some of its `ResponseError` impls.
|
||||
- Add public export for `EitherExtractError` in `error` module.
|
||||
|
||||
## 4.11.0
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "actix-web"
|
||||
version = "4.11.0"
|
||||
version = "4.12.0"
|
||||
description = "Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>", "Rob Ede <robjtede@icloud.com>"]
|
||||
keywords = ["actix", "http", "web", "framework", "async"]
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@
|
|||
<!-- prettier-ignore-start -->
|
||||
|
||||
[](https://crates.io/crates/actix-web)
|
||||
[](https://docs.rs/actix-web/4.11.0)
|
||||
[](https://docs.rs/actix-web/4.12.0)
|
||||

|
||||

|
||||
[](https://deps.rs/crate/actix-web/4.11.0)
|
||||
[](https://deps.rs/crate/actix-web/4.12.0)
|
||||
<br />
|
||||
[](https://github.com/actix/actix-web/actions/workflows/ci.yml)
|
||||
[](https://codecov.io/gh/actix/actix-web)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ mod response_error;
|
|||
|
||||
pub(crate) use self::macros::{downcast_dyn, downcast_get_type_id};
|
||||
pub use self::{error::Error, internal::*, response_error::ResponseError};
|
||||
pub use crate::types::EitherExtractError;
|
||||
|
||||
/// A convenience [`Result`](std::result::Result) for Actix Web operations.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use futures_core::Stream;
|
|||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
body::{BodyStream, BoxBody, MessageBody},
|
||||
body::{BodyStream, BoxBody, MessageBody, SizedStream},
|
||||
dev::Extensions,
|
||||
error::{Error, JsonPayloadError},
|
||||
http::{
|
||||
|
|
@ -335,17 +335,18 @@ impl HttpResponseBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(parts) = self.inner() {
|
||||
if let Some(length) = parts.headers.get(header::CONTENT_LENGTH) {
|
||||
if let Ok(length) = length.to_str() {
|
||||
if let Ok(length) = length.parse::<u64>() {
|
||||
self.no_chunking(length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let content_length = self
|
||||
.inner()
|
||||
.and_then(|parts| parts.headers.get(header::CONTENT_LENGTH))
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.and_then(|value| value.parse::<u64>().ok());
|
||||
|
||||
self.body(BodyStream::new(stream))
|
||||
if let Some(len) = content_length {
|
||||
self.no_chunking(len);
|
||||
self.body(SizedStream::new(len, stream))
|
||||
} else {
|
||||
self.body(BodyStream::new(stream))
|
||||
}
|
||||
}
|
||||
|
||||
/// Set a JSON body and build the `HttpResponse`.
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ mod query;
|
|||
mod readlines;
|
||||
|
||||
pub use self::{
|
||||
either::Either,
|
||||
either::{Either, EitherExtractError},
|
||||
form::{Form, FormConfig, UrlEncoded},
|
||||
header::Header,
|
||||
html::Html,
|
||||
|
|
|
|||
Loading…
Reference in New Issue