mirror of
https://github.com/actix/actix-web.git
synced 2024-11-18 07:35:36 +00:00
Merge branch 'master' into replace-unsafe-content-length-helper
This commit is contained in:
commit
2a72e8d119
21 changed files with 184 additions and 71 deletions
|
@ -3,13 +3,17 @@
|
|||
|
||||
## [2.0.NEXT] - 2020-01-xx
|
||||
|
||||
### Added
|
||||
|
||||
* Add helper function for creating routes with `TRACE` method guard `web::trace()`
|
||||
|
||||
### Changed
|
||||
|
||||
* Use `sha-1` crate instead of unmaintained `sha1` crate
|
||||
|
||||
* Skip empty chunks when returning response from a `Stream` #1308
|
||||
|
||||
* Update the `time` dependency to 0.2.5
|
||||
* Update the `time` dependency to 0.2.7
|
||||
|
||||
## [2.0.0] - 2019-12-25
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ regex = "1.3"
|
|||
serde = { version = "1.0", features=["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde_urlencoded = "0.6.1"
|
||||
time = { version = "0.2.5", default-features = false, features = ["std"] }
|
||||
time = { version = "0.2.7", default-features = false, features = ["std"] }
|
||||
url = "2.1"
|
||||
open-ssl = { version="0.10", package = "openssl", optional = true }
|
||||
rust-tls = { version = "0.16.0", package = "rustls", optional = true }
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
### Changed
|
||||
|
||||
* Update the `time` dependency to 0.2.5
|
||||
* Update the `time` dependency to 0.2.7
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ serde_json = "1.0"
|
|||
sha-1 = "0.8"
|
||||
slab = "0.4"
|
||||
serde_urlencoded = "0.6.1"
|
||||
time = { version = "0.2.5", default-features = false, features = ["std"] }
|
||||
time = { version = "0.2.7", default-features = false, features = ["std"] }
|
||||
|
||||
# for secure cookie
|
||||
ring = { version = "0.16.9", optional = true }
|
||||
|
|
|
@ -37,8 +37,12 @@ pub trait MessageBody {
|
|||
fn size(&self) -> BodySize;
|
||||
|
||||
fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<Bytes, Error>>>;
|
||||
|
||||
downcast_get_type_id!();
|
||||
}
|
||||
|
||||
downcast!(MessageBody);
|
||||
|
||||
impl MessageBody for () {
|
||||
fn size(&self) -> BodySize {
|
||||
BodySize::Empty
|
||||
|
@ -416,7 +420,10 @@ where
|
|||
S: Stream<Item = Result<Bytes, Error>>,
|
||||
{
|
||||
pub fn new(size: u64, stream: S) -> Self {
|
||||
SizedStream { size, stream: Box::pin(stream) }
|
||||
SizedStream {
|
||||
size,
|
||||
stream: Box::pin(stream),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -643,4 +650,18 @@ mod tests {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_body_casting() {
|
||||
let mut body = String::from("hello cast");
|
||||
let resp_body: &mut dyn MessageBody = &mut body;
|
||||
let body = resp_body.downcast_ref::<String>().unwrap();
|
||||
assert_eq!(body, "hello cast");
|
||||
let body = &mut resp_body.downcast_mut::<String>().unwrap();
|
||||
body.push_str("!");
|
||||
let body = resp_body.downcast_ref::<String>().unwrap();
|
||||
assert_eq!(body, "hello cast!");
|
||||
let not_body = resp_body.downcast_ref::<()>();
|
||||
assert!(not_body.is_none());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ use h2::client::{handshake, Connection, SendRequest};
|
|||
use http::uri::Authority;
|
||||
use indexmap::IndexSet;
|
||||
use slab::Slab;
|
||||
use pin_project::pin_project;
|
||||
|
||||
use super::connection::{ConnectionType, IoConnection};
|
||||
use super::error::ConnectError;
|
||||
|
@ -422,6 +423,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[pin_project]
|
||||
struct ConnectorPoolSupport<T, Io>
|
||||
where
|
||||
Io: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||
|
@ -439,7 +441,7 @@ where
|
|||
type Output = ();
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = unsafe { self.get_unchecked_mut() };
|
||||
let this = self.project();
|
||||
|
||||
let mut inner = this.inner.as_ref().borrow_mut();
|
||||
inner.waker.register(cx.waker());
|
||||
|
|
|
@ -990,7 +990,7 @@ impl<'a, 'b> PartialEq<Cookie<'b>> for Cookie<'a> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Cookie, SameSite};
|
||||
use time::{offset, PrimitiveDateTime};
|
||||
use time::PrimitiveDateTime;
|
||||
|
||||
#[test]
|
||||
fn format() {
|
||||
|
@ -1015,7 +1015,7 @@ mod tests {
|
|||
assert_eq!(&cookie.to_string(), "foo=bar; Domain=www.rust-lang.org");
|
||||
|
||||
let time_str = "Wed, 21 Oct 2015 07:28:00 GMT";
|
||||
let expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%M:%S").unwrap().using_offset(offset!(UTC));
|
||||
let expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%M:%S").unwrap().assume_utc();
|
||||
let cookie = Cookie::build("foo", "bar").expires(expires).finish();
|
||||
assert_eq!(
|
||||
&cookie.to_string(),
|
||||
|
|
|
@ -6,7 +6,7 @@ use std::fmt;
|
|||
use std::str::Utf8Error;
|
||||
|
||||
use percent_encoding::percent_decode;
|
||||
use time::{Duration, offset};
|
||||
use time::Duration;
|
||||
|
||||
use super::{Cookie, CookieStr, SameSite};
|
||||
|
||||
|
@ -188,7 +188,7 @@ fn parse_inner<'c>(s: &str, decode: bool) -> Result<Cookie<'c>, ParseError> {
|
|||
.or_else(|| time::parse(v, "%a, %d-%b-%Y %H:%M:%S").ok());
|
||||
|
||||
if let Some(time) = tm {
|
||||
cookie.expires = Some(time.using_offset(offset!(UTC)))
|
||||
cookie.expires = Some(time.assume_utc())
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
@ -216,7 +216,7 @@ where
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Cookie, SameSite};
|
||||
use time::{offset, Duration, PrimitiveDateTime};
|
||||
use time::{Duration, PrimitiveDateTime};
|
||||
|
||||
macro_rules! assert_eq_parse {
|
||||
($string:expr, $expected:expr) => {
|
||||
|
@ -376,7 +376,7 @@ mod tests {
|
|||
);
|
||||
|
||||
let time_str = "Wed, 21 Oct 2015 07:28:00 GMT";
|
||||
let expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%M:%S").unwrap().using_offset(offset!(UTC));
|
||||
let expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%M:%S").unwrap().assume_utc();
|
||||
expected.set_expires(expires);
|
||||
assert_eq_parse!(
|
||||
" foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \
|
||||
|
@ -385,7 +385,7 @@ mod tests {
|
|||
);
|
||||
|
||||
unexpected.set_domain("foo.com");
|
||||
let bad_expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%S:%M").unwrap().using_offset(offset!(UTC));
|
||||
let bad_expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%S:%M").unwrap().assume_utc();
|
||||
expected.set_expires(bad_expires);
|
||||
assert_ne_parse!(
|
||||
" foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//! Error and Result module
|
||||
use std::any::TypeId;
|
||||
use std::cell::RefCell;
|
||||
use std::io::Write;
|
||||
use std::str::Utf8Error;
|
||||
|
@ -60,12 +59,6 @@ impl Error {
|
|||
}
|
||||
}
|
||||
|
||||
/// A struct with a private constructor, for use with
|
||||
/// `__private_get_type_id__`. Its single field is private,
|
||||
/// ensuring that it can only be constructed from this module
|
||||
#[doc(hidden)]
|
||||
pub struct PrivateHelper(());
|
||||
|
||||
/// Error that can be converted to `Response`
|
||||
pub trait ResponseError: fmt::Debug + fmt::Display {
|
||||
/// Response's status code
|
||||
|
@ -89,43 +82,10 @@ pub trait ResponseError: fmt::Debug + fmt::Display {
|
|||
resp.set_body(Body::from(buf))
|
||||
}
|
||||
|
||||
/// A helper method to get the type ID of the type
|
||||
/// this trait is implemented on.
|
||||
/// This method is unsafe to *implement*, since `downcast_ref` relies
|
||||
/// on the returned `TypeId` to perform a cast.
|
||||
///
|
||||
/// Unfortunately, Rust has no notion of a trait method that is
|
||||
/// unsafe to implement (marking it as `unsafe` makes it unsafe
|
||||
/// to *call*). As a workaround, we require this method
|
||||
/// to return a private type along with the `TypeId`. This
|
||||
/// private type (`PrivateHelper`) has a private constructor,
|
||||
/// making it impossible for safe code to construct outside of
|
||||
/// this module. This ensures that safe code cannot violate
|
||||
/// type-safety by implementing this method.
|
||||
#[doc(hidden)]
|
||||
fn __private_get_type_id__(&self) -> (TypeId, PrivateHelper)
|
||||
where
|
||||
Self: 'static,
|
||||
{
|
||||
(TypeId::of::<Self>(), PrivateHelper(()))
|
||||
}
|
||||
downcast_get_type_id!();
|
||||
}
|
||||
|
||||
impl dyn ResponseError + 'static {
|
||||
/// Downcasts a response error to a specific type.
|
||||
pub fn downcast_ref<T: ResponseError + 'static>(&self) -> Option<&T> {
|
||||
if self.__private_get_type_id__().0 == TypeId::of::<T>() {
|
||||
// Safety: external crates cannot override the default
|
||||
// implementation of `__private_get_type_id__`, since
|
||||
// it requires returning a private type. We can therefore
|
||||
// rely on the returned `TypeId`, which ensures that this
|
||||
// case is correct.
|
||||
unsafe { Some(&*(self as *const dyn ResponseError as *const T)) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
downcast!(ResponseError);
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
|
|
@ -63,7 +63,7 @@ header! {
|
|||
(AcceptCharset, ACCEPT_CHARSET) => (QualityItem<Charset>)+
|
||||
|
||||
test_accept_charset {
|
||||
/// Test case from RFC
|
||||
// Test case from RFC
|
||||
test_header!(test1, vec![b"iso-8859-5, unicode-1-1;q=0.8"]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ impl FromStr for HttpDate {
|
|||
|
||||
fn from_str(s: &str) -> Result<HttpDate, ParseError> {
|
||||
match time_parser::parse_http_date(s) {
|
||||
Some(t) => Ok(HttpDate(t.using_offset(offset!(UTC)))),
|
||||
Some(t) => Ok(HttpDate(t.assume_utc())),
|
||||
None => Err(ParseError::Header)
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ impl From<OffsetDateTime> for HttpDate {
|
|||
|
||||
impl From<SystemTime> for HttpDate {
|
||||
fn from(sys: SystemTime) -> HttpDate {
|
||||
HttpDate(PrimitiveDateTime::from(sys).using_offset(offset!(UTC)))
|
||||
HttpDate(PrimitiveDateTime::from(sys).assume_utc())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,14 +66,14 @@ impl From<HttpDate> for SystemTime {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::HttpDate;
|
||||
use time::{PrimitiveDateTime, date, time, offset};
|
||||
use time::{PrimitiveDateTime, date, time};
|
||||
|
||||
#[test]
|
||||
fn test_date() {
|
||||
let nov_07 = HttpDate(PrimitiveDateTime::new(
|
||||
date!(1994-11-07),
|
||||
time!(8:48:37)
|
||||
).using_offset(offset!(UTC)));
|
||||
).assume_utc());
|
||||
|
||||
assert_eq!(
|
||||
"Sun, 07 Nov 1994 08:48:37 GMT".parse::<HttpDate>().unwrap(),
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
pub mod body;
|
||||
mod builder;
|
||||
pub mod client;
|
||||
|
|
95
actix-http/src/macros.rs
Normal file
95
actix-http/src/macros.rs
Normal file
|
@ -0,0 +1,95 @@
|
|||
#[macro_export]
|
||||
macro_rules! downcast_get_type_id {
|
||||
() => {
|
||||
/// A helper method to get the type ID of the type
|
||||
/// this trait is implemented on.
|
||||
/// This method is unsafe to *implement*, since `downcast_ref` relies
|
||||
/// on the returned `TypeId` to perform a cast.
|
||||
///
|
||||
/// Unfortunately, Rust has no notion of a trait method that is
|
||||
/// unsafe to implement (marking it as `unsafe` makes it unsafe
|
||||
/// to *call*). As a workaround, we require this method
|
||||
/// to return a private type along with the `TypeId`. This
|
||||
/// private type (`PrivateHelper`) has a private constructor,
|
||||
/// making it impossible for safe code to construct outside of
|
||||
/// this module. This ensures that safe code cannot violate
|
||||
/// type-safety by implementing this method.
|
||||
#[doc(hidden)]
|
||||
fn __private_get_type_id__(&self) -> (std::any::TypeId, PrivateHelper)
|
||||
where
|
||||
Self: 'static,
|
||||
{
|
||||
(std::any::TypeId::of::<Self>(), PrivateHelper(()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Generate implementation for dyn $name
|
||||
#[macro_export]
|
||||
macro_rules! downcast {
|
||||
($name:ident) => {
|
||||
/// A struct with a private constructor, for use with
|
||||
/// `__private_get_type_id__`. Its single field is private,
|
||||
/// ensuring that it can only be constructed from this module
|
||||
#[doc(hidden)]
|
||||
pub struct PrivateHelper(());
|
||||
|
||||
impl dyn $name + 'static {
|
||||
/// Downcasts generic body to a specific type.
|
||||
pub fn downcast_ref<T: $name + 'static>(&self) -> Option<&T> {
|
||||
if self.__private_get_type_id__().0 == std::any::TypeId::of::<T>() {
|
||||
// Safety: external crates cannot override the default
|
||||
// implementation of `__private_get_type_id__`, since
|
||||
// it requires returning a private type. We can therefore
|
||||
// rely on the returned `TypeId`, which ensures that this
|
||||
// case is correct.
|
||||
unsafe { Some(&*(self as *const dyn $name as *const T)) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Downcasts a generic body to a mutable specific type.
|
||||
pub fn downcast_mut<T: $name + 'static>(&mut self) -> Option<&mut T> {
|
||||
if self.__private_get_type_id__().0 == std::any::TypeId::of::<T>() {
|
||||
// Safety: external crates cannot override the default
|
||||
// implementation of `__private_get_type_id__`, since
|
||||
// it requires returning a private type. We can therefore
|
||||
// rely on the returned `TypeId`, which ensures that this
|
||||
// case is correct.
|
||||
unsafe {
|
||||
Some(&mut *(self as *const dyn $name as *const T as *mut T))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
trait MB {
|
||||
downcast_get_type_id!();
|
||||
}
|
||||
|
||||
downcast!(MB);
|
||||
|
||||
impl MB for String {}
|
||||
impl MB for () {}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_any_casting() {
|
||||
let mut body = String::from("hello cast");
|
||||
let resp_body: &mut dyn MB = &mut body;
|
||||
let body = resp_body.downcast_ref::<String>().unwrap();
|
||||
assert_eq!(body, "hello cast");
|
||||
let body = &mut resp_body.downcast_mut::<String>().unwrap();
|
||||
body.push_str("!");
|
||||
let body = resp_body.downcast_ref::<String>().unwrap();
|
||||
assert_eq!(body, "hello cast!");
|
||||
let not_body = resp_body.downcast_ref::<()>();
|
||||
assert!(not_body.is_none());
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use time::{PrimitiveDateTime, Date};
|
||||
use time::{OffsetDateTime, PrimitiveDateTime, Date};
|
||||
|
||||
/// Attempt to parse a `time` string as one of either RFC 1123, RFC 850, or asctime.
|
||||
pub fn parse_http_date(time: &str) -> Option<PrimitiveDateTime> {
|
||||
|
@ -19,7 +19,7 @@ fn try_parse_rfc_850(time: &str) -> Option<PrimitiveDateTime> {
|
|||
// If the `time` string contains a two-digit year, then as per RFC 2616 § 19.3,
|
||||
// we consider the year as part of this century if it's within the next 50 years,
|
||||
// otherwise we consider as part of the previous century.
|
||||
let now = PrimitiveDateTime::now();
|
||||
let now = OffsetDateTime::now();
|
||||
let century_start_year = (now.year() / 100) * 100;
|
||||
let mut expanded_year = century_start_year + dt.year();
|
||||
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
# Changes
|
||||
|
||||
## [0.2.NEXT] - 2020-xx-xx
|
||||
## [0.2.1] - 2020-02-25
|
||||
|
||||
* Allow the handler function to be named as `config` #1290
|
||||
* Add `#[allow(missing_docs)]` attribute to generated structs [#1368]
|
||||
* Allow the handler function to be named as `config` [#1290]
|
||||
|
||||
[#1368]: https://github.com/actix/actix-web/issues/1368
|
||||
[#1290]: https://github.com/actix/actix-web/issues/1290
|
||||
|
||||
## [0.2.0] - 2019-12-13
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "actix-web-codegen"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
description = "Actix web proc macros"
|
||||
readme = "README.md"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||
|
@ -17,6 +17,6 @@ syn = { version = "^1", features = ["full", "parsing"] }
|
|||
proc-macro2 = "^1"
|
||||
|
||||
[dev-dependencies]
|
||||
actix-rt = { version = "1.0.0" }
|
||||
actix-web = { version = "2.0.0-rc" }
|
||||
futures = { version = "0.3.1" }
|
||||
actix-rt = "1.0.0"
|
||||
actix-web = "2.0.0"
|
||||
futures = "0.3.1"
|
||||
|
|
|
@ -191,7 +191,7 @@ impl Route {
|
|||
let extra_guards = &self.args.guards;
|
||||
let resource_type = &self.resource_type;
|
||||
let stream = quote! {
|
||||
#[allow(non_camel_case_types)]
|
||||
#[allow(non_camel_case_types, missing_docs)]
|
||||
pub struct #name;
|
||||
|
||||
impl actix_web::dev::HttpServiceFactory for #name {
|
||||
|
|
|
@ -7,6 +7,12 @@
|
|||
//! Actix web is a small, pragmatic, and extremely fast web framework
|
||||
//! for Rust.
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! The `#[actix_rt::main]` macro in the example below is provided by the Actix runtime
|
||||
//! crate, [`actix-rt`](https://crates.io/crates/actix-rt). You will need to include
|
||||
//! `actix-rt` in your dependencies for it to run.
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use actix_web::{web, App, Responder, HttpServer};
|
||||
//!
|
||||
|
|
18
src/web.rs
18
src/web.rs
|
@ -193,6 +193,24 @@ pub fn head() -> Route {
|
|||
method(Method::HEAD)
|
||||
}
|
||||
|
||||
/// Create *route* with `TRACE` method guard.
|
||||
///
|
||||
/// ```rust
|
||||
/// use actix_web::{web, App, HttpResponse};
|
||||
///
|
||||
/// let app = App::new().service(
|
||||
/// web::resource("/{project_id}")
|
||||
/// .route(web::trace().to(|| HttpResponse::Ok()))
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// In the above example, one `HEAD` route gets added:
|
||||
/// * /{project_id}
|
||||
///
|
||||
pub fn trace() -> Route {
|
||||
method(Method::TRACE)
|
||||
}
|
||||
|
||||
/// Create *route* and add method guard.
|
||||
///
|
||||
/// ```rust
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## [Unreleased] - 2020-xx-xx
|
||||
|
||||
* Update the `time` dependency to 0.2.5
|
||||
* Update the `time` dependency to 0.2.7
|
||||
|
||||
|
||||
## [1.0.0] - 2019-12-13
|
||||
|
|
|
@ -51,7 +51,7 @@ serde_json = "1.0"
|
|||
sha1 = "0.6"
|
||||
slab = "0.4"
|
||||
serde_urlencoded = "0.6.1"
|
||||
time = { version = "0.2.5", default-features = false, features = ["std"] }
|
||||
time = { version = "0.2.7", default-features = false, features = ["std"] }
|
||||
open-ssl = { version="0.10", package="openssl", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
Loading…
Reference in a new issue