1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-06-13 02:39:32 +00:00

Merge branch 'master' into replace-unsafe-content-length-helper

This commit is contained in:
Yuki Okushi 2020-02-25 14:30:04 +09:00 committed by GitHub
commit 2a72e8d119
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 184 additions and 71 deletions

View file

@ -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

View file

@ -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 }

View file

@ -4,7 +4,7 @@
### Changed
* Update the `time` dependency to 0.2.5
* Update the `time` dependency to 0.2.7
### Fixed

View file

@ -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 }

View file

@ -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());
}
}

View file

@ -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());

View file

@ -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(),

View file

@ -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; \

View file

@ -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 {

View file

@ -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"]);
}
}

View file

@ -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(),

View file

@ -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
View 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());
}
}

View file

@ -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();

View file

@ -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

View file

@ -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"

View file

@ -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 {

View file

@ -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};
//!

View file

@ -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

View file

@ -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

View file

@ -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]