From 043f763c51675b0d4bc0d8a66e8a6883e155bdd8 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 11 Sep 2019 20:07:39 +0600 Subject: [PATCH 001/176] prepare actix-http release --- actix-http/CHANGES.md | 5 +---- actix-http/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 80da691cd..d603cde7b 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,14 +1,11 @@ # Changes -## +## [0.2.11] - 2019-09-11 ### Added * Add support for sending HTTP requests with `Rc` in addition to sending HTTP requests with `RequestHead` - -## [0.2.10] - 2019-09-xx - ### Fixed * on_connect result isn't added to request extensions for http2 requests #1009 diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 290a8fbae..3019b2897 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "0.2.9" +version = "0.2.10" authors = ["Nikolay Kim "] description = "Actix http primitives" readme = "README.md" From 71f8577713791a6991a2e7120077d52347d38a55 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 11 Sep 2019 20:13:28 +0600 Subject: [PATCH 002/176] prepare awc release --- awc/CHANGES.md | 5 +---- awc/Cargo.toml | 6 +++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/awc/CHANGES.md b/awc/CHANGES.md index 27962a6f5..4a52a9df2 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,14 +1,11 @@ # Changes -## +## [0.2.5] - 2019-09-11 ### Added * Add `FrozenClientRequest` to support retries for sending HTTP requests - -## [0.2.5] - 2019-09-06 - ### Changed * Ensure that the `Host` header is set when initiating a WebSocket client connection. diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 7f42501e9..3d15c943e 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "awc" -version = "0.2.4" +version = "0.2.5" authors = ["Nikolay Kim "] description = "Actix http client." readme = "README.md" @@ -44,7 +44,7 @@ flate2-rust = ["actix-http/flate2-rust"] [dependencies] actix-codec = "0.1.2" actix-service = "0.4.1" -actix-http = "0.2.9" +actix-http = "0.2.10" base64 = "0.10.1" bytes = "0.4" derive_more = "0.15.0" @@ -63,7 +63,7 @@ rustls = { version = "0.15.2", optional = true } [dev-dependencies] actix-rt = "0.2.2" actix-web = { version = "1.0.0", features=["ssl"] } -actix-http = { version = "0.2.4", features=["ssl"] } +actix-http = { version = "0.2.10", features=["ssl"] } actix-http-test = { version = "0.2.0", features=["ssl"] } actix-utils = "0.4.1" actix-server = { version = "0.6.0", features=["ssl", "rust-tls"] } From 45d2fd429928d243d80a6bce246d2542f9e8cde7 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 12 Sep 2019 10:40:56 +0600 Subject: [PATCH 003/176] export frozen request related types; refactor code layout --- awc/CHANGES.md | 7 + awc/src/frozen.rs | 235 ++++++++++++++++++++ awc/src/lib.rs | 4 + awc/src/request.rs | 535 +++++---------------------------------------- awc/src/sender.rs | 282 ++++++++++++++++++++++++ 5 files changed, 581 insertions(+), 482 deletions(-) create mode 100644 awc/src/frozen.rs create mode 100644 awc/src/sender.rs diff --git a/awc/CHANGES.md b/awc/CHANGES.md index 4a52a9df2..94ad65ffe 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,5 +1,12 @@ # Changes +## [0.2.6] - 2019-09-12 + +### Added + +* Export frozen request related types. + + ## [0.2.5] - 2019-09-11 ### Added diff --git a/awc/src/frozen.rs b/awc/src/frozen.rs new file mode 100644 index 000000000..d9f65d431 --- /dev/null +++ b/awc/src/frozen.rs @@ -0,0 +1,235 @@ +use std::net; +use std::rc::Rc; +use std::time::Duration; + +use bytes::Bytes; +use futures::Stream; +use serde::Serialize; + +use actix_http::body::Body; +use actix_http::http::header::IntoHeaderValue; +use actix_http::http::{ + Error as HttpError, HeaderMap, HeaderName, HttpTryFrom, Method, Uri, +}; +use actix_http::{Error, RequestHead}; + +use crate::sender::{RequestSender, SendClientRequest}; +use crate::ClientConfig; + +/// `FrozenClientRequest` struct represents clonable client request. +/// It could be used to send same request multiple times. +#[derive(Clone)] +pub struct FrozenClientRequest { + pub(crate) head: Rc, + pub(crate) addr: Option, + pub(crate) response_decompress: bool, + pub(crate) timeout: Option, + pub(crate) config: Rc, +} + +impl FrozenClientRequest { + /// Get HTTP URI of request + pub fn get_uri(&self) -> &Uri { + &self.head.uri + } + + /// Get HTTP method of this request + pub fn get_method(&self) -> &Method { + &self.head.method + } + + /// Returns request's headers. + pub fn headers(&self) -> &HeaderMap { + &self.head.headers + } + + /// Send a body. + pub fn send_body(&self, body: B) -> SendClientRequest + where + B: Into, + { + RequestSender::Rc(self.head.clone(), None).send_body( + self.addr, + self.response_decompress, + self.timeout, + self.config.as_ref(), + body, + ) + } + + /// Send a json body. + pub fn send_json(&self, value: &T) -> SendClientRequest { + RequestSender::Rc(self.head.clone(), None).send_json( + self.addr, + self.response_decompress, + self.timeout, + self.config.as_ref(), + value, + ) + } + + /// Send an urlencoded body. + pub fn send_form(&self, value: &T) -> SendClientRequest { + RequestSender::Rc(self.head.clone(), None).send_form( + self.addr, + self.response_decompress, + self.timeout, + self.config.as_ref(), + value, + ) + } + + /// Send a streaming body. + pub fn send_stream(&self, stream: S) -> SendClientRequest + where + S: Stream + 'static, + E: Into + 'static, + { + RequestSender::Rc(self.head.clone(), None).send_stream( + self.addr, + self.response_decompress, + self.timeout, + self.config.as_ref(), + stream, + ) + } + + /// Send an empty body. + pub fn send(&self) -> SendClientRequest { + RequestSender::Rc(self.head.clone(), None).send( + self.addr, + self.response_decompress, + self.timeout, + self.config.as_ref(), + ) + } + + /// Create a `FrozenSendBuilder` with extra headers + pub fn extra_headers(&self, extra_headers: HeaderMap) -> FrozenSendBuilder { + FrozenSendBuilder::new(self.clone(), extra_headers) + } + + /// Create a `FrozenSendBuilder` with an extra header + pub fn extra_header(&self, key: K, value: V) -> FrozenSendBuilder + where + HeaderName: HttpTryFrom, + V: IntoHeaderValue, + { + self.extra_headers(HeaderMap::new()) + .extra_header(key, value) + } +} + +/// Builder that allows to modify extra headers. +pub struct FrozenSendBuilder { + req: FrozenClientRequest, + extra_headers: HeaderMap, + err: Option, +} + +impl FrozenSendBuilder { + pub(crate) fn new(req: FrozenClientRequest, extra_headers: HeaderMap) -> Self { + Self { + req, + extra_headers, + err: None, + } + } + + /// Insert a header, it overrides existing header in `FrozenClientRequest`. + pub fn extra_header(mut self, key: K, value: V) -> Self + where + HeaderName: HttpTryFrom, + V: IntoHeaderValue, + { + match HeaderName::try_from(key) { + Ok(key) => match value.try_into() { + Ok(value) => self.extra_headers.insert(key, value), + Err(e) => self.err = Some(e.into()), + }, + Err(e) => self.err = Some(e.into()), + } + self + } + + /// Complete request construction and send a body. + pub fn send_body(self, body: B) -> SendClientRequest + where + B: Into, + { + if let Some(e) = self.err { + return e.into(); + } + + RequestSender::Rc(self.req.head, Some(self.extra_headers)).send_body( + self.req.addr, + self.req.response_decompress, + self.req.timeout, + self.req.config.as_ref(), + body, + ) + } + + /// Complete request construction and send a json body. + pub fn send_json(self, value: &T) -> SendClientRequest { + if let Some(e) = self.err { + return e.into(); + } + + RequestSender::Rc(self.req.head, Some(self.extra_headers)).send_json( + self.req.addr, + self.req.response_decompress, + self.req.timeout, + self.req.config.as_ref(), + value, + ) + } + + /// Complete request construction and send an urlencoded body. + pub fn send_form(self, value: &T) -> SendClientRequest { + if let Some(e) = self.err { + return e.into(); + } + + RequestSender::Rc(self.req.head, Some(self.extra_headers)).send_form( + self.req.addr, + self.req.response_decompress, + self.req.timeout, + self.req.config.as_ref(), + value, + ) + } + + /// Complete request construction and send a streaming body. + pub fn send_stream(self, stream: S) -> SendClientRequest + where + S: Stream + 'static, + E: Into + 'static, + { + if let Some(e) = self.err { + return e.into(); + } + + RequestSender::Rc(self.req.head, Some(self.extra_headers)).send_stream( + self.req.addr, + self.req.response_decompress, + self.req.timeout, + self.req.config.as_ref(), + stream, + ) + } + + /// Complete request construction and send an empty body. + pub fn send(self) -> SendClientRequest { + if let Some(e) = self.err { + return e.into(); + } + + RequestSender::Rc(self.req.head, Some(self.extra_headers)).send( + self.req.addr, + self.req.response_decompress, + self.req.timeout, + self.req.config.as_ref(), + ) + } +} diff --git a/awc/src/lib.rs b/awc/src/lib.rs index da63bbd93..58c9056b2 100644 --- a/awc/src/lib.rs +++ b/awc/src/lib.rs @@ -33,15 +33,19 @@ use actix_http::RequestHead; mod builder; mod connect; pub mod error; +mod frozen; mod request; mod response; +mod sender; pub mod test; pub mod ws; pub use self::builder::ClientBuilder; pub use self::connect::BoxedSocket; +pub use self::frozen::{FrozenClientRequest, FrozenSendBuilder}; pub use self::request::ClientRequest; pub use self::response::{ClientResponse, JsonBody, MessageBody}; +pub use self::sender::SendClientRequest; use self::connect::{Connect, ConnectorWrapper}; diff --git a/awc/src/request.rs b/awc/src/request.rs index 4dd07c5d8..d597a1638 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -1,29 +1,26 @@ use std::fmt::Write as FmtWrite; use std::io::Write; use std::rc::Rc; -use std::time::{Duration, Instant}; +use std::time::Duration; use std::{fmt, net}; use bytes::{BufMut, Bytes, BytesMut}; -use futures::{Async, Future, Poll, Stream, try_ready}; +use futures::Stream; use percent_encoding::percent_encode; use serde::Serialize; -use serde_json; -use tokio_timer::Delay; -use derive_more::From; -use actix_http::body::{Body, BodyStream}; +use actix_http::body::Body; use actix_http::cookie::{Cookie, CookieJar, USERINFO}; -use actix_http::encoding::Decoder; -use actix_http::http::header::{self, ContentEncoding, Header, IntoHeaderValue}; +use actix_http::http::header::{self, Header, IntoHeaderValue}; use actix_http::http::{ uri, ConnectionType, Error as HttpError, HeaderMap, HeaderName, HeaderValue, HttpTryFrom, Method, Uri, Version, }; -use actix_http::{Error, Payload, PayloadStream, RequestHead}; +use actix_http::{Error, RequestHead}; -use crate::error::{InvalidUrl, SendRequestError, FreezeRequestError}; -use crate::response::ClientResponse; +use crate::error::{FreezeRequestError, InvalidUrl}; +use crate::frozen::FrozenClientRequest; +use crate::sender::{PrepForSendingError, RequestSender, SendClientRequest}; use crate::ClientConfig; #[cfg(any(feature = "brotli", feature = "flate2-zlib", feature = "flate2-rust"))] @@ -375,6 +372,8 @@ impl ClientRequest { } } + /// Freeze request builder and construct `FrozenClientRequest`, + /// which could be used for sending same request multiple times. pub fn freeze(self) -> Result { let slf = match self.prep_for_sending() { Ok(slf) => slf, @@ -393,10 +392,7 @@ impl ClientRequest { } /// Complete request construction and send body. - pub fn send_body( - self, - body: B, - ) -> SendBody + pub fn send_body(self, body: B) -> SendClientRequest where B: Into, { @@ -405,47 +401,51 @@ impl ClientRequest { Err(e) => return e.into(), }; - RequestSender::Owned(slf.head) - .send_body(slf.addr, slf.response_decompress, slf.timeout, slf.config.as_ref(), body) + RequestSender::Owned(slf.head).send_body( + slf.addr, + slf.response_decompress, + slf.timeout, + slf.config.as_ref(), + body, + ) } /// Set a JSON body and generate `ClientRequest` - pub fn send_json( - self, - value: &T, - ) -> SendBody - { + pub fn send_json(self, value: &T) -> SendClientRequest { let slf = match self.prep_for_sending() { Ok(slf) => slf, Err(e) => return e.into(), }; - RequestSender::Owned(slf.head) - .send_json(slf.addr, slf.response_decompress, slf.timeout, slf.config.as_ref(), value) + RequestSender::Owned(slf.head).send_json( + slf.addr, + slf.response_decompress, + slf.timeout, + slf.config.as_ref(), + value, + ) } /// Set a urlencoded body and generate `ClientRequest` /// /// `ClientRequestBuilder` can not be used after this call. - pub fn send_form( - self, - value: &T, - ) -> SendBody - { + pub fn send_form(self, value: &T) -> SendClientRequest { let slf = match self.prep_for_sending() { Ok(slf) => slf, Err(e) => return e.into(), }; - RequestSender::Owned(slf.head) - .send_form(slf.addr, slf.response_decompress, slf.timeout, slf.config.as_ref(), value) + RequestSender::Owned(slf.head).send_form( + slf.addr, + slf.response_decompress, + slf.timeout, + slf.config.as_ref(), + value, + ) } /// Set an streaming body and generate `ClientRequest`. - pub fn send_stream( - self, - stream: S, - ) -> SendBody + pub fn send_stream(self, stream: S) -> SendClientRequest where S: Stream + 'static, E: Into + 'static, @@ -455,22 +455,28 @@ impl ClientRequest { Err(e) => return e.into(), }; - RequestSender::Owned(slf.head) - .send_stream(slf.addr, slf.response_decompress, slf.timeout, slf.config.as_ref(), stream) + RequestSender::Owned(slf.head).send_stream( + slf.addr, + slf.response_decompress, + slf.timeout, + slf.config.as_ref(), + stream, + ) } /// Set an empty body and generate `ClientRequest`. - pub fn send( - self, - ) -> SendBody - { + pub fn send(self) -> SendClientRequest { let slf = match self.prep_for_sending() { Ok(slf) => slf, Err(e) => return e.into(), }; - RequestSender::Owned(slf.head) - .send(slf.addr, slf.response_decompress, slf.timeout, slf.config.as_ref()) + RequestSender::Owned(slf.head).send( + slf.addr, + slf.response_decompress, + slf.timeout, + slf.config.as_ref(), + ) } fn prep_for_sending(mut self) -> Result { @@ -528,10 +534,10 @@ impl ClientRequest { slf = slf.set_header_if_none(header::ACCEPT_ENCODING, HTTPS_ENCODING) } else { #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] - { - slf = slf - .set_header_if_none(header::ACCEPT_ENCODING, "gzip, deflate") - } + { + slf = slf + .set_header_if_none(header::ACCEPT_ENCODING, "gzip, deflate") + } }; } } @@ -555,441 +561,6 @@ impl fmt::Debug for ClientRequest { } } -#[derive(Clone)] -pub struct FrozenClientRequest { - pub(crate) head: Rc, - pub(crate) addr: Option, - pub(crate) response_decompress: bool, - pub(crate) timeout: Option, - pub(crate) config: Rc, -} - -impl FrozenClientRequest { - /// Get HTTP URI of request - pub fn get_uri(&self) -> &Uri { - &self.head.uri - } - - /// Get HTTP method of this request - pub fn get_method(&self) -> &Method { - &self.head.method - } - - /// Returns request's headers. - pub fn headers(&self) -> &HeaderMap { - &self.head.headers - } - - /// Send a body. - pub fn send_body( - &self, - body: B, - ) -> SendBody - where - B: Into, - { - RequestSender::Rc(self.head.clone(), None) - .send_body(self.addr, self.response_decompress, self.timeout, self.config.as_ref(), body) - } - - /// Send a json body. - pub fn send_json( - &self, - value: &T, - ) -> SendBody - { - RequestSender::Rc(self.head.clone(), None) - .send_json(self.addr, self.response_decompress, self.timeout, self.config.as_ref(), value) - } - - /// Send an urlencoded body. - pub fn send_form( - &self, - value: &T, - ) -> SendBody - { - RequestSender::Rc(self.head.clone(), None) - .send_form(self.addr, self.response_decompress, self.timeout, self.config.as_ref(), value) - } - - /// Send a streaming body. - pub fn send_stream( - &self, - stream: S, - ) -> SendBody - where - S: Stream + 'static, - E: Into + 'static, - { - RequestSender::Rc(self.head.clone(), None) - .send_stream(self.addr, self.response_decompress, self.timeout, self.config.as_ref(), stream) - } - - /// Send an empty body. - pub fn send( - &self, - ) -> SendBody - { - RequestSender::Rc(self.head.clone(), None) - .send(self.addr, self.response_decompress, self.timeout, self.config.as_ref()) - } - - /// Create a `FrozenSendBuilder` with extra headers - pub fn extra_headers(&self, extra_headers: HeaderMap) -> FrozenSendBuilder { - FrozenSendBuilder::new(self.clone(), extra_headers) - } - - /// Create a `FrozenSendBuilder` with an extra header - pub fn extra_header(&self, key: K, value: V) -> FrozenSendBuilder - where - HeaderName: HttpTryFrom, - V: IntoHeaderValue, - { - self.extra_headers(HeaderMap::new()).extra_header(key, value) - } -} - -pub struct FrozenSendBuilder { - req: FrozenClientRequest, - extra_headers: HeaderMap, - err: Option, -} - -impl FrozenSendBuilder { - pub(crate) fn new(req: FrozenClientRequest, extra_headers: HeaderMap) -> Self { - Self { - req, - extra_headers, - err: None, - } - } - - /// Insert a header, it overrides existing header in `FrozenClientRequest`. - pub fn extra_header(mut self, key: K, value: V) -> Self - where - HeaderName: HttpTryFrom, - V: IntoHeaderValue, - { - match HeaderName::try_from(key) { - Ok(key) => match value.try_into() { - Ok(value) => self.extra_headers.insert(key, value), - Err(e) => self.err = Some(e.into()), - }, - Err(e) => self.err = Some(e.into()), - } - self - } - - /// Complete request construction and send a body. - pub fn send_body( - self, - body: B, - ) -> SendBody - where - B: Into, - { - if let Some(e) = self.err { - return e.into() - } - - RequestSender::Rc(self.req.head, Some(self.extra_headers)) - .send_body(self.req.addr, self.req.response_decompress, self.req.timeout, self.req.config.as_ref(), body) - } - - /// Complete request construction and send a json body. - pub fn send_json( - self, - value: &T, - ) -> SendBody - { - if let Some(e) = self.err { - return e.into() - } - - RequestSender::Rc(self.req.head, Some(self.extra_headers)) - .send_json(self.req.addr, self.req.response_decompress, self.req.timeout, self.req.config.as_ref(), value) - } - - /// Complete request construction and send an urlencoded body. - pub fn send_form( - self, - value: &T, - ) -> SendBody - { - if let Some(e) = self.err { - return e.into() - } - - RequestSender::Rc(self.req.head, Some(self.extra_headers)) - .send_form(self.req.addr, self.req.response_decompress, self.req.timeout, self.req.config.as_ref(), value) - } - - /// Complete request construction and send a streaming body. - pub fn send_stream( - self, - stream: S, - ) -> SendBody - where - S: Stream + 'static, - E: Into + 'static, - { - if let Some(e) = self.err { - return e.into() - } - - RequestSender::Rc(self.req.head, Some(self.extra_headers)) - .send_stream(self.req.addr, self.req.response_decompress, self.req.timeout, self.req.config.as_ref(), stream) - } - - /// Complete request construction and send an empty body. - pub fn send( - self, - ) -> SendBody - { - if let Some(e) = self.err { - return e.into() - } - - RequestSender::Rc(self.req.head, Some(self.extra_headers)) - .send(self.req.addr, self.req.response_decompress, self.req.timeout, self.req.config.as_ref()) - } -} - -#[derive(Debug, From)] -enum PrepForSendingError { - Url(InvalidUrl), - Http(HttpError), -} - -impl Into for PrepForSendingError { - fn into(self) -> FreezeRequestError { - match self { - PrepForSendingError::Url(e) => FreezeRequestError::Url(e), - PrepForSendingError::Http(e) => FreezeRequestError::Http(e), - } - } -} - -impl Into for PrepForSendingError { - fn into(self) -> SendRequestError { - match self { - PrepForSendingError::Url(e) => SendRequestError::Url(e), - PrepForSendingError::Http(e) => SendRequestError::Http(e), - } - } -} - -pub enum SendBody -{ - Fut(Box>, Option, bool), - Err(Option), -} - -impl SendBody -{ - pub fn new( - send: Box>, - response_decompress: bool, - timeout: Option, - ) -> SendBody - { - let delay = timeout.map(|t| Delay::new(Instant::now() + t)); - SendBody::Fut(send, delay, response_decompress) - } -} - -impl Future for SendBody -{ - type Item = ClientResponse>>; - type Error = SendRequestError; - - fn poll(&mut self) -> Poll { - match self { - SendBody::Fut(send, delay, response_decompress) => { - if delay.is_some() { - match delay.poll() { - Ok(Async::NotReady) => (), - _ => return Err(SendRequestError::Timeout), - } - } - - let res = try_ready!(send.poll()) - .map_body(|head, payload| { - if *response_decompress { - Payload::Stream(Decoder::from_headers(payload, &head.headers)) - } else { - Payload::Stream(Decoder::new(payload, ContentEncoding::Identity)) - } - }); - - Ok(Async::Ready(res)) - }, - SendBody::Err(ref mut e) => { - match e.take() { - Some(e) => Err(e.into()), - None => panic!("Attempting to call completed future"), - } - } - } - } -} - - -impl From for SendBody -{ - fn from(e: SendRequestError) -> Self { - SendBody::Err(Some(e)) - } -} - -impl From for SendBody -{ - fn from(e: Error) -> Self { - SendBody::Err(Some(e.into())) - } -} - -impl From for SendBody -{ - fn from(e: HttpError) -> Self { - SendBody::Err(Some(e.into())) - } -} - -impl From for SendBody -{ - fn from(e: PrepForSendingError) -> Self { - SendBody::Err(Some(e.into())) - } -} - -#[derive(Debug)] -enum RequestSender { - Owned(RequestHead), - Rc(Rc, Option), -} - -impl RequestSender { - pub fn send_body( - self, - addr: Option, - response_decompress: bool, - timeout: Option, - config: &ClientConfig, - body: B, - ) -> SendBody - where - B: Into, - { - let mut connector = config.connector.borrow_mut(); - - let fut = match self { - RequestSender::Owned(head) => connector.send_request(head, body.into(), addr), - RequestSender::Rc(head, extra_headers) => connector.send_request_extra(head, extra_headers, body.into(), addr), - }; - - SendBody::new(fut, response_decompress, timeout.or_else(|| config.timeout.clone())) - } - - pub fn send_json( - mut self, - addr: Option, - response_decompress: bool, - timeout: Option, - config: &ClientConfig, - value: &T, - ) -> SendBody - { - let body = match serde_json::to_string(value) { - Ok(body) => body, - Err(e) => return Error::from(e).into(), - }; - - if let Err(e) = self.set_header_if_none(header::CONTENT_TYPE, "application/json") { - return e.into(); - } - - self.send_body(addr, response_decompress, timeout, config, Body::Bytes(Bytes::from(body))) - } - - pub fn send_form( - mut self, - addr: Option, - response_decompress: bool, - timeout: Option, - config: &ClientConfig, - value: &T, - ) -> SendBody - { - let body = match serde_urlencoded::to_string(value) { - Ok(body) => body, - Err(e) => return Error::from(e).into(), - }; - - // set content-type - if let Err(e) = self.set_header_if_none(header::CONTENT_TYPE, "application/x-www-form-urlencoded") { - return e.into(); - } - - self.send_body(addr, response_decompress, timeout, config, Body::Bytes(Bytes::from(body))) - } - - pub fn send_stream( - self, - addr: Option, - response_decompress: bool, - timeout: Option, - config: &ClientConfig, - stream: S, - ) -> SendBody - where - S: Stream + 'static, - E: Into + 'static, - { - self.send_body(addr, response_decompress, timeout, config, Body::from_message(BodyStream::new(stream))) - } - - pub fn send( - self, - addr: Option, - response_decompress: bool, - timeout: Option, - config: &ClientConfig, - ) -> SendBody - { - self.send_body(addr, response_decompress, timeout, config, Body::Empty) - } - - fn set_header_if_none(&mut self, key: HeaderName, value: V) -> Result<(), HttpError> - where - V: IntoHeaderValue, - { - match self { - RequestSender::Owned(head) => { - if !head.headers.contains_key(&key) { - match value.try_into() { - Ok(value) => head.headers.insert(key, value), - Err(e) => return Err(e.into()), - } - } - }, - RequestSender::Rc(head, extra_headers) => { - if !head.headers.contains_key(&key) && !extra_headers.iter().any(|h| h.contains_key(&key)) { - match value.try_into(){ - Ok(v) => { - let h = extra_headers.get_or_insert(HeaderMap::new()); - h.insert(key, v) - }, - Err(e) => return Err(e.into()), - }; - } - } - } - - Ok(()) - } -} - #[cfg(test)] mod tests { use std::time::SystemTime; diff --git a/awc/src/sender.rs b/awc/src/sender.rs new file mode 100644 index 000000000..c8e169cb1 --- /dev/null +++ b/awc/src/sender.rs @@ -0,0 +1,282 @@ +use std::net; +use std::rc::Rc; +use std::time::{Duration, Instant}; + +use bytes::Bytes; +use derive_more::From; +use futures::{try_ready, Async, Future, Poll, Stream}; +use serde::Serialize; +use serde_json; +use tokio_timer::Delay; + +use actix_http::body::{Body, BodyStream}; +use actix_http::encoding::Decoder; +use actix_http::http::header::{self, ContentEncoding, IntoHeaderValue}; +use actix_http::http::{Error as HttpError, HeaderMap, HeaderName}; +use actix_http::{Error, Payload, PayloadStream, RequestHead}; + +use crate::error::{FreezeRequestError, InvalidUrl, SendRequestError}; +use crate::response::ClientResponse; +use crate::ClientConfig; + +#[derive(Debug, From)] +pub(crate) enum PrepForSendingError { + Url(InvalidUrl), + Http(HttpError), +} + +impl Into for PrepForSendingError { + fn into(self) -> FreezeRequestError { + match self { + PrepForSendingError::Url(e) => FreezeRequestError::Url(e), + PrepForSendingError::Http(e) => FreezeRequestError::Http(e), + } + } +} + +impl Into for PrepForSendingError { + fn into(self) -> SendRequestError { + match self { + PrepForSendingError::Url(e) => SendRequestError::Url(e), + PrepForSendingError::Http(e) => SendRequestError::Http(e), + } + } +} + +/// Future that sends request's payload and resolves to a server response. +#[must_use = "futures do nothing unless polled"] +pub enum SendClientRequest { + Fut( + Box>, + Option, + bool, + ), + Err(Option), +} + +impl SendClientRequest { + pub(crate) fn new( + send: Box>, + response_decompress: bool, + timeout: Option, + ) -> SendClientRequest { + let delay = timeout.map(|t| Delay::new(Instant::now() + t)); + SendClientRequest::Fut(send, delay, response_decompress) + } +} + +impl Future for SendClientRequest { + type Item = ClientResponse>>; + type Error = SendRequestError; + + fn poll(&mut self) -> Poll { + match self { + SendClientRequest::Fut(send, delay, response_decompress) => { + if delay.is_some() { + match delay.poll() { + Ok(Async::NotReady) => (), + _ => return Err(SendRequestError::Timeout), + } + } + + let res = try_ready!(send.poll()).map_body(|head, payload| { + if *response_decompress { + Payload::Stream(Decoder::from_headers(payload, &head.headers)) + } else { + Payload::Stream(Decoder::new(payload, ContentEncoding::Identity)) + } + }); + + Ok(Async::Ready(res)) + } + SendClientRequest::Err(ref mut e) => match e.take() { + Some(e) => Err(e.into()), + None => panic!("Attempting to call completed future"), + }, + } + } +} + +impl From for SendClientRequest { + fn from(e: SendRequestError) -> Self { + SendClientRequest::Err(Some(e)) + } +} + +impl From for SendClientRequest { + fn from(e: Error) -> Self { + SendClientRequest::Err(Some(e.into())) + } +} + +impl From for SendClientRequest { + fn from(e: HttpError) -> Self { + SendClientRequest::Err(Some(e.into())) + } +} + +impl From for SendClientRequest { + fn from(e: PrepForSendingError) -> Self { + SendClientRequest::Err(Some(e.into())) + } +} + +#[derive(Debug)] +pub(crate) enum RequestSender { + Owned(RequestHead), + Rc(Rc, Option), +} + +impl RequestSender { + pub(crate) fn send_body( + self, + addr: Option, + response_decompress: bool, + timeout: Option, + config: &ClientConfig, + body: B, + ) -> SendClientRequest + where + B: Into, + { + let mut connector = config.connector.borrow_mut(); + + let fut = match self { + RequestSender::Owned(head) => { + connector.send_request(head, body.into(), addr) + } + RequestSender::Rc(head, extra_headers) => { + connector.send_request_extra(head, extra_headers, body.into(), addr) + } + }; + + SendClientRequest::new( + fut, + response_decompress, + timeout.or_else(|| config.timeout.clone()), + ) + } + + pub(crate) fn send_json( + mut self, + addr: Option, + response_decompress: bool, + timeout: Option, + config: &ClientConfig, + value: &T, + ) -> SendClientRequest { + let body = match serde_json::to_string(value) { + Ok(body) => body, + Err(e) => return Error::from(e).into(), + }; + + if let Err(e) = self.set_header_if_none(header::CONTENT_TYPE, "application/json") + { + return e.into(); + } + + self.send_body( + addr, + response_decompress, + timeout, + config, + Body::Bytes(Bytes::from(body)), + ) + } + + pub(crate) fn send_form( + mut self, + addr: Option, + response_decompress: bool, + timeout: Option, + config: &ClientConfig, + value: &T, + ) -> SendClientRequest { + let body = match serde_urlencoded::to_string(value) { + Ok(body) => body, + Err(e) => return Error::from(e).into(), + }; + + // set content-type + if let Err(e) = self.set_header_if_none( + header::CONTENT_TYPE, + "application/x-www-form-urlencoded", + ) { + return e.into(); + } + + self.send_body( + addr, + response_decompress, + timeout, + config, + Body::Bytes(Bytes::from(body)), + ) + } + + pub(crate) fn send_stream( + self, + addr: Option, + response_decompress: bool, + timeout: Option, + config: &ClientConfig, + stream: S, + ) -> SendClientRequest + where + S: Stream + 'static, + E: Into + 'static, + { + self.send_body( + addr, + response_decompress, + timeout, + config, + Body::from_message(BodyStream::new(stream)), + ) + } + + pub(crate) fn send( + self, + addr: Option, + response_decompress: bool, + timeout: Option, + config: &ClientConfig, + ) -> SendClientRequest { + self.send_body(addr, response_decompress, timeout, config, Body::Empty) + } + + fn set_header_if_none( + &mut self, + key: HeaderName, + value: V, + ) -> Result<(), HttpError> + where + V: IntoHeaderValue, + { + match self { + RequestSender::Owned(head) => { + if !head.headers.contains_key(&key) { + match value.try_into() { + Ok(value) => head.headers.insert(key, value), + Err(e) => return Err(e.into()), + } + } + } + RequestSender::Rc(head, extra_headers) => { + if !head.headers.contains_key(&key) + && !extra_headers.iter().any(|h| h.contains_key(&key)) + { + match value.try_into() { + Ok(v) => { + let h = extra_headers.get_or_insert(HeaderMap::new()); + h.insert(key, v) + } + Err(e) => return Err(e.into()), + }; + } + } + } + + Ok(()) + } +} From 60b7aebd0a6de57cc480e8fdc9a755743654bde1 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 12 Sep 2019 21:52:46 +0600 Subject: [PATCH 004/176] fmt & clippy --- actix-http/Cargo.toml | 2 +- actix-http/src/client/error.rs | 2 +- actix-http/src/client/h1proto.rs | 28 +++++++++++++--------------- actix-http/src/client/h2proto.rs | 20 +++++++++++++------- actix-http/src/client/mod.rs | 2 +- actix-http/src/client/pool.rs | 2 +- actix-http/src/h1/client.rs | 10 +++++++--- actix-http/src/h1/encoder.rs | 23 +++++++++++++++-------- actix-multipart/src/server.rs | 28 ++++++++++++++++------------ awc/src/connect.rs | 21 +++++++++++++++------ awc/src/error.rs | 4 +++- awc/src/sender.rs | 4 ++-- awc/src/ws.rs | 5 ++++- src/app_service.rs | 2 +- src/middleware/mod.rs | 4 ++-- 15 files changed, 95 insertions(+), 62 deletions(-) diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 3019b2897..cc7c885e7 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -66,7 +66,7 @@ hashbrown = "0.5.0" h2 = "0.1.16" http = "0.1.17" httparse = "1.3" -indexmap = "1.0" +indexmap = "1.2" lazy_static = "1.0" language-tags = "0.2" log = "0.4" diff --git a/actix-http/src/client/error.rs b/actix-http/src/client/error.rs index 40aef2cce..0ac5f30f5 100644 --- a/actix-http/src/client/error.rs +++ b/actix-http/src/client/error.rs @@ -147,4 +147,4 @@ impl From for SendRequestError { FreezeRequestError::Http(e) => e.into(), } } -} \ No newline at end of file +} diff --git a/actix-http/src/client/h1proto.rs b/actix-http/src/client/h1proto.rs index fa920ab92..b078c6a67 100644 --- a/actix-http/src/client/h1proto.rs +++ b/actix-http/src/client/h1proto.rs @@ -8,10 +8,10 @@ use futures::{Async, Future, Poll, Sink, Stream}; use crate::error::PayloadError; use crate::h1; +use crate::header::HeaderMap; use crate::http::header::{IntoHeaderValue, HOST}; use crate::message::{RequestHeadType, ResponseHead}; use crate::payload::{Payload, PayloadStream}; -use crate::header::HeaderMap; use super::connection::{ConnectionLifetime, ConnectionType, IoConnection}; use super::error::{ConnectError, SendRequestError}; @@ -30,7 +30,9 @@ where B: MessageBody, { // set request host header - if !head.as_ref().headers.contains_key(HOST) && !head.extra_headers().iter().any(|h| h.contains_key(HOST)) { + if !head.as_ref().headers.contains_key(HOST) + && !head.extra_headers().iter().any(|h| h.contains_key(HOST)) + { if let Some(host) = head.as_ref().uri.host() { let mut wrt = BytesMut::with_capacity(host.len() + 5).writer(); @@ -40,20 +42,16 @@ where }; match wrt.get_mut().take().freeze().try_into() { - Ok(value) => { - match head { - RequestHeadType::Owned(ref mut head) => { - head.headers.insert(HOST, value) - }, - RequestHeadType::Rc(_, ref mut extra_headers) => { - let headers = extra_headers.get_or_insert(HeaderMap::new()); - headers.insert(HOST, value) - }, + Ok(value) => match head { + RequestHeadType::Owned(ref mut head) => { + head.headers.insert(HOST, value) } - } - Err(e) => { - log::error!("Can not set HOST header {}", e) - } + RequestHeadType::Rc(_, ref mut extra_headers) => { + let headers = extra_headers.get_or_insert(HeaderMap::new()); + headers.insert(HOST, value) + } + }, + Err(e) => log::error!("Can not set HOST header {}", e), } } } diff --git a/actix-http/src/client/h2proto.rs b/actix-http/src/client/h2proto.rs index 2993d89d8..5744a1547 100644 --- a/actix-http/src/client/h2proto.rs +++ b/actix-http/src/client/h2proto.rs @@ -9,9 +9,9 @@ use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, TRANSFER_ENCODING}; use http::{request::Request, HttpTryFrom, Method, Version}; use crate::body::{BodySize, MessageBody}; +use crate::header::HeaderMap; use crate::message::{RequestHeadType, ResponseHead}; use crate::payload::Payload; -use crate::header::HeaderMap; use super::connection::{ConnectionType, IoConnection}; use super::error::SendRequestError; @@ -69,15 +69,21 @@ where // Extracting extra headers from RequestHeadType. HeaderMap::new() does not allocate. let (head, extra_headers) = match head { - RequestHeadType::Owned(head) => (RequestHeadType::Owned(head), HeaderMap::new()), - RequestHeadType::Rc(head, extra_headers) => (RequestHeadType::Rc(head, None), extra_headers.unwrap_or(HeaderMap::new())), + RequestHeadType::Owned(head) => { + (RequestHeadType::Owned(head), HeaderMap::new()) + } + RequestHeadType::Rc(head, extra_headers) => ( + RequestHeadType::Rc(head, None), + extra_headers.unwrap_or_else(HeaderMap::new), + ), }; // merging headers from head and extra headers. - let headers = head.as_ref().headers.iter() - .filter(|(name, _)| { - !extra_headers.contains_key(*name) - }) + let headers = head + .as_ref() + .headers + .iter() + .filter(|(name, _)| !extra_headers.contains_key(*name)) .chain(extra_headers.iter()); // copy headers diff --git a/actix-http/src/client/mod.rs b/actix-http/src/client/mod.rs index 04427ce42..a45aebcd5 100644 --- a/actix-http/src/client/mod.rs +++ b/actix-http/src/client/mod.rs @@ -10,7 +10,7 @@ mod pool; pub use self::connection::Connection; pub use self::connector::Connector; -pub use self::error::{ConnectError, InvalidUrl, SendRequestError, FreezeRequestError}; +pub use self::error::{ConnectError, FreezeRequestError, InvalidUrl, SendRequestError}; pub use self::pool::Protocol; #[derive(Clone)] diff --git a/actix-http/src/client/pool.rs b/actix-http/src/client/pool.rs index 24a187392..a3522ff8a 100644 --- a/actix-http/src/client/pool.rs +++ b/actix-http/src/client/pool.rs @@ -326,7 +326,7 @@ impl Inner { fn release_waiter(&mut self, key: &Key, token: usize) { self.waiters.remove(token); - self.waiters_queue.remove(&(key.clone(), token)); + let _ = self.waiters_queue.shift_remove(&(key.clone(), token)); } } diff --git a/actix-http/src/h1/client.rs b/actix-http/src/h1/client.rs index c0bbcc694..bea629c4f 100644 --- a/actix-http/src/h1/client.rs +++ b/actix-http/src/h1/client.rs @@ -16,9 +16,11 @@ use super::{Message, MessageType}; use crate::body::BodySize; use crate::config::ServiceConfig; use crate::error::{ParseError, PayloadError}; -use crate::helpers; -use crate::message::{ConnectionType, Head, MessagePool, RequestHead, RequestHeadType, ResponseHead}; use crate::header::HeaderMap; +use crate::helpers; +use crate::message::{ + ConnectionType, Head, MessagePool, RequestHead, RequestHeadType, ResponseHead, +}; bitflags! { struct Flags: u8 { @@ -197,7 +199,9 @@ impl Encoder for ClientCodec { Message::Item((mut head, length)) => { let inner = &mut self.inner; inner.version = head.as_ref().version; - inner.flags.set(Flags::HEAD, head.as_ref().method == Method::HEAD); + inner + .flags + .set(Flags::HEAD, head.as_ref().method == Method::HEAD); // connection status inner.ctype = match head.as_ref().connection_type() { diff --git a/actix-http/src/h1/encoder.rs b/actix-http/src/h1/encoder.rs index 380dfe328..51ea497e0 100644 --- a/actix-http/src/h1/encoder.rs +++ b/actix-http/src/h1/encoder.rs @@ -2,9 +2,9 @@ use std::fmt::Write as FmtWrite; use std::io::Write; use std::marker::PhantomData; +use std::rc::Rc; use std::str::FromStr; use std::{cmp, fmt, io, mem}; -use std::rc::Rc; use bytes::{BufMut, Bytes, BytesMut}; @@ -16,7 +16,7 @@ use crate::http::header::{ HeaderValue, ACCEPT_ENCODING, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING, }; use crate::http::{HeaderMap, Method, StatusCode, Version}; -use crate::message::{ConnectionType, Head, RequestHead, ResponseHead, RequestHeadType}; +use crate::message::{ConnectionType, Head, RequestHead, RequestHeadType, ResponseHead}; use crate::request::Request; use crate::response::Response; @@ -134,10 +134,11 @@ pub(crate) trait MessageType: Sized { // merging headers from head and extra headers. HeaderMap::new() does not allocate. let empty_headers = HeaderMap::new(); let extra_headers = self.extra_headers().unwrap_or(&empty_headers); - let headers = self.headers().inner.iter() - .filter(|(name, _)| { - !extra_headers.contains_key(*name) - }) + let headers = self + .headers() + .inner + .iter() + .filter(|(name, _)| !extra_headers.contains_key(*name)) .chain(extra_headers.inner.iter()); // write headers @@ -604,10 +605,16 @@ mod tests { let mut bytes = BytesMut::with_capacity(2048); let mut head = RequestHead::default(); - head.headers.insert(AUTHORIZATION, HeaderValue::from_static("some authorization")); + head.headers.insert( + AUTHORIZATION, + HeaderValue::from_static("some authorization"), + ); let mut extra_headers = HeaderMap::new(); - extra_headers.insert(AUTHORIZATION,HeaderValue::from_static("another authorization")); + extra_headers.insert( + AUTHORIZATION, + HeaderValue::from_static("another authorization"), + ); extra_headers.insert(DATE, HeaderValue::from_static("date")); let mut head = RequestHeadType::Rc(Rc::new(head), Some(extra_headers)); diff --git a/actix-multipart/src/server.rs b/actix-multipart/src/server.rs index 3312a580a..a7c787f46 100644 --- a/actix-multipart/src/server.rs +++ b/actix-multipart/src/server.rs @@ -178,13 +178,15 @@ impl InnerMultipart { Some(chunk) => { if chunk.len() < boundary.len() + 4 || &chunk[..2] != b"--" - || &chunk[2..boundary.len() + 2] != boundary.as_bytes() { + || &chunk[2..boundary.len() + 2] != boundary.as_bytes() + { Err(MultipartError::Boundary) } else if &chunk[boundary.len() + 2..] == b"\r\n" { Ok(Some(false)) } else if &chunk[boundary.len() + 2..boundary.len() + 4] == b"--" && (chunk.len() == boundary.len() + 4 - || &chunk[boundary.len() + 4..] == b"\r\n") { + || &chunk[boundary.len() + 4..] == b"\r\n") + { Ok(Some(true)) } else { Err(MultipartError::Boundary) @@ -781,8 +783,10 @@ impl PayloadBuffer { /// Read bytes until new line delimiter or eof pub fn readline_or_eof(&mut self) -> Result, MultipartError> { match self.readline() { - Err(MultipartError::Incomplete) if self.eof => Ok(Some(self.buf.take().freeze())), - line => line + Err(MultipartError::Incomplete) if self.eof => { + Ok(Some(self.buf.take().freeze())) + } + line => line, } } @@ -859,14 +863,14 @@ mod tests { fn create_simple_request_with_header() -> (Bytes, HeaderMap) { let bytes = Bytes::from( "testasdadsad\r\n\ - --abbc761f78ff4d7cb7573b5a23f96ef0\r\n\ - Content-Disposition: form-data; name=\"file\"; filename=\"fn.txt\"\r\n\ - Content-Type: text/plain; charset=utf-8\r\nContent-Length: 4\r\n\r\n\ - test\r\n\ - --abbc761f78ff4d7cb7573b5a23f96ef0\r\n\ - Content-Type: text/plain; charset=utf-8\r\nContent-Length: 4\r\n\r\n\ - data\r\n\ - --abbc761f78ff4d7cb7573b5a23f96ef0--\r\n" + --abbc761f78ff4d7cb7573b5a23f96ef0\r\n\ + Content-Disposition: form-data; name=\"file\"; filename=\"fn.txt\"\r\n\ + Content-Type: text/plain; charset=utf-8\r\nContent-Length: 4\r\n\r\n\ + test\r\n\ + --abbc761f78ff4d7cb7573b5a23f96ef0\r\n\ + Content-Type: text/plain; charset=utf-8\r\nContent-Length: 4\r\n\r\n\ + data\r\n\ + --abbc761f78ff4d7cb7573b5a23f96ef0--\r\n", ); let mut headers = HeaderMap::new(); headers.insert( diff --git a/awc/src/connect.rs b/awc/src/connect.rs index 82fd6a759..97864d300 100644 --- a/awc/src/connect.rs +++ b/awc/src/connect.rs @@ -1,5 +1,5 @@ -use std::{fmt, io, net}; use std::rc::Rc; +use std::{fmt, io, net}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_http::body::Body; @@ -7,8 +7,8 @@ use actix_http::client::{ Connect as ClientConnect, ConnectError, Connection, SendRequestError, }; use actix_http::h1::ClientCodec; -use actix_http::{RequestHead, RequestHeadType, ResponseHead}; use actix_http::http::HeaderMap; +use actix_http::{RequestHead, RequestHeadType, ResponseHead}; use actix_service::Service; use futures::{Future, Poll}; @@ -82,7 +82,9 @@ where }) .from_err() // send request - .and_then(move |connection| connection.send_request(RequestHeadType::from(head), body)) + .and_then(move |connection| { + connection.send_request(RequestHeadType::from(head), body) + }) .map(|(head, payload)| ClientResponse::new(head, payload)), ) } @@ -103,7 +105,10 @@ where }) .from_err() // send request - .and_then(move |connection| connection.send_request(RequestHeadType::Rc(head, extra_headers), body)) + .and_then(move |connection| { + connection + .send_request(RequestHeadType::Rc(head, extra_headers), body) + }) .map(|(head, payload)| ClientResponse::new(head, payload)), ) } @@ -127,7 +132,9 @@ where }) .from_err() // send request - .and_then(move |connection| connection.open_tunnel(RequestHeadType::from(head))) + .and_then(move |connection| { + connection.open_tunnel(RequestHeadType::from(head)) + }) .map(|(head, framed)| { let framed = framed.map_io(|io| BoxedSocket(Box::new(Socket(io)))); (head, framed) @@ -155,7 +162,9 @@ where }) .from_err() // send request - .and_then(move |connection| connection.open_tunnel(RequestHeadType::Rc(head, extra_headers))) + .and_then(move |connection| { + connection.open_tunnel(RequestHeadType::Rc(head, extra_headers)) + }) .map(|(head, framed)| { let framed = framed.map_io(|io| BoxedSocket(Box::new(Socket(io)))); (head, framed) diff --git a/awc/src/error.rs b/awc/src/error.rs index 4eb929007..eb8d03e2b 100644 --- a/awc/src/error.rs +++ b/awc/src/error.rs @@ -1,5 +1,7 @@ //! Http client errors -pub use actix_http::client::{ConnectError, InvalidUrl, SendRequestError, FreezeRequestError}; +pub use actix_http::client::{ + ConnectError, FreezeRequestError, InvalidUrl, SendRequestError, +}; pub use actix_http::error::PayloadError; pub use actix_http::ws::HandshakeError as WsHandshakeError; pub use actix_http::ws::ProtocolError as WsProtocolError; diff --git a/awc/src/sender.rs b/awc/src/sender.rs index c8e169cb1..95109b92d 100644 --- a/awc/src/sender.rs +++ b/awc/src/sender.rs @@ -90,7 +90,7 @@ impl Future for SendClientRequest { Ok(Async::Ready(res)) } SendClientRequest::Err(ref mut e) => match e.take() { - Some(e) => Err(e.into()), + Some(e) => Err(e), None => panic!("Attempting to call completed future"), }, } @@ -153,7 +153,7 @@ impl RequestSender { SendClientRequest::new( fut, response_decompress, - timeout.or_else(|| config.timeout.clone()), + timeout.or_else(|| config.timeout), ) } diff --git a/awc/src/ws.rs b/awc/src/ws.rs index 67be9e9d8..77cbc7ca4 100644 --- a/awc/src/ws.rs +++ b/awc/src/ws.rs @@ -234,7 +234,10 @@ impl WebsocketsRequest { } if !self.head.headers.contains_key(header::HOST) { - self.head.headers.insert(header::HOST, HeaderValue::from_str(uri.host().unwrap()).unwrap()); + self.head.headers.insert( + header::HOST, + HeaderValue::from_str(uri.host().unwrap()).unwrap(), + ); } // set cookies diff --git a/src/app_service.rs b/src/app_service.rs index 736c35010..513b4aa4b 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -169,7 +169,7 @@ where match self.data_factories_fut[idx].poll()? { Async::Ready(f) => { self.data_factories.push(f); - self.data_factories_fut.remove(idx); + let _ = self.data_factories_fut.remove(idx); } Async::NotReady => idx += 1, } diff --git a/src/middleware/mod.rs b/src/middleware/mod.rs index 311d0ee99..84e0758bf 100644 --- a/src/middleware/mod.rs +++ b/src/middleware/mod.rs @@ -2,13 +2,13 @@ mod compress; pub use self::compress::{BodyEncoding, Compress}; +mod condition; mod defaultheaders; pub mod errhandlers; mod logger; mod normalize; -mod condition; +pub use self::condition::Condition; pub use self::defaultheaders::DefaultHeaders; pub use self::logger::Logger; pub use self::normalize::NormalizePath; -pub use self::condition::Condition; From e35d930ef9d2c7c33a36cd9760f44b0b3bb2d6f5 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 12 Sep 2019 21:58:08 +0600 Subject: [PATCH 005/176] prepare releases --- actix-multipart/CHANGES.md | 3 ++- actix-multipart/Cargo.toml | 2 +- awc/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/actix-multipart/CHANGES.md b/actix-multipart/CHANGES.md index 365dca286..ca61176c7 100644 --- a/actix-multipart/CHANGES.md +++ b/actix-multipart/CHANGES.md @@ -1,5 +1,6 @@ # Changes -## [0.1.4] - 2019-xx-xx + +## [0.1.4] - 2019-09-12 * Multipart handling now parses requests which do not end in CRLF #1038 diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index b26681e25..2168c259a 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-multipart" -version = "0.1.3" +version = "0.1.4" authors = ["Nikolay Kim "] description = "Multipart support for actix web framework." readme = "README.md" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 3d15c943e..3a86193c6 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "awc" -version = "0.2.5" +version = "0.2.6" authors = ["Nikolay Kim "] description = "Actix http client." readme = "README.md" From a32573bb58727059afa470bf5d596b03f6616b7e Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 13 Sep 2019 11:56:24 +0600 Subject: [PATCH 006/176] Allow to re-construct ServiceRequest from HttpRequest and Payload #1088 --- CHANGES.md | 8 +++---- actix-http/CHANGES.md | 2 ++ src/service.rs | 49 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f37f8b466..2a2e2e414 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,13 +1,13 @@ # Changes -## not released yet + +## [1.0.8] - 2019-09-xx ### Added -* Add `middleware::Conditon` that conditionally enables another middleware +* Add `middleware::Conditon` that conditionally enables another middleware -### Fixed +* Allow to re-construct `ServiceRequest` from `HttpRequest` and `Payload` -* h2 will use error response #1080 ## [1.0.7] - 2019-08-29 diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index d603cde7b..849839378 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -8,6 +8,8 @@ ### Fixed +* h2 will use error response #1080 + * on_connect result isn't added to request extensions for http2 requests #1009 diff --git a/src/service.rs b/src/service.rs index 1d475cf15..8b94dd284 100644 --- a/src/service.rs +++ b/src/service.rs @@ -68,6 +68,34 @@ impl ServiceRequest { (self.0, pl) } + /// Construct request from parts. + /// + /// `ServiceRequest` can be re-constructed only if `req` hasnt been cloned. + pub fn from_parts( + mut req: HttpRequest, + pl: Payload, + ) -> Result { + if Rc::strong_count(&req.0) == 1 && Rc::weak_count(&req.0) == 0 { + Rc::get_mut(&mut req.0).unwrap().payload = pl; + Ok(ServiceRequest(req)) + } else { + Err((req, pl)) + } + } + + /// Construct request from request. + /// + /// `HttpRequest` implements `Clone` trait via `Rc` type. `ServiceRequest` + /// can be re-constructed only if rc's strong pointers count eq 1 and + /// weak pointers count is 0. + pub fn from_request(req: HttpRequest) -> Result { + if Rc::strong_count(&req.0) == 1 && Rc::weak_count(&req.0) == 0 { + Ok(ServiceRequest(req)) + } else { + Err(req) + } + } + /// Create service response #[inline] pub fn into_response>>(self, res: R) -> ServiceResponse { @@ -514,6 +542,27 @@ mod tests { use crate::test::{call_service, init_service, TestRequest}; use crate::{guard, http, web, App, HttpResponse}; + #[test] + fn test_service_request() { + let req = TestRequest::default().to_srv_request(); + let (r, pl) = req.into_parts(); + assert!(ServiceRequest::from_parts(r, pl).is_ok()); + + let req = TestRequest::default().to_srv_request(); + let (r, pl) = req.into_parts(); + let _r2 = r.clone(); + assert!(ServiceRequest::from_parts(r, pl).is_err()); + + let req = TestRequest::default().to_srv_request(); + let (r, _pl) = req.into_parts(); + assert!(ServiceRequest::from_request(r).is_ok()); + + let req = TestRequest::default().to_srv_request(); + let (r, _pl) = req.into_parts(); + let _r2 = r.clone(); + assert!(ServiceRequest::from_request(r).is_err()); + } + #[test] fn test_service() { let mut srv = init_service( From c1f99e0775b986d57244e4ef20faa8982f0dac88 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Mon, 16 Sep 2019 07:52:23 +0900 Subject: [PATCH 007/176] Remove `mem::uninitialized()` (#1090) --- actix-http/src/client/connector.rs | 2 +- actix-http/src/h1/decoder.rs | 13 ++++++++----- actix-http/src/helpers.rs | 2 +- actix-http/src/test.rs | 2 +- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index 98e8618c3..d92441f25 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -212,7 +212,7 @@ where pub fn finish( self, ) -> impl Service - + Clone { + + Clone { #[cfg(not(any(feature = "ssl", feature = "rust-tls")))] { let connector = TimeoutService::new( diff --git a/actix-http/src/h1/decoder.rs b/actix-http/src/h1/decoder.rs index c7ef065a5..ce113a145 100644 --- a/actix-http/src/h1/decoder.rs +++ b/actix-http/src/h1/decoder.rs @@ -1,5 +1,6 @@ +use std::io; use std::marker::PhantomData; -use std::{io, mem}; +use std::mem::MaybeUninit; use actix_codec::Decoder; use bytes::{Bytes, BytesMut}; @@ -186,11 +187,12 @@ impl MessageType for Request { fn decode(src: &mut BytesMut) -> Result, ParseError> { // Unsafe: we read only this data only after httparse parses headers into. // performance bump for pipeline benchmarks. - let mut headers: [HeaderIndex; MAX_HEADERS] = unsafe { mem::uninitialized() }; + let mut headers: [HeaderIndex; MAX_HEADERS] = + unsafe { MaybeUninit::uninit().assume_init() }; let (len, method, uri, ver, h_len) = { let mut parsed: [httparse::Header; MAX_HEADERS] = - unsafe { mem::uninitialized() }; + unsafe { MaybeUninit::uninit().assume_init() }; let mut req = httparse::Request::new(&mut parsed); match req.parse(src)? { @@ -260,11 +262,12 @@ impl MessageType for ResponseHead { fn decode(src: &mut BytesMut) -> Result, ParseError> { // Unsafe: we read only this data only after httparse parses headers into. // performance bump for pipeline benchmarks. - let mut headers: [HeaderIndex; MAX_HEADERS] = unsafe { mem::uninitialized() }; + let mut headers: [HeaderIndex; MAX_HEADERS] = + unsafe { MaybeUninit::uninit().assume_init() }; let (len, ver, status, h_len) = { let mut parsed: [httparse::Header; MAX_HEADERS] = - unsafe { mem::uninitialized() }; + unsafe { MaybeUninit::uninit().assume_init() }; let mut res = httparse::Response::new(&mut parsed); match res.parse(src)? { diff --git a/actix-http/src/helpers.rs b/actix-http/src/helpers.rs index e4583ee37..84403d8fd 100644 --- a/actix-http/src/helpers.rs +++ b/actix-http/src/helpers.rs @@ -115,7 +115,7 @@ pub fn write_content_length(mut n: usize, bytes: &mut BytesMut) { pub(crate) fn convert_usize(mut n: usize, bytes: &mut BytesMut) { let mut curr: isize = 39; - let mut buf: [u8; 41] = unsafe { mem::uninitialized() }; + let mut buf: [u8; 41] = unsafe { mem::MaybeUninit::uninit().assume_init() }; buf[39] = b'\r'; buf[40] = b'\n'; let buf_ptr = buf.as_mut_ptr(); diff --git a/actix-http/src/test.rs b/actix-http/src/test.rs index ce81a54d5..ed5b81a35 100644 --- a/actix-http/src/test.rs +++ b/actix-http/src/test.rs @@ -150,7 +150,7 @@ impl TestRequest { /// Complete request creation and generate `Request` instance pub fn finish(&mut self) -> Request { - let inner = self.0.take().expect("cannot reuse test request builder");; + let inner = self.0.take().expect("cannot reuse test request builder"); let mut req = if let Some(pl) = inner.payload { Request::with_payload(pl) From 7c9f9afc46d2bf1c0a69826b2de41fa4f59f97a9 Mon Sep 17 00:00:00 2001 From: nWacky <38620459+nWacky@users.noreply.github.com> Date: Tue, 17 Sep 2019 03:57:39 +0300 Subject: [PATCH 008/176] Add ability to use `Infallible` as `HttpResponse` error type (#1093) * Add `std::convert::Infallible` implementantion for `ResponseError` * Add from `std::convert::Infallible` to `Error` * Remove `ResponseError` implementantion for `Infallible` * Remove useless docs * Better comment * Update changelog * Update actix_http::changelog --- actix-http/CHANGES.md | 3 +++ actix-http/src/error.rs | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 849839378..6820626f5 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -6,6 +6,9 @@ * Add support for sending HTTP requests with `Rc` in addition to sending HTTP requests with `RequestHead` +* Allow to use `std::convert::Infallible` as `actix_http::error::Error` + + ### Fixed * h2 will use error response #1080 diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index 2c01c86db..90c35e486 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -132,6 +132,14 @@ impl std::error::Error for Error { } } +impl From for Error { + fn from(_: std::convert::Infallible) -> Self { + // `std::convert::Infallible` indicates an error + // that will never happen + unreachable!() + } +} + /// Convert `Error` to a `Response` instance impl From for Response { fn from(err: Error) -> Self { From 32a1c365975acffb25959f5887a6530396788504 Mon Sep 17 00:00:00 2001 From: Jos van den Oever Date: Tue, 17 Sep 2019 02:58:04 +0200 Subject: [PATCH 009/176] Make UrlencodedError::Overflow more informative (#1089) --- CHANGES.md | 1 + src/error.rs | 13 +++++++++---- src/types/form.rs | 16 +++++++++++----- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 2a2e2e414..95f8b75e1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,7 @@ * Allow to re-construct `ServiceRequest` from `HttpRequest` and `Payload` +* Make UrlEncodedError::Overflow more informativve ## [1.0.7] - 2019-08-29 diff --git a/src/error.rs b/src/error.rs index 9f31582ed..a60276a7a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -32,8 +32,12 @@ pub enum UrlencodedError { #[display(fmt = "Can not decode chunked transfer encoding")] Chunked, /// Payload size is bigger than allowed. (default: 256kB) - #[display(fmt = "Urlencoded payload size is bigger than allowed (default: 256kB)")] - Overflow, + #[display( + fmt = "Urlencoded payload size is bigger ({} bytes) than allowed (default: {} bytes)", + size, + limit + )] + Overflow { size: usize, limit: usize }, /// Payload size is now known #[display(fmt = "Payload size is now known")] UnknownLength, @@ -52,7 +56,7 @@ pub enum UrlencodedError { impl ResponseError for UrlencodedError { fn error_response(&self) -> HttpResponse { match *self { - UrlencodedError::Overflow => { + UrlencodedError::Overflow { .. } => { HttpResponse::new(StatusCode::PAYLOAD_TOO_LARGE) } UrlencodedError::UnknownLength => { @@ -164,7 +168,8 @@ mod tests { #[test] fn test_urlencoded_error() { - let resp: HttpResponse = UrlencodedError::Overflow.error_response(); + let resp: HttpResponse = + UrlencodedError::Overflow { size: 0, limit: 0 }.error_response(); assert_eq!(resp.status(), StatusCode::PAYLOAD_TOO_LARGE); let resp: HttpResponse = UrlencodedError::UnknownLength.error_response(); assert_eq!(resp.status(), StatusCode::LENGTH_REQUIRED); diff --git a/src/types/form.rs b/src/types/form.rs index 9ab98b17b..3bc067ab5 100644 --- a/src/types/form.rs +++ b/src/types/form.rs @@ -318,7 +318,7 @@ where let limit = self.limit; if let Some(len) = self.length.take() { if len > limit { - return Err(UrlencodedError::Overflow); + return Err(UrlencodedError::Overflow { size: len, limit }); } } @@ -331,7 +331,10 @@ where .from_err() .fold(BytesMut::with_capacity(8192), move |mut body, chunk| { if (body.len() + chunk.len()) > limit { - Err(UrlencodedError::Overflow) + Err(UrlencodedError::Overflow { + size: body.len() + chunk.len(), + limit, + }) } else { body.extend_from_slice(&chunk); Ok(body) @@ -390,8 +393,8 @@ mod tests { fn eq(err: UrlencodedError, other: UrlencodedError) -> bool { match err { - UrlencodedError::Overflow => match other { - UrlencodedError::Overflow => true, + UrlencodedError::Overflow { .. } => match other { + UrlencodedError::Overflow { .. } => true, _ => false, }, UrlencodedError::UnknownLength => match other { @@ -420,7 +423,10 @@ mod tests { .header(CONTENT_LENGTH, "1000000") .to_http_parts(); let info = block_on(UrlEncoded::::new(&req, &mut pl)); - assert!(eq(info.err().unwrap(), UrlencodedError::Overflow)); + assert!(eq( + info.err().unwrap(), + UrlencodedError::Overflow { size: 0, limit: 0 } + )); let (req, mut pl) = TestRequest::with_header(CONTENT_TYPE, "text/plain") .header(CONTENT_LENGTH, "10") From e4503046de1263148e1b56394144b1828bbfdac0 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Tue, 17 Sep 2019 21:45:06 +0600 Subject: [PATCH 010/176] Do not override current System --- test-server/CHANGES.md | 5 +++ test-server/Cargo.toml | 8 ++--- test-server/src/lib.rs | 81 +++++++++++++++++++++--------------------- 3 files changed, 50 insertions(+), 44 deletions(-) diff --git a/test-server/CHANGES.md b/test-server/CHANGES.md index c3fe5b285..798dbf506 100644 --- a/test-server/CHANGES.md +++ b/test-server/CHANGES.md @@ -1,10 +1,15 @@ # Changes +## [0.2.5] - 2019-0917 ### Changed * Update serde_urlencoded to "0.6.1" +### Fixed + +* Do not override current `System` + ## [0.2.4] - 2019-07-18 diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 22809c060..77301b0a9 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http-test" -version = "0.2.4" +version = "0.2.5" authors = ["Nikolay Kim "] description = "Actix http test server" readme = "README.md" @@ -35,7 +35,7 @@ actix-rt = "0.2.2" actix-service = "0.4.1" actix-server = "0.6.0" actix-utils = "0.4.1" -awc = "0.2.2" +awc = "0.2.6" actix-connect = "0.2.2" base64 = "0.10" @@ -56,5 +56,5 @@ tokio-timer = "0.2" openssl = { version="0.10", optional = true } [dev-dependencies] -actix-web = "1.0.0" -actix-http = "0.2.4" +actix-web = "1.0.7" +actix-http = "0.2.9" diff --git a/test-server/src/lib.rs b/test-server/src/lib.rs index aba53980c..a2366bf48 100644 --- a/test-server/src/lib.rs +++ b/test-server/src/lib.rs @@ -103,8 +103,8 @@ pub struct TestServer; /// Test server controller pub struct TestServerRuntime { addr: net::SocketAddr, - rt: Runtime, client: Client, + system: System, } impl TestServer { @@ -130,45 +130,47 @@ impl TestServer { }); let (system, addr) = rx.recv().unwrap(); - let mut rt = Runtime::new().unwrap(); - let client = rt - .block_on(lazy(move || { - let connector = { - #[cfg(feature = "ssl")] - { - use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; + let client = block_on(lazy(move || { + let connector = { + #[cfg(feature = "ssl")] + { + use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; - let mut builder = - SslConnector::builder(SslMethod::tls()).unwrap(); - builder.set_verify(SslVerifyMode::NONE); - let _ = builder.set_alpn_protos(b"\x02h2\x08http/1.1").map_err( - |e| log::error!("Can not set alpn protocol: {:?}", e), - ); - Connector::new() - .conn_lifetime(time::Duration::from_secs(0)) - .timeout(time::Duration::from_millis(500)) - .ssl(builder.build()) - .finish() - } - #[cfg(not(feature = "ssl"))] - { - Connector::new() - .conn_lifetime(time::Duration::from_secs(0)) - .timeout(time::Duration::from_millis(500)) - .finish() - } - }; + let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); + builder.set_verify(SslVerifyMode::NONE); + let _ = builder + .set_alpn_protos(b"\x02h2\x08http/1.1") + .map_err(|e| log::error!("Can not set alpn protocol: {:?}", e)); + Connector::new() + .conn_lifetime(time::Duration::from_secs(0)) + .timeout(time::Duration::from_millis(500)) + .ssl(builder.build()) + .finish() + } + #[cfg(not(feature = "ssl"))] + { + Connector::new() + .conn_lifetime(time::Duration::from_secs(0)) + .timeout(time::Duration::from_millis(500)) + .finish() + } + }; - Ok::(Client::build().connector(connector).finish()) - })) - .unwrap(); - rt.block_on(lazy( + Ok::(Client::build().connector(connector).finish()) + })) + .unwrap(); + + block_on(lazy( || Ok::<_, ()>(actix_connect::start_default_resolver()), )) .unwrap(); - System::set_current(system); - TestServerRuntime { addr, rt, client } + + TestServerRuntime { + addr, + client, + system, + } } /// Get first available unused address @@ -188,7 +190,7 @@ impl TestServerRuntime { where F: Future, { - self.rt.block_on(fut) + block_on(fut) } /// Execute future on current core @@ -197,7 +199,7 @@ impl TestServerRuntime { F: FnOnce() -> R, R: Future, { - self.rt.block_on(lazy(f)) + block_on(lazy(f)) } /// Execute function on current core @@ -205,7 +207,7 @@ impl TestServerRuntime { where F: FnOnce() -> R, { - self.rt.block_on(lazy(|| Ok::<_, ()>(fut()))).unwrap() + block_on(lazy(|| Ok::<_, ()>(fut()))).unwrap() } /// Construct test server url @@ -324,8 +326,7 @@ impl TestServerRuntime { { let url = self.url(path); let connect = self.client.ws(url).connect(); - self.rt - .block_on(lazy(move || connect.map(|(_, framed)| framed))) + block_on(lazy(move || connect.map(|(_, framed)| framed))) } /// Connect to a websocket server @@ -338,7 +339,7 @@ impl TestServerRuntime { /// Stop http server fn stop(&mut self) { - System::current().stop(); + self.system.stop(); } } From 58c7065f08dddb2a9370eee468a10aa29d8cd641 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Tue, 17 Sep 2019 17:36:39 -0700 Subject: [PATCH 011/176] Implement `register_data` method on `Resource` and `Scope`. (#1094) * Implement `register_data` method on `Resource` and `Scope`. * Split Scope::register_data tests out from Scope::data tests. * CHANGES.md: Mention {Scope,Resource}::register_data. --- CHANGES.md | 10 +++++++++- src/resource.rs | 40 ++++++++++++++++++++++++++++++++++++++-- src/scope.rs | 31 +++++++++++++++++++++++++++++-- 3 files changed, 76 insertions(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 95f8b75e1..ead84ac8c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,10 +1,18 @@ # Changes +## not released yet + +### Added + +* Add `Scope::register_data` and `Resource::register_data` methods, parallel to + `App::register_data`. ## [1.0.8] - 2019-09-xx ### Added -* Add `middleware::Conditon` that conditionally enables another middleware +* Add `middleware::Condition` that conditionally enables another middleware + +### Fixed * Allow to re-construct `ServiceRequest` from `HttpRequest` and `Payload` diff --git a/src/resource.rs b/src/resource.rs index b872049d0..b711fc322 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -189,11 +189,21 @@ where /// )); /// } /// ``` - pub fn data(mut self, data: U) -> Self { + pub fn data(self, data: U) -> Self { + self.register_data(Data::new(data)) + } + + /// Set or override application data. + /// + /// This method has the same effect as [`Resource::data`](#method.data), + /// except that instead of taking a value of some type `T`, it expects a + /// value of type `Data`. Use a `Data` extractor to retrieve its + /// value. + pub fn register_data(mut self, data: Data) -> Self { if self.data.is_none() { self.data = Some(Extensions::new()); } - self.data.as_mut().unwrap().insert(Data::new(data)); + self.data.as_mut().unwrap().insert(data); self } @@ -763,4 +773,30 @@ mod tests { let resp = call_service(&mut srv, req); assert_eq!(resp.status(), StatusCode::NO_CONTENT); } + + #[test] + fn test_data() { + let mut srv = init_service( + App::new() + .data(1.0f64) + .data(1usize) + .register_data(web::Data::new('-')) + .service( + web::resource("/test") + .data(10usize) + .register_data(web::Data::new('*')) + .guard(guard::Get()) + .to(|data1: web::Data, data2: web::Data, data3: web::Data| { + assert_eq!(*data1, 10); + assert_eq!(*data2, '*'); + assert_eq!(*data3, 1.0); + HttpResponse::Ok() + }), + ) + ); + + let req = TestRequest::get().uri("/test").to_request(); + let resp = call_service(&mut srv, req); + assert_eq!(resp.status(), StatusCode::OK); + } } diff --git a/src/scope.rs b/src/scope.rs index 760fee478..06ebbd940 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -148,11 +148,20 @@ where /// ); /// } /// ``` - pub fn data(mut self, data: U) -> Self { + pub fn data(self, data: U) -> Self { + self.register_data(Data::new(data)) + } + + /// Set or override application data. + /// + /// This method has the same effect as [`Scope::data`](#method.data), except + /// that instead of taking a value of some type `T`, it expects a value of + /// type `Data`. Use a `Data` extractor to retrieve its value. + pub fn register_data(mut self, data: Data) -> Self { if self.data.is_none() { self.data = Some(Extensions::new()); } - self.data.as_mut().unwrap().insert(Data::new(data)); + self.data.as_mut().unwrap().insert(data); self } @@ -1082,6 +1091,24 @@ mod tests { assert_eq!(resp.status(), StatusCode::OK); } + #[test] + fn test_override_register_data() { + let mut srv = init_service(App::new().register_data(web::Data::new(1usize)).service( + web::scope("app").register_data(web::Data::new(10usize)).route( + "/t", + web::get().to(|data: web::Data| { + assert_eq!(*data, 10); + let _ = data.clone(); + HttpResponse::Ok() + }), + ), + )); + + let req = TestRequest::with_uri("/app/t").to_request(); + let resp = call_service(&mut srv, req); + assert_eq!(resp.status(), StatusCode::OK); + } + #[test] fn test_scope_config() { let mut srv = From aa39b8ca6fe7160446cc1adbd943c63014413e30 Mon Sep 17 00:00:00 2001 From: Sarfaraz Nawaz Date: Wed, 25 Sep 2019 11:33:52 +0800 Subject: [PATCH 012/176] Add support for serde_json::Value to be passed as argument to ResponseBuilder.body() (#1096) * Add support for serde_json::Value to be passed as argument to ResponseBuilder.body() * Update actix-http/CHANGES.md --- actix-http/CHANGES.md | 7 +++++++ actix-http/src/body.rs | 19 +++++++++++++++++++ actix-http/src/response.rs | 8 ++++++++ 3 files changed, 34 insertions(+) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 6820626f5..acba0796f 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,5 +1,12 @@ # Changes +## Not released yet + +### Added + +* Add support for serde_json::Value to be passed as argument to ResponseBuilder.body() + + ## [0.2.11] - 2019-09-11 ### Added diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs index e728cdb98..b761738e1 100644 --- a/actix-http/src/body.rs +++ b/actix-http/src/body.rs @@ -234,6 +234,12 @@ impl From for Body { } } +impl From for Body { + fn from(v: serde_json::Value) -> Body { + Body::Bytes(v.to_string().into()) + } +} + impl From> for Body where S: Stream + 'static, @@ -548,4 +554,17 @@ mod tests { assert!(format!("{:?}", Body::Empty).contains("Body::Empty")); assert!(format!("{:?}", Body::Bytes(Bytes::from_static(b"1"))).contains("1")); } + + #[test] + fn test_serde_json() { + use serde_json::json; + assert_eq!( + Body::from(serde_json::Value::String("test".into())).size(), + BodySize::Sized(6) + ); + assert_eq!( + Body::from(json!({"test-key":"test-value"})).size(), + BodySize::Sized(25) + ); + } } diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index 124bf5f92..5b0b3bc87 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -992,6 +992,14 @@ mod tests { assert_eq!(resp.body().get_ref(), b"[\"v1\",\"v2\",\"v3\"]"); } + #[test] + fn test_serde_json_in_body() { + use serde_json::json; + let resp = + Response::build(StatusCode::OK).body(json!({"test-key":"test-value"})); + assert_eq!(resp.body().get_ref(), br#"{"test-key":"test-value"}"#); + } + #[test] fn test_into_response() { let resp: Response = "test".into(); From d9af8f66ba7ab645f160ee195136fcd1147cea99 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 25 Sep 2019 10:28:41 +0600 Subject: [PATCH 013/176] Use actix-testing for testing utils --- CHANGES.md | 14 +++--- Cargo.toml | 3 +- actix-http/src/client/connector.rs | 2 +- src/resource.rs | 18 ++++--- src/scope.rs | 22 +++++---- src/test.rs | 77 +----------------------------- 6 files changed, 36 insertions(+), 100 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index ead84ac8c..c27c88657 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,23 +1,23 @@ # Changes -## not released yet + +## [1.0.8] - 2019-09-xx ### Added * Add `Scope::register_data` and `Resource::register_data` methods, parallel to `App::register_data`. -## [1.0.8] - 2019-09-xx - -### Added - * Add `middleware::Condition` that conditionally enables another middleware -### Fixed - * Allow to re-construct `ServiceRequest` from `HttpRequest` and `Payload` +### Changed + * Make UrlEncodedError::Overflow more informativve +* Use actix-testing for testing utils + + ## [1.0.7] - 2019-08-29 ### Fixed diff --git a/Cargo.toml b/Cargo.toml index c2d3b0d2b..b0d34e1c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "1.0.7" +version = "1.0.8" authors = ["Nikolay Kim "] description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." readme = "README.md" @@ -81,6 +81,7 @@ actix-web-codegen = "0.1.2" actix-http = "0.2.9" actix-server = "0.6.0" actix-server-config = "0.1.2" +actix-testing = "0.1.0" actix-threadpool = "0.1.1" awc = { version = "0.2.4", optional = true } diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index d92441f25..98e8618c3 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -212,7 +212,7 @@ where pub fn finish( self, ) -> impl Service - + Clone { + + Clone { #[cfg(not(any(feature = "ssl", feature = "rust-tls")))] { let connector = TimeoutService::new( diff --git a/src/resource.rs b/src/resource.rs index b711fc322..3ee0167a0 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -786,13 +786,17 @@ mod tests { .data(10usize) .register_data(web::Data::new('*')) .guard(guard::Get()) - .to(|data1: web::Data, data2: web::Data, data3: web::Data| { - assert_eq!(*data1, 10); - assert_eq!(*data2, '*'); - assert_eq!(*data3, 1.0); - HttpResponse::Ok() - }), - ) + .to( + |data1: web::Data, + data2: web::Data, + data3: web::Data| { + assert_eq!(*data1, 10); + assert_eq!(*data2, '*'); + assert_eq!(*data3, 1.0); + HttpResponse::Ok() + }, + ), + ), ); let req = TestRequest::get().uri("/test").to_request(); diff --git a/src/scope.rs b/src/scope.rs index 06ebbd940..c152bc334 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -1093,16 +1093,20 @@ mod tests { #[test] fn test_override_register_data() { - let mut srv = init_service(App::new().register_data(web::Data::new(1usize)).service( - web::scope("app").register_data(web::Data::new(10usize)).route( - "/t", - web::get().to(|data: web::Data| { - assert_eq!(*data, 10); - let _ = data.clone(); - HttpResponse::Ok() - }), + let mut srv = init_service( + App::new().register_data(web::Data::new(1usize)).service( + web::scope("app") + .register_data(web::Data::new(10usize)) + .route( + "/t", + web::get().to(|data: web::Data| { + assert_eq!(*data, 10); + let _ = data.clone(); + HttpResponse::Ok() + }), + ), ), - )); + ); let req = TestRequest::with_uri("/app/t").to_request(); let resp = call_service(&mut srv, req); diff --git a/src/test.rs b/src/test.rs index 903679cad..6563253cc 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,5 +1,4 @@ //! Various helpers for Actix applications to use during testing. -use std::cell::RefCell; use std::rc::Rc; use actix_http::http::header::{ContentType, Header, HeaderName, IntoHeaderValue}; @@ -7,17 +6,17 @@ use actix_http::http::{HttpTryFrom, Method, StatusCode, Uri, Version}; use actix_http::test::TestRequest as HttpTestRequest; use actix_http::{cookie::Cookie, Extensions, Request}; use actix_router::{Path, ResourceDef, Url}; -use actix_rt::{System, SystemRunner}; use actix_server_config::ServerConfig; use actix_service::{IntoNewService, IntoService, NewService, Service}; use bytes::{Bytes, BytesMut}; -use futures::future::{lazy, ok, Future, IntoFuture}; +use futures::future::{ok, Future}; use futures::Stream; use serde::de::DeserializeOwned; use serde::Serialize; use serde_json; pub use actix_http::test::TestBuffer; +pub use actix_testing::{block_fn, block_on, run_on}; use crate::config::{AppConfig, AppConfigInner}; use crate::data::Data; @@ -27,78 +26,6 @@ use crate::rmap::ResourceMap; use crate::service::{ServiceRequest, ServiceResponse}; use crate::{Error, HttpRequest, HttpResponse}; -thread_local! { - static RT: RefCell = { - RefCell::new(Inner(Some(System::builder().build()))) - }; -} - -struct Inner(Option); - -impl Inner { - fn get_mut(&mut self) -> &mut SystemRunner { - self.0.as_mut().unwrap() - } -} - -impl Drop for Inner { - fn drop(&mut self) { - std::mem::forget(self.0.take().unwrap()) - } -} - -/// Runs the provided future, blocking the current thread until the future -/// completes. -/// -/// This function can be used to synchronously block the current thread -/// until the provided `future` has resolved either successfully or with an -/// error. The result of the future is then returned from this function -/// call. -/// -/// Note that this function is intended to be used only for testing purpose. -/// This function panics on nested call. -pub fn block_on(f: F) -> Result -where - F: IntoFuture, -{ - RT.with(move |rt| rt.borrow_mut().get_mut().block_on(f.into_future())) -} - -/// Runs the provided function, blocking the current thread until the result -/// future completes. -/// -/// This function can be used to synchronously block the current thread -/// until the provided `future` has resolved either successfully or with an -/// error. The result of the future is then returned from this function -/// call. -/// -/// Note that this function is intended to be used only for testing purpose. -/// This function panics on nested call. -pub fn block_fn(f: F) -> Result -where - F: FnOnce() -> R, - R: IntoFuture, -{ - RT.with(move |rt| rt.borrow_mut().get_mut().block_on(lazy(f))) -} - -#[doc(hidden)] -/// Runs the provided function, with runtime enabled. -/// -/// Note that this function is intended to be used only for testing purpose. -/// This function panics on nested call. -pub fn run_on(f: F) -> R -where - F: FnOnce() -> R, -{ - RT.with(move |rt| { - rt.borrow_mut() - .get_mut() - .block_on(lazy(|| Ok::<_, ()>(f()))) - }) - .unwrap() -} - /// Create service that always responds with `HttpResponse::Ok()` pub fn ok_service( ) -> impl Service, Error = Error> From 23f04c4f38ee3ff47eafb2f26338e2e6561f591e Mon Sep 17 00:00:00 2001 From: Sven-Hendrik Haase Date: Wed, 25 Sep 2019 08:50:45 +0200 Subject: [PATCH 014/176] Add remaining getter methods from private head field --- awc/src/request.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/awc/src/request.rs b/awc/src/request.rs index d597a1638..a90cf60b2 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -96,7 +96,7 @@ impl ClientRequest { self } - /// Get HTTP URI of request + /// Get HTTP URI of request. pub fn get_uri(&self) -> &Uri { &self.head.uri } @@ -132,6 +132,16 @@ impl ClientRequest { self } + /// Get HTTP version of this request. + pub fn get_version(&self) -> &Version { + &self.head.version + } + + /// Get peer address of this request. + pub fn get_peer_addr(&self) -> &Option { + &self.head.peer_addr + } + #[inline] /// Returns request's headers. pub fn headers(&self) -> &HeaderMap { From c659c33919c4880dbe3d220773f20fc6c5b58070 Mon Sep 17 00:00:00 2001 From: karlri <49443488+karlri@users.noreply.github.com> Date: Wed, 25 Sep 2019 11:16:51 +0200 Subject: [PATCH 015/176] Feature uds: Add listen_uds to ServerBuilder (#1085) Allows using an existing Unix Listener instead of binding to a path. Useful for when running as a daemon under systemd. Change-Id: I54a0e78c321d8b7a9ded381083217af590e9a7fa --- CHANGES.md | 3 +++ Cargo.toml | 2 +- src/server.rs | 31 +++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index c27c88657..86e5e8a46 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,9 @@ * Allow to re-construct `ServiceRequest` from `HttpRequest` and `Payload` +* Add `HttpServer::listen_uds` for ability to listen on UDS FD rather than path, + which is useful for example with systemd. + ### Changed * Make UrlEncodedError::Overflow more informativve diff --git a/Cargo.toml b/Cargo.toml index b0d34e1c6..5a01c4422 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,7 +79,7 @@ actix-router = "0.1.5" actix-rt = "0.2.4" actix-web-codegen = "0.1.2" actix-http = "0.2.9" -actix-server = "0.6.0" +actix-server = "0.6.1" actix-server-config = "0.1.2" actix-testing = "0.1.0" actix-threadpool = "0.1.1" diff --git a/src/server.rs b/src/server.rs index d1a019a19..51492eb01 100644 --- a/src/server.rs +++ b/src/server.rs @@ -435,6 +435,37 @@ where Ok(self) } + #[cfg(feature = "uds")] + /// Start listening for unix domain connections on existing listener. + /// + /// This method is available with `uds` feature. + pub fn listen_uds( + mut self, + lst: std::os::unix::net::UnixListener, + ) -> io::Result { + let cfg = self.config.clone(); + let factory = self.factory.clone(); + // todo duplicated: + self.sockets.push(Socket { + scheme: "http", + addr: net::SocketAddr::new( + net::IpAddr::V4(net::Ipv4Addr::new(127, 0, 0, 1)), + 8080, + ), + }); + + let addr = format!("actix-web-service-{:?}", lst.local_addr()?); + + self.builder = self.builder.listen_uds(addr, lst, move || { + let c = cfg.lock(); + HttpService::build() + .keep_alive(c.keep_alive) + .client_timeout(c.client_timeout) + .finish(factory()) + })?; + Ok(self) + } + #[cfg(feature = "uds")] /// Start listening for incoming unix domain connections. /// From 3d4e45a0e56979ba2f316e752e399f1ccb35154f Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 25 Sep 2019 15:30:20 +0600 Subject: [PATCH 016/176] prepare release --- awc/CHANGES.md | 8 ++++++++ awc/Cargo.toml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/awc/CHANGES.md b/awc/CHANGES.md index 94ad65ffe..3ea1790b4 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,5 +1,13 @@ # Changes + +## [0.2.7] - 2019-09-25 + +### Added + +* Add remaining getter methods from private head field #1101 + + ## [0.2.6] - 2019-09-12 ### Added diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 3a86193c6..6f0f63f92 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "awc" -version = "0.2.6" +version = "0.2.7" authors = ["Nikolay Kim "] description = "Actix http client." readme = "README.md" From 3ff01a9fc487d729781c4206ab3c9ea920c8c23b Mon Sep 17 00:00:00 2001 From: Sven-Hendrik Haase Date: Wed, 25 Sep 2019 11:35:28 +0200 Subject: [PATCH 017/176] Add changelog entry for #1101 (#1102) --- awc/CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awc/CHANGES.md b/awc/CHANGES.md index 3ea1790b4..6f8fe2dbd 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -5,7 +5,7 @@ ### Added -* Add remaining getter methods from private head field #1101 +* Remaining getter methods for `ClientRequest`'s private `head` field #1101 ## [0.2.6] - 2019-09-12 From 4f3e97fff800267c6c21440540d6c01a4f297538 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 25 Sep 2019 15:39:09 +0600 Subject: [PATCH 018/176] prepare actix-web release --- CHANGES.md | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 86e5e8a46..b984e68cc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## [1.0.8] - 2019-09-xx +## [1.0.8] - 2019-09-25 ### Added diff --git a/Cargo.toml b/Cargo.toml index 5a01c4422..35ca28b2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,7 +83,7 @@ actix-server = "0.6.1" actix-server-config = "0.1.2" actix-testing = "0.1.0" actix-threadpool = "0.1.1" -awc = { version = "0.2.4", optional = true } +awc = { version = "0.2.7", optional = true } bytes = "0.4" derive_more = "0.15.0" From 5169d306ae5311b0955b8927ad0fbdbdb348df1d Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 27 Sep 2019 07:03:12 +0600 Subject: [PATCH 019/176] update ConnectionInfo.remote() doc string --- actix-http/CHANGES.md | 2 +- src/info.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index acba0796f..06756033f 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -7,7 +7,7 @@ * Add support for serde_json::Value to be passed as argument to ResponseBuilder.body() -## [0.2.11] - 2019-09-11 +## [0.2.10] - 2019-09-11 ### Added diff --git a/src/info.rs b/src/info.rs index ba59605de..61914516e 100644 --- a/src/info.rs +++ b/src/info.rs @@ -155,9 +155,9 @@ impl ConnectionInfo { &self.host } - /// Remote IP of client initiated HTTP request. + /// Remote socket addr of client initiated HTTP request. /// - /// The IP is resolved through the following headers, in this order: + /// The addr is resolved through the following headers, in this order: /// /// - Forwarded /// - X-Forwarded-For From f81ae37677e36eba58ff059466efb5c66eb89824 Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Tue, 1 Oct 2019 11:05:38 +0300 Subject: [PATCH 020/176] Add From for crate::dev::Payload (#1110) * Add From for crate::dev::Payload * Make dev::Payload field of Payload public and add into_inner method * Add changelog entry --- CHANGES.md | 6 ++++++ src/types/payload.rs | 9 ++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index b984e68cc..4ff7d1e66 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Changes +## [1.0.9] - 2019-xx-xx + +### Added + +* Add `Payload::into_inner` method and make stored `def::Payload` public. (#1110) + ## [1.0.8] - 2019-09-25 ### Added diff --git a/src/types/payload.rs b/src/types/payload.rs index f33e2e5f1..8fc5f093e 100644 --- a/src/types/payload.rs +++ b/src/types/payload.rs @@ -43,7 +43,14 @@ use crate::request::HttpRequest; /// ); /// } /// ``` -pub struct Payload(crate::dev::Payload); +pub struct Payload(pub crate::dev::Payload); + +impl Payload { + /// Deconstruct to a inner value + pub fn into_inner(self) -> crate::dev::Payload { + self.0 + } +} impl Stream for Payload { type Item = Bytes; From fba31d4e0aa87f1e7f8c59f00079b6ba1aa534cc Mon Sep 17 00:00:00 2001 From: Zac Pullar-Strecker Date: Wed, 2 Oct 2019 16:48:25 +1300 Subject: [PATCH 021/176] Expose ContentDisposition in actix-multipart to fix broken doc link (#1114) * Expose ContentDisposition in actix-multipart to fix broken doc link * Revert "Expose ContentDisposition in actix-multipart to fix broken doc link" This reverts commit e90d71d16cb552cd3e1745646fabcc48e0b4e379. * Unhide actix-http::header::common docs These types are used in other exported documented interfaces and create broken links if not documented. See `actix_multipart::Field.content_disposition` --- actix-http/src/header/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/actix-http/src/header/mod.rs b/actix-http/src/header/mod.rs index 37cf94508..59cbb11c4 100644 --- a/actix-http/src/header/mod.rs +++ b/actix-http/src/header/mod.rs @@ -16,7 +16,6 @@ use crate::httpmessage::HttpMessage; mod common; pub(crate) mod map; mod shared; -#[doc(hidden)] pub use self::common::*; #[doc(hidden)] pub use self::shared::*; From 15d3c1ae816b81ef6ff0b5aa284cbc909dfd39e3 Mon Sep 17 00:00:00 2001 From: Koen Hoeijmakers Date: Mon, 7 Oct 2019 05:05:17 +0200 Subject: [PATCH 022/176] Update docs of guard.rs (#1116) * Update guard.rs --- src/guard.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/guard.rs b/src/guard.rs index e0b4055ba..c60192587 100644 --- a/src/guard.rs +++ b/src/guard.rs @@ -1,16 +1,16 @@ //! Route match guards. //! -//! Guards are one of the way how actix-web router chooses -//! handler service. In essence it just function that accepts -//! reference to a `RequestHead` instance and returns boolean. +//! Guards are one of the ways how actix-web router chooses a +//! handler service. In essence it is just a function that accepts a +//! reference to a `RequestHead` instance and returns a boolean. //! It is possible to add guards to *scopes*, *resources* //! and *routes*. Actix provide several guards by default, like various //! http methods, header, etc. To become a guard, type must implement `Guard` //! trait. Simple functions coulds guards as well. //! -//! Guard can not modify request object. But it is possible to -//! to store extra attributes on a request by using `Extensions` container. -//! Extensions container available via `RequestHead::extensions()` method. +//! Guards can not modify the request object. But it is possible +//! to store extra attributes on a request by using the `Extensions` container. +//! Extensions containers are available via the `RequestHead::extensions()` method. //! //! ```rust //! use actix_web::{web, http, dev, guard, App, HttpResponse}; @@ -29,11 +29,11 @@ use actix_http::http::{self, header, uri::Uri, HttpTryFrom}; use actix_http::RequestHead; -/// Trait defines resource guards. Guards are used for routes selection. +/// Trait defines resource guards. Guards are used for route selection. /// -/// Guard can not modify request object. But it is possible to -/// to store extra attributes on request by using `Extensions` container, -/// Extensions container available via `RequestHead::extensions()` method. +/// Guards can not modify the request object. But it is possible +/// to store extra attributes on a request by using the `Extensions` container. +/// Extensions containers are available via the `RequestHead::extensions()` method. pub trait Guard { /// Check if request matches predicate fn check(&self, request: &RequestHead) -> bool; From f089cf185b64cf1f337b47d814a9b6882e7f0d42 Mon Sep 17 00:00:00 2001 From: SuperHacker-liuan <30787037+SuperHacker-liuan@users.noreply.github.com> Date: Mon, 7 Oct 2019 12:56:24 +0800 Subject: [PATCH 023/176] Let ResponseError render w/ 'text/plain; charset=utf-8' header (#1118) (#1119) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Let ResponseError render w/ 'text/plain; charset=utf-8' header (#1118) Trait ResponseError originally render Error messages with header `text/plain` , which causes browsers (i.e. Firefox 70.0) with Non-English locale unable to render UTF-8 responses with non-English characters correctly. i.e. emoji. This fix solved this problem by specifying the charset of `text/plain` as utf-8, which is the default charset in rust. Before actix-web consider to support other charsets, this hotfix is enough. Test case: fn test() -> Result { Err(actix_web::error::ErrorForbidden("😋test")) } * Update actix-http/CHANGES.md for #1118 --- actix-http/CHANGES.md | 4 ++++ actix-http/src/error.rs | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 06756033f..624aca5eb 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -6,6 +6,10 @@ * Add support for serde_json::Value to be passed as argument to ResponseBuilder.body() +### Fixed + +* To be compatible with non-English error responses, `ResponseError` rendered with `text/plain; charset=utf-8` header #1118 + ## [0.2.10] - 2019-09-11 diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index 90c35e486..cd9613d21 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -75,7 +75,7 @@ pub trait ResponseError: fmt::Debug + fmt::Display { let _ = write!(Writer(&mut buf), "{}", self); resp.headers_mut().insert( header::CONTENT_TYPE, - header::HeaderValue::from_static("text/plain"), + header::HeaderValue::from_static("text/plain; charset=utf-8"), ); resp.set_body(Body::from(buf)) } @@ -536,7 +536,7 @@ where let _ = write!(Writer(&mut buf), "{}", self); res.headers_mut().insert( header::CONTENT_TYPE, - header::HeaderValue::from_static("text/plain"), + header::HeaderValue::from_static("text/plain; charset=utf-8"), ); res.set_body(Body::from(buf)) } From 0f09415469843eea4000dc48085101dcf8d75e9b Mon Sep 17 00:00:00 2001 From: Priit Laes Date: Mon, 7 Oct 2019 08:29:11 +0300 Subject: [PATCH 024/176] Convert documentation examples to Rust 2018 edition (#1120) * Convert types::query examples to rust-2018 edition * Convert types::json examples to rust-2018 edition * Convert types::path examples to rust-2018 edition * Convert types::form examples to rust-2018 edition * Convert rest of the examples to rust-2018 edition. --- src/extract.rs | 4 ++-- src/request.rs | 2 +- src/route.rs | 4 ++-- src/types/form.rs | 13 ++++++------- src/types/json.rs | 12 ++++++------ src/types/path.rs | 7 +++---- src/types/query.rs | 6 +++--- 7 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/extract.rs b/src/extract.rs index 1687973ac..425637311 100644 --- a/src/extract.rs +++ b/src/extract.rs @@ -46,9 +46,9 @@ pub trait FromRequest: Sized { /// ## Example /// /// ```rust -/// # #[macro_use] extern crate serde_derive; /// use actix_web::{web, dev, App, Error, HttpRequest, FromRequest}; /// use actix_web::error::ErrorBadRequest; +/// use serde_derive::Deserialize; /// use rand; /// /// #[derive(Debug, Deserialize)] @@ -119,9 +119,9 @@ where /// ## Example /// /// ```rust -/// # #[macro_use] extern crate serde_derive; /// use actix_web::{web, dev, App, Result, Error, HttpRequest, FromRequest}; /// use actix_web::error::ErrorBadRequest; +/// use serde_derive::Deserialize; /// use rand; /// /// #[derive(Debug, Deserialize)] diff --git a/src/request.rs b/src/request.rs index 6d9d26e8c..ea27e303c 100644 --- a/src/request.rs +++ b/src/request.rs @@ -271,8 +271,8 @@ impl Drop for HttpRequest { /// ## Example /// /// ```rust -/// # #[macro_use] extern crate serde_derive; /// use actix_web::{web, App, HttpRequest}; +/// use serde_derive::Deserialize; /// /// /// extract `Thing` from request /// fn index(req: HttpRequest) -> String { diff --git a/src/route.rs b/src/route.rs index f4d303632..35b842944 100644 --- a/src/route.rs +++ b/src/route.rs @@ -178,8 +178,8 @@ impl Route { /// Set handler function, use request extractors for parameters. /// /// ```rust - /// #[macro_use] extern crate serde_derive; /// use actix_web::{web, http, App}; + /// use serde_derive::Deserialize; /// /// #[derive(Deserialize)] /// struct Info { @@ -239,9 +239,9 @@ impl Route { /// /// ```rust /// # use futures::future::ok; - /// #[macro_use] extern crate serde_derive; /// use actix_web::{web, App, Error}; /// use futures::Future; + /// use serde_derive::Deserialize; /// /// #[derive(Deserialize)] /// struct Info { diff --git a/src/types/form.rs b/src/types/form.rs index 3bc067ab5..c727ce0e5 100644 --- a/src/types/form.rs +++ b/src/types/form.rs @@ -35,9 +35,8 @@ use crate::responder::Responder; /// /// ### Example /// ```rust -/// # extern crate actix_web; -/// #[macro_use] extern crate serde_derive; -/// use actix_web::{web, App}; +/// use actix_web::web; +/// use serde_derive::Deserialize; /// /// #[derive(Deserialize)] /// struct FormData { @@ -61,9 +60,9 @@ use crate::responder::Responder; /// /// ### Example /// ```rust -/// # #[macro_use] extern crate serde_derive; -/// # use actix_web::*; -/// # +/// use actix_web::*; +/// use serde_derive::Serialize; +/// /// #[derive(Serialize)] /// struct SomeForm { /// name: String, @@ -167,8 +166,8 @@ impl Responder for Form { /// Form extractor configuration /// /// ```rust -/// #[macro_use] extern crate serde_derive; /// use actix_web::{web, App, FromRequest, Result}; +/// use serde_derive::Deserialize; /// /// #[derive(Deserialize)] /// struct FormData { diff --git a/src/types/json.rs b/src/types/json.rs index f309a3c5a..e80d0a45f 100644 --- a/src/types/json.rs +++ b/src/types/json.rs @@ -33,8 +33,8 @@ use crate::responder::Responder; /// ## Example /// /// ```rust -/// #[macro_use] extern crate serde_derive; /// use actix_web::{web, App}; +/// use serde_derive::Deserialize; /// /// #[derive(Deserialize)] /// struct Info { @@ -60,9 +60,9 @@ use crate::responder::Responder; /// trait from *serde*. /// /// ```rust -/// # #[macro_use] extern crate serde_derive; -/// # use actix_web::*; -/// # +/// use actix_web::*; +/// use serde_derive::Serialize; +/// /// #[derive(Serialize)] /// struct MyObj { /// name: String, @@ -144,8 +144,8 @@ impl Responder for Json { /// ## Example /// /// ```rust -/// #[macro_use] extern crate serde_derive; /// use actix_web::{web, App}; +/// use serde_derive::Deserialize; /// /// #[derive(Deserialize)] /// struct Info { @@ -203,8 +203,8 @@ where /// Json extractor configuration /// /// ```rust -/// #[macro_use] extern crate serde_derive; /// use actix_web::{error, web, App, FromRequest, HttpResponse}; +/// use serde_derive::Deserialize; /// /// #[derive(Deserialize)] /// struct Info { diff --git a/src/types/path.rs b/src/types/path.rs index a46575764..fa7c6e110 100644 --- a/src/types/path.rs +++ b/src/types/path.rs @@ -39,8 +39,8 @@ use crate::FromRequest; /// implements `Deserialize` trait from *serde*. /// /// ```rust -/// #[macro_use] extern crate serde_derive; /// use actix_web::{web, App, Error}; +/// use serde_derive::Deserialize; /// /// #[derive(Deserialize)] /// struct Info { @@ -134,8 +134,8 @@ impl fmt::Display for Path { /// implements `Deserialize` trait from *serde*. /// /// ```rust -/// #[macro_use] extern crate serde_derive; /// use actix_web::{web, App, Error}; +/// use serde_derive::Deserialize; /// /// #[derive(Deserialize)] /// struct Info { @@ -190,10 +190,9 @@ where /// Path extractor configuration /// /// ```rust -/// # #[macro_use] -/// # extern crate serde_derive; /// use actix_web::web::PathConfig; /// use actix_web::{error, web, App, FromRequest, HttpResponse}; +/// use serde_derive::Deserialize; /// /// #[derive(Deserialize, Debug)] /// enum Folder { diff --git a/src/types/query.rs b/src/types/query.rs index 60b07085d..817b2ed7b 100644 --- a/src/types/query.rs +++ b/src/types/query.rs @@ -21,8 +21,8 @@ use crate::request::HttpRequest; /// ## Example /// /// ```rust -/// #[macro_use] extern crate serde_derive; /// use actix_web::{web, App}; +/// use serde_derive::Deserialize; /// /// #[derive(Debug, Deserialize)] /// pub enum ResponseType { @@ -99,8 +99,8 @@ impl fmt::Display for Query { /// ## Example /// /// ```rust -/// #[macro_use] extern crate serde_derive; /// use actix_web::{web, App}; +/// use serde_derive::Deserialize; /// /// #[derive(Debug, Deserialize)] /// pub enum ResponseType { @@ -169,8 +169,8 @@ where /// ## Example /// /// ```rust -/// #[macro_use] extern crate serde_derive; /// use actix_web::{error, web, App, FromRequest, HttpResponse}; +/// use serde_derive::Deserialize; /// /// #[derive(Deserialize)] /// struct Info { From 4de2e8a8983f96937d3c7d660ec9bd76951b5ebc Mon Sep 17 00:00:00 2001 From: Naim A <227396+naim94a@users.noreply.github.com> Date: Tue, 8 Oct 2019 07:09:40 +0300 Subject: [PATCH 025/176] [actix-files] Allow user defined guards for NamedFile (actix#1113) (#1115) * [actix-files] remove request method checks from NamedFile * [actix-files] added custom guard checks to FilesService * [actix-files] modify method check tests (NamedFile -> Files) * [actix-files] add test for custom guards in Files * [actix-files] update changelog --- actix-files/CHANGES.md | 2 ++ actix-files/src/lib.rs | 77 +++++++++++++++++++++++++++++++++++----- actix-files/src/named.rs | 12 +------ 3 files changed, 72 insertions(+), 19 deletions(-) diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index 49ecdbffc..2421890d1 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -6,6 +6,8 @@ * Bump up `percent-encoding` crate version to 2.1 +* Allow user defined request guards for `Files` #1113 + ## [0.1.4] - 2019-07-20 * Allow to disable `Content-Disposition` header #686 diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index c99d3265f..7fc3c45c4 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -15,8 +15,10 @@ use actix_web::dev::{ AppService, HttpServiceFactory, Payload, ResourceDef, ServiceRequest, ServiceResponse, }; +use actix_web::guard::Guard; use actix_web::error::{BlockingError, Error, ErrorInternalServerError}; -use actix_web::http::header::DispositionType; +use actix_web::http::header::{self, DispositionType}; +use actix_web::http::Method; use actix_web::{web, FromRequest, HttpRequest, HttpResponse, Responder}; use bytes::Bytes; use futures::future::{ok, Either, FutureResult}; @@ -235,6 +237,7 @@ pub struct Files { renderer: Rc, mime_override: Option>, file_flags: named::Flags, + guards: Option>>, } impl Clone for Files { @@ -248,6 +251,7 @@ impl Clone for Files { file_flags: self.file_flags, path: self.path.clone(), mime_override: self.mime_override.clone(), + guards: self.guards.clone(), } } } @@ -273,6 +277,7 @@ impl Files { renderer: Rc::new(directory_listing), mime_override: None, file_flags: named::Flags::default(), + guards: None, } } @@ -331,6 +336,15 @@ impl Files { self } + /// Specifies custom guards to use for directory listings and files. + /// + /// Default behaviour allows GET and HEAD. + #[inline] + pub fn use_guards(mut self, guards: G) -> Self { + self.guards = Some(Rc::new(Box::new(guards))); + self + } + /// Disable `Content-Disposition` header. /// /// By default Content-Disposition` header is enabled. @@ -392,6 +406,7 @@ impl NewService for Files { renderer: self.renderer.clone(), mime_override: self.mime_override.clone(), file_flags: self.file_flags, + guards: self.guards.clone(), }; if let Some(ref default) = *self.default.borrow() { @@ -418,6 +433,7 @@ pub struct FilesService { renderer: Rc, mime_override: Option>, file_flags: named::Flags, + guards: Option>>, } impl FilesService { @@ -454,6 +470,25 @@ impl Service for FilesService { fn call(&mut self, req: ServiceRequest) -> Self::Future { // let (req, pl) = req.into_parts(); + let is_method_valid = if let Some(guard) = &self.guards { + // execute user defined guards + (**guard).check(req.head()) + } else { + // default behaviour + match *req.method() { + Method::HEAD | Method::GET => true, + _ => false, + } + }; + + if !is_method_valid { + return Either::A(ok(req.into_response( + actix_web::HttpResponse::MethodNotAllowed() + .header(header::CONTENT_TYPE, "text/plain") + .body("Request did not meet this resource's requirements.") + ))); + } + let real_path = match PathBufWrp::get_pathbuf(req.match_info().path()) { Ok(item) => item, Err(e) => return Either::A(ok(req.error_response(e))), @@ -576,6 +611,7 @@ mod tests { use bytes::BytesMut; use super::*; + use actix_web::guard; use actix_web::http::header::{ self, ContentDisposition, DispositionParam, DispositionType, }; @@ -1010,20 +1046,45 @@ mod tests { } #[test] - fn test_named_file_not_allowed() { - let file = NamedFile::open("Cargo.toml").unwrap(); + fn test_files_not_allowed() { + let mut srv = test::init_service( + App::new().service(Files::new("/", ".")), + ); + let req = TestRequest::default() + .uri("/Cargo.toml") .method(Method::POST) - .to_http_request(); - let resp = file.respond_to(&req).unwrap(); + .to_request(); + + let resp = test::call_service(&mut srv, req); assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); - let file = NamedFile::open("Cargo.toml").unwrap(); - let req = TestRequest::default().method(Method::PUT).to_http_request(); - let resp = file.respond_to(&req).unwrap(); + let mut srv = test::init_service( + App::new().service(Files::new("/", ".")), + ); + let req = TestRequest::default().method(Method::PUT).uri("/Cargo.toml").to_request(); + let resp = test::call_service(&mut srv, req); assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); } + #[test] + fn test_files_guards() { + let mut srv = test::init_service( + App::new().service( + Files::new("/", ".") + .use_guards(guard::Post()) + ), + ); + + let req = TestRequest::default() + .uri("/Cargo.toml") + .method(Method::POST) + .to_request(); + + let resp = test::call_service(&mut srv, req); + assert_eq!(resp.status(), StatusCode::OK); + } + #[test] fn test_named_file_content_encoding() { let mut srv = test::init_service(App::new().wrap(Compress::default()).service( diff --git a/actix-files/src/named.rs b/actix-files/src/named.rs index f548a7a1b..ca1a909a4 100644 --- a/actix-files/src/named.rs +++ b/actix-files/src/named.rs @@ -15,7 +15,7 @@ use actix_http::body::SizedStream; use actix_web::http::header::{ self, ContentDisposition, DispositionParam, DispositionType, }; -use actix_web::http::{ContentEncoding, Method, StatusCode}; +use actix_web::http::{ContentEncoding, StatusCode}; use actix_web::middleware::BodyEncoding; use actix_web::{Error, HttpMessage, HttpRequest, HttpResponse, Responder}; @@ -324,16 +324,6 @@ impl Responder for NamedFile { return Ok(resp.streaming(reader)); } - match *req.method() { - Method::HEAD | Method::GET => (), - _ => { - return Ok(HttpResponse::MethodNotAllowed() - .header(header::CONTENT_TYPE, "text/plain") - .header(header::ALLOW, "GET, HEAD") - .body("This resource only supports GET and HEAD.")); - } - } - let etag = if self.flags.contains(Flags::ETAG) { self.etag() } else { From a464ffc23daea1e0bc02bfea9622ae7bf33a2c12 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Tue, 8 Oct 2019 10:13:16 +0600 Subject: [PATCH 026/176] prepare actix-files release --- actix-files/CHANGES.md | 3 ++- actix-files/Cargo.toml | 6 +++--- actix-files/src/lib.rs | 22 +++++++++------------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index 2421890d1..5999f2764 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## [0.1.5] - unreleased +## [0.1.5] - 2019-10-08 * Bump up `mime_guess` crate version to 2.0.1 @@ -8,6 +8,7 @@ * Allow user defined request guards for `Files` #1113 + ## [0.1.4] - 2019-07-20 * Allow to disable `Content-Disposition` header #686 diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 8f36cddc3..971db7929 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-files" -version = "0.1.4" +version = "0.1.5" authors = ["Nikolay Kim "] description = "Static files support for actix web." readme = "README.md" @@ -18,7 +18,7 @@ name = "actix_files" path = "src/lib.rs" [dependencies] -actix-web = { version = "1.0.2", default-features = false } +actix-web = { version = "1.0.8", default-features = false } actix-http = "0.2.9" actix-service = "0.4.1" bitflags = "1" @@ -32,4 +32,4 @@ percent-encoding = "2.1" v_htmlescape = "0.4" [dev-dependencies] -actix-web = { version = "1.0.2", features=["ssl"] } +actix-web = { version = "1.0.8", features=["ssl"] } diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index 7fc3c45c4..1cc26629d 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -15,8 +15,8 @@ use actix_web::dev::{ AppService, HttpServiceFactory, Payload, ResourceDef, ServiceRequest, ServiceResponse, }; -use actix_web::guard::Guard; use actix_web::error::{BlockingError, Error, ErrorInternalServerError}; +use actix_web::guard::Guard; use actix_web::http::header::{self, DispositionType}; use actix_web::http::Method; use actix_web::{web, FromRequest, HttpRequest, HttpResponse, Responder}; @@ -485,7 +485,7 @@ impl Service for FilesService { return Either::A(ok(req.into_response( actix_web::HttpResponse::MethodNotAllowed() .header(header::CONTENT_TYPE, "text/plain") - .body("Request did not meet this resource's requirements.") + .body("Request did not meet this resource's requirements."), ))); } @@ -1047,9 +1047,7 @@ mod tests { #[test] fn test_files_not_allowed() { - let mut srv = test::init_service( - App::new().service(Files::new("/", ".")), - ); + let mut srv = test::init_service(App::new().service(Files::new("/", "."))); let req = TestRequest::default() .uri("/Cargo.toml") @@ -1059,10 +1057,11 @@ mod tests { let resp = test::call_service(&mut srv, req); assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); - let mut srv = test::init_service( - App::new().service(Files::new("/", ".")), - ); - let req = TestRequest::default().method(Method::PUT).uri("/Cargo.toml").to_request(); + let mut srv = test::init_service(App::new().service(Files::new("/", "."))); + let req = TestRequest::default() + .method(Method::PUT) + .uri("/Cargo.toml") + .to_request(); let resp = test::call_service(&mut srv, req); assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); } @@ -1070,10 +1069,7 @@ mod tests { #[test] fn test_files_guards() { let mut srv = test::init_service( - App::new().service( - Files::new("/", ".") - .use_guards(guard::Post()) - ), + App::new().service(Files::new("/", ".").use_guards(guard::Post())), ); let req = TestRequest::default() From cc0b4be5b7efa51f58f7b97ec67c57d60c1f2849 Mon Sep 17 00:00:00 2001 From: Nathan Date: Wed, 9 Oct 2019 09:11:55 -0400 Subject: [PATCH 027/176] Fix typo in response.rs body() comment (#1126) Fixes https://github.com/actix/actix-web/issues/1125 --- actix-http/src/response.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index 5b0b3bc87..a1541b53e 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -194,7 +194,7 @@ impl Response { self.head.extensions.borrow_mut() } - /// Get body os this response + /// Get body of this response #[inline] pub fn body(&self) -> &ResponseBody { &self.body From effa96f5e4697d7dad7fd73f68b4cca055fa3f25 Mon Sep 17 00:00:00 2001 From: MaySantucci Date: Sat, 12 Oct 2019 02:45:12 +0200 Subject: [PATCH 028/176] Removed httpcode 'MovedPermanenty'. (#1128) --- actix-http/src/httpcodes.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/actix-http/src/httpcodes.rs b/actix-http/src/httpcodes.rs index 3cac35eb7..0c7f23fc8 100644 --- a/actix-http/src/httpcodes.rs +++ b/actix-http/src/httpcodes.rs @@ -29,7 +29,6 @@ impl Response { STATIC_RESP!(AlreadyReported, StatusCode::ALREADY_REPORTED); STATIC_RESP!(MultipleChoices, StatusCode::MULTIPLE_CHOICES); - STATIC_RESP!(MovedPermanenty, StatusCode::MOVED_PERMANENTLY); STATIC_RESP!(MovedPermanently, StatusCode::MOVED_PERMANENTLY); STATIC_RESP!(Found, StatusCode::FOUND); STATIC_RESP!(SeeOther, StatusCode::SEE_OTHER); From a48e616def6463826a47e20f84fdfaa43266d82d Mon Sep 17 00:00:00 2001 From: Roberto Huertas Date: Mon, 14 Oct 2019 17:23:15 +0200 Subject: [PATCH 029/176] feat(files): add possibility to redirect to slash-ended path (#1134) When accessing to a folder without a final slash, the index file will be loaded ok, but if it has references (like a css or an image in an html file) these resources won't be loaded correctly if they are using relative paths. In order to solve this, this PR adds the possibility to detect folders without a final slash and make a 302 redirect to mitigate this issue. The behavior is off by default. We're adding a new method called `redirect_to_slash_directory` which can be used to enable this behavior. --- actix-files/CHANGES.md | 9 +++---- actix-files/src/lib.rs | 53 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index 5999f2764..5eb4e9a64 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [0.1.6] - TBD + +* Add option to redirect to a slash-ended path `Files` #1132 + ## [0.1.5] - 2019-10-08 * Bump up `mime_guess` crate version to 2.0.1 @@ -7,18 +11,15 @@ * Bump up `percent-encoding` crate version to 2.1 * Allow user defined request guards for `Files` #1113 - - + ## [0.1.4] - 2019-07-20 * Allow to disable `Content-Disposition` header #686 - ## [0.1.3] - 2019-06-28 * Do not set `Content-Length` header, let actix-http set it #930 - ## [0.1.2] - 2019-06-13 * Content-Length is 0 for NamedFile HEAD request #914 diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index 1cc26629d..61674ca37 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -233,6 +233,7 @@ pub struct Files { directory: PathBuf, index: Option, show_index: bool, + redirect_to_slash: bool, default: Rc>>>, renderer: Rc, mime_override: Option>, @@ -246,6 +247,7 @@ impl Clone for Files { directory: self.directory.clone(), index: self.index.clone(), show_index: self.show_index, + redirect_to_slash: self.redirect_to_slash, default: self.default.clone(), renderer: self.renderer.clone(), file_flags: self.file_flags, @@ -273,6 +275,7 @@ impl Files { directory: dir, index: None, show_index: false, + redirect_to_slash: false, default: Rc::new(RefCell::new(None)), renderer: Rc::new(directory_listing), mime_override: None, @@ -289,6 +292,14 @@ impl Files { self } + /// Redirects to a slash-ended path when browsing a directory. + /// + /// By default never redirect. + pub fn redirect_to_slash_directory(mut self) -> Self { + self.redirect_to_slash = true; + self + } + /// Set custom directory renderer pub fn files_listing_renderer(mut self, f: F) -> Self where @@ -389,10 +400,10 @@ impl HttpServiceFactory for Files { } impl NewService for Files { - type Config = (); type Request = ServiceRequest; type Response = ServiceResponse; type Error = Error; + type Config = (); type Service = FilesService; type InitError = (); type Future = Box>; @@ -402,6 +413,7 @@ impl NewService for Files { directory: self.directory.clone(), index: self.index.clone(), show_index: self.show_index, + redirect_to_slash: self.redirect_to_slash, default: None, renderer: self.renderer.clone(), mime_override: self.mime_override.clone(), @@ -429,6 +441,7 @@ pub struct FilesService { directory: PathBuf, index: Option, show_index: bool, + redirect_to_slash: bool, default: Option, renderer: Rc, mime_override: Option>, @@ -502,6 +515,16 @@ impl Service for FilesService { if path.is_dir() { if let Some(ref redir_index) = self.index { + if self.redirect_to_slash && !req.path().ends_with('/') { + let redirect_to = format!("{}/", req.path()); + return Either::A(ok(req.into_response( + HttpResponse::Found() + .header(header::LOCATION, redirect_to) + .body("") + .into_body(), + ))); + } + let path = path.join(redir_index); match NamedFile::open(path) { @@ -1169,6 +1192,34 @@ mod tests { assert!(format!("{:?}", bytes).contains("/tests/test.png")); } + #[test] + fn test_redirect_to_slash_directory() { + // should not redirect if no index + let mut srv = test::init_service( + App::new().service(Files::new("/", ".").redirect_to_slash_directory()), + ); + let req = TestRequest::with_uri("/tests").to_request(); + let resp = test::call_service(&mut srv, req); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + + // should redirect if index present + let mut srv = test::init_service( + App::new().service( + Files::new("/", ".") + .index_file("test.png") + .redirect_to_slash_directory(), + ), + ); + let req = TestRequest::with_uri("/tests").to_request(); + let resp = test::call_service(&mut srv, req); + assert_eq!(resp.status(), StatusCode::FOUND); + + // should not redirect if the path is wrong + let req = TestRequest::with_uri("/not_existing").to_request(); + let resp = test::call_service(&mut srv, req); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + } + #[test] fn test_static_files_bad_directory() { let _st: Files = Files::new("/", "missing"); From 062e51e8ce6c2bfb34044e62682df7c8fa88f65a Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Mon, 14 Oct 2019 21:26:26 +0600 Subject: [PATCH 030/176] prep actix-file release --- actix-files/CHANGES.md | 4 ++-- actix-files/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index 5eb4e9a64..d6825c61d 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## [0.1.6] - TBD +## [0.1.6] - 2019-10-14 * Add option to redirect to a slash-ended path `Files` #1132 @@ -11,7 +11,7 @@ * Bump up `percent-encoding` crate version to 2.1 * Allow user defined request guards for `Files` #1113 - + ## [0.1.4] - 2019-07-20 * Allow to disable `Content-Disposition` header #686 diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 971db7929..9695cebe7 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-files" -version = "0.1.5" +version = "0.1.6" authors = ["Nikolay Kim "] description = "Static files support for actix web." readme = "README.md" From 967f9654051a5bd73941a5f21441aadb23cc3b90 Mon Sep 17 00:00:00 2001 From: DanSnow Date: Mon, 14 Oct 2019 23:34:17 +0800 Subject: [PATCH 031/176] Update `syn` & `quote` to 1.0 (#1133) * chore(actix-web-codegen): Upgrade syn and quote to 1.0 * feat(actix-web-codegen): Generate better error message * doc(actix-web-codegen): Update CHANGES.md * fix: Build with stable rust --- actix-web-codegen/CHANGES.md | 5 + actix-web-codegen/Cargo.toml | 5 +- actix-web-codegen/src/lib.rs | 45 ++++-- actix-web-codegen/src/route.rs | 242 ++++++++++++++++++--------------- 4 files changed, 178 insertions(+), 119 deletions(-) diff --git a/actix-web-codegen/CHANGES.md b/actix-web-codegen/CHANGES.md index 7cc0c164f..d57bd5c60 100644 --- a/actix-web-codegen/CHANGES.md +++ b/actix-web-codegen/CHANGES.md @@ -1,5 +1,10 @@ # Changes +## [UNRELEASE] + +* Bump up `syn` & `quote` to 1.0 +* Provide better error message + ## [0.1.2] - 2019-06-04 * Add macros for head, options, trace, connect and patch http methods diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 29abb4897..585d4970d 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -12,8 +12,9 @@ workspace = ".." proc-macro = true [dependencies] -quote = "0.6.12" -syn = { version = "0.15.34", features = ["full", "parsing", "extra-traits"] } +quote = "1" +syn = { version = "1", features = ["full", "parsing"] } +proc-macro2 = "1" [dev-dependencies] actix-web = { version = "1.0.0" } diff --git a/actix-web-codegen/src/lib.rs b/actix-web-codegen/src/lib.rs index b3ae7dd9b..88fa4dfda 100644 --- a/actix-web-codegen/src/lib.rs +++ b/actix-web-codegen/src/lib.rs @@ -58,7 +58,10 @@ use syn::parse_macro_input; #[proc_macro_attribute] pub fn get(args: TokenStream, input: TokenStream) -> TokenStream { let args = parse_macro_input!(args as syn::AttributeArgs); - let gen = route::Args::new(&args, input, route::GuardType::Get); + let gen = match route::Route::new(args, input, route::GuardType::Get) { + Ok(gen) => gen, + Err(err) => return err.to_compile_error().into(), + }; gen.generate() } @@ -70,7 +73,10 @@ pub fn get(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn post(args: TokenStream, input: TokenStream) -> TokenStream { let args = parse_macro_input!(args as syn::AttributeArgs); - let gen = route::Args::new(&args, input, route::GuardType::Post); + let gen = match route::Route::new(args, input, route::GuardType::Post) { + Ok(gen) => gen, + Err(err) => return err.to_compile_error().into(), + }; gen.generate() } @@ -82,7 +88,10 @@ pub fn post(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn put(args: TokenStream, input: TokenStream) -> TokenStream { let args = parse_macro_input!(args as syn::AttributeArgs); - let gen = route::Args::new(&args, input, route::GuardType::Put); + let gen = match route::Route::new(args, input, route::GuardType::Put) { + Ok(gen) => gen, + Err(err) => return err.to_compile_error().into(), + }; gen.generate() } @@ -94,7 +103,10 @@ pub fn put(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn delete(args: TokenStream, input: TokenStream) -> TokenStream { let args = parse_macro_input!(args as syn::AttributeArgs); - let gen = route::Args::new(&args, input, route::GuardType::Delete); + let gen = match route::Route::new(args, input, route::GuardType::Delete) { + Ok(gen) => gen, + Err(err) => return err.to_compile_error().into(), + }; gen.generate() } @@ -106,7 +118,10 @@ pub fn delete(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn head(args: TokenStream, input: TokenStream) -> TokenStream { let args = parse_macro_input!(args as syn::AttributeArgs); - let gen = route::Args::new(&args, input, route::GuardType::Head); + let gen = match route::Route::new(args, input, route::GuardType::Head) { + Ok(gen) => gen, + Err(err) => return err.to_compile_error().into(), + }; gen.generate() } @@ -118,7 +133,10 @@ pub fn head(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn connect(args: TokenStream, input: TokenStream) -> TokenStream { let args = parse_macro_input!(args as syn::AttributeArgs); - let gen = route::Args::new(&args, input, route::GuardType::Connect); + let gen = match route::Route::new(args, input, route::GuardType::Connect) { + Ok(gen) => gen, + Err(err) => return err.to_compile_error().into(), + }; gen.generate() } @@ -130,7 +148,10 @@ pub fn connect(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn options(args: TokenStream, input: TokenStream) -> TokenStream { let args = parse_macro_input!(args as syn::AttributeArgs); - let gen = route::Args::new(&args, input, route::GuardType::Options); + let gen = match route::Route::new(args, input, route::GuardType::Options) { + Ok(gen) => gen, + Err(err) => return err.to_compile_error().into(), + }; gen.generate() } @@ -142,7 +163,10 @@ pub fn options(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn trace(args: TokenStream, input: TokenStream) -> TokenStream { let args = parse_macro_input!(args as syn::AttributeArgs); - let gen = route::Args::new(&args, input, route::GuardType::Trace); + let gen = match route::Route::new(args, input, route::GuardType::Trace) { + Ok(gen) => gen, + Err(err) => return err.to_compile_error().into(), + }; gen.generate() } @@ -154,6 +178,9 @@ pub fn trace(args: TokenStream, input: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn patch(args: TokenStream, input: TokenStream) -> TokenStream { let args = parse_macro_input!(args as syn::AttributeArgs); - let gen = route::Args::new(&args, input, route::GuardType::Patch); + let gen = match route::Route::new(args, input, route::GuardType::Patch) { + Ok(gen) => gen, + Err(err) => return err.to_compile_error().into(), + }; gen.generate() } diff --git a/actix-web-codegen/src/route.rs b/actix-web-codegen/src/route.rs index 5215f60c8..e792a7f0a 100644 --- a/actix-web-codegen/src/route.rs +++ b/actix-web-codegen/src/route.rs @@ -1,21 +1,23 @@ extern crate proc_macro; -use std::fmt; - use proc_macro::TokenStream; -use quote::quote; +use proc_macro2::{Span, TokenStream as TokenStream2}; +use quote::{quote, ToTokens, TokenStreamExt}; +use syn::{AttributeArgs, Ident, NestedMeta}; enum ResourceType { Async, Sync, } -impl fmt::Display for ResourceType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ResourceType::Async => write!(f, "to_async"), - ResourceType::Sync => write!(f, "to"), - } +impl ToTokens for ResourceType { + fn to_tokens(&self, stream: &mut TokenStream2) { + let ident = match self { + ResourceType::Async => "to_async", + ResourceType::Sync => "to", + }; + let ident = Ident::new(ident, Span::call_site()); + stream.append(ident); } } @@ -32,63 +34,89 @@ pub enum GuardType { Patch, } -impl fmt::Display for GuardType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - GuardType::Get => write!(f, "Get"), - GuardType::Post => write!(f, "Post"), - GuardType::Put => write!(f, "Put"), - GuardType::Delete => write!(f, "Delete"), - GuardType::Head => write!(f, "Head"), - GuardType::Connect => write!(f, "Connect"), - GuardType::Options => write!(f, "Options"), - GuardType::Trace => write!(f, "Trace"), - GuardType::Patch => write!(f, "Patch"), +impl GuardType { + fn as_str(&self) -> &'static str { + match self { + GuardType::Get => "Get", + GuardType::Post => "Post", + GuardType::Put => "Put", + GuardType::Delete => "Delete", + GuardType::Head => "Head", + GuardType::Connect => "Connect", + GuardType::Options => "Options", + GuardType::Trace => "Trace", + GuardType::Patch => "Patch", } } } -pub struct Args { - name: syn::Ident, - path: String, - ast: syn::ItemFn, - resource_type: ResourceType, - pub guard: GuardType, - pub extra_guards: Vec, +impl ToTokens for GuardType { + fn to_tokens(&self, stream: &mut TokenStream2) { + let ident = self.as_str(); + let ident = Ident::new(ident, Span::call_site()); + stream.append(ident); + } } -impl fmt::Display for Args { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let ast = &self.ast; - let guards = format!(".guard(actix_web::guard::{}())", self.guard); - let guards = self.extra_guards.iter().fold(guards, |acc, val| { - format!("{}.guard(actix_web::guard::fn_guard({}))", acc, val) - }); +struct Args { + path: syn::LitStr, + guards: Vec, +} - write!( - f, - " -#[allow(non_camel_case_types)] -pub struct {name}; - -impl actix_web::dev::HttpServiceFactory for {name} {{ - fn register(self, config: &mut actix_web::dev::AppService) {{ - {ast} - - let resource = actix_web::Resource::new(\"{path}\"){guards}.{to}({name}); - - actix_web::dev::HttpServiceFactory::register(resource, config) - }} -}}", - name = self.name, - ast = quote!(#ast), - path = self.path, - guards = guards, - to = self.resource_type - ) +impl Args { + fn new(args: AttributeArgs) -> syn::Result { + let mut path = None; + let mut guards = Vec::new(); + for arg in args { + match arg { + NestedMeta::Lit(syn::Lit::Str(lit)) => match path { + None => { + path = Some(lit); + } + _ => { + return Err(syn::Error::new_spanned( + lit, + "Multiple paths specified! Should be only one!", + )); + } + }, + NestedMeta::Meta(syn::Meta::NameValue(nv)) => { + if nv.path.is_ident("guard") { + if let syn::Lit::Str(lit) = nv.lit { + guards.push(Ident::new(&lit.value(), Span::call_site())); + } else { + return Err(syn::Error::new_spanned( + nv.lit, + "Attribute guard expects literal string!", + )); + } + } else { + return Err(syn::Error::new_spanned( + nv.path, + "Unknown attribute key is specified. Allowed: guard", + )); + } + } + arg => { + return Err(syn::Error::new_spanned(arg, "Unknown attribute")); + } + } + } + Ok(Args { + path: path.unwrap(), + guards, + }) } } +pub struct Route { + name: syn::Ident, + args: Args, + ast: syn::ItemFn, + resource_type: ResourceType, + guard: GuardType, +} + fn guess_resource_type(typ: &syn::Type) -> ResourceType { let mut guess = ResourceType::Sync; @@ -111,75 +139,73 @@ fn guess_resource_type(typ: &syn::Type) -> ResourceType { guess } -impl Args { - pub fn new(args: &[syn::NestedMeta], input: TokenStream, guard: GuardType) -> Self { +impl Route { + pub fn new( + args: AttributeArgs, + input: TokenStream, + guard: GuardType, + ) -> syn::Result { if args.is_empty() { - panic!( - "invalid server definition, expected: #[{}(\"some path\")]", - guard - ); + return Err(syn::Error::new( + Span::call_site(), + format!( + r#"invalid server definition, expected #[{}("")]"#, + guard.as_str().to_ascii_lowercase() + ), + )); } + let ast: syn::ItemFn = syn::parse(input)?; + let name = ast.sig.ident.clone(); - let ast: syn::ItemFn = syn::parse(input).expect("Parse input as function"); - let name = ast.ident.clone(); + let args = Args::new(args)?; - let mut extra_guards = Vec::new(); - let mut path = None; - for arg in args { - match arg { - syn::NestedMeta::Literal(syn::Lit::Str(ref fname)) => { - if path.is_some() { - panic!("Multiple paths specified! Should be only one!") - } - let fname = quote!(#fname).to_string(); - path = Some(fname.as_str()[1..fname.len() - 1].to_owned()) - } - syn::NestedMeta::Meta(syn::Meta::NameValue(ident)) => { - match ident.ident.to_string().to_lowercase().as_str() { - "guard" => match ident.lit { - syn::Lit::Str(ref text) => extra_guards.push(text.value()), - _ => panic!("Attribute guard expects literal string!"), - }, - attr => panic!( - "Unknown attribute key is specified: {}. Allowed: guard", - attr - ), - } - } - attr => panic!("Unknown attribute{:?}", attr), - } - } - - let resource_type = if ast.asyncness.is_some() { + let resource_type = if ast.sig.asyncness.is_some() { ResourceType::Async } else { - match ast.decl.output { - syn::ReturnType::Default => panic!( - "Function {} has no return type. Cannot be used as handler", - name - ), + match ast.sig.output { + syn::ReturnType::Default => { + return Err(syn::Error::new_spanned( + ast, + "Function has no return type. Cannot be used as handler", + )); + } syn::ReturnType::Type(_, ref typ) => guess_resource_type(typ.as_ref()), } }; - let path = path.unwrap(); - - Self { + Ok(Self { name, - path, + args, ast, resource_type, guard, - extra_guards, - } + }) } pub fn generate(&self) -> TokenStream { - let text = self.to_string(); + let name = &self.name; + let guard = &self.guard; + let ast = &self.ast; + let path = &self.args.path; + let extra_guards = &self.args.guards; + let resource_type = &self.resource_type; + let stream = quote! { + #[allow(non_camel_case_types)] + pub struct #name; - match text.parse() { - Ok(res) => res, - Err(error) => panic!("Error: {:?}\nGenerated code: {}", error, text), - } + impl actix_web::dev::HttpServiceFactory for #name { + fn register(self, config: &mut actix_web::dev::AppService) { + #ast + + let resource = actix_web::Resource::new(#path) + .guard(actix_web::guard::#guard()) + #(.guard(actix_web::guard::fn_guard(#extra_guards)))* + .#resource_type(#name); + + actix_web::dev::HttpServiceFactory::register(resource, config) + } + } + }; + stream.into() } } From 1ca9d87f0a4dc97cf9a427debc65c2c384b8110e Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Mon, 14 Oct 2019 21:35:53 +0600 Subject: [PATCH 032/176] prep actix-web-codegen release --- actix-web-codegen/CHANGES.md | 3 ++- actix-web-codegen/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/actix-web-codegen/CHANGES.md b/actix-web-codegen/CHANGES.md index d57bd5c60..2beea62cf 100644 --- a/actix-web-codegen/CHANGES.md +++ b/actix-web-codegen/CHANGES.md @@ -1,8 +1,9 @@ # Changes -## [UNRELEASE] +## [0.1.3] - 2019-10-14 * Bump up `syn` & `quote` to 1.0 + * Provide better error message ## [0.1.2] - 2019-06-04 diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 585d4970d..981e00323 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web-codegen" -version = "0.1.2" +version = "0.1.3" description = "Actix web proc macros" readme = "README.md" authors = ["Nikolay Kim "] From ace98e3a1e62cdfac4c21e22955c05157e373d35 Mon Sep 17 00:00:00 2001 From: Anton Lazarev Date: Mon, 14 Oct 2019 16:05:54 -0700 Subject: [PATCH 033/176] support Host guards when Host header is unset (#1129) --- CHANGES.md | 4 ++++ src/guard.rs | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 4ff7d1e66..689ab13dd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,10 @@ * Add `Payload::into_inner` method and make stored `def::Payload` public. (#1110) +### Changed + +* Support `Host` guards when the `Host` header is unset (e.g. HTTP/2 requests) (#1129) + ## [1.0.8] - 2019-09-25 ### Added diff --git a/src/guard.rs b/src/guard.rs index c60192587..aad19c8f8 100644 --- a/src/guard.rs +++ b/src/guard.rs @@ -276,10 +276,11 @@ pub fn Host>(host: H) -> HostGuard { fn get_host_uri(req: &RequestHead) -> Option { use core::str::FromStr; - let host_value = req.headers.get(header::HOST)?; - let host = host_value.to_str().ok()?; - let uri = Uri::from_str(host).ok()?; - Some(uri) + req.headers.get(header::HOST) + .and_then(|host_value| host_value.to_str().ok()) + .or_else(|| req.uri.host()) + .map(|host: &str| Uri::from_str(host).ok()) + .and_then(|host_success| host_success) } #[doc(hidden)] @@ -400,6 +401,31 @@ mod tests { assert!(!pred.check(req.head())); } + #[test] + fn test_host_without_header() { + let req = TestRequest::default() + .uri("www.rust-lang.org") + .to_http_request(); + + let pred = Host("www.rust-lang.org"); + assert!(pred.check(req.head())); + + let pred = Host("www.rust-lang.org").scheme("https"); + assert!(pred.check(req.head())); + + let pred = Host("blog.rust-lang.org"); + assert!(!pred.check(req.head())); + + let pred = Host("blog.rust-lang.org").scheme("https"); + assert!(!pred.check(req.head())); + + let pred = Host("crates.io"); + assert!(!pred.check(req.head())); + + let pred = Host("localhost"); + assert!(!pred.check(req.head())); + } + #[test] fn test_methods() { let req = TestRequest::default().to_http_request(); From f0612f757001dde1509892f386d5d3033194f540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathas=20Concei=C3=A7=C3=A3o?= Date: Sat, 26 Oct 2019 02:27:14 -0300 Subject: [PATCH 034/176] awc: Add support for setting query from Serialize type for client request (#1130) Signed-off-by: Jonathas-Conceicao --- awc/CHANGES.md | 3 +++ awc/src/request.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/awc/CHANGES.md b/awc/CHANGES.md index 6f8fe2dbd..9b8e27c96 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,5 +1,8 @@ # Changes +## [0.2.8] - 2019-10-24 + +* Add support for setting query from Serialize type for client request. ## [0.2.7] - 2019-09-25 diff --git a/awc/src/request.rs b/awc/src/request.rs index a90cf60b2..6ff68ae66 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -382,6 +382,27 @@ impl ClientRequest { } } + /// Sets the query part of the request + pub fn query( + mut self, + query: &T, + ) -> Result { + let mut parts = self.head.uri.clone().into_parts(); + + if let Some(path_and_query) = parts.path_and_query { + let query = serde_urlencoded::to_string(query)?; + let path = path_and_query.path(); + parts.path_and_query = format!("{}?{}", path, query).parse().ok(); + + match Uri::from_parts(parts) { + Ok(uri) => self.head.uri = uri, + Err(e) => self.err = Some(e.into()), + } + } + + Ok(self) + } + /// Freeze request builder and construct `FrozenClientRequest`, /// which could be used for sending same request multiple times. pub fn freeze(self) -> Result { @@ -690,4 +711,13 @@ mod tests { "Bearer someS3cr3tAutht0k3n" ); } + + #[test] + fn client_query() { + let req = Client::new() + .get("/") + .query(&[("key1", "val1"), ("key2", "val2")]) + .unwrap(); + assert_eq!(req.get_uri().query().unwrap(), "key1=val1&key2=val2"); + } } From edcde6707657d3e8bdd1df21533fe80aa235f0dd Mon Sep 17 00:00:00 2001 From: Hung-I Wang Date: Wed, 6 Nov 2019 22:08:37 +0800 Subject: [PATCH 035/176] Fix escaping/encoding problems in Content-Disposition header (#1151) * Fix filename encoding in Content-Disposition of acitx_files::NamedFile * Add more comments on how to use Content-Disposition header properly & Fix some trivial problems * Improve Content-Disposition filename(*) parameters of actix_files::NamedFile * Tweak Content-Disposition parse to accept empty param value in quoted-string * Fix typos in comments in .../content_disposition.rs (pointed out by @JohnTitor) * Update CHANGES.md * Update CHANGES.md again --- CHANGES.md | 1 + actix-files/src/lib.rs | 25 ++++++ actix-files/src/named.rs | 13 ++- .../src/header/common/content_disposition.rs | 90 +++++++++++++++++-- 4 files changed, 119 insertions(+), 10 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 689ab13dd..dcb57630f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,7 @@ ### Added * Add `Payload::into_inner` method and make stored `def::Payload` public. (#1110) +* Add an additional `filename*` param in the `Content-Disposition` header of `actix_files::NamedFile` to be more compatible. (#1151) ### Changed diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index 61674ca37..16f40a20c 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -733,6 +733,31 @@ mod tests { assert!(resp.headers().get(header::CONTENT_DISPOSITION).is_none()); } + #[test] + fn test_named_file_non_ascii_file_name() { + let mut file = + NamedFile::from_file(File::open("Cargo.toml").unwrap(), "貨物.toml") + .unwrap(); + { + file.file(); + let _f: &File = &file; + } + { + let _f: &mut File = &mut file; + } + + let req = TestRequest::default().to_http_request(); + let resp = file.respond_to(&req).unwrap(); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + "text/x-toml" + ); + assert_eq!( + resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), + "inline; filename=\"貨物.toml\"; filename*=UTF-8''%E8%B2%A8%E7%89%A9.toml" + ); + } + #[test] fn test_named_file_set_content_type() { let mut file = NamedFile::open("Cargo.toml") diff --git a/actix-files/src/named.rs b/actix-files/src/named.rs index ca1a909a4..955982caf 100644 --- a/actix-files/src/named.rs +++ b/actix-files/src/named.rs @@ -13,7 +13,7 @@ use mime_guess::from_path; use actix_http::body::SizedStream; use actix_web::http::header::{ - self, ContentDisposition, DispositionParam, DispositionType, + self, Charset, ContentDisposition, DispositionParam, DispositionType, ExtendedValue, }; use actix_web::http::{ContentEncoding, StatusCode}; use actix_web::middleware::BodyEncoding; @@ -93,9 +93,18 @@ impl NamedFile { mime::IMAGE | mime::TEXT | mime::VIDEO => DispositionType::Inline, _ => DispositionType::Attachment, }; + let mut parameters = + vec![DispositionParam::Filename(String::from(filename.as_ref()))]; + if !filename.is_ascii() { + parameters.push(DispositionParam::FilenameExt(ExtendedValue { + charset: Charset::Ext(String::from("UTF-8")), + language_tag: None, + value: filename.into_owned().into_bytes(), + })) + } let cd = ContentDisposition { disposition: disposition_type, - parameters: vec![DispositionParam::Filename(filename.into_owned())], + parameters: parameters, }; (ct, cd) }; diff --git a/actix-http/src/header/common/content_disposition.rs b/actix-http/src/header/common/content_disposition.rs index 14fcc3517..b2b6f34d7 100644 --- a/actix-http/src/header/common/content_disposition.rs +++ b/actix-http/src/header/common/content_disposition.rs @@ -76,6 +76,11 @@ pub enum DispositionParam { /// the form. Name(String), /// A plain file name. + /// + /// It is [not supposed](https://tools.ietf.org/html/rfc6266#appendix-D) to contain any + /// non-ASCII characters when used in a *Content-Disposition* HTTP response header, where + /// [`FilenameExt`](DispositionParam::FilenameExt) with charset UTF-8 may be used instead + /// in case there are Unicode characters in file names. Filename(String), /// An extended file name. It must not exist for `ContentType::Formdata` according to /// [RFC7578 Section 4.2](https://tools.ietf.org/html/rfc7578#section-4.2). @@ -220,7 +225,16 @@ impl DispositionParam { /// ext-token = /// ``` /// -/// **Note**: filename* [must not](https://tools.ietf.org/html/rfc7578#section-4.2) be used within +/// # Note +/// +/// filename is [not supposed](https://tools.ietf.org/html/rfc6266#appendix-D) to contain any +/// non-ASCII characters when used in a *Content-Disposition* HTTP response header, where +/// filename* with charset UTF-8 may be used instead in case there are Unicode characters in file +/// names. +/// filename is [acceptable](https://tools.ietf.org/html/rfc7578#section-4.2) to be UTF-8 encoded +/// directly in a *Content-Disposition* header for *multipart/form-data*, though. +/// +/// filename* [must not](https://tools.ietf.org/html/rfc7578#section-4.2) be used within /// *multipart/form-data*. /// /// # Example @@ -251,6 +265,22 @@ impl DispositionParam { /// }; /// assert_eq!(cd2.get_name(), Some("file")); // field name /// assert_eq!(cd2.get_filename(), Some("bill.odt")); +/// +/// // HTTP response header with Unicode characters in file names +/// let cd3 = ContentDisposition { +/// disposition: DispositionType::Attachment, +/// parameters: vec![ +/// DispositionParam::FilenameExt(ExtendedValue { +/// charset: Charset::Ext(String::from("UTF-8")), +/// language_tag: None, +/// value: String::from("\u{1f600}.svg").into_bytes(), +/// }), +/// // fallback for better compatibility +/// DispositionParam::Filename(String::from("Grinning-Face-Emoji.svg")) +/// ], +/// }; +/// assert_eq!(cd3.get_filename_ext().map(|ev| ev.value.as_ref()), +/// Some("\u{1f600}.svg".as_bytes())); /// ``` /// /// # WARN @@ -333,15 +363,17 @@ impl ContentDisposition { // token: won't contains semicolon according to RFC 2616 Section 2.2 let (token, new_left) = split_once_and_trim(left, ';'); left = new_left; + if token.is_empty() { + // quoted-string can be empty, but token cannot be empty + return Err(crate::error::ParseError::Header); + } token.to_owned() }; - if value.is_empty() { - return Err(crate::error::ParseError::Header); - } let param = if param_name.eq_ignore_ascii_case("name") { DispositionParam::Name(value) } else if param_name.eq_ignore_ascii_case("filename") { + // See also comments in test_from_raw_uncessary_percent_decode. DispositionParam::Filename(value) } else { DispositionParam::Unknown(param_name.to_owned(), value) @@ -466,11 +498,40 @@ impl fmt::Display for DispositionType { impl fmt::Display for DispositionParam { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // All ASCII control charaters (0-30, 127) excepting horizontal tab, double quote, and + // All ASCII control characters (0-30, 127) including horizontal tab, double quote, and // backslash should be escaped in quoted-string (i.e. "foobar"). - // Ref: RFC6266 S4.1 -> RFC2616 S2.2; RFC 7578 S4.2 -> RFC2183 S2 -> ... . + // Ref: RFC6266 S4.1 -> RFC2616 S3.6 + // filename-parm = "filename" "=" value + // value = token | quoted-string + // quoted-string = ( <"> *(qdtext | quoted-pair ) <"> ) + // qdtext = > + // quoted-pair = "\" CHAR + // TEXT = + // LWS = [CRLF] 1*( SP | HT ) + // OCTET = + // CHAR = + // CTL = + // + // Ref: RFC7578 S4.2 -> RFC2183 S2 -> RFC2045 S5.1 + // parameter := attribute "=" value + // attribute := token + // ; Matching of attributes + // ; is ALWAYS case-insensitive. + // value := token / quoted-string + // token := 1* + // tspecials := "(" / ")" / "<" / ">" / "@" / + // "," / ";" / ":" / "\" / <"> + // "/" / "[" / "]" / "?" / "=" + // ; Must be in quoted-string, + // ; to use within parameter values + // + // + // See also comments in test_from_raw_uncessary_percent_decode. lazy_static! { - static ref RE: Regex = Regex::new("[\x01-\x08\x10\x1F\x7F\"\\\\]").unwrap(); + static ref RE: Regex = Regex::new("[\x00-\x08\x10-\x1F\x7F\"\\\\]").unwrap(); } match self { DispositionParam::Name(ref value) => write!(f, "name={}", value), @@ -774,8 +835,18 @@ mod tests { #[test] fn test_from_raw_uncessary_percent_decode() { + // In fact, RFC7578 (multipart/form-data) Section 2 and 4.2 suggests that filename with + // non-ASCII characters MAY be percent-encoded. + // On the contrary, RFC6266 or other RFCs related to Content-Disposition response header + // do not mention such percent-encoding. + // So, it appears to be undecidable whether to percent-decode or not without + // knowing the usage scenario (multipart/form-data v.s. HTTP response header) and + // inevitable to unnecessarily percent-decode filename with %XX in the former scenario. + // Fortunately, it seems that almost all mainstream browsers just send UTF-8 encoded file + // names in quoted-string format (tested on Edge, IE11, Chrome and Firefox) without + // percent-encoding. So we do not bother to attempt to percent-decode. let a = HeaderValue::from_static( - "form-data; name=photo; filename=\"%74%65%73%74%2e%70%6e%67\"", // Should not be decoded! + "form-data; name=photo; filename=\"%74%65%73%74%2e%70%6e%67\"", ); let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap(); let b = ContentDisposition { @@ -811,6 +882,9 @@ mod tests { let a = HeaderValue::from_static("inline; filename= "); assert!(ContentDisposition::from_raw(&a).is_err()); + + let a = HeaderValue::from_static("inline; filename=\"\""); + assert!(ContentDisposition::from_raw(&a).expect("parse cd").get_filename().expect("filename").is_empty()); } #[test] From 61b38e8d0df8ae5b4db59ff1d72ca65fbd75b8a2 Mon Sep 17 00:00:00 2001 From: Erlend Langseth <3rlendhl@gmail.com> Date: Wed, 6 Nov 2019 15:09:22 +0100 Subject: [PATCH 036/176] Increase timeouts in test-server (#1153) --- test-server/CHANGES.md | 1 + test-server/src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test-server/CHANGES.md b/test-server/CHANGES.md index 798dbf506..57068fe95 100644 --- a/test-server/CHANGES.md +++ b/test-server/CHANGES.md @@ -5,6 +5,7 @@ ### Changed * Update serde_urlencoded to "0.6.1" +* Increase TestServerRuntime timeouts from 500ms to 3000ms ### Fixed diff --git a/test-server/src/lib.rs b/test-server/src/lib.rs index a2366bf48..ebdec688f 100644 --- a/test-server/src/lib.rs +++ b/test-server/src/lib.rs @@ -144,7 +144,7 @@ impl TestServer { .map_err(|e| log::error!("Can not set alpn protocol: {:?}", e)); Connector::new() .conn_lifetime(time::Duration::from_secs(0)) - .timeout(time::Duration::from_millis(500)) + .timeout(time::Duration::from_millis(3000)) .ssl(builder.build()) .finish() } @@ -152,7 +152,7 @@ impl TestServer { { Connector::new() .conn_lifetime(time::Duration::from_secs(0)) - .timeout(time::Duration::from_millis(500)) + .timeout(time::Duration::from_millis(3000)) .finish() } }; From 885ff7396e792403990f713df1cd3bfd0b018059 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 6 Nov 2019 10:35:13 -0800 Subject: [PATCH 037/176] prepare actox-http release --- CHANGES.md | 1 - Cargo.toml | 2 +- actix-files/CHANGES.md | 4 ++++ actix-http/CHANGES.md | 9 +++++---- actix-http/Cargo.toml | 4 ++-- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index dcb57630f..689ab13dd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,7 +5,6 @@ ### Added * Add `Payload::into_inner` method and make stored `def::Payload` public. (#1110) -* Add an additional `filename*` param in the `Content-Disposition` header of `actix_files::NamedFile` to be more compatible. (#1151) ### Changed diff --git a/Cargo.toml b/Cargo.toml index 35ca28b2c..96b015e12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,7 +89,7 @@ bytes = "0.4" derive_more = "0.15.0" encoding_rs = "0.8" futures = "0.1.25" -hashbrown = "0.5.0" +hashbrown = "0.6.3" log = "0.4" mime = "0.3" net2 = "0.2.33" diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index d6825c61d..5ec56593c 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [0.1.7] - 2019-11-06 + +* Add an additional `filename*` param in the `Content-Disposition` header of `actix_files::NamedFile` to be more compatible. (#1151) + ## [0.1.6] - 2019-10-14 * Add option to redirect to a slash-ended path `Files` #1132 diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 624aca5eb..4cb5644c3 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,11 +1,15 @@ # Changes -## Not released yet +## [0.2.11] - 2019-11-06 ### Added * Add support for serde_json::Value to be passed as argument to ResponseBuilder.body() +* Add an additional `filename*` param in the `Content-Disposition` header of `actix_files::NamedFile` to be more compatible. (#1151) + +* Allow to use `std::convert::Infallible` as `actix_http::error::Error` + ### Fixed * To be compatible with non-English error responses, `ResponseError` rendered with `text/plain; charset=utf-8` header #1118 @@ -17,9 +21,6 @@ * Add support for sending HTTP requests with `Rc` in addition to sending HTTP requests with `RequestHead` -* Allow to use `std::convert::Infallible` as `actix_http::error::Error` - - ### Fixed * h2 will use error response #1080 diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index cc7c885e7..ee0ded597 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "0.2.10" +version = "0.2.11" authors = ["Nikolay Kim "] description = "Actix http primitives" readme = "README.md" @@ -62,7 +62,7 @@ derive_more = "0.15.0" either = "1.5.2" encoding_rs = "0.8" futures = "0.1.25" -hashbrown = "0.5.0" +hashbrown = "0.6.3" h2 = "0.1.16" http = "0.1.17" httparse = "1.3" From f7f410d033c8d34295892a7d52a4a6dc51ef2e77 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 6 Nov 2019 11:20:47 -0800 Subject: [PATCH 038/176] fix test order dep --- actix-http/src/h1/encoder.rs | 44 +++++++++++++++++++----------------- src/guard.rs | 3 ++- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/actix-http/src/h1/encoder.rs b/actix-http/src/h1/encoder.rs index 51ea497e0..6396f3b55 100644 --- a/actix-http/src/h1/encoder.rs +++ b/actix-http/src/h1/encoder.rs @@ -548,10 +548,11 @@ mod tests { ConnectionType::Close, &ServiceConfig::default(), ); - assert_eq!( - bytes.take().freeze(), - Bytes::from_static(b"\r\nContent-Length: 0\r\nConnection: close\r\nDate: date\r\nContent-Type: plain/text\r\n\r\n") - ); + let data = String::from_utf8(Vec::from(bytes.take().freeze().as_ref())).unwrap(); + assert!(data.contains("Content-Length: 0\r\n")); + assert!(data.contains("Connection: close\r\n")); + assert!(data.contains("Content-Type: plain/text\r\n")); + assert!(data.contains("Date: date\r\n")); let _ = head.encode_headers( &mut bytes, @@ -560,10 +561,10 @@ mod tests { ConnectionType::KeepAlive, &ServiceConfig::default(), ); - assert_eq!( - bytes.take().freeze(), - Bytes::from_static(b"\r\nTransfer-Encoding: chunked\r\nDate: date\r\nContent-Type: plain/text\r\n\r\n") - ); + let data = String::from_utf8(Vec::from(bytes.take().freeze().as_ref())).unwrap(); + assert!(data.contains("Transfer-Encoding: chunked\r\n")); + assert!(data.contains("Content-Type: plain/text\r\n")); + assert!(data.contains("Date: date\r\n")); let _ = head.encode_headers( &mut bytes, @@ -572,10 +573,10 @@ mod tests { ConnectionType::KeepAlive, &ServiceConfig::default(), ); - assert_eq!( - bytes.take().freeze(), - Bytes::from_static(b"\r\nContent-Length: 100\r\nDate: date\r\nContent-Type: plain/text\r\n\r\n") - ); + let data = String::from_utf8(Vec::from(bytes.take().freeze().as_ref())).unwrap(); + assert!(data.contains("Content-Length: 100\r\n")); + assert!(data.contains("Content-Type: plain/text\r\n")); + assert!(data.contains("Date: date\r\n")); let mut head = RequestHead::default(); head.set_camel_case_headers(false); @@ -586,7 +587,6 @@ mod tests { .append(CONTENT_TYPE, HeaderValue::from_static("xml")); let mut head = RequestHeadType::Owned(head); - let _ = head.encode_headers( &mut bytes, Version::HTTP_11, @@ -594,10 +594,11 @@ mod tests { ConnectionType::KeepAlive, &ServiceConfig::default(), ); - assert_eq!( - bytes.take().freeze(), - Bytes::from_static(b"\r\ntransfer-encoding: chunked\r\ndate: date\r\ncontent-type: xml\r\ncontent-type: plain/text\r\n\r\n") - ); + let data = String::from_utf8(Vec::from(bytes.take().freeze().as_ref())).unwrap(); + assert!(data.contains("transfer-encoding: chunked\r\n")); + assert!(data.contains("content-type: xml\r\n")); + assert!(data.contains("content-type: plain/text\r\n")); + assert!(data.contains("date: date\r\n")); } #[test] @@ -626,9 +627,10 @@ mod tests { ConnectionType::Close, &ServiceConfig::default(), ); - assert_eq!( - bytes.take().freeze(), - Bytes::from_static(b"\r\ncontent-length: 0\r\nconnection: close\r\nauthorization: another authorization\r\ndate: date\r\n\r\n") - ); + let data = String::from_utf8(Vec::from(bytes.take().freeze().as_ref())).unwrap(); + assert!(data.contains("content-length: 0\r\n")); + assert!(data.contains("connection: close\r\n")); + assert!(data.contains("authorization: another authorization\r\n")); + assert!(data.contains("date: date\r\n")); } } diff --git a/src/guard.rs b/src/guard.rs index aad19c8f8..3db525f9a 100644 --- a/src/guard.rs +++ b/src/guard.rs @@ -276,7 +276,8 @@ pub fn Host>(host: H) -> HostGuard { fn get_host_uri(req: &RequestHead) -> Option { use core::str::FromStr; - req.headers.get(header::HOST) + req.headers + .get(header::HOST) .and_then(|host_value| host_value.to_str().ok()) .or_else(|| req.uri.host()) .map(|host: &str| Uri::from_str(host).ok()) From b2934ad8d2c3315e0eae85634a209a17e7b4a6af Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 6 Nov 2019 11:25:26 -0800 Subject: [PATCH 039/176] prep actix-file release --- actix-files/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 9695cebe7..1bc063e55 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-files" -version = "0.1.6" +version = "0.1.7" authors = ["Nikolay Kim "] description = "Static files support for actix web." readme = "README.md" @@ -19,7 +19,7 @@ path = "src/lib.rs" [dependencies] actix-web = { version = "1.0.8", default-features = false } -actix-http = "0.2.9" +actix-http = "0.2.11" actix-service = "0.4.1" bitflags = "1" bytes = "0.4" From fba02fdd8cde150615772c7fde8d3dd0811bef06 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 6 Nov 2019 11:33:25 -0800 Subject: [PATCH 040/176] prep awc release --- awc/CHANGES.md | 3 ++- awc/Cargo.toml | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/awc/CHANGES.md b/awc/CHANGES.md index 9b8e27c96..89423f80e 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,9 +1,10 @@ # Changes -## [0.2.8] - 2019-10-24 +## [0.2.8] - 2019-11-06 * Add support for setting query from Serialize type for client request. + ## [0.2.7] - 2019-09-25 ### Added diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 6f0f63f92..4b0e612b8 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "awc" -version = "0.2.7" +version = "0.2.8" authors = ["Nikolay Kim "] description = "Actix http client." readme = "README.md" @@ -44,7 +44,7 @@ flate2-rust = ["actix-http/flate2-rust"] [dependencies] actix-codec = "0.1.2" actix-service = "0.4.1" -actix-http = "0.2.10" +actix-http = "0.2.11" base64 = "0.10.1" bytes = "0.4" derive_more = "0.15.0" @@ -62,8 +62,8 @@ rustls = { version = "0.15.2", optional = true } [dev-dependencies] actix-rt = "0.2.2" -actix-web = { version = "1.0.0", features=["ssl"] } -actix-http = { version = "0.2.10", features=["ssl"] } +actix-web = { version = "1.0.8", features=["ssl"] } +actix-http = { version = "0.2.11", features=["ssl"] } actix-http-test = { version = "0.2.0", features=["ssl"] } actix-utils = "0.4.1" actix-server = { version = "0.6.0", features=["ssl", "rust-tls"] } From 88110ed268a3e46ccce610486abfb2cf8f39fbde Mon Sep 17 00:00:00 2001 From: Feiko Nanninga Date: Thu, 14 Nov 2019 03:32:47 +0100 Subject: [PATCH 041/176] Add security note to ConnectionInfo::remote() (#1158) --- src/info.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/info.rs b/src/info.rs index 61914516e..a9c3e4eeb 100644 --- a/src/info.rs +++ b/src/info.rs @@ -162,6 +162,12 @@ impl ConnectionInfo { /// - Forwarded /// - X-Forwarded-For /// - peer name of opened socket + /// + /// # Security + /// Do not use this function for security purposes, unless you can ensure the Forwarded and + /// X-Forwarded-For headers cannot be spoofed by the client. If you want the client's socket + /// address explicitly, use + /// [`HttpRequest::peer_addr()`](../web/struct.HttpRequest.html#method.peer_addr) instead. #[inline] pub fn remote(&self) -> Option<&str> { if let Some(ref r) = self.remote { From 0212c618c6594de8c44df02677a2f607288cd0c5 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 14 Nov 2019 08:55:37 +0600 Subject: [PATCH 042/176] prepare actix-web release --- CHANGES.md | 3 ++- Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 689ab13dd..bb17a7efc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## [1.0.9] - 2019-xx-xx +## [1.0.9] - 2019-11-14 ### Added @@ -10,6 +10,7 @@ * Support `Host` guards when the `Host` header is unset (e.g. HTTP/2 requests) (#1129) + ## [1.0.8] - 2019-09-25 ### Added diff --git a/Cargo.toml b/Cargo.toml index 96b015e12..54e4b2374 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "1.0.8" +version = "1.0.9" authors = ["Nikolay Kim "] description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." readme = "README.md" @@ -78,7 +78,7 @@ actix-utils = "0.4.4" actix-router = "0.1.5" actix-rt = "0.2.4" actix-web-codegen = "0.1.2" -actix-http = "0.2.9" +actix-http = "0.2.11" actix-server = "0.6.1" actix-server-config = "0.1.2" actix-testing = "0.1.0" From 5cb2d500d1289fe66c4fa8b4a10975c0f2399b46 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 14 Nov 2019 08:58:24 +0600 Subject: [PATCH 043/176] update actix-web-actors --- actix-web-actors/CHANGES.md | 4 ++++ actix-web-actors/Cargo.toml | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/actix-web-actors/CHANGES.md b/actix-web-actors/CHANGES.md index 0d1df7e55..c1417c9c4 100644 --- a/actix-web-actors/CHANGES.md +++ b/actix-web-actors/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [1.0.3] - 2019-11-14 + +* Update actix-web and actix-http dependencies + ## [1.0.2] - 2019-07-20 * Add `ws::start_with_addr()`, returning the address of the created actor, along diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index 356109da5..d5a6ce2c4 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web-actors" -version = "1.0.2" +version = "1.0.3" authors = ["Nikolay Kim "] description = "Actix actors support for actix web framework." readme = "README.md" @@ -19,8 +19,8 @@ path = "src/lib.rs" [dependencies] actix = "0.8.3" -actix-web = "1.0.3" -actix-http = "0.2.5" +actix-web = "1.0.9" +actix-http = "0.2.11" actix-codec = "0.1.2" bytes = "0.4" futures = "0.1.25" From 8cba1170e6064ba6754abec97776fd25038768f7 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 15 Nov 2019 15:54:11 +0600 Subject: [PATCH 044/176] make actix-http compile with std::future --- Cargo.toml | 54 +++-- actix-http/Cargo.toml | 69 +++--- actix-http/src/body.rs | 110 +++++----- actix-http/src/builder.rs | 83 ++++--- actix-http/src/client/connector.rs | 9 +- actix-http/src/client/h1proto.rs | 5 +- actix-http/src/client/h2proto.rs | 4 +- actix-http/src/client/pool.rs | 26 +-- actix-http/src/cloneable.rs | 6 +- actix-http/src/config.rs | 24 +-- actix-http/src/encoding/decoder.rs | 52 +++-- actix-http/src/encoding/encoder.rs | 42 ++-- actix-http/src/error.rs | 37 ++-- actix-http/src/h1/decoder.rs | 104 ++++----- actix-http/src/h1/dispatcher.rs | 226 +++++++++++-------- actix-http/src/h1/expect.rs | 19 +- actix-http/src/h1/payload.rs | 78 +++---- actix-http/src/h1/service.rs | 207 +++++++++++------- actix-http/src/h1/upgrade.rs | 18 +- actix-http/src/h1/utils.rs | 47 ++-- actix-http/src/h2/dispatcher.rs | 191 +++++++++------- actix-http/src/h2/mod.rs | 29 +-- actix-http/src/h2/service.rs | 129 ++++++----- actix-http/src/lib.rs | 9 +- actix-http/src/payload.rs | 25 ++- actix-http/src/response.rs | 38 ++-- actix-http/src/service.rs | 336 +++++++++++++++++++---------- actix-http/src/test.rs | 21 +- 28 files changed, 1176 insertions(+), 822 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 54e4b2374..ab812d1b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,19 +28,20 @@ path = "src/lib.rs" [workspace] members = [ - ".", - "awc", - "actix-http", - "actix-cors", - "actix-files", - "actix-framed", - "actix-session", - "actix-identity", - "actix-multipart", - "actix-web-actors", - "actix-web-codegen", - "test-server", +# ".", +# "awc", +# #"actix-http", +# "actix-cors", +# "actix-files", +# "actix-framed", +# "actix-session", +# "actix-identity", +# "actix-multipart", +# "actix-web-actors", +# "actix-web-codegen", +# "test-server", ] +exclude = ["actix-http"] [features] default = ["brotli", "flate2-zlib", "client", "fail"] @@ -122,12 +123,23 @@ opt-level = 3 codegen-units = 1 [patch.crates-io] -actix-web = { path = "." } -actix-http = { path = "actix-http" } -actix-http-test = { path = "test-server" } -actix-web-codegen = { path = "actix-web-codegen" } -actix-web-actors = { path = "actix-web-actors" } -actix-session = { path = "actix-session" } -actix-files = { path = "actix-files" } -actix-multipart = { path = "actix-multipart" } -awc = { path = "awc" } +# actix-web = { path = "." } +# actix-http = { path = "actix-http" } +# actix-http-test = { path = "test-server" } +# actix-web-codegen = { path = "actix-web-codegen" } +# actix-web-actors = { path = "actix-web-actors" } +# actix-session = { path = "actix-session" } +# actix-files = { path = "actix-files" } +# actix-multipart = { path = "actix-multipart" } +# awc = { path = "awc" } + +actix-codec = { path = "../actix-net/actix-codec" } +actix-connect = { path = "../actix-net/actix-connect" } +actix-ioframe = { path = "../actix-net/actix-ioframe" } +actix-rt = { path = "../actix-net/actix-rt" } +actix-server = { path = "../actix-net/actix-server" } +actix-server-config = { path = "../actix-net/actix-server-config" } +actix-service = { path = "../actix-net/actix-service" } +actix-testing = { path = "../actix-net/actix-testing" } +actix-threadpool = { path = "../actix-net/actix-threadpool" } +actix-utils = { path = "../actix-net/actix-utils" } diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index ee0ded597..1cc5e43a1 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "0.2.11" +version = "0.3.0-alpha.1" authors = ["Nikolay Kim "] description = "Actix http primitives" readme = "README.md" @@ -13,10 +13,11 @@ categories = ["network-programming", "asynchronous", "web-programming::websocket"] license = "MIT/Apache-2.0" edition = "2018" -workspace = ".." + +# workspace = ".." [package.metadata.docs.rs] -features = ["ssl", "fail", "brotli", "flate2-zlib", "secure-cookies"] +features = ["openssl", "fail", "brotli", "flate2-zlib", "secure-cookies"] [lib] name = "actix_http" @@ -26,10 +27,10 @@ path = "src/lib.rs" default = [] # openssl -ssl = ["openssl", "actix-connect/ssl"] +openssl = ["open-ssl", "actix-connect/openssl"] # rustls support -rust-tls = ["rustls", "webpki-roots", "actix-connect/rust-tls"] +rustls = ["rust-tls", "webpki-roots", "actix-connect/rustls"] # brotli encoding, requires c compiler brotli = ["brotli2"] @@ -47,23 +48,24 @@ fail = ["failure"] secure-cookies = ["ring"] [dependencies] -actix-service = "0.4.1" -actix-codec = "0.1.2" -actix-connect = "0.2.4" -actix-utils = "0.4.4" -actix-server-config = "0.1.2" -actix-threadpool = "0.1.1" +actix-service = "1.0.0-alpha.1" +actix-codec = "0.2.0-alpha.1" +actix-connect = "1.0.0-alpha.1" +actix-utils = "0.5.0-alpha.1" +actix-server-config = "0.3.0-alpha.1" +actix-threadpool = "0.2.0-alpha.1" base64 = "0.10" bitflags = "1.0" bytes = "0.4" copyless = "0.1.4" +chrono = "0.4.6" derive_more = "0.15.0" either = "1.5.2" encoding_rs = "0.8" -futures = "0.1.25" +futures = "0.3.1" hashbrown = "0.6.3" -h2 = "0.1.16" +h2 = "0.2.0-alpha.3" http = "0.1.17" httparse = "1.3" indexmap = "1.2" @@ -80,13 +82,16 @@ sha1 = "0.6" slab = "0.4" serde_urlencoded = "0.6.1" time = "0.1.42" -tokio-tcp = "0.1.3" -tokio-timer = "0.2.8" -tokio-current-thread = "0.1" -trust-dns-resolver = { version="0.11.1", default-features = false } + +tokio = "=0.2.0-alpha.6" +tokio-io = "=0.2.0-alpha.6" +tokio-net = "=0.2.0-alpha.6" +tokio-timer = "0.3.0-alpha.6" +tokio-executor = "=0.2.0-alpha.6" +trust-dns-resolver = { version="0.18.0-alpha.1", default-features = false } # for secure cookie -ring = { version = "0.14.6", optional = true } +ring = { version = "0.16.9", optional = true } # compression brotli2 = { version="0.3.2", optional = true } @@ -94,17 +99,25 @@ flate2 = { version="1.0.7", optional = true, default-features = false } # optional deps failure = { version = "0.1.5", optional = true } -openssl = { version="0.10", optional = true } -rustls = { version = "0.15.2", optional = true } -webpki-roots = { version = "0.16", optional = true } -chrono = "0.4.6" +open-ssl = { version="0.10", package="openssl", optional = true } +rust-tls = { version = "0.16.0", package="rustls", optional = true } +webpki-roots = { version = "0.18", optional = true } [dev-dependencies] -actix-rt = "0.2.2" -actix-server = { version = "0.6.0", features=["ssl", "rust-tls"] } -actix-connect = { version = "0.2.0", features=["ssl"] } -actix-http-test = { version = "0.2.4", features=["ssl"] } +actix-rt = "1.0.0-alpha.1" +actix-server = { version = "0.8.0-alpha.1", features=["openssl"] } +actix-connect = { version = "1.0.0-alpha.1", features=["openssl"] } +#actix-http-test = { version = "0.2.4", features=["ssl"] } env_logger = "0.6" serde_derive = "1.0" -openssl = { version="0.10" } -tokio-tcp = "0.1" +open-ssl = { version="0.10", package="openssl" } + +[patch.crates-io] +actix-codec = { path = "../../actix-net/actix-codec" } +actix-connect = { path = "../../actix-net/actix-connect" } +actix-rt = { path = "../../actix-net/actix-rt" } +actix-server = { path = "../../actix-net/actix-server" } +actix-server-config = { path = "../../actix-net/actix-server-config" } +actix-service = { path = "../../actix-net/actix-service" } +actix-threadpool = { path = "../../actix-net/actix-threadpool" } +actix-utils = { path = "../../actix-net/actix-utils" } diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs index b761738e1..7b86bfb14 100644 --- a/actix-http/src/body.rs +++ b/actix-http/src/body.rs @@ -1,8 +1,10 @@ use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; use std::{fmt, mem}; use bytes::{Bytes, BytesMut}; -use futures::{Async, Poll, Stream}; +use futures::Stream; use crate::error::Error; @@ -29,10 +31,10 @@ impl BodySize { } /// Type that provides this trait can be streamed to a peer. -pub trait MessageBody { +pub trait MessageBody: Unpin { fn size(&self) -> BodySize; - fn poll_next(&mut self) -> Poll, Error>; + fn poll_next(&mut self, cx: &mut Context) -> Poll>>; } impl MessageBody for () { @@ -40,8 +42,8 @@ impl MessageBody for () { BodySize::Empty } - fn poll_next(&mut self) -> Poll, Error> { - Ok(Async::Ready(None)) + fn poll_next(&mut self, _: &mut Context) -> Poll>> { + Poll::Ready(None) } } @@ -50,8 +52,8 @@ impl MessageBody for Box { self.as_ref().size() } - fn poll_next(&mut self) -> Poll, Error> { - self.as_mut().poll_next() + fn poll_next(&mut self, cx: &mut Context) -> Poll>> { + self.as_mut().poll_next(cx) } } @@ -93,20 +95,19 @@ impl MessageBody for ResponseBody { } } - fn poll_next(&mut self) -> Poll, Error> { + fn poll_next(&mut self, cx: &mut Context) -> Poll>> { match self { - ResponseBody::Body(ref mut body) => body.poll_next(), - ResponseBody::Other(ref mut body) => body.poll_next(), + ResponseBody::Body(ref mut body) => body.poll_next(cx), + ResponseBody::Other(ref mut body) => body.poll_next(cx), } } } impl Stream for ResponseBody { - type Item = Bytes; - type Error = Error; + type Item = Result; - fn poll(&mut self) -> Poll, Self::Error> { - self.poll_next() + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.get_mut().poll_next(cx) } } @@ -144,19 +145,19 @@ impl MessageBody for Body { } } - fn poll_next(&mut self) -> Poll, Error> { + fn poll_next(&mut self, cx: &mut Context) -> Poll>> { match self { - Body::None => Ok(Async::Ready(None)), - Body::Empty => Ok(Async::Ready(None)), + Body::None => Poll::Ready(None), + Body::Empty => Poll::Ready(None), Body::Bytes(ref mut bin) => { let len = bin.len(); if len == 0 { - Ok(Async::Ready(None)) + Poll::Ready(None) } else { - Ok(Async::Ready(Some(mem::replace(bin, Bytes::new())))) + Poll::Ready(Some(Ok(mem::replace(bin, Bytes::new())))) } } - Body::Message(ref mut body) => body.poll_next(), + Body::Message(ref mut body) => body.poll_next(cx), } } } @@ -242,7 +243,7 @@ impl From for Body { impl From> for Body where - S: Stream + 'static, + S: Stream> + Unpin + 'static, { fn from(s: SizedStream) -> Body { Body::from_message(s) @@ -251,8 +252,8 @@ where impl From> for Body where - S: Stream + 'static, - E: Into + 'static, + S: Stream> + Unpin + 'static, + E: Into + Unpin + 'static, { fn from(s: BodyStream) -> Body { Body::from_message(s) @@ -264,11 +265,11 @@ impl MessageBody for Bytes { BodySize::Sized(self.len()) } - fn poll_next(&mut self) -> Poll, Error> { + fn poll_next(&mut self, _: &mut Context) -> Poll>> { if self.is_empty() { - Ok(Async::Ready(None)) + Poll::Ready(None) } else { - Ok(Async::Ready(Some(mem::replace(self, Bytes::new())))) + Poll::Ready(Some(Ok(mem::replace(self, Bytes::new())))) } } } @@ -278,13 +279,11 @@ impl MessageBody for BytesMut { BodySize::Sized(self.len()) } - fn poll_next(&mut self) -> Poll, Error> { + fn poll_next(&mut self, _: &mut Context) -> Poll>> { if self.is_empty() { - Ok(Async::Ready(None)) + Poll::Ready(None) } else { - Ok(Async::Ready(Some( - mem::replace(self, BytesMut::new()).freeze(), - ))) + Poll::Ready(Some(Ok(mem::replace(self, BytesMut::new()).freeze()))) } } } @@ -294,11 +293,11 @@ impl MessageBody for &'static str { BodySize::Sized(self.len()) } - fn poll_next(&mut self) -> Poll, Error> { + fn poll_next(&mut self, _: &mut Context) -> Poll>> { if self.is_empty() { - Ok(Async::Ready(None)) + Poll::Ready(None) } else { - Ok(Async::Ready(Some(Bytes::from_static( + Poll::Ready(Some(Ok(Bytes::from_static( mem::replace(self, "").as_ref(), )))) } @@ -310,13 +309,11 @@ impl MessageBody for &'static [u8] { BodySize::Sized(self.len()) } - fn poll_next(&mut self) -> Poll, Error> { + fn poll_next(&mut self, _: &mut Context) -> Poll>> { if self.is_empty() { - Ok(Async::Ready(None)) + Poll::Ready(None) } else { - Ok(Async::Ready(Some(Bytes::from_static(mem::replace( - self, b"", - ))))) + Poll::Ready(Some(Ok(Bytes::from_static(mem::replace(self, b""))))) } } } @@ -326,14 +323,11 @@ impl MessageBody for Vec { BodySize::Sized(self.len()) } - fn poll_next(&mut self) -> Poll, Error> { + fn poll_next(&mut self, _: &mut Context) -> Poll>> { if self.is_empty() { - Ok(Async::Ready(None)) + Poll::Ready(None) } else { - Ok(Async::Ready(Some(Bytes::from(mem::replace( - self, - Vec::new(), - ))))) + Poll::Ready(Some(Ok(Bytes::from(mem::replace(self, Vec::new()))))) } } } @@ -343,11 +337,11 @@ impl MessageBody for String { BodySize::Sized(self.len()) } - fn poll_next(&mut self) -> Poll, Error> { + fn poll_next(&mut self, _: &mut Context) -> Poll>> { if self.is_empty() { - Ok(Async::Ready(None)) + Poll::Ready(None) } else { - Ok(Async::Ready(Some(Bytes::from( + Poll::Ready(Some(Ok(Bytes::from( mem::replace(self, String::new()).into_bytes(), )))) } @@ -363,7 +357,7 @@ pub struct BodyStream { impl BodyStream where - S: Stream, + S: Stream>, E: Into, { pub fn new(stream: S) -> Self { @@ -376,15 +370,17 @@ where impl MessageBody for BodyStream where - S: Stream, - E: Into, + S: Stream> + Unpin, + E: Into + Unpin, { fn size(&self) -> BodySize { BodySize::Stream } - fn poll_next(&mut self) -> Poll, Error> { - self.stream.poll().map_err(std::convert::Into::into) + fn poll_next(&mut self, cx: &mut Context) -> Poll>> { + Pin::new(&mut self.stream) + .poll_next(cx) + .map(|res| res.map(|res| res.map_err(std::convert::Into::into))) } } @@ -397,7 +393,7 @@ pub struct SizedStream { impl SizedStream where - S: Stream, + S: Stream>, { pub fn new(size: u64, stream: S) -> Self { SizedStream { size, stream } @@ -406,14 +402,14 @@ where impl MessageBody for SizedStream where - S: Stream, + S: Stream> + Unpin, { fn size(&self) -> BodySize { BodySize::Sized64(self.size) } - fn poll_next(&mut self) -> Poll, Error> { - self.stream.poll() + fn poll_next(&mut self, cx: &mut Context) -> Poll>> { + Pin::new(&mut self.stream).poll_next(cx) } } diff --git a/actix-http/src/builder.rs b/actix-http/src/builder.rs index cd23b7265..8997d720c 100644 --- a/actix-http/src/builder.rs +++ b/actix-http/src/builder.rs @@ -4,7 +4,7 @@ use std::rc::Rc; use actix_codec::Framed; use actix_server_config::ServerConfig as SrvConfig; -use actix_service::{IntoNewService, NewService, Service}; +use actix_service::{IntoServiceFactory, Service, ServiceFactory}; use crate::body::MessageBody; use crate::config::{KeepAlive, ServiceConfig}; @@ -32,9 +32,12 @@ pub struct HttpServiceBuilder> { impl HttpServiceBuilder> where - S: NewService, - S::Error: Into, + S: ServiceFactory, + S::Error: Into + Unpin + 'static, S::InitError: fmt::Debug, + S::Future: Unpin, + S::Service: Unpin, + ::Future: Unpin + 'static, { /// Create instance of `ServiceConfigBuilder` pub fn new() -> Self { @@ -52,19 +55,28 @@ where impl HttpServiceBuilder where - S: NewService, - S::Error: Into, + S: ServiceFactory, + S::Error: Into + Unpin + 'static, S::InitError: fmt::Debug, - X: NewService, + S::Future: Unpin, + S::Service: Unpin, + ::Future: Unpin + 'static, + X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, - U: NewService< + X::Future: Unpin, + X::Service: Unpin, + ::Future: Unpin + 'static, + U: ServiceFactory< Config = SrvConfig, Request = (Request, Framed), Response = (), >, U::Error: fmt::Display, U::InitError: fmt::Debug, + U::Future: Unpin, + U::Service: Unpin, + ::Future: Unpin + 'static, { /// Set server keep-alive setting. /// @@ -108,16 +120,19 @@ where /// request will be forwarded to main service. pub fn expect(self, expect: F) -> HttpServiceBuilder where - F: IntoNewService, - X1: NewService, + F: IntoServiceFactory, + X1: ServiceFactory, X1::Error: Into, X1::InitError: fmt::Debug, + X1::Future: Unpin, + X1::Service: Unpin, + ::Future: Unpin + 'static, { HttpServiceBuilder { keep_alive: self.keep_alive, client_timeout: self.client_timeout, client_disconnect: self.client_disconnect, - expect: expect.into_new_service(), + expect: expect.into_factory(), upgrade: self.upgrade, on_connect: self.on_connect, _t: PhantomData, @@ -130,21 +145,24 @@ where /// and this service get called with original request and framed object. pub fn upgrade(self, upgrade: F) -> HttpServiceBuilder where - F: IntoNewService, - U1: NewService< + F: IntoServiceFactory, + U1: ServiceFactory< Config = SrvConfig, Request = (Request, Framed), Response = (), >, U1::Error: fmt::Display, U1::InitError: fmt::Debug, + U1::Future: Unpin, + U1::Service: Unpin, + ::Future: Unpin + 'static, { HttpServiceBuilder { keep_alive: self.keep_alive, client_timeout: self.client_timeout, client_disconnect: self.client_disconnect, expect: self.expect, - upgrade: Some(upgrade.into_new_service()), + upgrade: Some(upgrade.into_factory()), on_connect: self.on_connect, _t: PhantomData, } @@ -167,17 +185,21 @@ where pub fn h1(self, service: F) -> H1Service where B: MessageBody + 'static, - F: IntoNewService, - S::Error: Into, + F: IntoServiceFactory, + S::Future: Unpin, + S::Error: Into + Unpin + 'static, S::InitError: fmt::Debug, - S::Response: Into>, + S::Response: Into> + Unpin + 'static, + S::Service: Unpin, + ::Future: Unpin + 'static, + P: Unpin, { let cfg = ServiceConfig::new( self.keep_alive, self.client_timeout, self.client_disconnect, ); - H1Service::with_config(cfg, service.into_new_service()) + H1Service::with_config(cfg, service.into_factory()) .expect(self.expect) .upgrade(self.upgrade) .on_connect(self.on_connect) @@ -187,37 +209,42 @@ where pub fn h2(self, service: F) -> H2Service where B: MessageBody + 'static, - F: IntoNewService, - S::Error: Into, + F: IntoServiceFactory, + S::Error: Into + Unpin + 'static, S::InitError: fmt::Debug, - S::Response: Into>, - ::Future: 'static, + S::Response: Into> + Unpin + 'static, + S::Future: Unpin, + S::Service: Unpin, + ::Future: Unpin + 'static, + P: Unpin, { let cfg = ServiceConfig::new( self.keep_alive, self.client_timeout, self.client_disconnect, ); - H2Service::with_config(cfg, service.into_new_service()) - .on_connect(self.on_connect) + H2Service::with_config(cfg, service.into_factory()).on_connect(self.on_connect) } /// Finish service configuration and create `HttpService` instance. pub fn finish(self, service: F) -> HttpService where B: MessageBody + 'static, - F: IntoNewService, - S::Error: Into, + F: IntoServiceFactory, + S::Error: Into + Unpin + 'static, S::InitError: fmt::Debug, - S::Response: Into>, - ::Future: 'static, + S::Response: Into> + Unpin + 'static, + S::Future: Unpin, + S::Service: Unpin, + ::Future: Unpin + 'static, + P: Unpin, { let cfg = ServiceConfig::new( self.keep_alive, self.client_timeout, self.client_disconnect, ); - HttpService::with_config(cfg, service.into_new_service()) + HttpService::with_config(cfg, service.into_factory()) .expect(self.expect) .upgrade(self.upgrade) .on_connect(self.on_connect) diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index 98e8618c3..4ae28ba68 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -1,15 +1,18 @@ use std::fmt; +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; use std::time::Duration; use actix_codec::{AsyncRead, AsyncWrite}; use actix_connect::{ default_connector, Connect as TcpConnect, Connection as TcpConnection, }; -use actix_service::{apply_fn, Service, ServiceExt}; +use actix_service::{apply_fn, Service}; use actix_utils::timeout::{TimeoutError, TimeoutService}; use http::Uri; -use tokio_tcp::TcpStream; +use tokio_net::tcp::TcpStream; use super::connection::Connection; use super::error::ConnectError; @@ -212,7 +215,7 @@ where pub fn finish( self, ) -> impl Service - + Clone { + + Clone { #[cfg(not(any(feature = "ssl", feature = "rust-tls")))] { let connector = TimeoutService::new( diff --git a/actix-http/src/client/h1proto.rs b/actix-http/src/client/h1proto.rs index b078c6a67..14984253b 100644 --- a/actix-http/src/client/h1proto.rs +++ b/actix-http/src/client/h1proto.rs @@ -1,10 +1,13 @@ +use std::future::Future; use std::io::Write; +use std::pin::Pin; +use std::task::{Context, Poll}; use std::{io, time}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; use bytes::{BufMut, Bytes, BytesMut}; use futures::future::{ok, Either}; -use futures::{Async, Future, Poll, Sink, Stream}; +use futures::{Sink, Stream}; use crate::error::PayloadError; use crate::h1; diff --git a/actix-http/src/client/h2proto.rs b/actix-http/src/client/h2proto.rs index 5744a1547..50d74fe1b 100644 --- a/actix-http/src/client/h2proto.rs +++ b/actix-http/src/client/h2proto.rs @@ -1,9 +1,11 @@ +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; use std::time; use actix_codec::{AsyncRead, AsyncWrite}; use bytes::Bytes; use futures::future::{err, Either}; -use futures::{Async, Future, Poll}; use h2::{client::SendRequest, SendStream}; use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, TRANSFER_ENCODING}; use http::{request::Request, HttpTryFrom, Method, Version}; diff --git a/actix-http/src/client/pool.rs b/actix-http/src/client/pool.rs index a3522ff8a..4d02e0a13 100644 --- a/actix-http/src/client/pool.rs +++ b/actix-http/src/client/pool.rs @@ -1,22 +1,24 @@ use std::cell::RefCell; use std::collections::VecDeque; +use std::future::Future; use std::io; +use std::pin::Pin; use std::rc::Rc; +use std::task::{Context, Poll}; use std::time::{Duration, Instant}; use actix_codec::{AsyncRead, AsyncWrite}; use actix_service::Service; +use actix_utils::oneshot; +use actix_utils::task::LocalWaker; use bytes::Bytes; use futures::future::{err, ok, Either, FutureResult}; -use futures::task::AtomicTask; -use futures::unsync::oneshot; -use futures::{Async, Future, Poll}; use h2::client::{handshake, Handshake}; use hashbrown::HashMap; use http::uri::Authority; use indexmap::IndexSet; use slab::Slab; -use tokio_timer::{sleep, Delay}; +use tokio_timer::{delay_for, Delay}; use super::connection::{ConnectionType, IoConnection}; use super::error::ConnectError; @@ -140,7 +142,7 @@ where // start support future if !support { self.1.as_ref().borrow_mut().task = Some(AtomicTask::new()); - tokio_current_thread::spawn(ConnectorPoolSupport { + tokio_executor::current_thread::spawn(ConnectorPoolSupport { connector: self.0.clone(), inner: self.1.clone(), }) @@ -255,7 +257,7 @@ where if let Some(ref mut h2) = self.h2 { return match h2.poll() { Ok(Async::Ready((snd, connection))) => { - tokio_current_thread::spawn(connection.map_err(|_| ())); + tokio_executor::current_thread::spawn(connection.map_err(|_| ())); Ok(Async::Ready(IoConnection::new( ConnectionType::H2(snd), Instant::now(), @@ -373,7 +375,7 @@ where { if let Some(timeout) = self.disconnect_timeout { if let ConnectionType::H1(io) = conn.io { - tokio_current_thread::spawn(CloseConnection::new( + tokio_executor::current_thread::spawn(CloseConnection::new( io, timeout, )) } @@ -387,7 +389,7 @@ where Ok(n) if n > 0 => { if let Some(timeout) = self.disconnect_timeout { if let ConnectionType::H1(io) = io { - tokio_current_thread::spawn( + tokio_executor::current_thread::spawn( CloseConnection::new(io, timeout), ) } @@ -421,7 +423,7 @@ where self.acquired -= 1; if let Some(timeout) = self.disconnect_timeout { if let ConnectionType::H1(io) = io { - tokio_current_thread::spawn(CloseConnection::new(io, timeout)) + tokio_executor::current_thread::spawn(CloseConnection::new(io, timeout)) } } self.check_availibility(); @@ -448,7 +450,7 @@ where fn new(io: T, timeout: Duration) -> Self { CloseConnection { io, - timeout: sleep(timeout), + timeout: delay_for(timeout), } } } @@ -558,7 +560,7 @@ where inner: Rc>>, fut: F, ) { - tokio_current_thread::spawn(OpenWaitingConnection { + tokio_executor::current_thread::spawn(OpenWaitingConnection { key, fut, h2: None, @@ -593,7 +595,7 @@ where if let Some(ref mut h2) = self.h2 { return match h2.poll() { Ok(Async::Ready((snd, connection))) => { - tokio_current_thread::spawn(connection.map_err(|_| ())); + tokio_executor::current_thread::spawn(connection.map_err(|_| ())); let rx = self.rx.take().unwrap(); let _ = rx.send(Ok(IoConnection::new( ConnectionType::H2(snd), diff --git a/actix-http/src/cloneable.rs b/actix-http/src/cloneable.rs index ffc1d0611..18869c66d 100644 --- a/actix-http/src/cloneable.rs +++ b/actix-http/src/cloneable.rs @@ -1,8 +1,8 @@ use std::cell::UnsafeCell; use std::rc::Rc; +use std::task::{Context, Poll}; use actix_service::Service; -use futures::Poll; #[doc(hidden)] /// Service that allows to turn non-clone service to a service with `Clone` impl @@ -32,8 +32,8 @@ where type Error = T::Error; type Future = T::Future; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - unsafe { &mut *self.0.as_ref().get() }.poll_ready() + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + unsafe { &mut *self.0.as_ref().get() }.poll_ready(cx) } fn call(&mut self, req: T::Request) -> Self::Future { diff --git a/actix-http/src/config.rs b/actix-http/src/config.rs index bdfecef30..a2dab8f04 100644 --- a/actix-http/src/config.rs +++ b/actix-http/src/config.rs @@ -5,9 +5,9 @@ use std::rc::Rc; use std::time::{Duration, Instant}; use bytes::BytesMut; -use futures::{future, Future}; +use futures::{future, Future, FutureExt}; use time; -use tokio_timer::{sleep, Delay}; +use tokio_timer::{delay, delay_for, Delay}; // "Sun, 06 Nov 1994 08:49:37 GMT".len() const DATE_VALUE_LENGTH: usize = 29; @@ -104,10 +104,10 @@ impl ServiceConfig { #[inline] /// Client timeout for first request. pub fn client_timer(&self) -> Option { - let delay = self.0.client_timeout; - if delay != 0 { - Some(Delay::new( - self.0.timer.now() + Duration::from_millis(delay), + let delay_time = self.0.client_timeout; + if delay_time != 0 { + Some(delay( + self.0.timer.now() + Duration::from_millis(delay_time), )) } else { None @@ -138,7 +138,7 @@ impl ServiceConfig { /// Return keep-alive timer delay is configured. pub fn keep_alive_timer(&self) -> Option { if let Some(ka) = self.0.keep_alive { - Some(Delay::new(self.0.timer.now() + ka)) + Some(delay(self.0.timer.now() + ka)) } else { None } @@ -242,12 +242,12 @@ impl DateService { // periodic date update let s = self.clone(); - tokio_current_thread::spawn(sleep(Duration::from_millis(500)).then( - move |_| { + tokio_executor::current_thread::spawn( + delay_for(Duration::from_millis(500)).then(move |_| { s.0.reset(); - future::ok(()) - }, - )); + future::ready(()) + }), + ); } } diff --git a/actix-http/src/encoding/decoder.rs b/actix-http/src/encoding/decoder.rs index 4b56a1b62..1e51e8b56 100644 --- a/actix-http/src/encoding/decoder.rs +++ b/actix-http/src/encoding/decoder.rs @@ -1,4 +1,7 @@ +use std::future::Future; use std::io::{self, Write}; +use std::pin::Pin; +use std::task::{Context, Poll}; use actix_threadpool::{run, CpuFuture}; #[cfg(feature = "brotli")] @@ -6,7 +9,7 @@ use brotli2::write::BrotliDecoder; use bytes::Bytes; #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] use flate2::write::{GzDecoder, ZlibDecoder}; -use futures::{try_ready, Async, Future, Poll, Stream}; +use futures::{ready, Stream}; use super::Writer; use crate::error::PayloadError; @@ -18,12 +21,12 @@ pub struct Decoder { decoder: Option, stream: S, eof: bool, - fut: Option, ContentDecoder), io::Error>>, + fut: Option, ContentDecoder), io::Error>>>, } impl Decoder where - S: Stream, + S: Stream>, { /// Construct a decoder. #[inline] @@ -71,34 +74,41 @@ where impl Stream for Decoder where - S: Stream, + S: Stream> + Unpin, { - type Item = Bytes; - type Error = PayloadError; + type Item = Result; - fn poll(&mut self) -> Poll, Self::Error> { + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll> { loop { if let Some(ref mut fut) = self.fut { - let (chunk, decoder) = try_ready!(fut.poll()); + let (chunk, decoder) = match ready!(Pin::new(fut).poll(cx)) { + Ok(Ok(item)) => item, + Ok(Err(e)) => return Poll::Ready(Some(Err(e.into()))), + Err(e) => return Poll::Ready(Some(Err(e.into()))), + }; self.decoder = Some(decoder); self.fut.take(); if let Some(chunk) = chunk { - return Ok(Async::Ready(Some(chunk))); + return Poll::Ready(Some(Ok(chunk))); } } if self.eof { - return Ok(Async::Ready(None)); + return Poll::Ready(None); } - match self.stream.poll()? { - Async::Ready(Some(chunk)) => { + match Pin::new(&mut self.stream).poll_next(cx) { + Poll::Ready(Some(Err(err))) => return Poll::Ready(Some(Err(err))), + Poll::Ready(Some(Ok(chunk))) => { if let Some(mut decoder) = self.decoder.take() { if chunk.len() < INPLACE { let chunk = decoder.feed_data(chunk)?; self.decoder = Some(decoder); if let Some(chunk) = chunk { - return Ok(Async::Ready(Some(chunk))); + return Poll::Ready(Some(Ok(chunk))); } } else { self.fut = Some(run(move || { @@ -108,21 +118,25 @@ where } continue; } else { - return Ok(Async::Ready(Some(chunk))); + return Poll::Ready(Some(Ok(chunk))); } } - Async::Ready(None) => { + Poll::Ready(None) => { self.eof = true; return if let Some(mut decoder) = self.decoder.take() { - Ok(Async::Ready(decoder.feed_eof()?)) + match decoder.feed_eof() { + Ok(Some(res)) => Poll::Ready(Some(Ok(res))), + Ok(None) => Poll::Ready(None), + Err(err) => Poll::Ready(Some(Err(err.into()))), + } } else { - Ok(Async::Ready(None)) + Poll::Ready(None) }; } - Async::NotReady => break, + Poll::Pending => break, } } - Ok(Async::NotReady) + Poll::Pending } } diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index 58d8a2d9e..295d99a2a 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -1,5 +1,8 @@ //! Stream encoder +use std::future::Future; use std::io::{self, Write}; +use std::pin::Pin; +use std::task::{Context, Poll}; use actix_threadpool::{run, CpuFuture}; #[cfg(feature = "brotli")] @@ -7,7 +10,6 @@ use brotli2::write::BrotliEncoder; use bytes::Bytes; #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] use flate2::write::{GzEncoder, ZlibEncoder}; -use futures::{Async, Future, Poll}; use crate::body::{Body, BodySize, MessageBody, ResponseBody}; use crate::http::header::{ContentEncoding, CONTENT_ENCODING}; @@ -22,7 +24,7 @@ pub struct Encoder { eof: bool, body: EncoderBody, encoder: Option, - fut: Option>, + fut: Option>>, } impl Encoder { @@ -94,43 +96,46 @@ impl MessageBody for Encoder { } } - fn poll_next(&mut self) -> Poll, Error> { + fn poll_next(&mut self, cx: &mut Context) -> Poll>> { loop { if self.eof { - return Ok(Async::Ready(None)); + return Poll::Ready(None); } if let Some(ref mut fut) = self.fut { - let mut encoder = futures::try_ready!(fut.poll()); + let mut encoder = match futures::ready!(Pin::new(fut).poll(cx)) { + Ok(Ok(item)) => item, + Ok(Err(e)) => return Poll::Ready(Some(Err(e.into()))), + Err(e) => return Poll::Ready(Some(Err(e.into()))), + }; let chunk = encoder.take(); self.encoder = Some(encoder); self.fut.take(); if !chunk.is_empty() { - return Ok(Async::Ready(Some(chunk))); + return Poll::Ready(Some(Ok(chunk))); } } let result = match self.body { EncoderBody::Bytes(ref mut b) => { if b.is_empty() { - Async::Ready(None) + Poll::Ready(None) } else { - Async::Ready(Some(std::mem::replace(b, Bytes::new()))) + Poll::Ready(Some(Ok(std::mem::replace(b, Bytes::new())))) } } - EncoderBody::Stream(ref mut b) => b.poll_next()?, - EncoderBody::BoxedStream(ref mut b) => b.poll_next()?, + EncoderBody::Stream(ref mut b) => b.poll_next(cx), + EncoderBody::BoxedStream(ref mut b) => b.poll_next(cx), }; match result { - Async::NotReady => return Ok(Async::NotReady), - Async::Ready(Some(chunk)) => { + Poll::Ready(Some(Ok(chunk))) => { if let Some(mut encoder) = self.encoder.take() { if chunk.len() < INPLACE { encoder.write(&chunk)?; let chunk = encoder.take(); self.encoder = Some(encoder); if !chunk.is_empty() { - return Ok(Async::Ready(Some(chunk))); + return Poll::Ready(Some(Ok(chunk))); } } else { self.fut = Some(run(move || { @@ -139,22 +144,23 @@ impl MessageBody for Encoder { })); } } else { - return Ok(Async::Ready(Some(chunk))); + return Poll::Ready(Some(Ok(chunk))); } } - Async::Ready(None) => { + Poll::Ready(None) => { if let Some(encoder) = self.encoder.take() { let chunk = encoder.finish()?; if chunk.is_empty() { - return Ok(Async::Ready(None)); + return Poll::Ready(None); } else { self.eof = true; - return Ok(Async::Ready(Some(chunk))); + return Poll::Ready(Some(Ok(chunk))); } } else { - return Ok(Async::Ready(None)); + return Poll::Ready(None); } } + val => return val, } } } diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index cd9613d21..82027dbe3 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -6,11 +6,10 @@ use std::str::Utf8Error; use std::string::FromUtf8Error; use std::{fmt, io, result}; -pub use actix_threadpool::BlockingError; use actix_utils::timeout::TimeoutError; use bytes::BytesMut; use derive_more::{Display, From}; -use futures::Canceled; +use futures::channel::oneshot::Canceled; use http::uri::InvalidUri; use http::{header, Error as HttpError, StatusCode}; use httparse; @@ -197,8 +196,8 @@ impl ResponseError for DeError { } } -/// `InternalServerError` for `BlockingError` -impl ResponseError for BlockingError {} +/// `InternalServerError` for `Canceled` +impl ResponseError for Canceled {} /// Return `BAD_REQUEST` for `Utf8Error` impl ResponseError for Utf8Error { @@ -236,9 +235,6 @@ impl ResponseError for header::InvalidHeaderValueBytes { } } -/// `InternalServerError` for `futures::Canceled` -impl ResponseError for Canceled {} - /// A set of errors that can occur during parsing HTTP streams #[derive(Debug, Display)] pub enum ParseError { @@ -365,15 +361,12 @@ impl From for PayloadError { } } -impl From> for PayloadError { - fn from(err: BlockingError) -> Self { - match err { - BlockingError::Error(e) => PayloadError::Io(e), - BlockingError::Canceled => PayloadError::Io(io::Error::new( - io::ErrorKind::Other, - "Thread pool is gone", - )), - } +impl From for PayloadError { + fn from(_: Canceled) -> Self { + PayloadError::Io(io::Error::new( + io::ErrorKind::Other, + "Operation is canceled", + )) } } @@ -390,12 +383,12 @@ impl ResponseError for PayloadError { } } -/// Return `BadRequest` for `cookie::ParseError` -impl ResponseError for crate::cookie::ParseError { - fn error_response(&self) -> Response { - Response::new(StatusCode::BAD_REQUEST) - } -} +// /// Return `BadRequest` for `cookie::ParseError` +// impl ResponseError for crate::cookie::ParseError { +// fn error_response(&self) -> Response { +// Response::new(StatusCode::BAD_REQUEST) +// } +// } #[derive(Debug, Display, From)] /// A set of errors that can occur during dispatching http requests diff --git a/actix-http/src/h1/decoder.rs b/actix-http/src/h1/decoder.rs index ce113a145..272270ca1 100644 --- a/actix-http/src/h1/decoder.rs +++ b/actix-http/src/h1/decoder.rs @@ -1,10 +1,12 @@ +use std::future::Future; use std::io; use std::marker::PhantomData; use std::mem::MaybeUninit; +use std::pin::Pin; +use std::task::{Context, Poll}; use actix_codec::Decoder; use bytes::{Bytes, BytesMut}; -use futures::{Async, Poll}; use http::header::{HeaderName, HeaderValue}; use http::{header, HttpTryFrom, Method, StatusCode, Uri, Version}; use httparse; @@ -442,9 +444,10 @@ impl Decoder for PayloadDecoder { loop { let mut buf = None; // advances the chunked state - *state = match state.step(src, size, &mut buf)? { - Async::NotReady => return Ok(None), - Async::Ready(state) => state, + *state = match state.step(src, size, &mut buf) { + Poll::Pending => return Ok(None), + Poll::Ready(Ok(state)) => state, + Poll::Ready(Err(e)) => return Err(e), }; if *state == ChunkedState::End { trace!("End of chunked stream"); @@ -476,7 +479,7 @@ macro_rules! byte ( $rdr.split_to(1); b } else { - return Ok(Async::NotReady) + return Poll::Pending } }) ); @@ -487,7 +490,7 @@ impl ChunkedState { body: &mut BytesMut, size: &mut u64, buf: &mut Option, - ) -> Poll { + ) -> Poll> { use self::ChunkedState::*; match *self { Size => ChunkedState::read_size(body, size), @@ -499,10 +502,14 @@ impl ChunkedState { BodyLf => ChunkedState::read_body_lf(body), EndCr => ChunkedState::read_end_cr(body), EndLf => ChunkedState::read_end_lf(body), - End => Ok(Async::Ready(ChunkedState::End)), + End => Poll::Ready(Ok(ChunkedState::End)), } } - fn read_size(rdr: &mut BytesMut, size: &mut u64) -> Poll { + + fn read_size( + rdr: &mut BytesMut, + size: &mut u64, + ) -> Poll> { let radix = 16; match byte!(rdr) { b @ b'0'..=b'9' => { @@ -517,48 +524,49 @@ impl ChunkedState { *size *= radix; *size += u64::from(b + 10 - b'A'); } - b'\t' | b' ' => return Ok(Async::Ready(ChunkedState::SizeLws)), - b';' => return Ok(Async::Ready(ChunkedState::Extension)), - b'\r' => return Ok(Async::Ready(ChunkedState::SizeLf)), + b'\t' | b' ' => return Poll::Ready(Ok(ChunkedState::SizeLws)), + b';' => return Poll::Ready(Ok(ChunkedState::Extension)), + b'\r' => return Poll::Ready(Ok(ChunkedState::SizeLf)), _ => { - return Err(io::Error::new( + return Poll::Ready(Err(io::Error::new( io::ErrorKind::InvalidInput, "Invalid chunk size line: Invalid Size", - )); + ))); } } - Ok(Async::Ready(ChunkedState::Size)) + Poll::Ready(Ok(ChunkedState::Size)) } - fn read_size_lws(rdr: &mut BytesMut) -> Poll { + + fn read_size_lws(rdr: &mut BytesMut) -> Poll> { trace!("read_size_lws"); match byte!(rdr) { // LWS can follow the chunk size, but no more digits can come - b'\t' | b' ' => Ok(Async::Ready(ChunkedState::SizeLws)), - b';' => Ok(Async::Ready(ChunkedState::Extension)), - b'\r' => Ok(Async::Ready(ChunkedState::SizeLf)), - _ => Err(io::Error::new( + b'\t' | b' ' => Poll::Ready(Ok(ChunkedState::SizeLws)), + b';' => Poll::Ready(Ok(ChunkedState::Extension)), + b'\r' => Poll::Ready(Ok(ChunkedState::SizeLf)), + _ => Poll::Ready(Err(io::Error::new( io::ErrorKind::InvalidInput, "Invalid chunk size linear white space", - )), + ))), } } - fn read_extension(rdr: &mut BytesMut) -> Poll { + fn read_extension(rdr: &mut BytesMut) -> Poll> { match byte!(rdr) { - b'\r' => Ok(Async::Ready(ChunkedState::SizeLf)), - _ => Ok(Async::Ready(ChunkedState::Extension)), // no supported extensions + b'\r' => Poll::Ready(Ok(ChunkedState::SizeLf)), + _ => Poll::Ready(Ok(ChunkedState::Extension)), // no supported extensions } } fn read_size_lf( rdr: &mut BytesMut, size: &mut u64, - ) -> Poll { + ) -> Poll> { match byte!(rdr) { - b'\n' if *size > 0 => Ok(Async::Ready(ChunkedState::Body)), - b'\n' if *size == 0 => Ok(Async::Ready(ChunkedState::EndCr)), - _ => Err(io::Error::new( + b'\n' if *size > 0 => Poll::Ready(Ok(ChunkedState::Body)), + b'\n' if *size == 0 => Poll::Ready(Ok(ChunkedState::EndCr)), + _ => Poll::Ready(Err(io::Error::new( io::ErrorKind::InvalidInput, "Invalid chunk size LF", - )), + ))), } } @@ -566,12 +574,12 @@ impl ChunkedState { rdr: &mut BytesMut, rem: &mut u64, buf: &mut Option, - ) -> Poll { + ) -> Poll> { trace!("Chunked read, remaining={:?}", rem); let len = rdr.len() as u64; if len == 0 { - Ok(Async::Ready(ChunkedState::Body)) + Poll::Ready(Ok(ChunkedState::Body)) } else { let slice; if *rem > len { @@ -583,47 +591,47 @@ impl ChunkedState { } *buf = Some(slice); if *rem > 0 { - Ok(Async::Ready(ChunkedState::Body)) + Poll::Ready(Ok(ChunkedState::Body)) } else { - Ok(Async::Ready(ChunkedState::BodyCr)) + Poll::Ready(Ok(ChunkedState::BodyCr)) } } } - fn read_body_cr(rdr: &mut BytesMut) -> Poll { + fn read_body_cr(rdr: &mut BytesMut) -> Poll> { match byte!(rdr) { - b'\r' => Ok(Async::Ready(ChunkedState::BodyLf)), - _ => Err(io::Error::new( + b'\r' => Poll::Ready(Ok(ChunkedState::BodyLf)), + _ => Poll::Ready(Err(io::Error::new( io::ErrorKind::InvalidInput, "Invalid chunk body CR", - )), + ))), } } - fn read_body_lf(rdr: &mut BytesMut) -> Poll { + fn read_body_lf(rdr: &mut BytesMut) -> Poll> { match byte!(rdr) { - b'\n' => Ok(Async::Ready(ChunkedState::Size)), - _ => Err(io::Error::new( + b'\n' => Poll::Ready(Ok(ChunkedState::Size)), + _ => Poll::Ready(Err(io::Error::new( io::ErrorKind::InvalidInput, "Invalid chunk body LF", - )), + ))), } } - fn read_end_cr(rdr: &mut BytesMut) -> Poll { + fn read_end_cr(rdr: &mut BytesMut) -> Poll> { match byte!(rdr) { - b'\r' => Ok(Async::Ready(ChunkedState::EndLf)), - _ => Err(io::Error::new( + b'\r' => Poll::Ready(Ok(ChunkedState::EndLf)), + _ => Poll::Ready(Err(io::Error::new( io::ErrorKind::InvalidInput, "Invalid chunk end CR", - )), + ))), } } - fn read_end_lf(rdr: &mut BytesMut) -> Poll { + fn read_end_lf(rdr: &mut BytesMut) -> Poll> { match byte!(rdr) { - b'\n' => Ok(Async::Ready(ChunkedState::End)), - _ => Err(io::Error::new( + b'\n' => Poll::Ready(Ok(ChunkedState::End)), + _ => Poll::Ready(Err(io::Error::new( io::ErrorKind::InvalidInput, "Invalid chunk end LF", - )), + ))), } } } diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index c82eb4ac8..16e36447d 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -1,15 +1,17 @@ use std::collections::VecDeque; +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; use std::time::Instant; -use std::{fmt, io, net}; +use std::{fmt, io, io::Write, net}; -use actix_codec::{Decoder, Encoder, Framed, FramedParts}; +use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed, FramedParts}; use actix_server_config::IoStream; use actix_service::Service; use bitflags::bitflags; use bytes::{BufMut, BytesMut}; -use futures::{Async, Future, Poll}; use log::{error, trace}; -use tokio_timer::Delay; +use tokio_timer::{delay, Delay}; use crate::body::{Body, BodySize, MessageBody, ResponseBody}; use crate::cloneable::CloneableService; @@ -46,11 +48,14 @@ pub struct Dispatcher where S: Service, S::Error: Into, + S::Future: Unpin, B: MessageBody, X: Service, X::Error: Into, + X::Future: Unpin, U: Service), Response = ()>, U::Error: fmt::Display, + U::Future: Unpin, { inner: DispatcherState, } @@ -59,11 +64,14 @@ enum DispatcherState where S: Service, S::Error: Into, + S::Future: Unpin, B: MessageBody, X: Service, X::Error: Into, + X::Future: Unpin, U: Service), Response = ()>, U::Error: fmt::Display, + U::Future: Unpin, { Normal(InnerDispatcher), Upgrade(U::Future), @@ -74,11 +82,14 @@ struct InnerDispatcher where S: Service, S::Error: Into, + S::Future: Unpin, B: MessageBody, X: Service, X::Error: Into, + X::Future: Unpin, U: Service), Response = ()>, U::Error: fmt::Display, + U::Future: Unpin, { service: CloneableService, expect: CloneableService, @@ -170,11 +181,14 @@ where S: Service, S::Error: Into, S::Response: Into>, + S::Future: Unpin, B: MessageBody, X: Service, X::Error: Into, + X::Future: Unpin, U: Service), Response = ()>, U::Error: fmt::Display, + U::Future: Unpin, { /// Create http/1 dispatcher. pub(crate) fn new( @@ -255,20 +269,23 @@ where S: Service, S::Error: Into, S::Response: Into>, + S::Future: Unpin, B: MessageBody, X: Service, X::Error: Into, + X::Future: Unpin, U: Service), Response = ()>, U::Error: fmt::Display, + U::Future: Unpin, { - fn can_read(&self) -> bool { + fn can_read(&self, cx: &mut Context) -> bool { if self .flags .intersects(Flags::READ_DISCONNECT | Flags::UPGRADE) { false } else if let Some(ref info) = self.payload { - info.need_read() == PayloadStatus::Read + info.need_read(cx) == PayloadStatus::Read } else { true } @@ -287,7 +304,7 @@ where /// /// true - got whouldblock /// false - didnt get whouldblock - fn poll_flush(&mut self) -> Result { + fn poll_flush(&mut self, cx: &mut Context) -> Result { if self.write_buf.is_empty() { return Ok(false); } @@ -295,23 +312,23 @@ where let len = self.write_buf.len(); let mut written = 0; while written < len { - match self.io.write(&self.write_buf[written..]) { - Ok(0) => { + match Pin::new(&mut self.io).poll_write(cx, &self.write_buf[written..]) { + Poll::Ready(Ok(0)) => { return Err(DispatchError::Io(io::Error::new( io::ErrorKind::WriteZero, "", ))); } - Ok(n) => { + Poll::Ready(Ok(n)) => { written += n; } - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { + Poll::Pending => { if written > 0 { let _ = self.write_buf.split_to(written); } return Ok(true); } - Err(err) => return Err(DispatchError::Io(err)), + Poll::Ready(Err(err)) => return Err(DispatchError::Io(err)), } } if written > 0 { @@ -350,12 +367,15 @@ where .extend_from_slice(b"HTTP/1.1 100 Continue\r\n\r\n"); } - fn poll_response(&mut self) -> Result { + fn poll_response( + &mut self, + cx: &mut Context, + ) -> Result { loop { let state = match self.state { State::None => match self.messages.pop_front() { Some(DispatcherMessage::Item(req)) => { - Some(self.handle_request(req)?) + Some(self.handle_request(req, cx)?) } Some(DispatcherMessage::Error(res)) => { Some(self.send_response(res, ResponseBody::Other(Body::Empty))?) @@ -365,54 +385,54 @@ where } None => None, }, - State::ExpectCall(ref mut fut) => match fut.poll() { - Ok(Async::Ready(req)) => { + State::ExpectCall(ref mut fut) => match Pin::new(fut).poll(cx) { + Poll::Ready(Ok(req)) => { self.send_continue(); self.state = State::ServiceCall(self.service.call(req)); continue; } - Ok(Async::NotReady) => None, - Err(e) => { + Poll::Ready(Err(e)) => { let res: Response = e.into().into(); let (res, body) = res.replace_body(()); Some(self.send_response(res, body.into_body())?) } + Poll::Pending => None, }, - State::ServiceCall(ref mut fut) => match fut.poll() { - Ok(Async::Ready(res)) => { + State::ServiceCall(ref mut fut) => match Pin::new(fut).poll(cx) { + Poll::Ready(Ok(res)) => { let (res, body) = res.into().replace_body(()); self.state = self.send_response(res, body)?; continue; } - Ok(Async::NotReady) => None, - Err(e) => { + Poll::Ready(Err(e)) => { let res: Response = e.into().into(); let (res, body) = res.replace_body(()); Some(self.send_response(res, body.into_body())?) } + Poll::Pending => None, }, State::SendPayload(ref mut stream) => { loop { if self.write_buf.len() < HW_BUFFER_SIZE { - match stream - .poll_next() - .map_err(|_| DispatchError::Unknown)? - { - Async::Ready(Some(item)) => { + match stream.poll_next(cx) { + Poll::Ready(Some(Ok(item))) => { self.codec.encode( Message::Chunk(Some(item)), &mut self.write_buf, )?; continue; } - Async::Ready(None) => { + Poll::Ready(None) => { self.codec.encode( Message::Chunk(None), &mut self.write_buf, )?; self.state = State::None; } - Async::NotReady => return Ok(PollResponse::DoNothing), + Poll::Ready(Some(Err(_))) => { + return Err(DispatchError::Unknown) + } + Poll::Pending => return Ok(PollResponse::DoNothing), } } else { return Ok(PollResponse::DrainWriteBuf); @@ -433,7 +453,7 @@ where // if read-backpressure is enabled and we consumed some data. // we may read more data and retry if self.state.is_call() { - if self.poll_request()? { + if self.poll_request(cx)? { continue; } } else if !self.messages.is_empty() { @@ -446,17 +466,21 @@ where Ok(PollResponse::DoNothing) } - fn handle_request(&mut self, req: Request) -> Result, DispatchError> { + fn handle_request( + &mut self, + req: Request, + cx: &mut Context, + ) -> Result, DispatchError> { // Handle `EXPECT: 100-Continue` header let req = if req.head().expect() { let mut task = self.expect.call(req); - match task.poll() { - Ok(Async::Ready(req)) => { + match Pin::new(&mut task).poll(cx) { + Poll::Ready(Ok(req)) => { self.send_continue(); req } - Ok(Async::NotReady) => return Ok(State::ExpectCall(task)), - Err(e) => { + Poll::Pending => return Ok(State::ExpectCall(task)), + Poll::Ready(Err(e)) => { let e = e.into(); let res: Response = e.into(); let (res, body) = res.replace_body(()); @@ -469,13 +493,13 @@ where // Call service let mut task = self.service.call(req); - match task.poll() { - Ok(Async::Ready(res)) => { + match Pin::new(&mut task).poll(cx) { + Poll::Ready(Ok(res)) => { let (res, body) = res.into().replace_body(()); self.send_response(res, body) } - Ok(Async::NotReady) => Ok(State::ServiceCall(task)), - Err(e) => { + Poll::Pending => Ok(State::ServiceCall(task)), + Poll::Ready(Err(e)) => { let res: Response = e.into().into(); let (res, body) = res.replace_body(()); self.send_response(res, body.into_body()) @@ -484,9 +508,12 @@ where } /// Process one incoming requests - pub(self) fn poll_request(&mut self) -> Result { + pub(self) fn poll_request( + &mut self, + cx: &mut Context, + ) -> Result { // limit a mount of non processed requests - if self.messages.len() >= MAX_PIPELINED_MESSAGES || !self.can_read() { + if self.messages.len() >= MAX_PIPELINED_MESSAGES || !self.can_read(cx) { return Ok(false); } @@ -521,7 +548,7 @@ where // handle request early if self.state.is_empty() { - self.state = self.handle_request(req)?; + self.state = self.handle_request(req, cx)?; } else { self.messages.push_back(DispatcherMessage::Item(req)); } @@ -587,12 +614,12 @@ where } /// keep-alive timer - fn poll_keepalive(&mut self) -> Result<(), DispatchError> { + fn poll_keepalive(&mut self, cx: &mut Context) -> Result<(), DispatchError> { if self.ka_timer.is_none() { // shutdown timeout if self.flags.contains(Flags::SHUTDOWN) { if let Some(interval) = self.codec.config().client_disconnect_timer() { - self.ka_timer = Some(Delay::new(interval)); + self.ka_timer = Some(delay(interval)); } else { self.flags.insert(Flags::READ_DISCONNECT); if let Some(mut payload) = self.payload.take() { @@ -605,11 +632,8 @@ where } } - match self.ka_timer.as_mut().unwrap().poll().map_err(|e| { - error!("Timer error {:?}", e); - DispatchError::Unknown - })? { - Async::Ready(_) => { + match Pin::new(&mut self.ka_timer.as_mut().unwrap()).poll(cx) { + Poll::Ready(()) => { // if we get timeout during shutdown, drop connection if self.flags.contains(Flags::SHUTDOWN) { return Err(DispatchError::DisconnectTimeout); @@ -624,9 +648,9 @@ where if let Some(deadline) = self.codec.config().client_disconnect_timer() { - if let Some(timer) = self.ka_timer.as_mut() { + if let Some(mut timer) = self.ka_timer.as_mut() { timer.reset(deadline); - let _ = timer.poll(); + let _ = Pin::new(&mut timer).poll(cx); } } else { // no shutdown timeout, drop socket @@ -650,17 +674,17 @@ where } else if let Some(deadline) = self.codec.config().keep_alive_expire() { - if let Some(timer) = self.ka_timer.as_mut() { + if let Some(mut timer) = self.ka_timer.as_mut() { timer.reset(deadline); - let _ = timer.poll(); + let _ = Pin::new(&mut timer).poll(cx); } } - } else if let Some(timer) = self.ka_timer.as_mut() { + } else if let Some(mut timer) = self.ka_timer.as_mut() { timer.reset(self.ka_expire); - let _ = timer.poll(); + let _ = Pin::new(&mut timer).poll(cx); } } - Async::NotReady => (), + Poll::Pending => (), } Ok(()) @@ -673,33 +697,37 @@ where S: Service, S::Error: Into, S::Response: Into>, + S::Future: Unpin, B: MessageBody, X: Service, X::Error: Into, + X::Future: Unpin, U: Service), Response = ()>, U::Error: fmt::Display, + U::Future: Unpin, { - type Item = (); - type Error = DispatchError; + type Output = Result<(), DispatchError>; #[inline] - fn poll(&mut self) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { match self.inner { DispatcherState::Normal(ref mut inner) => { - inner.poll_keepalive()?; + inner.poll_keepalive(cx)?; if inner.flags.contains(Flags::SHUTDOWN) { if inner.flags.contains(Flags::WRITE_DISCONNECT) { - Ok(Async::Ready(())) + Poll::Ready(Ok(())) } else { // flush buffer - inner.poll_flush()?; + inner.poll_flush(cx)?; if !inner.write_buf.is_empty() { - Ok(Async::NotReady) + Poll::Pending } else { - match inner.io.shutdown()? { - Async::Ready(_) => Ok(Async::Ready(())), - Async::NotReady => Ok(Async::NotReady), + match Pin::new(&mut inner.io).poll_shutdown(cx) { + Poll::Ready(res) => { + Poll::Ready(res.map_err(DispatchError::from)) + } + Poll::Pending => Poll::Pending, } } } @@ -707,12 +735,12 @@ where // read socket into a buf let should_disconnect = if !inner.flags.contains(Flags::READ_DISCONNECT) { - read_available(&mut inner.io, &mut inner.read_buf)? + read_available(cx, &mut inner.io, &mut inner.read_buf)? } else { None }; - inner.poll_request()?; + inner.poll_request(cx)?; if let Some(true) = should_disconnect { inner.flags.insert(Flags::READ_DISCONNECT); if let Some(mut payload) = inner.payload.take() { @@ -724,7 +752,7 @@ where if inner.write_buf.remaining_mut() < LW_BUFFER_SIZE { inner.write_buf.reserve(HW_BUFFER_SIZE); } - let result = inner.poll_response()?; + let result = inner.poll_response(cx)?; let drain = result == PollResponse::DrainWriteBuf; // switch to upgrade handler @@ -742,7 +770,7 @@ where self.inner = DispatcherState::Upgrade( inner.upgrade.unwrap().call((req, framed)), ); - return self.poll(); + return self.poll(cx); } else { panic!() } @@ -751,14 +779,14 @@ where // we didnt get WouldBlock from write operation, // so data get written to kernel completely (OSX) // and we have to write again otherwise response can get stuck - if inner.poll_flush()? || !drain { + if inner.poll_flush(cx)? || !drain { break; } } // client is gone if inner.flags.contains(Flags::WRITE_DISCONNECT) { - return Ok(Async::Ready(())); + return Poll::Ready(Ok(())); } let is_empty = inner.state.is_empty(); @@ -771,38 +799,44 @@ where // keep-alive and stream errors if is_empty && inner.write_buf.is_empty() { if let Some(err) = inner.error.take() { - Err(err) + Poll::Ready(Err(err)) } // disconnect if keep-alive is not enabled else if inner.flags.contains(Flags::STARTED) && !inner.flags.intersects(Flags::KEEPALIVE) { inner.flags.insert(Flags::SHUTDOWN); - self.poll() + self.poll(cx) } // disconnect if shutdown else if inner.flags.contains(Flags::SHUTDOWN) { - self.poll() + self.poll(cx) } else { - Ok(Async::NotReady) + Poll::Pending } } else { - Ok(Async::NotReady) + Poll::Pending } } } - DispatcherState::Upgrade(ref mut fut) => fut.poll().map_err(|e| { - error!("Upgrade handler error: {}", e); - DispatchError::Upgrade - }), + DispatcherState::Upgrade(ref mut fut) => { + Pin::new(fut).poll(cx).map_err(|e| { + error!("Upgrade handler error: {}", e); + DispatchError::Upgrade + }) + } DispatcherState::None => panic!(), } } } -fn read_available(io: &mut T, buf: &mut BytesMut) -> Result, io::Error> +fn read_available( + cx: &mut Context, + io: &mut T, + buf: &mut BytesMut, +) -> Result, io::Error> where - T: io::Read, + T: AsyncRead + Unpin, { let mut read_some = false; loop { @@ -810,19 +844,18 @@ where buf.reserve(HW_BUFFER_SIZE); } - let read = unsafe { io.read(buf.bytes_mut()) }; - match read { - Ok(n) => { + match read(cx, io, buf) { + Poll::Pending => { + return if read_some { Ok(Some(false)) } else { Ok(None) }; + } + Poll::Ready(Ok(n)) => { if n == 0 { return Ok(Some(true)); } else { read_some = true; - unsafe { - buf.advance_mut(n); - } } } - Err(e) => { + Poll::Ready(Err(e)) => { return if e.kind() == io::ErrorKind::WouldBlock { if read_some { Ok(Some(false)) @@ -833,12 +866,23 @@ where Ok(Some(true)) } else { Err(e) - }; + } } } } } +fn read( + cx: &mut Context, + io: &mut T, + buf: &mut BytesMut, +) -> Poll> +where + T: AsyncRead + Unpin, +{ + Pin::new(io).poll_read_buf(cx, buf) +} + #[cfg(test)] mod tests { use actix_service::IntoService; diff --git a/actix-http/src/h1/expect.rs b/actix-http/src/h1/expect.rs index 32b6bd9c4..79831eae1 100644 --- a/actix-http/src/h1/expect.rs +++ b/actix-http/src/h1/expect.rs @@ -1,21 +1,24 @@ +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; + use actix_server_config::ServerConfig; -use actix_service::{NewService, Service}; -use futures::future::{ok, FutureResult}; -use futures::{Async, Poll}; +use actix_service::{Service, ServiceFactory}; +use futures::future::{ok, Ready}; use crate::error::Error; use crate::request::Request; pub struct ExpectHandler; -impl NewService for ExpectHandler { +impl ServiceFactory for ExpectHandler { type Config = ServerConfig; type Request = Request; type Response = Request; type Error = Error; type Service = ExpectHandler; type InitError = Error; - type Future = FutureResult; + type Future = Ready>; fn new_service(&self, _: &ServerConfig) -> Self::Future { ok(ExpectHandler) @@ -26,10 +29,10 @@ impl Service for ExpectHandler { type Request = Request; type Response = Request; type Error = Error; - type Future = FutureResult; + type Future = Ready>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, req: Request) -> Self::Future { diff --git a/actix-http/src/h1/payload.rs b/actix-http/src/h1/payload.rs index 28acb64bb..036138f98 100644 --- a/actix-http/src/h1/payload.rs +++ b/actix-http/src/h1/payload.rs @@ -1,12 +1,14 @@ //! Payload stream use std::cell::RefCell; use std::collections::VecDeque; +use std::future::Future; +use std::pin::Pin; use std::rc::{Rc, Weak}; +use std::task::{Context, Poll}; +use actix_utils::task::LocalWaker; use bytes::Bytes; -use futures::task::current as current_task; -use futures::task::Task; -use futures::{Async, Poll, Stream}; +use futures::Stream; use crate::error::PayloadError; @@ -77,15 +79,24 @@ impl Payload { pub fn unread_data(&mut self, data: Bytes) { self.inner.borrow_mut().unread_data(data); } + + #[inline] + pub fn readany( + &mut self, + cx: &mut Context, + ) -> Poll>> { + self.inner.borrow_mut().readany(cx) + } } impl Stream for Payload { - type Item = Bytes; - type Error = PayloadError; + type Item = Result; - #[inline] - fn poll(&mut self) -> Poll, PayloadError> { - self.inner.borrow_mut().readany() + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll>> { + self.inner.borrow_mut().readany(cx) } } @@ -117,19 +128,14 @@ impl PayloadSender { } #[inline] - pub fn need_read(&self) -> PayloadStatus { + pub fn need_read(&self, cx: &mut Context) -> PayloadStatus { // we check need_read only if Payload (other side) is alive, // otherwise always return true (consume payload) if let Some(shared) = self.inner.upgrade() { if shared.borrow().need_read { PayloadStatus::Read } else { - #[cfg(not(test))] - { - if shared.borrow_mut().io_task.is_none() { - shared.borrow_mut().io_task = Some(current_task()); - } - } + shared.borrow_mut().io_task.register(cx.waker()); PayloadStatus::Pause } } else { @@ -145,8 +151,8 @@ struct Inner { err: Option, need_read: bool, items: VecDeque, - task: Option, - io_task: Option, + task: LocalWaker, + io_task: LocalWaker, } impl Inner { @@ -157,8 +163,8 @@ impl Inner { err: None, items: VecDeque::new(), need_read: true, - task: None, - io_task: None, + task: LocalWaker::new(), + io_task: LocalWaker::new(), } } @@ -178,7 +184,7 @@ impl Inner { self.items.push_back(data); self.need_read = self.len < MAX_BUFFER_SIZE; if let Some(task) = self.task.take() { - task.notify() + task.wake() } } @@ -187,34 +193,28 @@ impl Inner { self.len } - fn readany(&mut self) -> Poll, PayloadError> { + fn readany( + &mut self, + cx: &mut Context, + ) -> Poll>> { if let Some(data) = self.items.pop_front() { self.len -= data.len(); self.need_read = self.len < MAX_BUFFER_SIZE; - if self.need_read && self.task.is_none() && !self.eof { - self.task = Some(current_task()); + if self.need_read && !self.eof { + self.task.register(cx.waker()); } - if let Some(task) = self.io_task.take() { - task.notify() - } - Ok(Async::Ready(Some(data))) + self.io_task.wake(); + Poll::Ready(Some(Ok(data))) } else if let Some(err) = self.err.take() { - Err(err) + Poll::Ready(Some(Err(err))) } else if self.eof { - Ok(Async::Ready(None)) + Poll::Ready(None) } else { self.need_read = true; - #[cfg(not(test))] - { - if self.task.is_none() { - self.task = Some(current_task()); - } - if let Some(task) = self.io_task.take() { - task.notify() - } - } - Ok(Async::NotReady) + self.task.register(cx.waker()); + self.io_task.wake(); + Poll::Pending } } diff --git a/actix-http/src/h1/service.rs b/actix-http/src/h1/service.rs index 89bf08e9b..95596af76 100644 --- a/actix-http/src/h1/service.rs +++ b/actix-http/src/h1/service.rs @@ -1,12 +1,15 @@ use std::fmt; +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; use std::rc::Rc; +use std::task::{Context, Poll}; use actix_codec::Framed; use actix_server_config::{Io, IoStream, ServerConfig as SrvConfig}; -use actix_service::{IntoNewService, NewService, Service}; -use futures::future::{ok, FutureResult}; -use futures::{try_ready, Async, Future, IntoFuture, Poll, Stream}; +use actix_service::{IntoServiceFactory, Service, ServiceFactory}; +use futures::future::{ok, Ready}; +use futures::{ready, Stream}; use crate::body::MessageBody; use crate::cloneable::CloneableService; @@ -20,7 +23,7 @@ use super::codec::Codec; use super::dispatcher::Dispatcher; use super::{ExpectHandler, Message, UpgradeHandler}; -/// `NewService` implementation for HTTP1 transport +/// `ServiceFactory` implementation for HTTP1 transport pub struct H1Service> { srv: S, cfg: ServiceConfig, @@ -32,19 +35,23 @@ pub struct H1Service> { impl H1Service where - S: NewService, + S: ServiceFactory, S::Error: Into, S::InitError: fmt::Debug, S::Response: Into>, + S::Future: Unpin, + S::Service: Unpin, + ::Future: Unpin, B: MessageBody, + P: Unpin, { /// Create new `HttpService` instance with default config. - pub fn new>(service: F) -> Self { + pub fn new>(service: F) -> Self { let cfg = ServiceConfig::new(KeepAlive::Timeout(5), 5000, 0); H1Service { cfg, - srv: service.into_new_service(), + srv: service.into_factory(), expect: ExpectHandler, upgrade: None, on_connect: None, @@ -53,10 +60,13 @@ where } /// Create new `HttpService` instance with config. - pub fn with_config>(cfg: ServiceConfig, service: F) -> Self { + pub fn with_config>( + cfg: ServiceConfig, + service: F, + ) -> Self { H1Service { cfg, - srv: service.into_new_service(), + srv: service.into_factory(), expect: ExpectHandler, upgrade: None, on_connect: None, @@ -67,17 +77,24 @@ where impl H1Service where - S: NewService, + S: ServiceFactory, S::Error: Into, S::Response: Into>, S::InitError: fmt::Debug, + S::Future: Unpin, + S::Service: Unpin, + ::Future: Unpin, B: MessageBody, + P: Unpin, { pub fn expect(self, expect: X1) -> H1Service where - X1: NewService, + X1: ServiceFactory, X1::Error: Into, X1::InitError: fmt::Debug, + X1::Future: Unpin, + X1::Service: Unpin, + ::Future: Unpin, { H1Service { expect, @@ -91,9 +108,12 @@ where pub fn upgrade(self, upgrade: Option) -> H1Service where - U1: NewService), Response = ()>, + U1: ServiceFactory), Response = ()>, U1::Error: fmt::Display, U1::InitError: fmt::Debug, + U1::Future: Unpin, + U1::Service: Unpin, + ::Future: Unpin, { H1Service { upgrade, @@ -115,24 +135,35 @@ where } } -impl NewService for H1Service +impl ServiceFactory for H1Service where T: IoStream, - S: NewService, + S: ServiceFactory, + S::Service: Unpin, S::Error: Into, S::Response: Into>, S::InitError: fmt::Debug, + S::Future: Unpin, + S::Service: Unpin, + ::Future: Unpin, B: MessageBody, - X: NewService, + X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, - U: NewService< + X::Future: Unpin, + X::Service: Unpin, + ::Future: Unpin, + U: ServiceFactory< Config = SrvConfig, Request = (Request, Framed), Response = (), >, U::Error: fmt::Display, U::InitError: fmt::Debug, + U::Future: Unpin, + U::Service: Unpin, + ::Future: Unpin, + P: Unpin, { type Config = SrvConfig; type Request = Io; @@ -144,7 +175,7 @@ where fn new_service(&self, cfg: &SrvConfig) -> Self::Future { H1ServiceResponse { - fut: self.srv.new_service(cfg).into_future(), + fut: self.srv.new_service(cfg), fut_ex: Some(self.expect.new_service(cfg)), fut_upg: self.upgrade.as_ref().map(|f| f.new_service(cfg)), expect: None, @@ -159,15 +190,25 @@ where #[doc(hidden)] pub struct H1ServiceResponse where - S: NewService, + S: ServiceFactory, S::Error: Into, S::InitError: fmt::Debug, - X: NewService, + S::Future: Unpin, + S::Service: Unpin, + ::Future: Unpin, + X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, - U: NewService), Response = ()>, + X::Future: Unpin, + X::Service: Unpin, + ::Future: Unpin, + U: ServiceFactory), Response = ()>, U::Error: fmt::Display, U::InitError: fmt::Debug, + U::Future: Unpin, + U::Service: Unpin, + ::Future: Unpin, + P: Unpin, { fut: S::Future, fut_ex: Option, @@ -182,49 +223,63 @@ where impl Future for H1ServiceResponse where T: IoStream, - S: NewService, + S: ServiceFactory, S::Error: Into, S::Response: Into>, S::InitError: fmt::Debug, + S::Future: Unpin, + S::Service: Unpin, + ::Future: Unpin, B: MessageBody, - X: NewService, + X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, - U: NewService), Response = ()>, + X::Future: Unpin, + X::Service: Unpin, + ::Future: Unpin, + U: ServiceFactory), Response = ()>, U::Error: fmt::Display, U::InitError: fmt::Debug, + U::Future: Unpin, + U::Service: Unpin, + ::Future: Unpin, + P: Unpin, { - type Item = H1ServiceHandler; - type Error = (); + type Output = + Result, ()>; - fn poll(&mut self) -> Poll { - if let Some(ref mut fut) = self.fut_ex { - let expect = try_ready!(fut - .poll() - .map_err(|e| log::error!("Init http service error: {:?}", e))); - self.expect = Some(expect); - self.fut_ex.take(); + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.get_mut(); + + if let Some(ref mut fut) = this.fut_ex { + let expect = ready!(Pin::new(fut) + .poll(cx) + .map_err(|e| log::error!("Init http service error: {:?}", e)))?; + this.expect = Some(expect); + this.fut_ex.take(); } - if let Some(ref mut fut) = self.fut_upg { - let upgrade = try_ready!(fut - .poll() - .map_err(|e| log::error!("Init http service error: {:?}", e))); - self.upgrade = Some(upgrade); - self.fut_ex.take(); + if let Some(ref mut fut) = this.fut_upg { + let upgrade = ready!(Pin::new(fut) + .poll(cx) + .map_err(|e| log::error!("Init http service error: {:?}", e)))?; + this.upgrade = Some(upgrade); + this.fut_ex.take(); } - let service = try_ready!(self - .fut - .poll() + let result = ready!(Pin::new(&mut this.fut) + .poll(cx) .map_err(|e| log::error!("Init http service error: {:?}", e))); - Ok(Async::Ready(H1ServiceHandler::new( - self.cfg.take().unwrap(), - service, - self.expect.take().unwrap(), - self.upgrade.take(), - self.on_connect.clone(), - ))) + + Poll::Ready(result.map(|service| { + H1ServiceHandler::new( + this.cfg.take().unwrap(), + service, + this.expect.take().unwrap(), + this.upgrade.take(), + this.on_connect.clone(), + ) + })) } } @@ -240,14 +295,18 @@ pub struct H1ServiceHandler { impl H1ServiceHandler where - S: Service, + S: Service + Unpin, S::Error: Into, S::Response: Into>, + S::Future: Unpin, B: MessageBody, - X: Service, + X: Service + Unpin, + X::Future: Unpin, X::Error: Into, - U: Service), Response = ()>, + U: Service), Response = ()> + Unpin, + U::Future: Unpin, U::Error: fmt::Display, + P: Unpin, { fn new( cfg: ServiceConfig, @@ -270,24 +329,28 @@ where impl Service for H1ServiceHandler where T: IoStream, - S: Service, + S: Service + Unpin, S::Error: Into, S::Response: Into>, + S::Future: Unpin, B: MessageBody, - X: Service, + X: Service + Unpin, X::Error: Into, - U: Service), Response = ()>, + X::Future: Unpin, + U: Service), Response = ()> + Unpin, U::Error: fmt::Display, + U::Future: Unpin, + P: Unpin, { type Request = Io; type Response = (); type Error = DispatchError; type Future = Dispatcher; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { let ready = self .expect - .poll_ready() + .poll_ready(cx) .map_err(|e| { let e = e.into(); log::error!("Http service readiness error: {:?}", e); @@ -297,7 +360,7 @@ where let ready = self .srv - .poll_ready() + .poll_ready(cx) .map_err(|e| { let e = e.into(); log::error!("Http service readiness error: {:?}", e); @@ -307,9 +370,9 @@ where && ready; if ready { - Ok(Async::Ready(())) + Poll::Ready(Ok(())) } else { - Ok(Async::NotReady) + Poll::Pending } } @@ -333,7 +396,7 @@ where } } -/// `NewService` implementation for `OneRequestService` service +/// `ServiceFactory` implementation for `OneRequestService` service #[derive(Default)] pub struct OneRequest { config: ServiceConfig, @@ -353,7 +416,7 @@ where } } -impl NewService for OneRequest +impl ServiceFactory for OneRequest where T: IoStream, { @@ -363,7 +426,7 @@ where type Error = ParseError; type InitError = (); type Service = OneRequestService; - type Future = FutureResult; + type Future = Ready>; fn new_service(&self, _: &SrvConfig) -> Self::Future { ok(OneRequestService { @@ -389,8 +452,8 @@ where type Error = ParseError; type Future = OneRequestServiceResponse; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, req: Self::Request) -> Self::Future { @@ -415,19 +478,19 @@ impl Future for OneRequestServiceResponse where T: IoStream, { - type Item = (Request, Framed); - type Error = ParseError; + type Output = Result<(Request, Framed), ParseError>; - fn poll(&mut self) -> Poll { - match self.framed.as_mut().unwrap().poll()? { - Async::Ready(Some(req)) => match req { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + match self.framed.as_mut().unwrap().next_item(cx) { + Poll::Ready(Some(Ok(req))) => match req { Message::Item(req) => { - Ok(Async::Ready((req, self.framed.take().unwrap()))) + Poll::Ready(Ok((req, self.framed.take().unwrap()))) } Message::Chunk(_) => unreachable!("Something is wrong"), }, - Async::Ready(None) => Err(ParseError::Incomplete), - Async::NotReady => Ok(Async::NotReady), + Poll::Ready(Some(Err(err))) => Poll::Ready(Err(err)), + Poll::Ready(None) => Poll::Ready(Err(ParseError::Incomplete)), + Poll::Pending => Poll::Pending, } } } diff --git a/actix-http/src/h1/upgrade.rs b/actix-http/src/h1/upgrade.rs index 0278f23e5..43ab53d01 100644 --- a/actix-http/src/h1/upgrade.rs +++ b/actix-http/src/h1/upgrade.rs @@ -1,10 +1,12 @@ +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; use actix_codec::Framed; use actix_server_config::ServerConfig; -use actix_service::{NewService, Service}; -use futures::future::FutureResult; -use futures::{Async, Poll}; +use actix_service::{Service, ServiceFactory}; +use futures::future::Ready; use crate::error::Error; use crate::h1::Codec; @@ -12,14 +14,14 @@ use crate::request::Request; pub struct UpgradeHandler(PhantomData); -impl NewService for UpgradeHandler { +impl ServiceFactory for UpgradeHandler { type Config = ServerConfig; type Request = (Request, Framed); type Response = (); type Error = Error; type Service = UpgradeHandler; type InitError = Error; - type Future = FutureResult; + type Future = Ready>; fn new_service(&self, _: &ServerConfig) -> Self::Future { unimplemented!() @@ -30,10 +32,10 @@ impl Service for UpgradeHandler { type Request = (Request, Framed); type Response = (); type Error = Error; - type Future = FutureResult; + type Future = Ready>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, _: Self::Request) -> Self::Future { diff --git a/actix-http/src/h1/utils.rs b/actix-http/src/h1/utils.rs index fdc4cf0bc..bc6914d3b 100644 --- a/actix-http/src/h1/utils.rs +++ b/actix-http/src/h1/utils.rs @@ -1,5 +1,9 @@ +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; + use actix_codec::{AsyncRead, AsyncWrite, Framed}; -use futures::{Async, Future, Poll, Sink}; +use futures::Sink; use crate::body::{BodySize, MessageBody, ResponseBody}; use crate::error::Error; @@ -30,63 +34,64 @@ where impl Future for SendResponse where - T: AsyncRead + AsyncWrite, + T: AsyncRead + AsyncWrite + Unpin, B: MessageBody, { - type Item = Framed; - type Error = Error; + type Output = Result, Error>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.get_mut(); - fn poll(&mut self) -> Poll { loop { - let mut body_ready = self.body.is_some(); - let framed = self.framed.as_mut().unwrap(); + let mut body_ready = this.body.is_some(); + let framed = this.framed.as_mut().unwrap(); // send body - if self.res.is_none() && self.body.is_some() { - while body_ready && self.body.is_some() && !framed.is_write_buf_full() { - match self.body.as_mut().unwrap().poll_next()? { - Async::Ready(item) => { + if this.res.is_none() && this.body.is_some() { + while body_ready && this.body.is_some() && !framed.is_write_buf_full() { + match this.body.as_mut().unwrap().poll_next(cx)? { + Poll::Ready(item) => { // body is done if item.is_none() { - let _ = self.body.take(); + let _ = this.body.take(); } framed.force_send(Message::Chunk(item))?; } - Async::NotReady => body_ready = false, + Poll::Pending => body_ready = false, } } } // flush write buffer if !framed.is_write_buf_empty() { - match framed.poll_complete()? { - Async::Ready(_) => { + match framed.flush(cx)? { + Poll::Ready(_) => { if body_ready { continue; } else { - return Ok(Async::NotReady); + return Poll::Pending; } } - Async::NotReady => return Ok(Async::NotReady), + Poll::Pending => return Poll::Pending, } } // send response - if let Some(res) = self.res.take() { + if let Some(res) = this.res.take() { framed.force_send(res)?; continue; } - if self.body.is_some() { + if this.body.is_some() { if body_ready { continue; } else { - return Ok(Async::NotReady); + return Poll::Pending; } } else { break; } } - Ok(Async::Ready(self.framed.take().unwrap())) + Poll::Ready(Ok(this.framed.take().unwrap())) } } diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index 888f9065e..2a44c83f9 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -1,5 +1,8 @@ use std::collections::VecDeque; +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; use std::time::Instant; use std::{fmt, mem, net}; @@ -8,7 +11,7 @@ use actix_server_config::IoStream; use actix_service::Service; use bitflags::bitflags; use bytes::{Bytes, BytesMut}; -use futures::{try_ready, Async, Future, Poll, Sink, Stream}; +use futures::{ready, Sink, Stream}; use h2::server::{Connection, SendResponse}; use h2::{RecvStream, SendStream}; use http::header::{ @@ -43,13 +46,24 @@ pub struct Dispatcher, B: MessageBody _t: PhantomData, } +impl Unpin for Dispatcher +where + T: IoStream, + S: Service, + S::Error: Into + Unpin + 'static, + S::Future: Unpin + 'static, + S::Response: Into> + Unpin + 'static, + B: MessageBody + 'static, +{ +} + impl Dispatcher where T: IoStream, S: Service, - S::Error: Into, - S::Future: 'static, - S::Response: Into>, + S::Error: Into + Unpin + 'static, + S::Future: Unpin + 'static, + S::Response: Into> + Unpin + 'static, B: MessageBody + 'static, { pub(crate) fn new( @@ -93,61 +107,75 @@ impl Future for Dispatcher where T: IoStream, S: Service, - S::Error: Into, - S::Future: 'static, - S::Response: Into>, + S::Error: Into + Unpin + 'static, + S::Future: Unpin + 'static, + S::Response: Into> + Unpin + 'static, B: MessageBody + 'static, { - type Item = (); - type Error = DispatchError; + type Output = Result<(), DispatchError>; #[inline] - fn poll(&mut self) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.get_mut(); + loop { - match self.connection.poll()? { - Async::Ready(None) => return Ok(Async::Ready(())), - Async::Ready(Some((req, res))) => { + match Pin::new(&mut this.connection).poll_accept(cx) { + Poll::Ready(None) => return Poll::Ready(Ok(())), + Poll::Ready(Some(Err(err))) => return Poll::Ready(Err(err.into())), + Poll::Ready(Some(Ok((req, res)))) => { // update keep-alive expire - if self.ka_timer.is_some() { - if let Some(expire) = self.config.keep_alive_expire() { - self.ka_expire = expire; + if this.ka_timer.is_some() { + if let Some(expire) = this.config.keep_alive_expire() { + this.ka_expire = expire; } } let (parts, body) = req.into_parts(); - let mut req = Request::with_payload(body.into()); + // let b: () = body; + let mut req = Request::with_payload(Payload::< + crate::payload::PayloadStream, + >::H2( + crate::h2::Payload::new(body) + )); let head = &mut req.head_mut(); head.uri = parts.uri; head.method = parts.method; head.version = parts.version; head.headers = parts.headers.into(); - head.peer_addr = self.peer_addr; + head.peer_addr = this.peer_addr; // set on_connect data - if let Some(ref on_connect) = self.on_connect { + if let Some(ref on_connect) = this.on_connect { on_connect.set(&mut req.extensions_mut()); } - tokio_current_thread::spawn(ServiceResponse:: { - state: ServiceResponseState::ServiceCall( - self.service.call(req), - Some(res), - ), - config: self.config.clone(), - buffer: None, - }) + // tokio_executor::current_thread::spawn(ServiceResponse::< + // S::Future, + // S::Response, + // S::Error, + // B, + // > { + // state: ServiceResponseState::ServiceCall( + // this.service.call(req), + // Some(res), + // ), + // config: this.config.clone(), + // buffer: None, + // _t: PhantomData, + // }); } - Async::NotReady => return Ok(Async::NotReady), + Poll::Pending => return Poll::Pending, } } } } -struct ServiceResponse { +struct ServiceResponse { state: ServiceResponseState, config: ServiceConfig, buffer: Option, + _t: PhantomData<(I, E)>, } enum ServiceResponseState { @@ -155,11 +183,11 @@ enum ServiceResponseState { SendPayload(SendStream, ResponseBody), } -impl ServiceResponse +impl ServiceResponse where - F: Future, - F::Error: Into, - F::Item: Into>, + F: Future> + Unpin, + E: Into + Unpin + 'static, + I: Into> + Unpin + 'static, B: MessageBody + 'static, { fn prepare_response( @@ -223,109 +251,116 @@ where } } -impl Future for ServiceResponse +impl Future for ServiceResponse where - F: Future, - F::Error: Into, - F::Item: Into>, + F: Future> + Unpin, + E: Into + Unpin + 'static, + I: Into> + Unpin + 'static, B: MessageBody + 'static, { - type Item = (); - type Error = (); + type Output = (); - fn poll(&mut self) -> Poll { - match self.state { + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.get_mut(); + + match this.state { ServiceResponseState::ServiceCall(ref mut call, ref mut send) => { - match call.poll() { - Ok(Async::Ready(res)) => { + match Pin::new(call).poll(cx) { + Poll::Ready(Ok(res)) => { let (res, body) = res.into().replace_body(()); let mut send = send.take().unwrap(); let mut size = body.size(); - let h2_res = self.prepare_response(res.head(), &mut size); + let h2_res = this.prepare_response(res.head(), &mut size); - let stream = - send.send_response(h2_res, size.is_eof()).map_err(|e| { + let stream = match send.send_response(h2_res, size.is_eof()) { + Err(e) => { trace!("Error sending h2 response: {:?}", e); - })?; + return Poll::Ready(()); + } + Ok(stream) => stream, + }; if size.is_eof() { - Ok(Async::Ready(())) + Poll::Ready(()) } else { - self.state = ServiceResponseState::SendPayload(stream, body); - self.poll() + this.state = ServiceResponseState::SendPayload(stream, body); + Pin::new(this).poll(cx) } } - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(e) => { + Poll::Pending => Poll::Pending, + Poll::Ready(Err(e)) => { let res: Response = e.into().into(); let (res, body) = res.replace_body(()); let mut send = send.take().unwrap(); let mut size = body.size(); - let h2_res = self.prepare_response(res.head(), &mut size); + let h2_res = this.prepare_response(res.head(), &mut size); - let stream = - send.send_response(h2_res, size.is_eof()).map_err(|e| { + let stream = match send.send_response(h2_res, size.is_eof()) { + Err(e) => { trace!("Error sending h2 response: {:?}", e); - })?; + return Poll::Ready(()); + } + Ok(stream) => stream, + }; if size.is_eof() { - Ok(Async::Ready(())) + Poll::Ready(()) } else { - self.state = ServiceResponseState::SendPayload( + this.state = ServiceResponseState::SendPayload( stream, body.into_body(), ); - self.poll() + Pin::new(this).poll(cx) } } } } ServiceResponseState::SendPayload(ref mut stream, ref mut body) => loop { loop { - if let Some(ref mut buffer) = self.buffer { - match stream.poll_capacity().map_err(|e| warn!("{:?}", e))? { - Async::NotReady => return Ok(Async::NotReady), - Async::Ready(None) => return Ok(Async::Ready(())), - Async::Ready(Some(cap)) => { + if let Some(ref mut buffer) = this.buffer { + match stream.poll_capacity(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(None) => return Poll::Ready(()), + Poll::Ready(Some(Ok(cap))) => { let len = buffer.len(); let bytes = buffer.split_to(std::cmp::min(cap, len)); if let Err(e) = stream.send_data(bytes, false) { warn!("{:?}", e); - return Err(()); + return Poll::Ready(()); } else if !buffer.is_empty() { let cap = std::cmp::min(buffer.len(), CHUNK_SIZE); stream.reserve_capacity(cap); } else { - self.buffer.take(); + this.buffer.take(); } } + Poll::Ready(Some(Err(e))) => { + warn!("{:?}", e); + return Poll::Ready(()); + } } } else { - match body.poll_next() { - Ok(Async::NotReady) => { - return Ok(Async::NotReady); - } - Ok(Async::Ready(None)) => { + match body.poll_next(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(None) => { if let Err(e) = stream.send_data(Bytes::new(), true) { warn!("{:?}", e); - return Err(()); - } else { - return Ok(Async::Ready(())); } + return Poll::Ready(()); } - Ok(Async::Ready(Some(chunk))) => { + Poll::Ready(Some(Ok(chunk))) => { stream.reserve_capacity(std::cmp::min( chunk.len(), CHUNK_SIZE, )); - self.buffer = Some(chunk); + this.buffer = Some(chunk); } - Err(e) => { + Poll::Ready(Some(Err(e))) => { error!("Response payload stream error: {:?}", e); - return Err(()); + return Poll::Ready(()); } } } diff --git a/actix-http/src/h2/mod.rs b/actix-http/src/h2/mod.rs index c5972123f..9c902f18c 100644 --- a/actix-http/src/h2/mod.rs +++ b/actix-http/src/h2/mod.rs @@ -1,9 +1,11 @@ #![allow(dead_code, unused_imports)] - use std::fmt; +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; use bytes::Bytes; -use futures::{Async, Poll, Stream}; +use futures::Stream; use h2::RecvStream; mod dispatcher; @@ -25,22 +27,23 @@ impl Payload { } impl Stream for Payload { - type Item = Bytes; - type Error = PayloadError; + type Item = Result; - fn poll(&mut self) -> Poll, Self::Error> { - match self.pl.poll() { - Ok(Async::Ready(Some(chunk))) => { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let this = self.get_mut(); + + match Pin::new(&mut this.pl).poll_data(cx) { + Poll::Ready(Some(Ok(chunk))) => { let len = chunk.len(); - if let Err(err) = self.pl.release_capacity().release_capacity(len) { - Err(err.into()) + if let Err(err) = this.pl.release_capacity().release_capacity(len) { + Poll::Ready(Some(Err(err.into()))) } else { - Ok(Async::Ready(Some(chunk))) + Poll::Ready(Some(Ok(chunk))) } } - Ok(Async::Ready(None)) => Ok(Async::Ready(None)), - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(err) => Err(err.into()), + Poll::Ready(Some(Err(err))) => Poll::Ready(Some(Err(err.into()))), + Poll::Pending => Poll::Pending, + Poll::Ready(None) => Poll::Ready(None), } } } diff --git a/actix-http/src/h2/service.rs b/actix-http/src/h2/service.rs index e894cf660..559c99308 100644 --- a/actix-http/src/h2/service.rs +++ b/actix-http/src/h2/service.rs @@ -1,13 +1,16 @@ use std::fmt::Debug; +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; use std::{io, net, rc}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_server_config::{Io, IoStream, ServerConfig as SrvConfig}; -use actix_service::{IntoNewService, NewService, Service}; +use actix_service::{IntoServiceFactory, Service, ServiceFactory}; use bytes::Bytes; -use futures::future::{ok, FutureResult}; -use futures::{try_ready, Async, Future, IntoFuture, Poll, Stream}; +use futures::future::{ok, Ready}; +use futures::{ready, Stream}; use h2::server::{self, Connection, Handshake}; use h2::RecvStream; use log::error; @@ -23,7 +26,7 @@ use crate::response::Response; use super::dispatcher::Dispatcher; -/// `NewService` implementation for HTTP2 transport +/// `ServiceFactory` implementation for HTTP2 transport pub struct H2Service { srv: S, cfg: ServiceConfig, @@ -33,30 +36,35 @@ pub struct H2Service { impl H2Service where - S: NewService, - S::Error: Into, - S::Response: Into>, - ::Future: 'static, + S: ServiceFactory, + S::Error: Into + Unpin + 'static, + S::Response: Into> + Unpin + 'static, + S::Future: Unpin, + ::Future: Unpin + 'static, B: MessageBody + 'static, + P: Unpin, { /// Create new `HttpService` instance. - pub fn new>(service: F) -> Self { + pub fn new>(service: F) -> Self { let cfg = ServiceConfig::new(KeepAlive::Timeout(5), 5000, 0); H2Service { cfg, on_connect: None, - srv: service.into_new_service(), + srv: service.into_factory(), _t: PhantomData, } } /// Create new `HttpService` instance with config. - pub fn with_config>(cfg: ServiceConfig, service: F) -> Self { + pub fn with_config>( + cfg: ServiceConfig, + service: F, + ) -> Self { H2Service { cfg, on_connect: None, - srv: service.into_new_service(), + srv: service.into_factory(), _t: PhantomData, } } @@ -71,14 +79,16 @@ where } } -impl NewService for H2Service +impl ServiceFactory for H2Service where T: IoStream, - S: NewService, - S::Error: Into, - S::Response: Into>, - ::Future: 'static, + S: ServiceFactory, + S::Error: Into + Unpin + 'static, + S::Response: Into> + Unpin + 'static, + S::Future: Unpin, + ::Future: Unpin + 'static, B: MessageBody + 'static, + P: Unpin, { type Config = SrvConfig; type Request = Io; @@ -90,7 +100,7 @@ where fn new_service(&self, cfg: &SrvConfig) -> Self::Future { H2ServiceResponse { - fut: self.srv.new_service(cfg).into_future(), + fut: self.srv.new_service(cfg), cfg: Some(self.cfg.clone()), on_connect: self.on_connect.clone(), _t: PhantomData, @@ -99,8 +109,8 @@ where } #[doc(hidden)] -pub struct H2ServiceResponse { - fut: ::Future, +pub struct H2ServiceResponse { + fut: S::Future, cfg: Option, on_connect: Option Box>>, _t: PhantomData<(T, P, B)>, @@ -109,22 +119,26 @@ pub struct H2ServiceResponse { impl Future for H2ServiceResponse where T: IoStream, - S: NewService, - S::Error: Into, - S::Response: Into>, - ::Future: 'static, + S: ServiceFactory, + S::Error: Into + Unpin + 'static, + S::Response: Into> + Unpin + 'static, + S::Future: Unpin, + ::Future: Unpin + 'static, B: MessageBody + 'static, + P: Unpin, { - type Item = H2ServiceHandler; - type Error = S::InitError; + type Output = Result, S::InitError>; - fn poll(&mut self) -> Poll { - let service = try_ready!(self.fut.poll()); - Ok(Async::Ready(H2ServiceHandler::new( - self.cfg.take().unwrap(), - self.on_connect.clone(), - service, - ))) + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.get_mut(); + + Poll::Ready(ready!(Pin::new(&mut this.fut).poll(cx)).map(|service| { + H2ServiceHandler::new( + this.cfg.take().unwrap(), + this.on_connect.clone(), + service, + ) + })) } } @@ -139,10 +153,11 @@ pub struct H2ServiceHandler { impl H2ServiceHandler where S: Service, - S::Error: Into, - S::Future: 'static, - S::Response: Into>, + S::Error: Into + Unpin + 'static, + S::Future: Unpin + 'static, + S::Response: Into> + Unpin + 'static, B: MessageBody + 'static, + P: Unpin, { fn new( cfg: ServiceConfig, @@ -162,18 +177,19 @@ impl Service for H2ServiceHandler where T: IoStream, S: Service, - S::Error: Into, - S::Future: 'static, - S::Response: Into>, + S::Error: Into + Unpin + 'static, + S::Future: Unpin + 'static, + S::Response: Into> + Unpin + 'static, B: MessageBody + 'static, + P: Unpin, { type Request = Io; type Response = (); type Error = DispatchError; type Future = H2ServiceHandlerResponse; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.srv.poll_ready().map_err(|e| { + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.srv.poll_ready(cx).map_err(|e| { let e = e.into(); error!("Service readiness error: {:?}", e); DispatchError::Service(e) @@ -219,9 +235,9 @@ pub struct H2ServiceHandlerResponse where T: IoStream, S: Service, - S::Error: Into, - S::Future: 'static, - S::Response: Into>, + S::Error: Into + Unpin + 'static, + S::Future: Unpin + 'static, + S::Response: Into> + Unpin + 'static, B: MessageBody + 'static, { state: State, @@ -231,25 +247,24 @@ impl Future for H2ServiceHandlerResponse where T: IoStream, S: Service, - S::Error: Into, - S::Future: 'static, - S::Response: Into>, + S::Error: Into + Unpin + 'static, + S::Future: Unpin + 'static, + S::Response: Into> + Unpin + 'static, B: MessageBody, { - type Item = (); - type Error = DispatchError; + type Output = Result<(), DispatchError>; - fn poll(&mut self) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { match self.state { - State::Incoming(ref mut disp) => disp.poll(), + State::Incoming(ref mut disp) => Pin::new(disp).poll(cx), State::Handshake( ref mut srv, ref mut config, ref peer_addr, ref mut on_connect, ref mut handshake, - ) => match handshake.poll() { - Ok(Async::Ready(conn)) => { + ) => match Pin::new(handshake).poll(cx) { + Poll::Ready(Ok(conn)) => { self.state = State::Incoming(Dispatcher::new( srv.take().unwrap(), conn, @@ -258,13 +273,13 @@ where None, *peer_addr, )); - self.poll() + self.poll(cx) } - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(err) => { + Poll::Ready(Err(err)) => { trace!("H2 handshake error: {}", err); - Err(err.into()) + Poll::Ready(Err(err.into())) } + Poll::Pending => Poll::Pending, }, } } diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs index b57fdddce..cf528aeec 100644 --- a/actix-http/src/lib.rs +++ b/actix-http/src/lib.rs @@ -4,7 +4,8 @@ clippy::too_many_arguments, clippy::new_without_default, clippy::borrow_interior_mutable_const, - clippy::write_with_newline + clippy::write_with_newline, + unused_imports )] #[macro_use] @@ -12,7 +13,7 @@ extern crate log; pub mod body; mod builder; -pub mod client; +// pub mod client; mod cloneable; mod config; pub mod encoding; @@ -31,8 +32,8 @@ pub mod cookie; pub mod error; pub mod h1; pub mod h2; -pub mod test; -pub mod ws; +// pub mod test; +// pub mod ws; pub use self::builder::HttpServiceBuilder; pub use self::config::{KeepAlive, ServiceConfig}; diff --git a/actix-http/src/payload.rs b/actix-http/src/payload.rs index 0ce209705..f2cc6414f 100644 --- a/actix-http/src/payload.rs +++ b/actix-http/src/payload.rs @@ -1,11 +1,15 @@ +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; + use bytes::Bytes; -use futures::{Async, Poll, Stream}; +use futures::Stream; use h2::RecvStream; use crate::error::PayloadError; /// Type represent boxed payload -pub type PayloadStream = Box>; +pub type PayloadStream = Pin>>>; /// Type represent streaming payload pub enum Payload { @@ -48,18 +52,17 @@ impl Payload { impl Stream for Payload where - S: Stream, + S: Stream> + Unpin, { - type Item = Bytes; - type Error = PayloadError; + type Item = Result; #[inline] - fn poll(&mut self) -> Poll, Self::Error> { - match self { - Payload::None => Ok(Async::Ready(None)), - Payload::H1(ref mut pl) => pl.poll(), - Payload::H2(ref mut pl) => pl.poll(), - Payload::Stream(ref mut pl) => pl.poll(), + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + match self.get_mut() { + Payload::None => Poll::Ready(None), + Payload::H1(ref mut pl) => pl.readany(cx), + Payload::H2(ref mut pl) => Pin::new(pl).poll_next(cx), + Payload::Stream(ref mut pl) => Pin::new(pl).poll_next(cx), } } } diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index a1541b53e..5b3d17cb4 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -4,7 +4,7 @@ use std::io::Write; use std::{fmt, str}; use bytes::{BufMut, Bytes, BytesMut}; -use futures::future::{ok, FutureResult, IntoFuture}; +use futures::future::{ok, Ready}; use futures::Stream; use serde::Serialize; use serde_json; @@ -280,15 +280,15 @@ impl fmt::Debug for Response { } } -impl IntoFuture for Response { - type Item = Response; - type Error = Error; - type Future = FutureResult; +// impl IntoFuture for Response { +// type Item = Response; +// type Error = Error; +// type Future = FutureResult; - fn into_future(self) -> Self::Future { - ok(self) - } -} +// fn into_future(self) -> Self::Future { +// ok(self) +// } +// } pub struct CookieIter<'a> { iter: header::GetAll<'a>, @@ -635,8 +635,8 @@ impl ResponseBuilder { /// `ResponseBuilder` can not be used after this call. pub fn streaming(&mut self, stream: S) -> Response where - S: Stream + 'static, - E: Into + 'static, + S: Stream> + Unpin + 'static, + E: Into + Unpin + 'static, { self.body(Body::from_message(BodyStream::new(stream))) } @@ -757,15 +757,15 @@ impl<'a> From<&'a ResponseHead> for ResponseBuilder { } } -impl IntoFuture for ResponseBuilder { - type Item = Response; - type Error = Error; - type Future = FutureResult; +// impl IntoFuture for ResponseBuilder { +// type Item = Response; +// type Error = Error; +// type Future = FutureResult; - fn into_future(mut self) -> Self::Future { - ok(self.finish()) - } -} +// fn into_future(mut self) -> Self::Future { +// ok(self.finish()) +// } +// } impl fmt::Debug for ResponseBuilder { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index 09b8077b3..65a0c7bd4 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -1,13 +1,15 @@ use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; use std::{fmt, io, net, rc}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_server_config::{ Io as ServerIo, IoStream, Protocol, ServerConfig as SrvConfig, }; -use actix_service::{IntoNewService, NewService, Service}; +use actix_service::{IntoServiceFactory, Service, ServiceFactory}; use bytes::{Buf, BufMut, Bytes, BytesMut}; -use futures::{try_ready, Async, Future, IntoFuture, Poll}; +use futures::{ready, Future}; use h2::server::{self, Handshake}; use crate::body::MessageBody; @@ -20,7 +22,7 @@ use crate::request::Request; use crate::response::Response; use crate::{h1, h2::Dispatcher}; -/// `NewService` HTTP1.1/HTTP2 transport implementation +/// `ServiceFactory` HTTP1.1/HTTP2 transport implementation pub struct HttpService> { srv: S, cfg: ServiceConfig, @@ -32,11 +34,13 @@ pub struct HttpService HttpService where - S: NewService, - S::Error: Into, + S: ServiceFactory, + S::Error: Into + Unpin + 'static, S::InitError: fmt::Debug, - S::Response: Into>, - ::Future: 'static, + S::Response: Into> + Unpin + 'static, + S::Future: Unpin, + S::Service: Unpin, + ::Future: Unpin + 'static, B: MessageBody + 'static, { /// Create builder for `HttpService` instance. @@ -47,20 +51,23 @@ where impl HttpService where - S: NewService, - S::Error: Into, + S: ServiceFactory, + S::Error: Into + Unpin + 'static, S::InitError: fmt::Debug, - S::Response: Into>, - ::Future: 'static, + S::Response: Into> + Unpin + 'static, + S::Future: Unpin, + S::Service: Unpin, + ::Future: Unpin + 'static, B: MessageBody + 'static, + P: Unpin, { /// Create new `HttpService` instance. - pub fn new>(service: F) -> Self { + pub fn new>(service: F) -> Self { let cfg = ServiceConfig::new(KeepAlive::Timeout(5), 5000, 0); HttpService { cfg, - srv: service.into_new_service(), + srv: service.into_factory(), expect: h1::ExpectHandler, upgrade: None, on_connect: None, @@ -69,13 +76,13 @@ where } /// Create new `HttpService` instance with config. - pub(crate) fn with_config>( + pub(crate) fn with_config>( cfg: ServiceConfig, service: F, ) -> Self { HttpService { cfg, - srv: service.into_new_service(), + srv: service.into_factory(), expect: h1::ExpectHandler, upgrade: None, on_connect: None, @@ -86,11 +93,15 @@ where impl HttpService where - S: NewService, - S::Error: Into, + S: ServiceFactory, + S::Error: Into + Unpin + 'static, S::InitError: fmt::Debug, - S::Response: Into>, + S::Response: Into> + Unpin + 'static, + S::Future: Unpin, + S::Service: Unpin, + ::Future: Unpin + 'static, B: MessageBody, + P: Unpin, { /// Provide service for `EXPECT: 100-Continue` support. /// @@ -99,9 +110,12 @@ where /// request will be forwarded to main service. pub fn expect(self, expect: X1) -> HttpService where - X1: NewService, + X1: ServiceFactory, X1::Error: Into, X1::InitError: fmt::Debug, + X1::Future: Unpin, + X1::Service: Unpin, + ::Future: Unpin + 'static, { HttpService { expect, @@ -119,13 +133,16 @@ where /// and this service get called with original request and framed object. pub fn upgrade(self, upgrade: Option) -> HttpService where - U1: NewService< + U1: ServiceFactory< Config = SrvConfig, Request = (Request, Framed), Response = (), >, U1::Error: fmt::Display, U1::InitError: fmt::Debug, + U1::Future: Unpin, + U1::Service: Unpin, + ::Future: Unpin + 'static, { HttpService { upgrade, @@ -147,25 +164,35 @@ where } } -impl NewService for HttpService +impl ServiceFactory for HttpService where - T: IoStream, - S: NewService, - S::Error: Into, + T: IoStream + Unpin, + S: ServiceFactory, + S::Service: Unpin, + S::Error: Into + Unpin + 'static, S::InitError: fmt::Debug, - S::Response: Into>, - ::Future: 'static, + S::Response: Into> + Unpin + 'static, + S::Future: Unpin, + S::Service: Unpin, + ::Future: Unpin + 'static, B: MessageBody + 'static, - X: NewService, + X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, - U: NewService< + X::Future: Unpin, + X::Service: Unpin, + ::Future: Unpin + 'static, + U: ServiceFactory< Config = SrvConfig, Request = (Request, Framed), Response = (), >, U::Error: fmt::Display, U::InitError: fmt::Debug, + U::Future: Unpin, + U::Service: Unpin, + ::Future: Unpin + 'static, + P: Unpin, { type Config = SrvConfig; type Request = ServerIo; @@ -177,7 +204,7 @@ where fn new_service(&self, cfg: &SrvConfig) -> Self::Future { HttpServiceResponse { - fut: self.srv.new_service(cfg).into_future(), + fut: self.srv.new_service(cfg), fut_ex: Some(self.expect.new_service(cfg)), fut_upg: self.upgrade.as_ref().map(|f| f.new_service(cfg)), expect: None, @@ -190,7 +217,14 @@ where } #[doc(hidden)] -pub struct HttpServiceResponse { +pub struct HttpServiceResponse< + T, + P, + S: ServiceFactory, + B, + X: ServiceFactory, + U: ServiceFactory, +> { fut: S::Future, fut_ex: Option, fut_upg: Option, @@ -204,50 +238,62 @@ pub struct HttpServiceResponse Future for HttpServiceResponse where T: IoStream, - S: NewService, - S::Error: Into, + S: ServiceFactory, + S::Error: Into + Unpin + 'static, S::InitError: fmt::Debug, - S::Response: Into>, - ::Future: 'static, + S::Response: Into> + Unpin + 'static, + S::Future: Unpin, + S::Service: Unpin, + ::Future: Unpin + 'static, B: MessageBody + 'static, - X: NewService, + X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, - U: NewService), Response = ()>, + X::Future: Unpin, + X::Service: Unpin, + ::Future: Unpin + 'static, + U: ServiceFactory), Response = ()>, U::Error: fmt::Display, U::InitError: fmt::Debug, + U::Future: Unpin, + U::Service: Unpin, + ::Future: Unpin + 'static, + P: Unpin, { - type Item = HttpServiceHandler; - type Error = (); + type Output = + Result, ()>; - fn poll(&mut self) -> Poll { - if let Some(ref mut fut) = self.fut_ex { - let expect = try_ready!(fut - .poll() - .map_err(|e| log::error!("Init http service error: {:?}", e))); - self.expect = Some(expect); - self.fut_ex.take(); + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.get_mut(); + + if let Some(ref mut fut) = this.fut_ex { + let expect = ready!(Pin::new(fut) + .poll(cx) + .map_err(|e| log::error!("Init http service error: {:?}", e)))?; + this.expect = Some(expect); + this.fut_ex.take(); } - if let Some(ref mut fut) = self.fut_upg { - let upgrade = try_ready!(fut - .poll() - .map_err(|e| log::error!("Init http service error: {:?}", e))); - self.upgrade = Some(upgrade); - self.fut_ex.take(); + if let Some(ref mut fut) = this.fut_upg { + let upgrade = ready!(Pin::new(fut) + .poll(cx) + .map_err(|e| log::error!("Init http service error: {:?}", e)))?; + this.upgrade = Some(upgrade); + this.fut_ex.take(); } - let service = try_ready!(self - .fut - .poll() + let result = ready!(Pin::new(&mut this.fut) + .poll(cx) .map_err(|e| log::error!("Init http service error: {:?}", e))); - Ok(Async::Ready(HttpServiceHandler::new( - self.cfg.take().unwrap(), - service, - self.expect.take().unwrap(), - self.upgrade.take(), - self.on_connect.clone(), - ))) + Poll::Ready(result.map(|service| { + HttpServiceHandler::new( + this.cfg.take().unwrap(), + service, + this.expect.take().unwrap(), + this.upgrade.take(), + this.on_connect.clone(), + ) + })) } } @@ -263,15 +309,19 @@ pub struct HttpServiceHandler { impl HttpServiceHandler where - S: Service, - S::Error: Into, + S: Service + Unpin, + S::Error: Into + Unpin + 'static, S::Future: 'static, - S::Response: Into>, + S::Response: Into> + Unpin + 'static, + S::Future: Unpin, B: MessageBody + 'static, - X: Service, + X: Service + Unpin, + X::Future: Unpin, X::Error: Into, - U: Service), Response = ()>, + U: Service), Response = ()> + Unpin, + U::Future: Unpin, U::Error: fmt::Display, + P: Unpin, { fn new( cfg: ServiceConfig, @@ -293,26 +343,29 @@ where impl Service for HttpServiceHandler where - T: IoStream, - S: Service, - S::Error: Into, - S::Future: 'static, - S::Response: Into>, + T: IoStream + Unpin, + S: Service + Unpin, + S::Error: Into + Unpin + 'static, + S::Future: Unpin + 'static, + S::Response: Into> + Unpin + 'static, B: MessageBody + 'static, - X: Service, + X: Service + Unpin, X::Error: Into, - U: Service), Response = ()>, + X::Future: Unpin, + U: Service), Response = ()> + Unpin, U::Error: fmt::Display, + U::Future: Unpin, + P: Unpin, { type Request = ServerIo; type Response = (); type Error = DispatchError; type Future = HttpServiceHandlerResponse; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { let ready = self .expect - .poll_ready() + .poll_ready(cx) .map_err(|e| { let e = e.into(); log::error!("Http service readiness error: {:?}", e); @@ -322,7 +375,7 @@ where let ready = self .srv - .poll_ready() + .poll_ready(cx) .map_err(|e| { let e = e.into(); log::error!("Http service readiness error: {:?}", e); @@ -332,9 +385,9 @@ where && ready; if ready { - Ok(Async::Ready(())) + Poll::Ready(Ok(())) } else { - Ok(Async::NotReady) + Poll::Pending } } @@ -391,15 +444,17 @@ where enum State where - S: Service, - S::Future: 'static, + S: Service + Unpin, + S::Future: Unpin + 'static, S::Error: Into, - T: IoStream, + T: IoStream + Unpin, B: MessageBody, - X: Service, + X: Service + Unpin, X::Error: Into, - U: Service), Response = ()>, + X::Future: Unpin, + U: Service), Response = ()> + Unpin, U::Error: fmt::Display, + U::Future: Unpin, { H1(h1::Dispatcher), H2(Dispatcher, S, B>), @@ -427,16 +482,18 @@ where pub struct HttpServiceHandlerResponse where - T: IoStream, - S: Service, - S::Error: Into, - S::Future: 'static, - S::Response: Into>, + T: IoStream + Unpin, + S: Service + Unpin, + S::Error: Into + Unpin + 'static, + S::Future: Unpin + 'static, + S::Response: Into> + Unpin + 'static, B: MessageBody + 'static, - X: Service, + X: Service + Unpin, X::Error: Into, - U: Service), Response = ()>, + X::Future: Unpin, + U: Service), Response = ()> + Unpin, U::Error: fmt::Display, + U::Future: Unpin, { state: State, } @@ -445,32 +502,33 @@ const HTTP2_PREFACE: [u8; 14] = *b"PRI * HTTP/2.0"; impl Future for HttpServiceHandlerResponse where - T: IoStream, - S: Service, - S::Error: Into, - S::Future: 'static, - S::Response: Into>, + T: IoStream + Unpin, + S: Service + Unpin, + S::Error: Into + Unpin + 'static, + S::Future: Unpin + 'static, + S::Response: Into> + Unpin + 'static, B: MessageBody, - X: Service, + X: Service + Unpin, + X::Future: Unpin, X::Error: Into, - U: Service), Response = ()>, + U: Service), Response = ()> + Unpin, + U::Future: Unpin, U::Error: fmt::Display, { - type Item = (); - type Error = DispatchError; + type Output = Result<(), DispatchError>; - fn poll(&mut self) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { match self.state { - State::H1(ref mut disp) => disp.poll(), - State::H2(ref mut disp) => disp.poll(), + State::H1(ref mut disp) => Pin::new(disp).poll(cx), + State::H2(ref mut disp) => Pin::new(disp).poll(cx), State::Unknown(ref mut data) => { if let Some(ref mut item) = data { loop { // Safety - we only write to the returned slice. let b = unsafe { item.1.bytes_mut() }; - let n = try_ready!(item.0.poll_read(b)); + let n = ready!(Pin::new(&mut item.0).poll_read(cx, b))?; if n == 0 { - return Ok(Async::Ready(())); + return Poll::Ready(Ok(())); } // Safety - we know that 'n' bytes have // been initialized via the contract of @@ -511,17 +569,17 @@ where on_connect, )) } - self.poll() + self.poll(cx) } State::Handshake(ref mut data) => { let conn = if let Some(ref mut item) = data { - match item.0.poll() { - Ok(Async::Ready(conn)) => conn, - Ok(Async::NotReady) => return Ok(Async::NotReady), - Err(err) => { + match Pin::new(&mut item.0).poll(cx) { + Poll::Ready(Ok(conn)) => conn, + Poll::Ready(Err(err)) => { trace!("H2 handshake error: {}", err); - return Err(err.into()); + return Poll::Ready(Err(err.into())); } + Poll::Pending => return Poll::Pending, } } else { panic!() @@ -530,7 +588,7 @@ where self.state = State::H2(Dispatcher::new( srv, conn, on_connect, cfg, None, peer_addr, )); - self.poll() + self.poll(cx) } } } @@ -542,6 +600,8 @@ struct Io { inner: T, } +impl Unpin for Io {} + impl io::Read for Io { fn read(&mut self, buf: &mut [u8]) -> io::Result { if let Some(mut bytes) = self.unread.take() { @@ -567,22 +627,62 @@ impl io::Write for Io { } } -impl AsyncRead for Io { +impl AsyncRead for Io { + // unsafe fn initializer(&self) -> io::Initializer { + // self.get_mut().inner.initializer() + // } + unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { self.inner.prepare_uninitialized_buffer(buf) } + + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + Pin::new(&mut self.get_mut().inner).poll_read(cx, buf) + } + + // fn poll_read_vectored( + // self: Pin<&mut Self>, + // cx: &mut Context<'_>, + // bufs: &mut [io::IoSliceMut<'_>], + // ) -> Poll> { + // self.get_mut().inner.poll_read_vectored(cx, bufs) + // } } -impl AsyncWrite for Io { - fn shutdown(&mut self) -> Poll<(), io::Error> { - self.inner.shutdown() +impl tokio_io::AsyncWrite for Io { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(&mut self.get_mut().inner).poll_write(cx, buf) } - fn write_buf(&mut self, buf: &mut B) -> Poll { - self.inner.write_buf(buf) + + // fn poll_write_vectored( + // self: Pin<&mut Self>, + // cx: &mut Context<'_>, + // bufs: &[io::IoSlice<'_>], + // ) -> Poll> { + // self.get_mut().inner.poll_write_vectored(cx, bufs) + // } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.get_mut().inner).poll_flush(cx) + } + + fn poll_shutdown( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + Pin::new(&mut self.get_mut().inner).poll_shutdown(cx) } } -impl IoStream for Io { +impl actix_server_config::IoStream for Io { #[inline] fn peer_addr(&self) -> Option { self.inner.peer_addr() diff --git a/actix-http/src/test.rs b/actix-http/src/test.rs index ed5b81a35..817bf480d 100644 --- a/actix-http/src/test.rs +++ b/actix-http/src/test.rs @@ -1,12 +1,13 @@ //! Test Various helpers for Actix applications to use during testing. use std::fmt::Write as FmtWrite; use std::io; +use std::pin::Pin; use std::str::FromStr; +use std::task::{Context, Poll}; use actix_codec::{AsyncRead, AsyncWrite}; use actix_server_config::IoStream; use bytes::{Buf, Bytes, BytesMut}; -use futures::{Async, Poll}; use http::header::{self, HeaderName, HeaderValue}; use http::{HttpTryFrom, Method, Uri, Version}; use percent_encoding::percent_encode; @@ -244,16 +245,16 @@ impl io::Write for TestBuffer { } } -impl AsyncRead for TestBuffer {} +// impl AsyncRead for TestBuffer {} -impl AsyncWrite for TestBuffer { - fn shutdown(&mut self) -> Poll<(), io::Error> { - Ok(Async::Ready(())) - } - fn write_buf(&mut self, _: &mut B) -> Poll { - Ok(Async::NotReady) - } -} +// impl AsyncWrite for TestBuffer { +// fn shutdown(&mut self) -> Poll<(), io::Error> { +// Ok(Async::Ready(())) +// } +// fn write_buf(&mut self, _: &mut B) -> Poll { +// Ok(Async::NotReady) +// } +// } impl IoStream for TestBuffer { fn set_nodelay(&mut self, _nodelay: bool) -> io::Result<()> { From 9e95efcc1604e88825c9fdfca19378b911b28f40 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Mon, 18 Nov 2019 18:42:27 +0600 Subject: [PATCH 045/176] migrate client to std::future --- actix-http/src/client/connection.rs | 177 ++++++------ actix-http/src/client/connector.rs | 177 +++++++----- actix-http/src/client/h1proto.rs | 283 +++++++++---------- actix-http/src/client/h2proto.rs | 254 ++++++++--------- actix-http/src/client/pool.rs | 408 +++++++++++++--------------- actix-http/src/h1/utils.rs | 4 +- actix-http/src/lib.rs | 6 +- actix-http/src/test.rs | 37 ++- actix-http/src/ws/transport.rs | 26 +- 9 files changed, 674 insertions(+), 698 deletions(-) diff --git a/actix-http/src/client/connection.rs b/actix-http/src/client/connection.rs index d2b94b3e5..70ffff6c0 100644 --- a/actix-http/src/client/connection.rs +++ b/actix-http/src/client/connection.rs @@ -1,9 +1,10 @@ +use std::pin::Pin; +use std::task::{Context, Poll}; use std::{fmt, io, time}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; use bytes::{Buf, Bytes}; -use futures::future::{err, Either, Future, FutureResult}; -use futures::Poll; +use futures::future::{err, Either, Future, FutureExt, LocalBoxFuture, Ready}; use h2::client::SendRequest; use crate::body::MessageBody; @@ -22,7 +23,7 @@ pub(crate) enum ConnectionType { pub trait Connection { type Io: AsyncRead + AsyncWrite; - type Future: Future; + type Future: Future>; fn protocol(&self) -> Protocol; @@ -34,15 +35,16 @@ pub trait Connection { ) -> Self::Future; type TunnelFuture: Future< - Item = (ResponseHead, Framed), - Error = SendRequestError, + Output = Result<(ResponseHead, Framed), SendRequestError>, >; /// Send request, returns Response and Framed fn open_tunnel>(self, head: H) -> Self::TunnelFuture; } -pub(crate) trait ConnectionLifetime: AsyncRead + AsyncWrite + 'static { +pub(crate) trait ConnectionLifetime: + AsyncRead + AsyncWrite + Unpin + 'static +{ /// Close connection fn close(&mut self); @@ -91,11 +93,11 @@ impl IoConnection { impl Connection for IoConnection where - T: AsyncRead + AsyncWrite + 'static, + T: AsyncRead + AsyncWrite + Unpin + 'static, { type Io = T; type Future = - Box>; + LocalBoxFuture<'static, Result<(ResponseHead, Payload), SendRequestError>>; fn protocol(&self) -> Protocol { match self.io { @@ -111,38 +113,30 @@ where body: B, ) -> Self::Future { match self.io.take().unwrap() { - ConnectionType::H1(io) => Box::new(h1proto::send_request( - io, - head.into(), - body, - self.created, - self.pool, - )), - ConnectionType::H2(io) => Box::new(h2proto::send_request( - io, - head.into(), - body, - self.created, - self.pool, - )), + ConnectionType::H1(io) => { + h1proto::send_request(io, head.into(), body, self.created, self.pool) + .boxed_local() + } + ConnectionType::H2(io) => { + h2proto::send_request(io, head.into(), body, self.created, self.pool) + .boxed_local() + } } } type TunnelFuture = Either< - Box< - dyn Future< - Item = (ResponseHead, Framed), - Error = SendRequestError, - >, + LocalBoxFuture< + 'static, + Result<(ResponseHead, Framed), SendRequestError>, >, - FutureResult<(ResponseHead, Framed), SendRequestError>, + Ready), SendRequestError>>, >; /// Send request, returns Response and Framed fn open_tunnel>(mut self, head: H) -> Self::TunnelFuture { match self.io.take().unwrap() { ConnectionType::H1(io) => { - Either::A(Box::new(h1proto::open_tunnel(io, head.into()))) + Either::Left(h1proto::open_tunnel(io, head.into()).boxed_local()) } ConnectionType::H2(io) => { if let Some(mut pool) = self.pool.take() { @@ -152,7 +146,7 @@ where None, )); } - Either::B(err(SendRequestError::TunnelNotSupported)) + Either::Right(err(SendRequestError::TunnelNotSupported)) } } } @@ -166,12 +160,12 @@ pub(crate) enum EitherConnection { impl Connection for EitherConnection where - A: AsyncRead + AsyncWrite + 'static, - B: AsyncRead + AsyncWrite + 'static, + A: AsyncRead + AsyncWrite + Unpin + 'static, + B: AsyncRead + AsyncWrite + Unpin + 'static, { type Io = EitherIo; type Future = - Box>; + LocalBoxFuture<'static, Result<(ResponseHead, Payload), SendRequestError>>; fn protocol(&self) -> Protocol { match self { @@ -191,24 +185,22 @@ where } } - type TunnelFuture = Box< - dyn Future< - Item = (ResponseHead, Framed), - Error = SendRequestError, - >, + type TunnelFuture = LocalBoxFuture< + 'static, + Result<(ResponseHead, Framed), SendRequestError>, >; /// Send request, returns Response and Framed fn open_tunnel>(self, head: H) -> Self::TunnelFuture { match self { - EitherConnection::A(con) => Box::new( - con.open_tunnel(head) - .map(|(head, framed)| (head, framed.map_io(EitherIo::A))), - ), - EitherConnection::B(con) => Box::new( - con.open_tunnel(head) - .map(|(head, framed)| (head, framed.map_io(EitherIo::B))), - ), + EitherConnection::A(con) => con + .open_tunnel(head) + .map(|res| res.map(|(head, framed)| (head, framed.map_io(EitherIo::A)))) + .boxed_local(), + EitherConnection::B(con) => con + .open_tunnel(head) + .map(|res| res.map(|(head, framed)| (head, framed.map_io(EitherIo::B)))) + .boxed_local(), } } } @@ -218,24 +210,22 @@ pub enum EitherIo { B(B), } -impl io::Read for EitherIo -where - A: io::Read, - B: io::Read, -{ - fn read(&mut self, buf: &mut [u8]) -> io::Result { - match self { - EitherIo::A(ref mut val) => val.read(buf), - EitherIo::B(ref mut val) => val.read(buf), - } - } -} - impl AsyncRead for EitherIo where - A: AsyncRead, - B: AsyncRead, + A: AsyncRead + Unpin, + B: AsyncRead + Unpin, { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + match self.get_mut() { + EitherIo::A(ref mut val) => Pin::new(val).poll_read(cx, buf), + EitherIo::B(ref mut val) => Pin::new(val).poll_read(cx, buf), + } + } + unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { match self { EitherIo::A(ref val) => val.prepare_uninitialized_buffer(buf), @@ -244,45 +234,50 @@ where } } -impl io::Write for EitherIo -where - A: io::Write, - B: io::Write, -{ - fn write(&mut self, buf: &[u8]) -> io::Result { - match self { - EitherIo::A(ref mut val) => val.write(buf), - EitherIo::B(ref mut val) => val.write(buf), - } - } - - fn flush(&mut self) -> io::Result<()> { - match self { - EitherIo::A(ref mut val) => val.flush(), - EitherIo::B(ref mut val) => val.flush(), - } - } -} - impl AsyncWrite for EitherIo where - A: AsyncWrite, - B: AsyncWrite, + A: AsyncWrite + Unpin, + B: AsyncWrite + Unpin, { - fn shutdown(&mut self) -> Poll<(), io::Error> { - match self { - EitherIo::A(ref mut val) => val.shutdown(), - EitherIo::B(ref mut val) => val.shutdown(), + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context, + buf: &[u8], + ) -> Poll> { + match self.get_mut() { + EitherIo::A(ref mut val) => Pin::new(val).poll_write(cx, buf), + EitherIo::B(ref mut val) => Pin::new(val).poll_write(cx, buf), } } - fn write_buf(&mut self, buf: &mut U) -> Poll + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match self.get_mut() { + EitherIo::A(ref mut val) => Pin::new(val).poll_flush(cx), + EitherIo::B(ref mut val) => Pin::new(val).poll_flush(cx), + } + } + + fn poll_shutdown( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + match self.get_mut() { + EitherIo::A(ref mut val) => Pin::new(val).poll_shutdown(cx), + EitherIo::B(ref mut val) => Pin::new(val).poll_shutdown(cx), + } + } + + fn poll_write_buf( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut U, + ) -> Poll> where Self: Sized, { - match self { - EitherIo::A(ref mut val) => val.write_buf(buf), - EitherIo::B(ref mut val) => val.write_buf(buf), + match self.get_mut() { + EitherIo::A(ref mut val) => Pin::new(val).poll_write_buf(cx, buf), + EitherIo::B(ref mut val) => Pin::new(val).poll_write_buf(cx, buf), } } } diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index 4ae28ba68..7421cb02e 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -11,6 +11,7 @@ use actix_connect::{ }; use actix_service::{apply_fn, Service}; use actix_utils::timeout::{TimeoutError, TimeoutService}; +use futures::future::Ready; use http::Uri; use tokio_net::tcp::TcpStream; @@ -116,12 +117,13 @@ impl Connector { /// Use custom connector. pub fn connector(self, connector: T1) -> Connector where - U1: AsyncRead + AsyncWrite + fmt::Debug, + U1: AsyncRead + AsyncWrite + Unpin + fmt::Debug, T1: Service< Request = TcpConnect, Response = TcpConnection, Error = actix_connect::ConnectError, > + Clone, + T1::Future: Unpin, { Connector { connector, @@ -138,13 +140,12 @@ impl Connector { impl Connector where - U: AsyncRead + AsyncWrite + fmt::Debug + 'static, + U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static, T: Service< Request = TcpConnect, Response = TcpConnection, Error = actix_connect::ConnectError, - > + Clone - + 'static, + > + 'static, { /// Connection timeout, i.e. max time to connect to remote host including dns name resolution. /// Set to 1 second by default. @@ -220,7 +221,7 @@ where { let connector = TimeoutService::new( self.timeout, - apply_fn(self.connector, |msg: Connect, srv| { + apply_fn(UnpinWrapper(self.connector), |msg: Connect, srv| { srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr)) }) .map_err(ConnectError::from) @@ -337,10 +338,48 @@ where } } +#[derive(Clone)] +struct UnpinWrapper(T); + +impl Unpin for UnpinWrapper {} + +impl Service for UnpinWrapper { + type Request = T::Request; + type Response = T::Response; + type Error = T::Error; + type Future = UnpinWrapperFut; + + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.0.poll_ready(cx) + } + + fn call(&mut self, req: T::Request) -> Self::Future { + UnpinWrapperFut { + fut: self.0.call(req), + } + } +} + +struct UnpinWrapperFut { + fut: T::Future, +} + +impl Unpin for UnpinWrapperFut {} + +impl Future for UnpinWrapperFut { + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + unsafe { Pin::new_unchecked(&mut self.get_mut().fut) }.poll(cx) + } +} + #[cfg(not(any(feature = "ssl", feature = "rust-tls")))] mod connect_impl { - use futures::future::{err, Either, FutureResult}; - use futures::Poll; + use std::task::{Context, Poll}; + + use futures::future::{err, Either, Ready}; + use futures::ready; use super::*; use crate::client::connection::IoConnection; @@ -349,7 +388,7 @@ mod connect_impl { where Io: AsyncRead + AsyncWrite + 'static, T: Service - + Clone + + Unpin + 'static, { pub(crate) tcp_pool: ConnectionPool, @@ -359,7 +398,7 @@ mod connect_impl { where Io: AsyncRead + AsyncWrite + 'static, T: Service - + Clone + + Unpin + 'static, { fn clone(&self) -> Self { @@ -371,29 +410,30 @@ mod connect_impl { impl Service for InnerConnector where - Io: AsyncRead + AsyncWrite + 'static, + Io: AsyncRead + AsyncWrite + Unpin + 'static, T: Service - + Clone + + Unpin + 'static, + T::Future: Unpin, { type Request = Connect; type Response = IoConnection; type Error = ConnectError; type Future = Either< as Service>::Future, - FutureResult, ConnectError>, + Ready, ConnectError>>, >; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.tcp_pool.poll_ready() + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.tcp_pool.poll_ready(cx) } fn call(&mut self, req: Connect) -> Self::Future { match req.uri.scheme_str() { Some("https") | Some("wss") => { - Either::B(err(ConnectError::SslIsNotSupported)) + Either::Right(err(ConnectError::SslIsNotSupported)) } - _ => Either::A(self.tcp_pool.call(req)), + _ => Either::Left(self.tcp_pool.call(req)), } } } @@ -403,18 +443,20 @@ mod connect_impl { mod connect_impl { use std::marker::PhantomData; - use futures::future::{Either, FutureResult}; - use futures::{Async, Future, Poll}; + use futures::future::Either; + use futures::ready; use super::*; use crate::client::connection::EitherConnection; pub(crate) struct InnerConnector where - Io1: AsyncRead + AsyncWrite + 'static, - Io2: AsyncRead + AsyncWrite + 'static, + Io1: AsyncRead + AsyncWrite + Unpin + 'static, + Io2: AsyncRead + AsyncWrite + Unpin + 'static, T1: Service, T2: Service, + T1::Future: Unpin, + T2::Future: Unpin, { pub(crate) tcp_pool: ConnectionPool, pub(crate) ssl_pool: ConnectionPool, @@ -422,14 +464,16 @@ mod connect_impl { impl Clone for InnerConnector where - Io1: AsyncRead + AsyncWrite + 'static, - Io2: AsyncRead + AsyncWrite + 'static, + Io1: AsyncRead + AsyncWrite + Unpin + 'static, + Io2: AsyncRead + AsyncWrite + Unpin + 'static, T1: Service - + Clone + + Unpin + 'static, T2: Service - + Clone + + Unpin + 'static, + T1::Future: Unpin, + T2::Future: Unpin, { fn clone(&self) -> Self { InnerConnector { @@ -441,52 +485,50 @@ mod connect_impl { impl Service for InnerConnector where - Io1: AsyncRead + AsyncWrite + 'static, - Io2: AsyncRead + AsyncWrite + 'static, + Io1: AsyncRead + AsyncWrite + Unpin + 'static, + Io2: AsyncRead + AsyncWrite + Unpin + 'static, T1: Service - + Clone + + Unpin + 'static, T2: Service - + Clone + + Unpin + 'static, + T1::Future: Unpin, + T2::Future: Unpin, { type Request = Connect; type Response = EitherConnection; type Error = ConnectError; type Future = Either< - FutureResult, - Either< - InnerConnectorResponseA, - InnerConnectorResponseB, - >, + InnerConnectorResponseA, + InnerConnectorResponseB, >; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.tcp_pool.poll_ready() + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.tcp_pool.poll_ready(cx) } fn call(&mut self, req: Connect) -> Self::Future { match req.uri.scheme_str() { - Some("https") | Some("wss") => { - Either::B(Either::B(InnerConnectorResponseB { - fut: self.ssl_pool.call(req), - _t: PhantomData, - })) - } - _ => Either::B(Either::A(InnerConnectorResponseA { + Some("https") | Some("wss") => Either::B(InnerConnectorResponseB { + fut: self.ssl_pool.call(req), + _t: PhantomData, + }), + _ => Either::A(InnerConnectorResponseA { fut: self.tcp_pool.call(req), _t: PhantomData, - })), + }), } } } pub(crate) struct InnerConnectorResponseA where - Io1: AsyncRead + AsyncWrite + 'static, + Io1: AsyncRead + AsyncWrite + Unpin + 'static, T: Service - + Clone + + Unpin + 'static, + T::Future: Unpin, { fut: as Service>::Future, _t: PhantomData, @@ -495,28 +537,29 @@ mod connect_impl { impl Future for InnerConnectorResponseA where T: Service - + Clone + + Unpin + 'static, - Io1: AsyncRead + AsyncWrite + 'static, - Io2: AsyncRead + AsyncWrite + 'static, + T::Future: Unpin, + Io1: AsyncRead + AsyncWrite + Unpin + 'static, + Io2: AsyncRead + AsyncWrite + Unpin + 'static, { - type Item = EitherConnection; - type Error = ConnectError; + type Output = Result, ConnectError>; - fn poll(&mut self) -> Poll { - match self.fut.poll()? { - Async::NotReady => Ok(Async::NotReady), - Async::Ready(res) => Ok(Async::Ready(EitherConnection::A(res))), - } + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + Poll::Ready( + ready!(Pin::new(&mut self.get_mut().fut).poll(cx)) + .map(|res| EitherConnection::A(res)), + ) } } pub(crate) struct InnerConnectorResponseB where - Io2: AsyncRead + AsyncWrite + 'static, + Io2: AsyncRead + AsyncWrite + Unpin + 'static, T: Service - + Clone + + Unpin + 'static, + T::Future: Unpin, { fut: as Service>::Future, _t: PhantomData, @@ -525,19 +568,19 @@ mod connect_impl { impl Future for InnerConnectorResponseB where T: Service - + Clone + + Unpin + 'static, - Io1: AsyncRead + AsyncWrite + 'static, - Io2: AsyncRead + AsyncWrite + 'static, + T::Future: Unpin, + Io1: AsyncRead + AsyncWrite + Unpin + 'static, + Io2: AsyncRead + AsyncWrite + Unpin + 'static, { - type Item = EitherConnection; - type Error = ConnectError; + type Output = Result, ConnectError>; - fn poll(&mut self) -> Poll { - match self.fut.poll()? { - Async::NotReady => Ok(Async::NotReady), - Async::Ready(res) => Ok(Async::Ready(EitherConnection::B(res))), - } + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + Poll::Ready( + ready!(Pin::new(&mut self.get_mut().fut).poll(cx)) + .map(|res| EitherConnection::B(res)), + ) } } } diff --git a/actix-http/src/client/h1proto.rs b/actix-http/src/client/h1proto.rs index 14984253b..f8902a0ef 100644 --- a/actix-http/src/client/h1proto.rs +++ b/actix-http/src/client/h1proto.rs @@ -6,8 +6,8 @@ use std::{io, time}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; use bytes::{BufMut, Bytes, BytesMut}; -use futures::future::{ok, Either}; -use futures::{Sink, Stream}; +use futures::future::{ok, poll_fn, Either}; +use futures::{Sink, SinkExt, Stream, StreamExt}; use crate::error::PayloadError; use crate::h1; @@ -21,15 +21,15 @@ use super::error::{ConnectError, SendRequestError}; use super::pool::Acquired; use crate::body::{BodySize, MessageBody}; -pub(crate) fn send_request( +pub(crate) async fn send_request( io: T, mut head: RequestHeadType, body: B, created: time::Instant, pool: Option>, -) -> impl Future +) -> Result<(ResponseHead, Payload), SendRequestError> where - T: AsyncRead + AsyncWrite + 'static, + T: AsyncRead + AsyncWrite + Unpin + 'static, B: MessageBody, { // set request host header @@ -65,68 +65,98 @@ where io: Some(io), }; - let len = body.size(); - // create Framed and send request - Framed::new(io, h1::ClientCodec::default()) - .send((head, len).into()) - .from_err() - // send request body - .and_then(move |framed| match body.size() { - BodySize::None | BodySize::Empty | BodySize::Sized(0) => { - Either::A(ok(framed)) - } - _ => Either::B(SendBody::new(body, framed)), - }) - // read response and init read body - .and_then(|framed| { - framed - .into_future() - .map_err(|(e, _)| SendRequestError::from(e)) - .and_then(|(item, framed)| { - if let Some(res) = item { - match framed.get_codec().message_type() { - h1::MessageType::None => { - let force_close = !framed.get_codec().keepalive(); - release_connection(framed, force_close); - Ok((res, Payload::None)) - } - _ => { - let pl: PayloadStream = Box::new(PlStream::new(framed)); - Ok((res, pl.into())) - } - } - } else { - Err(ConnectError::Disconnected.into()) - } - }) - }) + let mut framed = Framed::new(io, h1::ClientCodec::default()); + framed.send((head, body.size()).into()).await?; + + // send request body + match body.size() { + BodySize::None | BodySize::Empty | BodySize::Sized(0) => (), + _ => send_body(body, &mut framed).await?, + }; + + // read response and init read body + let (head, framed) = if let (Some(result), framed) = framed.into_future().await { + let item = result.map_err(SendRequestError::from)?; + (item, framed) + } else { + return Err(SendRequestError::from(ConnectError::Disconnected)); + }; + + match framed.get_codec().message_type() { + h1::MessageType::None => { + let force_close = !framed.get_codec().keepalive(); + release_connection(framed, force_close); + Ok((head, Payload::None)) + } + _ => { + let pl: PayloadStream = PlStream::new(framed).boxed_local(); + Ok((head, pl.into())) + } + } } -pub(crate) fn open_tunnel( +pub(crate) async fn open_tunnel( io: T, head: RequestHeadType, -) -> impl Future), Error = SendRequestError> +) -> Result<(ResponseHead, Framed), SendRequestError> where - T: AsyncRead + AsyncWrite + 'static, + T: AsyncRead + AsyncWrite + Unpin + 'static, { // create Framed and send request - Framed::new(io, h1::ClientCodec::default()) - .send((head, BodySize::None).into()) - .from_err() - // read response - .and_then(|framed| { - framed - .into_future() - .map_err(|(e, _)| SendRequestError::from(e)) - .and_then(|(head, framed)| { - if let Some(head) = head { - Ok((head, framed)) + let mut framed = Framed::new(io, h1::ClientCodec::default()); + framed.send((head, BodySize::None).into()).await?; + + // read response + if let (Some(result), framed) = framed.into_future().await { + let head = result.map_err(SendRequestError::from)?; + Ok((head, framed)) + } else { + Err(SendRequestError::from(ConnectError::Disconnected)) + } +} + +/// send request body to the peer +pub(crate) async fn send_body( + mut body: B, + framed: &mut Framed, +) -> Result<(), SendRequestError> +where + I: ConnectionLifetime, + B: MessageBody, +{ + let mut eof = false; + while !eof { + while !eof && !framed.is_write_buf_full() { + match poll_fn(|cx| body.poll_next(cx)).await { + Some(result) => { + framed.write(h1::Message::Chunk(Some(result?)))?; + } + None => { + eof = true; + framed.write(h1::Message::Chunk(None))?; + } + } + } + + if !framed.is_write_buf_empty() { + poll_fn(|cx| match framed.flush(cx) { + Poll::Ready(Ok(_)) => Poll::Ready(Ok(())), + Poll::Ready(Err(err)) => Poll::Ready(Err(err)), + Poll::Pending => { + if !framed.is_write_buf_full() { + Poll::Ready(Ok(())) } else { - Err(SendRequestError::from(ConnectError::Disconnected)) + Poll::Pending } - }) - }) + } + }) + .await?; + } + } + + SinkExt::flush(framed).await?; + Ok(()) } #[doc(hidden)] @@ -137,7 +167,10 @@ pub struct H1Connection { pool: Option>, } -impl ConnectionLifetime for H1Connection { +impl ConnectionLifetime for H1Connection +where + T: AsyncRead + AsyncWrite + Unpin + 'static, +{ /// Close connection fn close(&mut self) { if let Some(mut pool) = self.pool.take() { @@ -165,98 +198,41 @@ impl ConnectionLifetime for H1Connection } } -impl io::Read for H1Connection { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.io.as_mut().unwrap().read(buf) +impl AsyncRead for H1Connection { + unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { + self.io.as_ref().unwrap().prepare_uninitialized_buffer(buf) + } + + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + Pin::new(&mut self.io.as_mut().unwrap()).poll_read(cx, buf) } } -impl AsyncRead for H1Connection {} - -impl io::Write for H1Connection { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.io.as_mut().unwrap().write(buf) +impl AsyncWrite for H1Connection { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(&mut self.io.as_mut().unwrap()).poll_write(cx, buf) } - fn flush(&mut self) -> io::Result<()> { - self.io.as_mut().unwrap().flush() + fn poll_flush( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + Pin::new(self.io.as_mut().unwrap()).poll_flush(cx) } -} -impl AsyncWrite for H1Connection { - fn shutdown(&mut self) -> Poll<(), io::Error> { - self.io.as_mut().unwrap().shutdown() - } -} - -/// Future responsible for sending request body to the peer -pub(crate) struct SendBody { - body: Option, - framed: Option>, - flushed: bool, -} - -impl SendBody -where - I: AsyncRead + AsyncWrite + 'static, - B: MessageBody, -{ - pub(crate) fn new(body: B, framed: Framed) -> Self { - SendBody { - body: Some(body), - framed: Some(framed), - flushed: true, - } - } -} - -impl Future for SendBody -where - I: ConnectionLifetime, - B: MessageBody, -{ - type Item = Framed; - type Error = SendRequestError; - - fn poll(&mut self) -> Poll { - let mut body_ready = true; - loop { - while body_ready - && self.body.is_some() - && !self.framed.as_ref().unwrap().is_write_buf_full() - { - match self.body.as_mut().unwrap().poll_next()? { - Async::Ready(item) => { - // check if body is done - if item.is_none() { - let _ = self.body.take(); - } - self.flushed = false; - self.framed - .as_mut() - .unwrap() - .force_send(h1::Message::Chunk(item))?; - break; - } - Async::NotReady => body_ready = false, - } - } - - if !self.flushed { - match self.framed.as_mut().unwrap().poll_complete()? { - Async::Ready(_) => { - self.flushed = true; - continue; - } - Async::NotReady => return Ok(Async::NotReady), - } - } - - if self.body.is_none() { - return Ok(Async::Ready(self.framed.take().unwrap())); - } - return Ok(Async::NotReady); - } + fn poll_shutdown( + mut self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll> { + Pin::new(self.io.as_mut().unwrap()).poll_shutdown(cx) } } @@ -273,23 +249,24 @@ impl PlStream { } impl Stream for PlStream { - type Item = Bytes; - type Error = PayloadError; + type Item = Result; - fn poll(&mut self) -> Poll, Self::Error> { - match self.framed.as_mut().unwrap().poll()? { - Async::NotReady => Ok(Async::NotReady), - Async::Ready(Some(chunk)) => { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let this = self.get_mut(); + + match this.framed.as_mut().unwrap().next_item(cx)? { + Poll::Pending => Poll::Pending, + Poll::Ready(Some(chunk)) => { if let Some(chunk) = chunk { - Ok(Async::Ready(Some(chunk))) + Poll::Ready(Some(Ok(chunk))) } else { - let framed = self.framed.take().unwrap(); + let framed = this.framed.take().unwrap(); let force_close = !framed.get_codec().keepalive(); release_connection(framed, force_close); - Ok(Async::Ready(None)) + Poll::Ready(None) } } - Async::Ready(None) => Ok(Async::Ready(None)), + Poll::Ready(None) => Poll::Ready(None), } } } diff --git a/actix-http/src/client/h2proto.rs b/actix-http/src/client/h2proto.rs index 50d74fe1b..25299fd61 100644 --- a/actix-http/src/client/h2proto.rs +++ b/actix-http/src/client/h2proto.rs @@ -5,7 +5,7 @@ use std::time; use actix_codec::{AsyncRead, AsyncWrite}; use bytes::Bytes; -use futures::future::{err, Either}; +use futures::future::{err, poll_fn, Either}; use h2::{client::SendRequest, SendStream}; use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, TRANSFER_ENCODING}; use http::{request::Request, HttpTryFrom, Method, Version}; @@ -19,15 +19,15 @@ use super::connection::{ConnectionType, IoConnection}; use super::error::SendRequestError; use super::pool::Acquired; -pub(crate) fn send_request( - io: SendRequest, +pub(crate) async fn send_request( + mut io: SendRequest, head: RequestHeadType, body: B, created: time::Instant, pool: Option>, -) -> impl Future +) -> Result<(ResponseHead, Payload), SendRequestError> where - T: AsyncRead + AsyncWrite + 'static, + T: AsyncRead + AsyncWrite + Unpin + 'static, B: MessageBody, { trace!("Sending client request: {:?} {:?}", head, body.size()); @@ -38,158 +38,138 @@ where _ => false, }; - io.ready() - .map_err(SendRequestError::from) - .and_then(move |mut io| { - let mut req = Request::new(()); - *req.uri_mut() = head.as_ref().uri.clone(); - *req.method_mut() = head.as_ref().method.clone(); - *req.version_mut() = Version::HTTP_2; + let mut req = Request::new(()); + *req.uri_mut() = head.as_ref().uri.clone(); + *req.method_mut() = head.as_ref().method.clone(); + *req.version_mut() = Version::HTTP_2; - let mut skip_len = true; - // let mut has_date = false; + let mut skip_len = true; + // let mut has_date = false; - // Content length - let _ = match length { - BodySize::None => None, - BodySize::Stream => { - skip_len = false; - None - } - BodySize::Empty => req - .headers_mut() - .insert(CONTENT_LENGTH, HeaderValue::from_static("0")), - BodySize::Sized(len) => req.headers_mut().insert( - CONTENT_LENGTH, - HeaderValue::try_from(format!("{}", len)).unwrap(), - ), - BodySize::Sized64(len) => req.headers_mut().insert( - CONTENT_LENGTH, - HeaderValue::try_from(format!("{}", len)).unwrap(), - ), - }; + // Content length + let _ = match length { + BodySize::None => None, + BodySize::Stream => { + skip_len = false; + None + } + BodySize::Empty => req + .headers_mut() + .insert(CONTENT_LENGTH, HeaderValue::from_static("0")), + BodySize::Sized(len) => req.headers_mut().insert( + CONTENT_LENGTH, + HeaderValue::try_from(format!("{}", len)).unwrap(), + ), + BodySize::Sized64(len) => req.headers_mut().insert( + CONTENT_LENGTH, + HeaderValue::try_from(format!("{}", len)).unwrap(), + ), + }; - // Extracting extra headers from RequestHeadType. HeaderMap::new() does not allocate. - let (head, extra_headers) = match head { - RequestHeadType::Owned(head) => { - (RequestHeadType::Owned(head), HeaderMap::new()) - } - RequestHeadType::Rc(head, extra_headers) => ( - RequestHeadType::Rc(head, None), - extra_headers.unwrap_or_else(HeaderMap::new), - ), - }; + // Extracting extra headers from RequestHeadType. HeaderMap::new() does not allocate. + let (head, extra_headers) = match head { + RequestHeadType::Owned(head) => (RequestHeadType::Owned(head), HeaderMap::new()), + RequestHeadType::Rc(head, extra_headers) => ( + RequestHeadType::Rc(head, None), + extra_headers.unwrap_or_else(HeaderMap::new), + ), + }; - // merging headers from head and extra headers. - let headers = head - .as_ref() - .headers - .iter() - .filter(|(name, _)| !extra_headers.contains_key(*name)) - .chain(extra_headers.iter()); + // merging headers from head and extra headers. + let headers = head + .as_ref() + .headers + .iter() + .filter(|(name, _)| !extra_headers.contains_key(*name)) + .chain(extra_headers.iter()); - // copy headers - for (key, value) in headers { - match *key { - CONNECTION | TRANSFER_ENCODING => continue, // http2 specific - CONTENT_LENGTH if skip_len => continue, - // DATE => has_date = true, - _ => (), - } - req.headers_mut().append(key, value.clone()); + // copy headers + for (key, value) in headers { + match *key { + CONNECTION | TRANSFER_ENCODING => continue, // http2 specific + CONTENT_LENGTH if skip_len => continue, + // DATE => has_date = true, + _ => (), + } + req.headers_mut().append(key, value.clone()); + } + + let res = poll_fn(|cx| io.poll_ready(cx)).await; + if let Err(e) = res { + release(io, pool, created, e.is_io()); + return Err(SendRequestError::from(e)); + } + + let resp = match io.send_request(req, eof) { + Ok((fut, send)) => { + release(io, pool, created, false); + + if !eof { + send_body(body, send).await?; } + fut.await.map_err(SendRequestError::from)? + } + Err(e) => { + release(io, pool, created, e.is_io()); + return Err(e.into()); + } + }; - match io.send_request(req, eof) { - Ok((res, send)) => { - release(io, pool, created, false); + let (parts, body) = resp.into_parts(); + let payload = if head_req { Payload::None } else { body.into() }; - if !eof { - Either::A(Either::B( - SendBody { - body, - send, - buf: None, - } - .and_then(move |_| res.map_err(SendRequestError::from)), - )) - } else { - Either::B(res.map_err(SendRequestError::from)) - } - } - Err(e) => { - release(io, pool, created, e.is_io()); - Either::A(Either::A(err(e.into()))) - } - } - }) - .and_then(move |resp| { - let (parts, body) = resp.into_parts(); - let payload = if head_req { Payload::None } else { body.into() }; - - let mut head = ResponseHead::new(parts.status); - head.version = parts.version; - head.headers = parts.headers.into(); - Ok((head, payload)) - }) - .from_err() + let mut head = ResponseHead::new(parts.status); + head.version = parts.version; + head.headers = parts.headers.into(); + Ok((head, payload)) } -struct SendBody { - body: B, - send: SendStream, - buf: Option, -} - -impl Future for SendBody { - type Item = (); - type Error = SendRequestError; - - fn poll(&mut self) -> Poll { - loop { - if self.buf.is_none() { - match self.body.poll_next() { - Ok(Async::Ready(Some(buf))) => { - self.send.reserve_capacity(buf.len()); - self.buf = Some(buf); - } - Ok(Async::Ready(None)) => { - if let Err(e) = self.send.send_data(Bytes::new(), true) { - return Err(e.into()); - } - self.send.reserve_capacity(0); - return Ok(Async::Ready(())); - } - Ok(Async::NotReady) => return Ok(Async::NotReady), - Err(e) => return Err(e.into()), +async fn send_body( + mut body: B, + mut send: SendStream, +) -> Result<(), SendRequestError> { + let mut buf = None; + loop { + if buf.is_none() { + match poll_fn(|cx| body.poll_next(cx)).await { + Some(Ok(b)) => { + send.reserve_capacity(b.len()); + buf = Some(b); } - } - - match self.send.poll_capacity() { - Ok(Async::NotReady) => return Ok(Async::NotReady), - Ok(Async::Ready(None)) => return Ok(Async::Ready(())), - Ok(Async::Ready(Some(cap))) => { - let mut buf = self.buf.take().unwrap(); - let len = buf.len(); - let bytes = buf.split_to(std::cmp::min(cap, len)); - - if let Err(e) = self.send.send_data(bytes, false) { + Some(Err(e)) => return Err(e.into()), + None => { + if let Err(e) = send.send_data(Bytes::new(), true) { return Err(e.into()); - } else { - if !buf.is_empty() { - self.send.reserve_capacity(buf.len()); - self.buf = Some(buf); - } - continue; } + send.reserve_capacity(0); + return Ok(()); } - Err(e) => return Err(e.into()), } } + + match poll_fn(|cx| send.poll_capacity(cx)).await { + None => return Ok(()), + Some(Ok(cap)) => { + let b = buf.as_mut().unwrap(); + let len = b.len(); + let bytes = b.split_to(std::cmp::min(cap, len)); + + if let Err(e) = send.send_data(bytes, false) { + return Err(e.into()); + } else { + if !b.is_empty() { + send.reserve_capacity(b.len()); + } + continue; + } + } + Some(Err(e)) => return Err(e.into()), + } } } // release SendRequest object -fn release( +fn release( io: SendRequest, pool: Option>, created: time::Instant, diff --git a/actix-http/src/client/pool.rs b/actix-http/src/client/pool.rs index 4d02e0a13..ee4c4ab9f 100644 --- a/actix-http/src/client/pool.rs +++ b/actix-http/src/client/pool.rs @@ -9,11 +9,10 @@ use std::time::{Duration, Instant}; use actix_codec::{AsyncRead, AsyncWrite}; use actix_service::Service; -use actix_utils::oneshot; -use actix_utils::task::LocalWaker; +use actix_utils::{oneshot, task::LocalWaker}; use bytes::Bytes; -use futures::future::{err, ok, Either, FutureResult}; -use h2::client::{handshake, Handshake}; +use futures::future::{err, ok, poll_fn, Either, FutureExt, LocalBoxFuture, Ready}; +use h2::client::{handshake, Connection, SendRequest}; use hashbrown::HashMap; use http::uri::Authority; use indexmap::IndexSet; @@ -43,17 +42,15 @@ impl From for Key { } /// Connections pool -pub(crate) struct ConnectionPool( - T, - Rc>>, -); +pub(crate) struct ConnectionPool(Rc>, Rc>>); impl ConnectionPool where Io: AsyncRead + AsyncWrite + 'static, T: Service - + Clone + + Unpin + 'static, + T::Future: Unpin, { pub(crate) fn new( connector: T, @@ -63,7 +60,7 @@ where limit: usize, ) -> Self { ConnectionPool( - connector, + Rc::new(RefCell::new(connector)), Rc::new(RefCell::new(Inner { conn_lifetime, conn_keep_alive, @@ -73,7 +70,7 @@ where waiters: Slab::new(), waiters_queue: IndexSet::new(), available: HashMap::new(), - task: None, + waker: LocalWaker::new(), })), ) } @@ -81,8 +78,7 @@ where impl Clone for ConnectionPool where - T: Clone, - Io: AsyncRead + AsyncWrite + 'static, + Io: 'static, { fn clone(&self) -> Self { ConnectionPool(self.0.clone(), self.1.clone()) @@ -91,86 +87,118 @@ where impl Service for ConnectionPool where - Io: AsyncRead + AsyncWrite + 'static, + Io: AsyncRead + AsyncWrite + Unpin + 'static, T: Service - + Clone + + Unpin + 'static, + T::Future: Unpin, { type Request = Connect; type Response = IoConnection; type Error = ConnectError; - type Future = Either< - FutureResult, - Either, OpenConnection>, - >; + type Future = LocalBoxFuture<'static, Result, ConnectError>>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.0.poll_ready() + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.0.poll_ready(cx) } fn call(&mut self, req: Connect) -> Self::Future { - let key = if let Some(authority) = req.uri.authority_part() { - authority.clone().into() - } else { - return Either::A(err(ConnectError::Unresolverd)); + // start support future + tokio_executor::current_thread::spawn(ConnectorPoolSupport { + connector: self.0.clone(), + inner: self.1.clone(), + }); + + let mut connector = self.0.clone(); + let inner = self.1.clone(); + + let fut = async move { + let key = if let Some(authority) = req.uri.authority_part() { + authority.clone().into() + } else { + return Err(ConnectError::Unresolverd); + }; + + // acquire connection + match poll_fn(|cx| Poll::Ready(inner.borrow_mut().acquire(&key, cx))).await { + Acquire::Acquired(io, created) => { + // use existing connection + return Ok(IoConnection::new( + io, + created, + Some(Acquired(key, Some(inner))), + )); + } + Acquire::Available => { + // open tcp connection + let (io, proto) = connector.call(req).await?; + + let guard = OpenGuard::new(key, inner); + + if proto == Protocol::Http1 { + Ok(IoConnection::new( + ConnectionType::H1(io), + Instant::now(), + Some(guard.consume()), + )) + } else { + let (snd, connection) = handshake(io).await?; + tokio_executor::current_thread::spawn(connection.map(|_| ())); + Ok(IoConnection::new( + ConnectionType::H2(snd), + Instant::now(), + Some(guard.consume()), + )) + } + } + _ => { + // connection is not available, wait + let (rx, token) = inner.borrow_mut().wait_for(req); + + let guard = WaiterGuard::new(key, token, inner); + let res = match rx.await { + Err(_) => Err(ConnectError::Disconnected), + Ok(res) => res, + }; + guard.consume(); + res + } + } }; - // acquire connection - match self.1.as_ref().borrow_mut().acquire(&key) { - Acquire::Acquired(io, created) => { - // use existing connection - return Either::A(ok(IoConnection::new( - io, - created, - Some(Acquired(key, Some(self.1.clone()))), - ))); - } - Acquire::Available => { - // open new connection - return Either::B(Either::B(OpenConnection::new( - key, - self.1.clone(), - self.0.call(req), - ))); - } - _ => (), - } - - // connection is not available, wait - let (rx, token, support) = self.1.as_ref().borrow_mut().wait_for(req); - - // start support future - if !support { - self.1.as_ref().borrow_mut().task = Some(AtomicTask::new()); - tokio_executor::current_thread::spawn(ConnectorPoolSupport { - connector: self.0.clone(), - inner: self.1.clone(), - }) - } - - Either::B(Either::A(WaitForConnection { - rx, - key, - token, - inner: Some(self.1.clone()), - })) + fut.boxed_local() } } -#[doc(hidden)] -pub struct WaitForConnection +struct WaiterGuard where - Io: AsyncRead + AsyncWrite + 'static, + Io: AsyncRead + AsyncWrite + Unpin + 'static, { key: Key, token: usize, - rx: oneshot::Receiver, ConnectError>>, inner: Option>>>, } -impl Drop for WaitForConnection +impl WaiterGuard where - Io: AsyncRead + AsyncWrite + 'static, + Io: AsyncRead + AsyncWrite + Unpin + 'static, +{ + fn new(key: Key, token: usize, inner: Rc>>) -> Self { + Self { + key, + token, + inner: Some(inner), + } + } + + fn consume(mut self) { + let _ = self.inner.take(); + } +} + +impl Drop for WaiterGuard +where + Io: AsyncRead + AsyncWrite + Unpin + 'static, { fn drop(&mut self) { if let Some(i) = self.inner.take() { @@ -181,113 +209,43 @@ where } } -impl Future for WaitForConnection +struct OpenGuard where - Io: AsyncRead + AsyncWrite, + Io: AsyncRead + AsyncWrite + Unpin + 'static, { - type Item = IoConnection; - type Error = ConnectError; - - fn poll(&mut self) -> Poll { - match self.rx.poll() { - Ok(Async::Ready(item)) => match item { - Err(err) => Err(err), - Ok(conn) => { - let _ = self.inner.take(); - Ok(Async::Ready(conn)) - } - }, - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(_) => { - let _ = self.inner.take(); - Err(ConnectError::Disconnected) - } - } - } -} - -#[doc(hidden)] -pub struct OpenConnection -where - Io: AsyncRead + AsyncWrite + 'static, -{ - fut: F, key: Key, - h2: Option>, inner: Option>>>, } -impl OpenConnection +impl OpenGuard where - F: Future, - Io: AsyncRead + AsyncWrite + 'static, + Io: AsyncRead + AsyncWrite + Unpin + 'static, { - fn new(key: Key, inner: Rc>>, fut: F) -> Self { - OpenConnection { + fn new(key: Key, inner: Rc>>) -> Self { + Self { key, - fut, inner: Some(inner), - h2: None, } } + + fn consume(mut self) -> Acquired { + Acquired(self.key.clone(), self.inner.take()) + } } -impl Drop for OpenConnection +impl Drop for OpenGuard where - Io: AsyncRead + AsyncWrite + 'static, + Io: AsyncRead + AsyncWrite + Unpin + 'static, { fn drop(&mut self) { - if let Some(inner) = self.inner.take() { - let mut inner = inner.as_ref().borrow_mut(); + if let Some(i) = self.inner.take() { + let mut inner = i.as_ref().borrow_mut(); inner.release(); inner.check_availibility(); } } } -impl Future for OpenConnection -where - F: Future, - Io: AsyncRead + AsyncWrite, -{ - type Item = IoConnection; - type Error = ConnectError; - - fn poll(&mut self) -> Poll { - if let Some(ref mut h2) = self.h2 { - return match h2.poll() { - Ok(Async::Ready((snd, connection))) => { - tokio_executor::current_thread::spawn(connection.map_err(|_| ())); - Ok(Async::Ready(IoConnection::new( - ConnectionType::H2(snd), - Instant::now(), - Some(Acquired(self.key.clone(), self.inner.take())), - ))) - } - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(e) => Err(e.into()), - }; - } - - match self.fut.poll() { - Err(err) => Err(err), - Ok(Async::Ready((io, proto))) => { - if proto == Protocol::Http1 { - Ok(Async::Ready(IoConnection::new( - ConnectionType::H1(io), - Instant::now(), - Some(Acquired(self.key.clone(), self.inner.take())), - ))) - } else { - self.h2 = Some(handshake(io)); - self.poll() - } - } - Ok(Async::NotReady) => Ok(Async::NotReady), - } - } -} - enum Acquire { Acquired(ConnectionType, Instant), Available, @@ -314,7 +272,7 @@ pub(crate) struct Inner { )>, >, waiters_queue: IndexSet<(Key, usize)>, - task: Option, + waker: LocalWaker, } impl Inner { @@ -334,7 +292,7 @@ impl Inner { impl Inner where - Io: AsyncRead + AsyncWrite + 'static, + Io: AsyncRead + AsyncWrite + Unpin + 'static, { /// connection is not available, wait fn wait_for( @@ -343,7 +301,6 @@ where ) -> ( oneshot::Receiver, ConnectError>>, usize, - bool, ) { let (tx, rx) = oneshot::channel(); @@ -353,10 +310,10 @@ where entry.insert(Some((connect, tx))); assert!(self.waiters_queue.insert((key, token))); - (rx, token, self.task.is_some()) + (rx, token) } - fn acquire(&mut self, key: &Key) -> Acquire { + fn acquire(&mut self, key: &Key, cx: &mut Context) -> Acquire { // check limits if self.limit > 0 && self.acquired >= self.limit { return Acquire::NotAvailable; @@ -384,9 +341,9 @@ where let mut io = conn.io; let mut buf = [0; 2]; if let ConnectionType::H1(ref mut s) = io { - match s.read(&mut buf) { - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => (), - Ok(n) if n > 0 => { + match Pin::new(s).poll_read(cx, &mut buf) { + Poll::Pending => (), + Poll::Ready(Ok(n)) if n > 0 => { if let Some(timeout) = self.disconnect_timeout { if let ConnectionType::H1(io) = io { tokio_executor::current_thread::spawn( @@ -396,7 +353,7 @@ where } continue; } - Ok(_) | Err(_) => continue, + _ => continue, } } return Acquire::Acquired(io, conn.created); @@ -431,9 +388,7 @@ where fn check_availibility(&self) { if !self.waiters_queue.is_empty() && self.acquired < self.limit { - if let Some(t) = self.task.as_ref() { - t.notify() - } + self.waker.wake(); } } } @@ -457,17 +412,16 @@ where impl Future for CloseConnection where - T: AsyncWrite, + T: AsyncWrite + Unpin, { - type Item = (); - type Error = (); + type Output = (); - fn poll(&mut self) -> Poll<(), ()> { - match self.timeout.poll() { - Ok(Async::Ready(_)) | Err(_) => Ok(Async::Ready(())), - Ok(Async::NotReady) => match self.io.shutdown() { - Ok(Async::Ready(_)) | Err(_) => Ok(Async::Ready(())), - Ok(Async::NotReady) => Ok(Async::NotReady), + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> { + match Pin::new(&mut self.timeout).poll(cx) { + Poll::Ready(_) => Poll::Ready(()), + Poll::Pending => match Pin::new(&mut self.io).poll_shutdown(cx) { + Poll::Ready(_) => Poll::Ready(()), + Poll::Pending => Poll::Pending, }, } } @@ -483,16 +437,18 @@ where impl Future for ConnectorPoolSupport where - Io: AsyncRead + AsyncWrite + 'static, - T: Service, - T::Future: 'static, + Io: AsyncRead + AsyncWrite + Unpin + 'static, + T: Service + + Unpin, + T::Future: Unpin + 'static, { - type Item = (); - type Error = (); + type Output = (); - fn poll(&mut self) -> Poll { - let mut inner = self.inner.as_ref().borrow_mut(); - inner.task.as_ref().unwrap().register(); + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.get_mut(); + + let mut inner = this.inner.as_ref().borrow_mut(); + inner.waker.register(cx.waker()); // check waiters loop { @@ -507,14 +463,14 @@ where continue; } - match inner.acquire(&key) { + match inner.acquire(&key, cx) { Acquire::NotAvailable => break, Acquire::Acquired(io, created) => { let tx = inner.waiters.get_mut(token).unwrap().take().unwrap().1; if let Err(conn) = tx.send(Ok(IoConnection::new( io, created, - Some(Acquired(key.clone(), Some(self.inner.clone()))), + Some(Acquired(key.clone(), Some(this.inner.clone()))), ))) { let (io, created) = conn.unwrap().into_inner(); inner.release_conn(&key, io, created); @@ -526,33 +482,38 @@ where OpenWaitingConnection::spawn( key.clone(), tx, - self.inner.clone(), - self.connector.call(connect), + this.inner.clone(), + this.connector.call(connect), ); } } let _ = inner.waiters_queue.swap_remove_index(0); } - Ok(Async::NotReady) + Poll::Pending } } struct OpenWaitingConnection where - Io: AsyncRead + AsyncWrite + 'static, + Io: AsyncRead + AsyncWrite + Unpin + 'static, { fut: F, key: Key, - h2: Option>, + h2: Option< + LocalBoxFuture< + 'static, + Result<(SendRequest, Connection), h2::Error>, + >, + >, rx: Option, ConnectError>>>, inner: Option>>>, } impl OpenWaitingConnection where - F: Future + 'static, - Io: AsyncRead + AsyncWrite + 'static, + F: Future> + Unpin + 'static, + Io: AsyncRead + AsyncWrite + Unpin + 'static, { fn spawn( key: Key, @@ -572,7 +533,7 @@ where impl Drop for OpenWaitingConnection where - Io: AsyncRead + AsyncWrite + 'static, + Io: AsyncRead + AsyncWrite + Unpin + 'static, { fn drop(&mut self) { if let Some(inner) = self.inner.take() { @@ -585,59 +546,60 @@ where impl Future for OpenWaitingConnection where - F: Future, - Io: AsyncRead + AsyncWrite, + F: Future> + Unpin, + Io: AsyncRead + AsyncWrite + Unpin, { - type Item = (); - type Error = (); + type Output = (); - fn poll(&mut self) -> Poll { - if let Some(ref mut h2) = self.h2 { - return match h2.poll() { - Ok(Async::Ready((snd, connection))) => { - tokio_executor::current_thread::spawn(connection.map_err(|_| ())); - let rx = self.rx.take().unwrap(); + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.get_mut(); + + if let Some(ref mut h2) = this.h2 { + return match Pin::new(h2).poll(cx) { + Poll::Ready(Ok((snd, connection))) => { + tokio_executor::current_thread::spawn(connection.map(|_| ())); + let rx = this.rx.take().unwrap(); let _ = rx.send(Ok(IoConnection::new( ConnectionType::H2(snd), Instant::now(), - Some(Acquired(self.key.clone(), self.inner.take())), + Some(Acquired(this.key.clone(), this.inner.take())), ))); - Ok(Async::Ready(())) + Poll::Ready(()) } - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(err) => { - let _ = self.inner.take(); - if let Some(rx) = self.rx.take() { + Poll::Pending => Poll::Pending, + Poll::Ready(Err(err)) => { + let _ = this.inner.take(); + if let Some(rx) = this.rx.take() { let _ = rx.send(Err(ConnectError::H2(err))); } - Err(()) + Poll::Ready(()) } }; } - match self.fut.poll() { - Err(err) => { - let _ = self.inner.take(); - if let Some(rx) = self.rx.take() { + match Pin::new(&mut this.fut).poll(cx) { + Poll::Ready(Err(err)) => { + let _ = this.inner.take(); + if let Some(rx) = this.rx.take() { let _ = rx.send(Err(err)); } - Err(()) + Poll::Ready(()) } - Ok(Async::Ready((io, proto))) => { + Poll::Ready(Ok((io, proto))) => { if proto == Protocol::Http1 { - let rx = self.rx.take().unwrap(); + let rx = this.rx.take().unwrap(); let _ = rx.send(Ok(IoConnection::new( ConnectionType::H1(io), Instant::now(), - Some(Acquired(self.key.clone(), self.inner.take())), + Some(Acquired(this.key.clone(), this.inner.take())), ))); - Ok(Async::Ready(())) + Poll::Ready(()) } else { - self.h2 = Some(handshake(io)); - self.poll() + this.h2 = Some(handshake(io).boxed_local()); + Pin::new(this).poll(cx) } } - Ok(Async::NotReady) => Ok(Async::NotReady), + Poll::Pending => Poll::Pending, } } } @@ -646,7 +608,7 @@ pub(crate) struct Acquired(Key, Option>>>); impl Acquired where - T: AsyncRead + AsyncWrite + 'static, + T: AsyncRead + AsyncWrite + Unpin + 'static, { pub(crate) fn close(&mut self, conn: IoConnection) { if let Some(inner) = self.1.take() { diff --git a/actix-http/src/h1/utils.rs b/actix-http/src/h1/utils.rs index bc6914d3b..a992089c0 100644 --- a/actix-http/src/h1/utils.rs +++ b/actix-http/src/h1/utils.rs @@ -55,7 +55,7 @@ where if item.is_none() { let _ = this.body.take(); } - framed.force_send(Message::Chunk(item))?; + framed.write(Message::Chunk(item))?; } Poll::Pending => body_ready = false, } @@ -78,7 +78,7 @@ where // send response if let Some(res) = this.res.take() { - framed.force_send(res)?; + framed.write(res)?; continue; } diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs index cf528aeec..4d17347db 100644 --- a/actix-http/src/lib.rs +++ b/actix-http/src/lib.rs @@ -13,7 +13,7 @@ extern crate log; pub mod body; mod builder; -// pub mod client; +pub mod client; mod cloneable; mod config; pub mod encoding; @@ -32,8 +32,8 @@ pub mod cookie; pub mod error; pub mod h1; pub mod h2; -// pub mod test; -// pub mod ws; +pub mod test; +pub mod ws; pub use self::builder::HttpServiceBuilder; pub use self::config::{KeepAlive, ServiceConfig}; diff --git a/actix-http/src/test.rs b/actix-http/src/test.rs index 817bf480d..26f2c223f 100644 --- a/actix-http/src/test.rs +++ b/actix-http/src/test.rs @@ -1,6 +1,6 @@ //! Test Various helpers for Actix applications to use during testing. use std::fmt::Write as FmtWrite; -use std::io; +use std::io::{self, Read, Write}; use std::pin::Pin; use std::str::FromStr; use std::task::{Context, Poll}; @@ -245,16 +245,33 @@ impl io::Write for TestBuffer { } } -// impl AsyncRead for TestBuffer {} +impl AsyncRead for TestBuffer { + fn poll_read( + self: Pin<&mut Self>, + _: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + Poll::Ready(self.get_mut().read(buf)) + } +} -// impl AsyncWrite for TestBuffer { -// fn shutdown(&mut self) -> Poll<(), io::Error> { -// Ok(Async::Ready(())) -// } -// fn write_buf(&mut self, _: &mut B) -> Poll { -// Ok(Async::NotReady) -// } -// } +impl AsyncWrite for TestBuffer { + fn poll_write( + self: Pin<&mut Self>, + _: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Poll::Ready(self.get_mut().write(buf)) + } + + fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn poll_shutdown(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } +} impl IoStream for TestBuffer { fn set_nodelay(&mut self, _nodelay: bool) -> io::Result<()> { diff --git a/actix-http/src/ws/transport.rs b/actix-http/src/ws/transport.rs index da7782be5..c55e2eebd 100644 --- a/actix-http/src/ws/transport.rs +++ b/actix-http/src/ws/transport.rs @@ -1,24 +1,27 @@ +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; + use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_service::{IntoService, Service}; use actix_utils::framed::{FramedTransport, FramedTransportError}; -use futures::{Future, Poll}; use super::{Codec, Frame, Message}; pub struct Transport where S: Service + 'static, - T: AsyncRead + AsyncWrite, + T: AsyncRead + AsyncWrite + Unpin, { inner: FramedTransport, } impl Transport where - T: AsyncRead + AsyncWrite, - S: Service, + T: AsyncRead + AsyncWrite + Unpin, + S: Service + Unpin, S::Future: 'static, - S::Error: 'static, + S::Error: Unpin + 'static, { pub fn new>(io: T, service: F) -> Self { Transport { @@ -35,15 +38,14 @@ where impl Future for Transport where - T: AsyncRead + AsyncWrite, - S: Service, + T: AsyncRead + AsyncWrite + Unpin, + S: Service + Unpin, S::Future: 'static, - S::Error: 'static, + S::Error: Unpin + 'static, { - type Item = (); - type Error = FramedTransportError; + type Output = Result<(), FramedTransportError>; - fn poll(&mut self) -> Poll { - self.inner.poll() + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + Pin::new(&mut self.inner).poll(cx) } } From a6a2d2f444fc9c7c744c44a7ccf53aab6707c1e5 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Mon, 18 Nov 2019 20:40:10 +0600 Subject: [PATCH 046/176] update ssl impls --- actix-http/Cargo.toml | 4 +- actix-http/src/client/connector.rs | 126 +++++++++++++----------- actix-http/src/cookie/secure/key.rs | 47 +++++---- actix-http/src/cookie/secure/private.rs | 38 ++++--- actix-http/src/cookie/secure/signed.rs | 9 +- actix-http/src/h1/payload.rs | 11 +-- actix-http/tests/test_server.rs | 35 ++++--- 7 files changed, 150 insertions(+), 120 deletions(-) diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 1cc5e43a1..742938d5e 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -27,7 +27,7 @@ path = "src/lib.rs" default = [] # openssl -openssl = ["open-ssl", "actix-connect/openssl"] +openssl = ["open-ssl", "actix-connect/openssl", "tokio-openssl"] # rustls support rustls = ["rust-tls", "webpki-roots", "actix-connect/rustls"] @@ -100,6 +100,8 @@ flate2 = { version="1.0.7", optional = true, default-features = false } # optional deps failure = { version = "0.1.5", optional = true } open-ssl = { version="0.10", package="openssl", optional = true } +tokio-openssl = { version = "0.4.0-alpha.6", optional = true } + rust-tls = { version = "0.16.0", package="rustls", optional = true } webpki-roots = { version = "0.18", optional = true } diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index 7421cb02e..45625ca9f 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -20,22 +20,22 @@ use super::error::ConnectError; use super::pool::{ConnectionPool, Protocol}; use super::Connect; -#[cfg(feature = "ssl")] -use openssl::ssl::SslConnector as OpensslConnector; +#[cfg(feature = "openssl")] +use open_ssl::ssl::SslConnector as OpensslConnector; -#[cfg(feature = "rust-tls")] -use rustls::ClientConfig; -#[cfg(feature = "rust-tls")] +#[cfg(feature = "rustls")] +use rust_tls::ClientConfig; +#[cfg(feature = "rustls")] use std::sync::Arc; -#[cfg(any(feature = "ssl", feature = "rust-tls"))] +#[cfg(any(feature = "openssl", feature = "rustls"))] enum SslConnector { - #[cfg(feature = "ssl")] + #[cfg(feature = "openssl")] Openssl(OpensslConnector), - #[cfg(feature = "rust-tls")] + #[cfg(feature = "rustls")] Rustls(Arc), } -#[cfg(not(any(feature = "ssl", feature = "rust-tls")))] +#[cfg(not(any(feature = "openssl", feature = "rustls")))] type SslConnector = (); /// Manages http client network connectivity @@ -76,9 +76,9 @@ impl Connector<(), ()> { TcpStream, > { let ssl = { - #[cfg(feature = "ssl")] + #[cfg(feature = "openssl")] { - use openssl::ssl::SslMethod; + use open_ssl::ssl::SslMethod; let mut ssl = OpensslConnector::builder(SslMethod::tls()).unwrap(); let _ = ssl @@ -86,7 +86,7 @@ impl Connector<(), ()> { .map_err(|e| error!("Can not set alpn protocol: {:?}", e)); SslConnector::Openssl(ssl.build()) } - #[cfg(all(not(feature = "ssl"), feature = "rust-tls"))] + #[cfg(all(not(feature = "openssl"), feature = "rustls"))] { let protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; let mut config = ClientConfig::new(); @@ -96,7 +96,7 @@ impl Connector<(), ()> { .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); SslConnector::Rustls(Arc::new(config)) } - #[cfg(not(any(feature = "ssl", feature = "rust-tls")))] + #[cfg(not(any(feature = "openssl", feature = "rustls")))] {} }; @@ -145,7 +145,8 @@ where Request = TcpConnect, Response = TcpConnection, Error = actix_connect::ConnectError, - > + 'static, + > + Clone + + 'static, { /// Connection timeout, i.e. max time to connect to remote host including dns name resolution. /// Set to 1 second by default. @@ -154,14 +155,14 @@ where self } - #[cfg(feature = "ssl")] + #[cfg(feature = "openssl")] /// Use custom `SslConnector` instance. pub fn ssl(mut self, connector: OpensslConnector) -> Self { self.ssl = SslConnector::Openssl(connector); self } - #[cfg(feature = "rust-tls")] + #[cfg(feature = "rustls")] pub fn rustls(mut self, connector: Arc) -> Self { self.ssl = SslConnector::Rustls(connector); self @@ -217,7 +218,7 @@ where self, ) -> impl Service + Clone { - #[cfg(not(any(feature = "ssl", feature = "rust-tls")))] + #[cfg(not(any(feature = "openssl", feature = "rustls")))] { let connector = TimeoutService::new( self.timeout, @@ -242,46 +243,49 @@ where ), } } - #[cfg(any(feature = "ssl", feature = "rust-tls"))] + #[cfg(any(feature = "openssl", feature = "rustls"))] { const H2: &[u8] = b"h2"; - #[cfg(feature = "ssl")] + #[cfg(feature = "openssl")] use actix_connect::ssl::OpensslConnector; - #[cfg(feature = "rust-tls")] + #[cfg(feature = "rustls")] use actix_connect::ssl::RustlsConnector; - use actix_service::boxed::service; - #[cfg(feature = "rust-tls")] - use rustls::Session; + use actix_service::{boxed::service, pipeline}; + #[cfg(feature = "rustls")] + use rust_tls::Session; let ssl_service = TimeoutService::new( self.timeout, - apply_fn(self.connector.clone(), |msg: Connect, srv| { - srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr)) - }) - .map_err(ConnectError::from) + pipeline( + apply_fn( + UnpinWrapper(self.connector.clone()), + |msg: Connect, srv| { + srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr)) + }, + ) + .map_err(ConnectError::from), + ) .and_then(match self.ssl { - #[cfg(feature = "ssl")] - SslConnector::Openssl(ssl) => service( - OpensslConnector::service(ssl) - .map_err(ConnectError::from) - .map(|stream| { - let sock = stream.into_parts().0; - let h2 = sock - .get_ref() - .ssl() - .selected_alpn_protocol() - .map(|protos| protos.windows(2).any(|w| w == H2)) - .unwrap_or(false); - if h2 { - (Box::new(sock) as Box, Protocol::Http2) - } else { - (Box::new(sock) as Box, Protocol::Http1) - } - }), - ), - #[cfg(feature = "rust-tls")] + #[cfg(feature = "openssl")] + SslConnector::Openssl(ssl) => OpensslConnector::service(ssl) + .map(|stream| { + let sock = stream.into_parts().0; + let h2 = sock + .ssl() + .selected_alpn_protocol() + .map(|protos| protos.windows(2).any(|w| w == H2)) + .unwrap_or(false); + if h2 { + (Box::new(sock) as Box, Protocol::Http2) + } else { + (Box::new(sock) as Box, Protocol::Http1) + } + }) + .map_err(ConnectError::from), + + #[cfg(feature = "rustls")] SslConnector::Rustls(ssl) => service( - RustlsConnector::service(ssl) + UnpinWrapper(RustlsConnector::service(ssl)) .map_err(ConnectError::from) .map(|stream| { let sock = stream.into_parts().0; @@ -292,9 +296,15 @@ where .map(|protos| protos.windows(2).any(|w| w == H2)) .unwrap_or(false); if h2 { - (Box::new(sock) as Box, Protocol::Http2) + ( + Box::new(sock) as Box, + Protocol::Http2, + ) } else { - (Box::new(sock) as Box, Protocol::Http1) + ( + Box::new(sock) as Box, + Protocol::Http1, + ) } }), ), @@ -307,7 +317,7 @@ where let tcp_service = TimeoutService::new( self.timeout, - apply_fn(self.connector.clone(), |msg: Connect, srv| { + apply_fn(UnpinWrapper(self.connector), |msg: Connect, srv| { srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr)) }) .map_err(ConnectError::from) @@ -339,11 +349,11 @@ where } #[derive(Clone)] -struct UnpinWrapper(T); +struct UnpinWrapper(T); -impl Unpin for UnpinWrapper {} +impl Unpin for UnpinWrapper {} -impl Service for UnpinWrapper { +impl Service for UnpinWrapper { type Request = T::Request; type Response = T::Response; type Error = T::Error; @@ -374,7 +384,7 @@ impl Future for UnpinWrapperFut { } } -#[cfg(not(any(feature = "ssl", feature = "rust-tls")))] +#[cfg(not(any(feature = "openssl", feature = "rustls")))] mod connect_impl { use std::task::{Context, Poll}; @@ -439,7 +449,7 @@ mod connect_impl { } } -#[cfg(any(feature = "ssl", feature = "rust-tls"))] +#[cfg(any(feature = "openssl", feature = "rustls"))] mod connect_impl { use std::marker::PhantomData; @@ -510,11 +520,11 @@ mod connect_impl { fn call(&mut self, req: Connect) -> Self::Future { match req.uri.scheme_str() { - Some("https") | Some("wss") => Either::B(InnerConnectorResponseB { + Some("https") | Some("wss") => Either::Right(InnerConnectorResponseB { fut: self.ssl_pool.call(req), _t: PhantomData, }), - _ => Either::A(InnerConnectorResponseA { + _ => Either::Left(InnerConnectorResponseA { fut: self.tcp_pool.call(req), _t: PhantomData, }), diff --git a/actix-http/src/cookie/secure/key.rs b/actix-http/src/cookie/secure/key.rs index 39575c93f..95058ed81 100644 --- a/actix-http/src/cookie/secure/key.rs +++ b/actix-http/src/cookie/secure/key.rs @@ -1,13 +1,12 @@ -use ring::digest::{Algorithm, SHA256}; -use ring::hkdf::expand; -use ring::hmac::SigningKey; +use ring::hkdf::{Algorithm, KeyType, Prk, HKDF_SHA256}; +use ring::hmac; use ring::rand::{SecureRandom, SystemRandom}; use super::private::KEY_LEN as PRIVATE_KEY_LEN; use super::signed::KEY_LEN as SIGNED_KEY_LEN; -static HKDF_DIGEST: &Algorithm = &SHA256; -const KEYS_INFO: &str = "COOKIE;SIGNED:HMAC-SHA256;PRIVATE:AEAD-AES-256-GCM"; +static HKDF_DIGEST: Algorithm = HKDF_SHA256; +const KEYS_INFO: &[&[u8]] = &[b"COOKIE;SIGNED:HMAC-SHA256;PRIVATE:AEAD-AES-256-GCM"]; /// A cryptographic master key for use with `Signed` and/or `Private` jars. /// @@ -25,6 +24,13 @@ pub struct Key { encryption_key: [u8; PRIVATE_KEY_LEN], } +impl KeyType for &Key { + #[inline] + fn len(&self) -> usize { + SIGNED_KEY_LEN + PRIVATE_KEY_LEN + } +} + impl Key { /// Derives new signing/encryption keys from a master key. /// @@ -56,21 +62,26 @@ impl Key { ); } - // Expand the user's key into two. - let prk = SigningKey::new(HKDF_DIGEST, key); + // An empty `Key` structure; will be filled in with HKDF derived keys. + let mut output_key = Key { + signing_key: [0; SIGNED_KEY_LEN], + encryption_key: [0; PRIVATE_KEY_LEN], + }; + + // Expand the master key into two HKDF generated keys. let mut both_keys = [0; SIGNED_KEY_LEN + PRIVATE_KEY_LEN]; - expand(&prk, KEYS_INFO.as_bytes(), &mut both_keys); + let prk = Prk::new_less_safe(HKDF_DIGEST, key); + let okm = prk.expand(KEYS_INFO, &output_key).expect("okm expand"); + okm.fill(&mut both_keys).expect("fill keys"); - // Copy the keys into their respective arrays. - let mut signing_key = [0; SIGNED_KEY_LEN]; - let mut encryption_key = [0; PRIVATE_KEY_LEN]; - signing_key.copy_from_slice(&both_keys[..SIGNED_KEY_LEN]); - encryption_key.copy_from_slice(&both_keys[SIGNED_KEY_LEN..]); - - Key { - signing_key, - encryption_key, - } + // Copy the key parts into their respective fields. + output_key + .signing_key + .copy_from_slice(&both_keys[..SIGNED_KEY_LEN]); + output_key + .encryption_key + .copy_from_slice(&both_keys[SIGNED_KEY_LEN..]); + output_key } /// Generates signing/encryption keys from a secure, random source. Keys are diff --git a/actix-http/src/cookie/secure/private.rs b/actix-http/src/cookie/secure/private.rs index eb8e9beb1..6c16e94e8 100644 --- a/actix-http/src/cookie/secure/private.rs +++ b/actix-http/src/cookie/secure/private.rs @@ -1,8 +1,8 @@ use std::str; use log::warn; -use ring::aead::{open_in_place, seal_in_place, Aad, Algorithm, Nonce, AES_256_GCM}; -use ring::aead::{OpeningKey, SealingKey}; +use ring::aead::{Aad, Algorithm, Nonce, AES_256_GCM}; +use ring::aead::{LessSafeKey, UnboundKey}; use ring::rand::{SecureRandom, SystemRandom}; use super::Key; @@ -10,7 +10,7 @@ use crate::cookie::{Cookie, CookieJar}; // Keep these in sync, and keep the key len synced with the `private` docs as // well as the `KEYS_INFO` const in secure::Key. -static ALGO: &Algorithm = &AES_256_GCM; +static ALGO: &'static Algorithm = &AES_256_GCM; const NONCE_LEN: usize = 12; pub const KEY_LEN: usize = 32; @@ -53,11 +53,14 @@ impl<'a> PrivateJar<'a> { } let ad = Aad::from(name.as_bytes()); - let key = OpeningKey::new(ALGO, &self.key).expect("opening key"); - let (nonce, sealed) = data.split_at_mut(NONCE_LEN); + let key = LessSafeKey::new( + UnboundKey::new(&ALGO, &self.key).expect("matching key length"), + ); + let (nonce, mut sealed) = data.split_at_mut(NONCE_LEN); let nonce = Nonce::try_assume_unique_for_key(nonce).expect("invalid length of `nonce`"); - let unsealed = open_in_place(&key, nonce, ad, 0, sealed) + let unsealed = key + .open_in_place(nonce, ad, &mut sealed) .map_err(|_| "invalid key/nonce/value: bad seal")?; if let Ok(unsealed_utf8) = str::from_utf8(unsealed) { @@ -196,30 +199,33 @@ Please change it as soon as possible." fn encrypt_name_value(name: &[u8], value: &[u8], key: &[u8]) -> Vec { // Create the `SealingKey` structure. - let key = SealingKey::new(ALGO, key).expect("sealing key creation"); + let unbound = UnboundKey::new(&ALGO, key).expect("matching key length"); + let key = LessSafeKey::new(unbound); // Create a vec to hold the [nonce | cookie value | overhead]. - let overhead = ALGO.tag_len(); - let mut data = vec![0; NONCE_LEN + value.len() + overhead]; + let mut data = vec![0; NONCE_LEN + value.len() + ALGO.tag_len()]; // Randomly generate the nonce, then copy the cookie value as input. let (nonce, in_out) = data.split_at_mut(NONCE_LEN); + let (in_out, tag) = in_out.split_at_mut(value.len()); + in_out.copy_from_slice(value); + + // Randomly generate the nonce into the nonce piece. SystemRandom::new() .fill(nonce) .expect("couldn't random fill nonce"); - in_out[..value.len()].copy_from_slice(value); - let nonce = - Nonce::try_assume_unique_for_key(nonce).expect("invalid length of `nonce`"); + let nonce = Nonce::try_assume_unique_for_key(nonce).expect("invalid `nonce` length"); // Use cookie's name as associated data to prevent value swapping. let ad = Aad::from(name); + let ad_tag = key + .seal_in_place_separate_tag(nonce, ad, in_out) + .expect("in-place seal"); - // Perform the actual sealing operation and get the output length. - let output_len = - seal_in_place(&key, nonce, ad, in_out, overhead).expect("in-place seal"); + // Copy the tag into the tag piece. + tag.copy_from_slice(ad_tag.as_ref()); // Remove the overhead and return the sealed content. - data.truncate(NONCE_LEN + output_len); data } diff --git a/actix-http/src/cookie/secure/signed.rs b/actix-http/src/cookie/secure/signed.rs index 36a277cd5..3fcd2cd84 100644 --- a/actix-http/src/cookie/secure/signed.rs +++ b/actix-http/src/cookie/secure/signed.rs @@ -1,12 +1,11 @@ -use ring::digest::{Algorithm, SHA256}; -use ring::hmac::{sign, verify_with_own_key as verify, SigningKey}; +use ring::hmac::{self, sign, verify}; use super::Key; use crate::cookie::{Cookie, CookieJar}; // Keep these in sync, and keep the key len synced with the `signed` docs as // well as the `KEYS_INFO` const in secure::Key. -static HMAC_DIGEST: &Algorithm = &SHA256; +static HMAC_DIGEST: hmac::Algorithm = hmac::HMAC_SHA256; const BASE64_DIGEST_LEN: usize = 44; pub const KEY_LEN: usize = 32; @@ -21,7 +20,7 @@ pub const KEY_LEN: usize = 32; /// This type is only available when the `secure` feature is enabled. pub struct SignedJar<'a> { parent: &'a mut CookieJar, - key: SigningKey, + key: hmac::Key, } impl<'a> SignedJar<'a> { @@ -32,7 +31,7 @@ impl<'a> SignedJar<'a> { pub fn new(parent: &'a mut CookieJar, key: &Key) -> SignedJar<'a> { SignedJar { parent, - key: SigningKey::new(HMAC_DIGEST, key.signing()), + key: hmac::Key::new(HMAC_DIGEST, key.signing()), } } diff --git a/actix-http/src/h1/payload.rs b/actix-http/src/h1/payload.rs index 036138f98..20ff830e7 100644 --- a/actix-http/src/h1/payload.rs +++ b/actix-http/src/h1/payload.rs @@ -234,7 +234,7 @@ mod tests { fn test_unread_data() { Runtime::new() .unwrap() - .block_on(lazy(|| { + .block_on(async { let (_, mut payload) = Payload::create(false); payload.unread_data(Bytes::from("data")); @@ -242,13 +242,12 @@ mod tests { assert_eq!(payload.len(), 4); assert_eq!( - Async::Ready(Some(Bytes::from("data"))), - payload.poll().ok().unwrap() + Poll::Ready(Some(Bytes::from("data"))), + payload.next_item().await.ok().unwrap() ); - let res: Result<(), ()> = Ok(()); - result(res) - })) + result(()) + }) .unwrap(); } } diff --git a/actix-http/tests/test_server.rs b/actix-http/tests/test_server.rs index a31e4ac89..51ee9f2d6 100644 --- a/actix-http/tests/test_server.rs +++ b/actix-http/tests/test_server.rs @@ -4,10 +4,10 @@ use std::{net, thread}; use actix_http_test::TestServer; use actix_server_config::ServerConfig; -use actix_service::{new_service_cfg, service_fn, NewService}; +use actix_service::{factory_fn_cfg, pipeline, service_fn, ServiceFactory}; use bytes::Bytes; -use futures::future::{self, ok, Future}; -use futures::stream::{once, Stream}; +use futures::future::{self, err, ok, ready, Future, FutureExt}; +use futures::stream::{once, Stream, StreamExt}; use regex::Regex; use tokio_timer::sleep; @@ -58,9 +58,9 @@ fn test_expect_continue() { HttpService::build() .expect(service_fn(|req: Request| { if req.head().uri.query() == Some("yes=") { - Ok(req) + ok(req) } else { - Err(error::ErrorPreconditionFailed("error")) + err(error::ErrorPreconditionFailed("error")) } })) .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) @@ -117,9 +117,12 @@ fn test_chunked_payload() { HttpService::build().h1(|mut request: Request| { request .take_payload() - .map_err(|e| panic!(format!("Error reading payload: {}", e))) - .fold(0usize, |acc, chunk| future::ok::<_, ()>(acc + chunk.len())) - .map(|req_size| Response::Ok().body(format!("size={}", req_size))) + .map(|res| match res { + Ok(pl) => pl, + Err(e) => panic!(format!("Error reading payload: {}", e)), + }) + .fold(0usize, |acc, chunk| ready(acc + chunk.len())) + .map(|req_size| ok(Response::Ok().body(format!("size={}", req_size)))) }) }); @@ -414,7 +417,7 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[test] fn test_h1_body() { let mut srv = TestServer::new(|| { - HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().body(STR))) + HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) }); let response = srv.block_on(srv.get("/").send()).unwrap(); @@ -493,7 +496,7 @@ fn test_h1_head_binary2() { fn test_h1_body_length() { let mut srv = TestServer::new(|| { HttpService::build().h1(|_| { - let body = once(Ok(Bytes::from_static(STR.as_ref()))); + let body = once(ok(Bytes::from_static(STR.as_ref()))); ok::<_, ()>( Response::Ok().body(body::SizedStream::new(STR.len() as u64, body)), ) @@ -512,7 +515,7 @@ fn test_h1_body_length() { fn test_h1_body_chunked_explicit() { let mut srv = TestServer::new(|| { HttpService::build().h1(|_| { - let body = once::<_, Error>(Ok(Bytes::from_static(STR.as_ref()))); + let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); ok::<_, ()>( Response::Ok() .header(header::TRANSFER_ENCODING, "chunked") @@ -544,7 +547,7 @@ fn test_h1_body_chunked_explicit() { fn test_h1_body_chunked_implicit() { let mut srv = TestServer::new(|| { HttpService::build().h1(|_| { - let body = once::<_, Error>(Ok(Bytes::from_static(STR.as_ref()))); + let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); ok::<_, ()>(Response::Ok().streaming(body)) }) }); @@ -569,15 +572,15 @@ fn test_h1_body_chunked_implicit() { #[test] fn test_h1_response_http_error_handling() { let mut srv = TestServer::new(|| { - HttpService::build().h1(new_service_cfg(|_: &ServerConfig| { - Ok::<_, ()>(|_| { + HttpService::build().h1(factory_fn_cfg(|_: &ServerConfig| { + ok::<_, ()>(pipeline(|_| { let broken_header = Bytes::from_static(b"\0\0\0"); ok::<_, ()>( Response::Ok() .header(http::header::CONTENT_TYPE, broken_header) .body(STR), ) - }) + })) })) }); @@ -593,7 +596,7 @@ fn test_h1_response_http_error_handling() { fn test_h1_service_error() { let mut srv = TestServer::new(|| { HttpService::build() - .h1(|_| Err::(error::ErrorBadRequest("error"))) + .h1(|_| future::err::(error::ErrorBadRequest("error"))) }); let response = srv.block_on(srv.get("/").send()).unwrap(); From 5ab29b2e62fca5159d93e6deea1e4a48bfa1bcad Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Tue, 19 Nov 2019 09:55:17 +0600 Subject: [PATCH 047/176] migrate awc and test-server to std::future --- Cargo.toml | 2 +- actix-http/src/body.rs | 11 +- actix-http/src/client/connection.rs | 2 +- awc/Cargo.toml | 53 ++++--- awc/src/connect.rs | 230 ++++++++++++++-------------- awc/src/frozen.rs | 4 +- awc/src/request.rs | 2 +- awc/src/response.rs | 89 ++++++----- awc/src/sender.rs | 56 ++++--- awc/src/ws.rs | 169 ++++++++++---------- test-server/Cargo.toml | 47 ++++-- test-server/src/lib.rs | 108 ++----------- 12 files changed, 376 insertions(+), 397 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ab812d1b0..0b5c4f3d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ members = [ # "actix-web-codegen", # "test-server", ] -exclude = ["actix-http"] +exclude = ["awc", "actix-http", "test-server"] [features] default = ["brotli", "flate2-zlib", "client", "fail"] diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs index 7b86bfb14..44e76ae0e 100644 --- a/actix-http/src/body.rs +++ b/actix-http/src/body.rs @@ -253,7 +253,7 @@ where impl From> for Body where S: Stream> + Unpin + 'static, - E: Into + Unpin + 'static, + E: Into + 'static, { fn from(s: BodyStream) -> Body { Body::from_message(s) @@ -368,10 +368,17 @@ where } } +impl Unpin for BodyStream +where + S: Stream> + Unpin, + E: Into, +{ +} + impl MessageBody for BodyStream where S: Stream> + Unpin, - E: Into + Unpin, + E: Into, { fn size(&self) -> BodySize { BodySize::Stream diff --git a/actix-http/src/client/connection.rs b/actix-http/src/client/connection.rs index 70ffff6c0..0901fdb2d 100644 --- a/actix-http/src/client/connection.rs +++ b/actix-http/src/client/connection.rs @@ -22,7 +22,7 @@ pub(crate) enum ConnectionType { } pub trait Connection { - type Io: AsyncRead + AsyncWrite; + type Io: AsyncRead + AsyncWrite + Unpin; type Future: Future>; fn protocol(&self) -> Protocol; diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 4b0e612b8..ea181237e 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "awc" -version = "0.2.8" +version = "0.3.0-alpha.1" authors = ["Nikolay Kim "] description = "Actix http client." readme = "README.md" @@ -14,23 +14,23 @@ categories = ["network-programming", "asynchronous", license = "MIT/Apache-2.0" exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] edition = "2018" -workspace = ".." +# workspace = ".." [lib] name = "awc" path = "src/lib.rs" [package.metadata.docs.rs] -features = ["ssl", "brotli", "flate2-zlib"] +features = ["openssl", "brotli", "flate2-zlib"] [features] default = ["brotli", "flate2-zlib"] # openssl -ssl = ["openssl", "actix-http/ssl"] +openssl = ["open-ssl", "actix-http/openssl"] # rustls -rust-tls = ["rustls", "actix-http/rust-tls"] +rustls = ["rust-tls", "actix-http/rustls"] # brotli encoding, requires c compiler brotli = ["actix-http/brotli"] @@ -42,13 +42,14 @@ flate2-zlib = ["actix-http/flate2-zlib"] flate2-rust = ["actix-http/flate2-rust"] [dependencies] -actix-codec = "0.1.2" -actix-service = "0.4.1" -actix-http = "0.2.11" +actix-codec = "0.2.0-alpha.1" +actix-service = "1.0.0-alpha.1" +actix-http = "0.3.0-alpha.1" + base64 = "0.10.1" bytes = "0.4" derive_more = "0.15.0" -futures = "0.1.25" +futures = "0.3.1" log =" 0.4" mime = "0.3" percent-encoding = "2.1" @@ -56,21 +57,33 @@ rand = "0.7" serde = "1.0" serde_json = "1.0" serde_urlencoded = "0.6.1" -tokio-timer = "0.2.8" -openssl = { version="0.10", optional = true } -rustls = { version = "0.15.2", optional = true } +tokio-timer = "0.3.0-alpha.6" +open-ssl = { version="0.10", package="openssl", optional = true } +rust-tls = { version = "0.16.0", package="rustls", optional = true } [dev-dependencies] -actix-rt = "0.2.2" -actix-web = { version = "1.0.8", features=["ssl"] } -actix-http = { version = "0.2.11", features=["ssl"] } -actix-http-test = { version = "0.2.0", features=["ssl"] } -actix-utils = "0.4.1" -actix-server = { version = "0.6.0", features=["ssl", "rust-tls"] } +actix-rt = "1.0.0-alpha.1" +#actix-web = { version = "1.0.8", features=["ssl"] } +actix-http = { version = "0.3.0-alpha.1", features=["openssl"] } +#actix-http-test = { version = "0.2.0", features=["ssl"] } +actix-utils = "0.5.0-alpha.1" +actix-server = { version = "0.8.0-alpha.1", features=["openssl", "rustls"] } brotli2 = { version="0.3.2" } flate2 = { version="1.0.2" } env_logger = "0.6" rand = "0.7" tokio-tcp = "0.1" -webpki = "0.19" -rustls = { version = "0.15.2", features = ["dangerous_configuration"] } +webpki = { version = "0.21" } +rus-tls = { version = "0.16.0", package="rustls", features = ["dangerous_configuration"] } + +[patch.crates-io] +actix-http = { path = "../actix-http" } + +actix-codec = { path = "../../actix-net/actix-codec" } +actix-connect = { path = "../../actix-net/actix-connect" } +actix-rt = { path = "../../actix-net/actix-rt" } +actix-server = { path = "../../actix-net/actix-server" } +actix-server-config = { path = "../../actix-net/actix-server-config" } +actix-service = { path = "../../actix-net/actix-service" } +actix-threadpool = { path = "../../actix-net/actix-threadpool" } +actix-utils = { path = "../../actix-net/actix-utils" } diff --git a/awc/src/connect.rs b/awc/src/connect.rs index 97864d300..cc92fdbb6 100644 --- a/awc/src/connect.rs +++ b/awc/src/connect.rs @@ -1,4 +1,6 @@ +use std::pin::Pin; use std::rc::Rc; +use std::task::{Context, Poll}; use std::{fmt, io, net}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; @@ -10,7 +12,7 @@ use actix_http::h1::ClientCodec; use actix_http::http::HeaderMap; use actix_http::{RequestHead, RequestHeadType, ResponseHead}; use actix_service::Service; -use futures::{Future, Poll}; +use futures::future::{FutureExt, LocalBoxFuture}; use crate::response::ClientResponse; @@ -22,7 +24,7 @@ pub(crate) trait Connect { head: RequestHead, body: Body, addr: Option, - ) -> Box>; + ) -> LocalBoxFuture<'static, Result>; fn send_request_extra( &mut self, @@ -30,18 +32,16 @@ pub(crate) trait Connect { extra_headers: Option, body: Body, addr: Option, - ) -> Box>; + ) -> LocalBoxFuture<'static, Result>; /// Send request, returns Response and Framed fn open_tunnel( &mut self, head: RequestHead, addr: Option, - ) -> Box< - dyn Future< - Item = (ResponseHead, Framed), - Error = SendRequestError, - >, + ) -> LocalBoxFuture< + 'static, + Result<(ResponseHead, Framed), SendRequestError>, >; /// Send request and extra headers, returns Response and Framed @@ -50,11 +50,9 @@ pub(crate) trait Connect { head: Rc, extra_headers: Option, addr: Option, - ) -> Box< - dyn Future< - Item = (ResponseHead, Framed), - Error = SendRequestError, - >, + ) -> LocalBoxFuture< + 'static, + Result<(ResponseHead, Framed), SendRequestError>, >; } @@ -72,21 +70,23 @@ where head: RequestHead, body: Body, addr: Option, - ) -> Box> { - Box::new( - self.0 - // connect to the host - .call(ClientConnect { - uri: head.uri.clone(), - addr, - }) - .from_err() - // send request - .and_then(move |connection| { - connection.send_request(RequestHeadType::from(head), body) - }) - .map(|(head, payload)| ClientResponse::new(head, payload)), - ) + ) -> LocalBoxFuture<'static, Result> { + // connect to the host + let fut = self.0.call(ClientConnect { + uri: head.uri.clone(), + addr, + }); + + async move { + let connection = fut.await?; + + // send request + connection + .send_request(RequestHeadType::from(head), body) + .await + .map(|(head, payload)| ClientResponse::new(head, payload)) + } + .boxed_local() } fn send_request_extra( @@ -95,51 +95,51 @@ where extra_headers: Option, body: Body, addr: Option, - ) -> Box> { - Box::new( - self.0 - // connect to the host - .call(ClientConnect { - uri: head.uri.clone(), - addr, - }) - .from_err() - // send request - .and_then(move |connection| { - connection - .send_request(RequestHeadType::Rc(head, extra_headers), body) - }) - .map(|(head, payload)| ClientResponse::new(head, payload)), - ) + ) -> LocalBoxFuture<'static, Result> { + // connect to the host + let fut = self.0.call(ClientConnect { + uri: head.uri.clone(), + addr, + }); + + async move { + let connection = fut.await?; + + // send request + let (head, payload) = connection + .send_request(RequestHeadType::Rc(head, extra_headers), body) + .await?; + + Ok(ClientResponse::new(head, payload)) + } + .boxed_local() } fn open_tunnel( &mut self, head: RequestHead, addr: Option, - ) -> Box< - dyn Future< - Item = (ResponseHead, Framed), - Error = SendRequestError, - >, + ) -> LocalBoxFuture< + 'static, + Result<(ResponseHead, Framed), SendRequestError>, > { - Box::new( - self.0 - // connect to the host - .call(ClientConnect { - uri: head.uri.clone(), - addr, - }) - .from_err() - // send request - .and_then(move |connection| { - connection.open_tunnel(RequestHeadType::from(head)) - }) - .map(|(head, framed)| { - let framed = framed.map_io(|io| BoxedSocket(Box::new(Socket(io)))); - (head, framed) - }), - ) + // connect to the host + let fut = self.0.call(ClientConnect { + uri: head.uri.clone(), + addr, + }); + + async move { + let connection = fut.await?; + + // send request + let (head, framed) = + connection.open_tunnel(RequestHeadType::from(head)).await?; + + let framed = framed.map_io(|io| BoxedSocket(Box::new(Socket(io)))); + Ok((head, framed)) + } + .boxed_local() } fn open_tunnel_extra( @@ -147,48 +147,47 @@ where head: Rc, extra_headers: Option, addr: Option, - ) -> Box< - dyn Future< - Item = (ResponseHead, Framed), - Error = SendRequestError, - >, + ) -> LocalBoxFuture< + 'static, + Result<(ResponseHead, Framed), SendRequestError>, > { - Box::new( - self.0 - // connect to the host - .call(ClientConnect { - uri: head.uri.clone(), - addr, - }) - .from_err() - // send request - .and_then(move |connection| { - connection.open_tunnel(RequestHeadType::Rc(head, extra_headers)) - }) - .map(|(head, framed)| { - let framed = framed.map_io(|io| BoxedSocket(Box::new(Socket(io)))); - (head, framed) - }), - ) + // connect to the host + let fut = self.0.call(ClientConnect { + uri: head.uri.clone(), + addr, + }); + + async move { + let connection = fut.await?; + + // send request + let (head, framed) = connection + .open_tunnel(RequestHeadType::Rc(head, extra_headers)) + .await?; + + let framed = framed.map_io(|io| BoxedSocket(Box::new(Socket(io)))); + Ok((head, framed)) + } + .boxed_local() } } trait AsyncSocket { - fn as_read(&self) -> &dyn AsyncRead; - fn as_read_mut(&mut self) -> &mut dyn AsyncRead; - fn as_write(&mut self) -> &mut dyn AsyncWrite; + fn as_read(&self) -> &(dyn AsyncRead + Unpin); + fn as_read_mut(&mut self) -> &mut (dyn AsyncRead + Unpin); + fn as_write(&mut self) -> &mut (dyn AsyncWrite + Unpin); } -struct Socket(T); +struct Socket(T); -impl AsyncSocket for Socket { - fn as_read(&self) -> &dyn AsyncRead { +impl AsyncSocket for Socket { + fn as_read(&self) -> &(dyn AsyncRead + Unpin) { &self.0 } - fn as_read_mut(&mut self) -> &mut dyn AsyncRead { + fn as_read_mut(&mut self) -> &mut (dyn AsyncRead + Unpin) { &mut self.0 } - fn as_write(&mut self) -> &mut dyn AsyncWrite { + fn as_write(&mut self) -> &mut (dyn AsyncWrite + Unpin) { &mut self.0 } } @@ -201,30 +200,37 @@ impl fmt::Debug for BoxedSocket { } } -impl io::Read for BoxedSocket { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.0.as_read_mut().read(buf) - } -} - impl AsyncRead for BoxedSocket { unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { self.0.as_read().prepare_uninitialized_buffer(buf) } -} -impl io::Write for BoxedSocket { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.as_write().write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.0.as_write().flush() + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + Pin::new(self.get_mut().0.as_read_mut()).poll_read(cx, buf) } } impl AsyncWrite for BoxedSocket { - fn shutdown(&mut self) -> Poll<(), io::Error> { - self.0.as_write().shutdown() + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Pin::new(self.get_mut().0.as_write()).poll_write(cx, buf) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(self.get_mut().0.as_write()).poll_flush(cx) + } + + fn poll_shutdown( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + Pin::new(self.get_mut().0.as_write()).poll_shutdown(cx) } } diff --git a/awc/src/frozen.rs b/awc/src/frozen.rs index d9f65d431..61ba87aad 100644 --- a/awc/src/frozen.rs +++ b/awc/src/frozen.rs @@ -82,7 +82,7 @@ impl FrozenClientRequest { /// Send a streaming body. pub fn send_stream(&self, stream: S) -> SendClientRequest where - S: Stream + 'static, + S: Stream> + Unpin + 'static, E: Into + 'static, { RequestSender::Rc(self.head.clone(), None).send_stream( @@ -203,7 +203,7 @@ impl FrozenSendBuilder { /// Complete request construction and send a streaming body. pub fn send_stream(self, stream: S) -> SendClientRequest where - S: Stream + 'static, + S: Stream> + Unpin + 'static, E: Into + 'static, { if let Some(e) = self.err { diff --git a/awc/src/request.rs b/awc/src/request.rs index 6ff68ae66..831234437 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -478,7 +478,7 @@ impl ClientRequest { /// Set an streaming body and generate `ClientRequest`. pub fn send_stream(self, stream: S) -> SendClientRequest where - S: Stream + 'static, + S: Stream> + Unpin + 'static, E: Into + 'static, { let slf = match self.prep_for_sending() { diff --git a/awc/src/response.rs b/awc/src/response.rs index d186526de..a7b08eedc 100644 --- a/awc/src/response.rs +++ b/awc/src/response.rs @@ -1,9 +1,11 @@ use std::cell::{Ref, RefMut}; use std::fmt; use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; use bytes::{Bytes, BytesMut}; -use futures::{Async, Future, Poll, Stream}; +use futures::{ready, Future, Stream}; use actix_http::cookie::Cookie; use actix_http::error::{CookieParseError, PayloadError}; @@ -104,7 +106,7 @@ impl ClientResponse { impl ClientResponse where - S: Stream, + S: Stream>, { /// Loads http response's body. pub fn body(&mut self) -> MessageBody { @@ -125,13 +127,12 @@ where impl Stream for ClientResponse where - S: Stream, + S: Stream> + Unpin, { - type Item = Bytes; - type Error = PayloadError; + type Item = Result; - fn poll(&mut self) -> Poll, Self::Error> { - self.payload.poll() + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + Pin::new(&mut self.get_mut().payload).poll_next(cx) } } @@ -155,7 +156,7 @@ pub struct MessageBody { impl MessageBody where - S: Stream, + S: Stream>, { /// Create `MessageBody` for request. pub fn new(res: &mut ClientResponse) -> MessageBody { @@ -198,23 +199,24 @@ where impl Future for MessageBody where - S: Stream, + S: Stream> + Unpin, { - type Item = Bytes; - type Error = PayloadError; + type Output = Result; - fn poll(&mut self) -> Poll { - if let Some(err) = self.err.take() { - return Err(err); + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.get_mut(); + + if let Some(err) = this.err.take() { + return Poll::Ready(Err(err)); } - if let Some(len) = self.length.take() { - if len > self.fut.as_ref().unwrap().limit { - return Err(PayloadError::Overflow); + if let Some(len) = this.length.take() { + if len > this.fut.as_ref().unwrap().limit { + return Poll::Ready(Err(PayloadError::Overflow)); } } - self.fut.as_mut().unwrap().poll() + Pin::new(&mut this.fut.as_mut().unwrap()).poll(cx) } } @@ -233,7 +235,7 @@ pub struct JsonBody { impl JsonBody where - S: Stream, + S: Stream>, U: DeserializeOwned, { /// Create `JsonBody` for request. @@ -279,27 +281,35 @@ where } } -impl Future for JsonBody +impl Unpin for JsonBody where - T: Stream, + T: Stream> + Unpin, U: DeserializeOwned, { - type Item = U; - type Error = JsonPayloadError; +} - fn poll(&mut self) -> Poll { +impl Future for JsonBody +where + T: Stream> + Unpin, + U: DeserializeOwned, +{ + type Output = Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { if let Some(err) = self.err.take() { - return Err(err); + return Poll::Ready(Err(err)); } if let Some(len) = self.length.take() { if len > self.fut.as_ref().unwrap().limit { - return Err(JsonPayloadError::Payload(PayloadError::Overflow)); + return Poll::Ready(Err(JsonPayloadError::Payload( + PayloadError::Overflow, + ))); } } - let body = futures::try_ready!(self.fut.as_mut().unwrap().poll()); - Ok(Async::Ready(serde_json::from_slice::(&body)?)) + let body = ready!(Pin::new(&mut self.get_mut().fut.as_mut().unwrap()).poll(cx))?; + Poll::Ready(serde_json::from_slice::(&body).map_err(JsonPayloadError::from)) } } @@ -321,24 +331,25 @@ impl ReadBody { impl Future for ReadBody where - S: Stream, + S: Stream> + Unpin, { - type Item = Bytes; - type Error = PayloadError; + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.get_mut(); - fn poll(&mut self) -> Poll { loop { - return match self.stream.poll()? { - Async::Ready(Some(chunk)) => { - if (self.buf.len() + chunk.len()) > self.limit { - Err(PayloadError::Overflow) + return match Pin::new(&mut this.stream).poll_next(cx)? { + Poll::Ready(Some(chunk)) => { + if (this.buf.len() + chunk.len()) > this.limit { + Poll::Ready(Err(PayloadError::Overflow)) } else { - self.buf.extend_from_slice(&chunk); + this.buf.extend_from_slice(&chunk); continue; } } - Async::Ready(None) => Ok(Async::Ready(self.buf.take().freeze())), - Async::NotReady => Ok(Async::NotReady), + Poll::Ready(None) => Poll::Ready(Ok(this.buf.take().freeze())), + Poll::Pending => Poll::Pending, }; } } diff --git a/awc/src/sender.rs b/awc/src/sender.rs index 95109b92d..f7461113a 100644 --- a/awc/src/sender.rs +++ b/awc/src/sender.rs @@ -1,13 +1,15 @@ use std::net; +use std::pin::Pin; use std::rc::Rc; -use std::time::{Duration, Instant}; +use std::task::{Context, Poll}; +use std::time::Duration; use bytes::Bytes; use derive_more::From; -use futures::{try_ready, Async, Future, Poll, Stream}; +use futures::{future::LocalBoxFuture, ready, Future, Stream}; use serde::Serialize; use serde_json; -use tokio_timer::Delay; +use tokio_timer::{delay_for, Delay}; use actix_http::body::{Body, BodyStream}; use actix_http::encoding::Decoder; @@ -47,7 +49,7 @@ impl Into for PrepForSendingError { #[must_use = "futures do nothing unless polled"] pub enum SendClientRequest { Fut( - Box>, + LocalBoxFuture<'static, Result>, Option, bool, ), @@ -56,41 +58,51 @@ pub enum SendClientRequest { impl SendClientRequest { pub(crate) fn new( - send: Box>, + send: LocalBoxFuture<'static, Result>, response_decompress: bool, timeout: Option, ) -> SendClientRequest { - let delay = timeout.map(|t| Delay::new(Instant::now() + t)); + let delay = timeout.map(|t| delay_for(t)); SendClientRequest::Fut(send, delay, response_decompress) } } impl Future for SendClientRequest { - type Item = ClientResponse>>; - type Error = SendRequestError; + type Output = + Result>>, SendRequestError>; - fn poll(&mut self) -> Poll { - match self { + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.get_mut(); + + match this { SendClientRequest::Fut(send, delay, response_decompress) => { if delay.is_some() { - match delay.poll() { - Ok(Async::NotReady) => (), - _ => return Err(SendRequestError::Timeout), + match Pin::new(delay.as_mut().unwrap()).poll(cx) { + Poll::Pending => (), + _ => return Poll::Ready(Err(SendRequestError::Timeout)), } } - let res = try_ready!(send.poll()).map_body(|head, payload| { - if *response_decompress { - Payload::Stream(Decoder::from_headers(payload, &head.headers)) - } else { - Payload::Stream(Decoder::new(payload, ContentEncoding::Identity)) - } + let res = ready!(Pin::new(send).poll(cx)).map(|res| { + res.map_body(|head, payload| { + if *response_decompress { + Payload::Stream(Decoder::from_headers( + payload, + &head.headers, + )) + } else { + Payload::Stream(Decoder::new( + payload, + ContentEncoding::Identity, + )) + } + }) }); - Ok(Async::Ready(res)) + Poll::Ready(res) } SendClientRequest::Err(ref mut e) => match e.take() { - Some(e) => Err(e), + Some(e) => Poll::Ready(Err(e)), None => panic!("Attempting to call completed future"), }, } @@ -223,7 +235,7 @@ impl RequestSender { stream: S, ) -> SendClientRequest where - S: Stream + 'static, + S: Stream> + Unpin + 'static, E: Into + 'static, { self.send_body( diff --git a/awc/src/ws.rs b/awc/src/ws.rs index 77cbc7ca4..979a382af 100644 --- a/awc/src/ws.rs +++ b/awc/src/ws.rs @@ -7,7 +7,6 @@ use std::{fmt, str}; use actix_codec::Framed; use actix_http::cookie::{Cookie, CookieJar}; use actix_http::{ws, Payload, RequestHead}; -use futures::future::{err, Either, Future}; use percent_encoding::percent_encode; use tokio_timer::Timeout; @@ -210,27 +209,26 @@ impl WebsocketsRequest { } /// Complete request construction and connect to a websockets server. - pub fn connect( + pub async fn connect( mut self, - ) -> impl Future), Error = WsClientError> - { + ) -> Result<(ClientResponse, Framed), WsClientError> { if let Some(e) = self.err.take() { - return Either::A(err(e.into())); + return Err(e.into()); } // validate uri let uri = &self.head.uri; if uri.host().is_none() { - return Either::A(err(InvalidUrl::MissingHost.into())); + return Err(InvalidUrl::MissingHost.into()); } else if uri.scheme_part().is_none() { - return Either::A(err(InvalidUrl::MissingScheme.into())); + return Err(InvalidUrl::MissingScheme.into()); } else if let Some(scheme) = uri.scheme_part() { match scheme.as_str() { "http" | "ws" | "https" | "wss" => (), - _ => return Either::A(err(InvalidUrl::UnknownScheme.into())), + _ => return Err(InvalidUrl::UnknownScheme.into()), } } else { - return Either::A(err(InvalidUrl::UnknownScheme.into())); + return Err(InvalidUrl::UnknownScheme.into()); } if !self.head.headers.contains_key(header::HOST) { @@ -294,90 +292,83 @@ impl WebsocketsRequest { .config .connector .borrow_mut() - .open_tunnel(head, self.addr) - .from_err() - .and_then(move |(head, framed)| { - // verify response - if head.status != StatusCode::SWITCHING_PROTOCOLS { - return Err(WsClientError::InvalidResponseStatus(head.status)); - } - // Check for "UPGRADE" to websocket header - let has_hdr = if let Some(hdr) = head.headers.get(&header::UPGRADE) { - if let Ok(s) = hdr.to_str() { - s.to_ascii_lowercase().contains("websocket") - } else { - false - } - } else { - false - }; - if !has_hdr { - log::trace!("Invalid upgrade header"); - return Err(WsClientError::InvalidUpgradeHeader); - } - // Check for "CONNECTION" header - if let Some(conn) = head.headers.get(&header::CONNECTION) { - if let Ok(s) = conn.to_str() { - if !s.to_ascii_lowercase().contains("upgrade") { - log::trace!("Invalid connection header: {}", s); - return Err(WsClientError::InvalidConnectionHeader( - conn.clone(), - )); - } - } else { - log::trace!("Invalid connection header: {:?}", conn); - return Err(WsClientError::InvalidConnectionHeader( - conn.clone(), - )); - } - } else { - log::trace!("Missing connection header"); - return Err(WsClientError::MissingConnectionHeader); - } - - if let Some(hdr_key) = head.headers.get(&header::SEC_WEBSOCKET_ACCEPT) { - let encoded = ws::hash_key(key.as_ref()); - if hdr_key.as_bytes() != encoded.as_bytes() { - log::trace!( - "Invalid challenge response: expected: {} received: {:?}", - encoded, - key - ); - return Err(WsClientError::InvalidChallengeResponse( - encoded, - hdr_key.clone(), - )); - } - } else { - log::trace!("Missing SEC-WEBSOCKET-ACCEPT header"); - return Err(WsClientError::MissingWebSocketAcceptHeader); - }; - - // response and ws framed - Ok(( - ClientResponse::new(head, Payload::None), - framed.map_codec(|_| { - if server_mode { - ws::Codec::new().max_size(max_size) - } else { - ws::Codec::new().max_size(max_size).client_mode() - } - }), - )) - }); + .open_tunnel(head, self.addr); // set request timeout - if let Some(timeout) = self.config.timeout { - Either::B(Either::A(Timeout::new(fut, timeout).map_err(|e| { - if let Some(e) = e.into_inner() { - e - } else { - SendRequestError::Timeout.into() - } - }))) + let (head, framed) = if let Some(timeout) = self.config.timeout { + Timeout::new(fut, timeout) + .await + .map_err(|_| SendRequestError::Timeout.into()) + .and_then(|res| res)? } else { - Either::B(Either::B(fut)) + fut.await? + }; + + // verify response + if head.status != StatusCode::SWITCHING_PROTOCOLS { + return Err(WsClientError::InvalidResponseStatus(head.status)); } + + // Check for "UPGRADE" to websocket header + let has_hdr = if let Some(hdr) = head.headers.get(&header::UPGRADE) { + if let Ok(s) = hdr.to_str() { + s.to_ascii_lowercase().contains("websocket") + } else { + false + } + } else { + false + }; + if !has_hdr { + log::trace!("Invalid upgrade header"); + return Err(WsClientError::InvalidUpgradeHeader); + } + + // Check for "CONNECTION" header + if let Some(conn) = head.headers.get(&header::CONNECTION) { + if let Ok(s) = conn.to_str() { + if !s.to_ascii_lowercase().contains("upgrade") { + log::trace!("Invalid connection header: {}", s); + return Err(WsClientError::InvalidConnectionHeader(conn.clone())); + } + } else { + log::trace!("Invalid connection header: {:?}", conn); + return Err(WsClientError::InvalidConnectionHeader(conn.clone())); + } + } else { + log::trace!("Missing connection header"); + return Err(WsClientError::MissingConnectionHeader); + } + + if let Some(hdr_key) = head.headers.get(&header::SEC_WEBSOCKET_ACCEPT) { + let encoded = ws::hash_key(key.as_ref()); + if hdr_key.as_bytes() != encoded.as_bytes() { + log::trace!( + "Invalid challenge response: expected: {} received: {:?}", + encoded, + key + ); + return Err(WsClientError::InvalidChallengeResponse( + encoded, + hdr_key.clone(), + )); + } + } else { + log::trace!("Missing SEC-WEBSOCKET-ACCEPT header"); + return Err(WsClientError::MissingWebSocketAcceptHeader); + }; + + // response and ws framed + Ok(( + ClientResponse::new(head, Payload::None), + framed.map_codec(|_| { + if server_mode { + ws::Codec::new().max_size(max_size) + } else { + ws::Codec::new().max_size(max_size).client_mode() + } + }), + )) } } diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 77301b0a9..35bf1a0ed 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -14,7 +14,7 @@ categories = ["network-programming", "asynchronous", license = "MIT/Apache-2.0" exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] edition = "2018" -workspace = ".." +# workspace = ".." [package.metadata.docs.rs] features = [] @@ -27,20 +27,22 @@ path = "src/lib.rs" default = [] # openssl -ssl = ["openssl", "actix-server/ssl", "awc/ssl"] +openssl = ["open-ssl", "actix-server/openssl", "awc/openssl"] [dependencies] -actix-codec = "0.1.2" -actix-rt = "0.2.2" -actix-service = "0.4.1" -actix-server = "0.6.0" -actix-utils = "0.4.1" -awc = "0.2.6" -actix-connect = "0.2.2" +actix-service = "1.0.0-alpha.1" +actix-codec = "0.2.0-alpha.1" +actix-connect = "1.0.0-alpha.1" +actix-utils = "0.5.0-alpha.1" +actix-rt = "1.0.0-alpha.1" +actix-server = "0.8.0-alpha.1" +actix-server-config = "0.3.0-alpha.1" +actix-testing = "0.3.0-alpha.1" +awc = "0.3.0-alpha.1" base64 = "0.10" bytes = "0.4" -futures = "0.1" +futures = "0.3.1" http = "0.1.8" log = "0.4" env_logger = "0.6" @@ -51,10 +53,25 @@ sha1 = "0.6" slab = "0.4" serde_urlencoded = "0.6.1" time = "0.1" -tokio-tcp = "0.1" -tokio-timer = "0.2" -openssl = { version="0.10", optional = true } +tokio-net = "0.2.0-alpha.6" +tokio-timer = "0.3.0-alpha.6" + +open-ssl = { version="0.10", package="openssl", optional = true } [dev-dependencies] -actix-web = "1.0.7" -actix-http = "0.2.9" +#actix-web = "1.0.7" +actix-http = "0.3.0-alpha.1" + +[patch.crates-io] +actix-http = { path = "../actix-http" } +awc = { path = "../awc" } + +actix-codec = { path = "../../actix-net/actix-codec" } +actix-connect = { path = "../../actix-net/actix-connect" } +actix-rt = { path = "../../actix-net/actix-rt" } +actix-server = { path = "../../actix-net/actix-server" } +actix-server-config = { path = "../../actix-net/actix-server-config" } +actix-service = { path = "../../actix-net/actix-service" } +actix-testing = { path = "../../actix-net/actix-testing" } +actix-threadpool = { path = "../../actix-net/actix-threadpool" } +actix-utils = { path = "../../actix-net/actix-utils" } diff --git a/test-server/src/lib.rs b/test-server/src/lib.rs index ebdec688f..17acfe291 100644 --- a/test-server/src/lib.rs +++ b/test-server/src/lib.rs @@ -1,73 +1,18 @@ //! Various helpers for Actix applications to use during testing. -use std::cell::RefCell; use std::sync::mpsc; use std::{net, thread, time}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; -use actix_rt::{Runtime, System}; -use actix_server::{Server, StreamServiceFactory}; +use actix_rt::{System}; +use actix_server::{Server, ServiceFactory}; use awc::{error::PayloadError, ws, Client, ClientRequest, ClientResponse, Connector}; use bytes::Bytes; -use futures::future::lazy; -use futures::{Future, IntoFuture, Stream}; +use futures::{Stream, future::lazy}; use http::Method; use net2::TcpBuilder; -use tokio_tcp::TcpStream; +use tokio_net::tcp::TcpStream; -thread_local! { - static RT: RefCell = { - RefCell::new(Inner(Some(Runtime::new().unwrap()))) - }; -} - -struct Inner(Option); - -impl Inner { - fn get_mut(&mut self) -> &mut Runtime { - self.0.as_mut().unwrap() - } -} - -impl Drop for Inner { - fn drop(&mut self) { - std::mem::forget(self.0.take().unwrap()) - } -} - -/// Runs the provided future, blocking the current thread until the future -/// completes. -/// -/// This function can be used to synchronously block the current thread -/// until the provided `future` has resolved either successfully or with an -/// error. The result of the future is then returned from this function -/// call. -/// -/// Note that this function is intended to be used only for testing purpose. -/// This function panics on nested call. -pub fn block_on(f: F) -> Result -where - F: IntoFuture, -{ - RT.with(move |rt| rt.borrow_mut().get_mut().block_on(f.into_future())) -} - -/// Runs the provided function, blocking the current thread until the resul -/// future completes. -/// -/// This function can be used to synchronously block the current thread -/// until the provided `future` has resolved either successfully or with an -/// error. The result of the future is then returned from this function -/// call. -/// -/// Note that this function is intended to be used only for testing purpose. -/// This function panics on nested call. -pub fn block_fn(f: F) -> Result -where - F: FnOnce() -> R, - R: IntoFuture, -{ - RT.with(move |rt| rt.borrow_mut().get_mut().block_on(lazy(f))) -} +pub use actix_testing::*; /// The `TestServer` type. /// @@ -110,7 +55,7 @@ pub struct TestServerRuntime { impl TestServer { #[allow(clippy::new_ret_no_self)] /// Start new test server with application factory - pub fn new>(factory: F) -> TestServerRuntime { + pub fn new>(factory: F) -> TestServerRuntime { let (tx, rx) = mpsc::channel(); // run server in separate thread @@ -131,7 +76,7 @@ impl TestServer { let (system, addr) = rx.recv().unwrap(); - let client = block_on(lazy(move || { + let client = block_on(lazy(move |_| { let connector = { #[cfg(feature = "ssl")] { @@ -161,9 +106,9 @@ impl TestServer { })) .unwrap(); - block_on(lazy( - || Ok::<_, ()>(actix_connect::start_default_resolver()), - )) + block_on(lazy(|_| { + Ok::<_, ()>(actix_connect::start_default_resolver()) + })) .unwrap(); TestServerRuntime { @@ -185,31 +130,6 @@ impl TestServer { } impl TestServerRuntime { - /// Execute future on current core - pub fn block_on(&mut self, fut: F) -> Result - where - F: Future, - { - block_on(fut) - } - - /// Execute future on current core - pub fn block_on_fn(&mut self, f: F) -> Result - where - F: FnOnce() -> R, - R: Future, - { - block_on(lazy(f)) - } - - /// Execute function on current core - pub fn execute(&mut self, fut: F) -> R - where - F: FnOnce() -> R, - { - block_on(lazy(|| Ok::<_, ()>(fut()))).unwrap() - } - /// Construct test server url pub fn addr(&self) -> net::SocketAddr { self.addr @@ -313,9 +233,9 @@ impl TestServerRuntime { mut response: ClientResponse, ) -> Result where - S: Stream + 'static, + S: Stream> + Unpin + 'static, { - self.block_on(response.body().limit(10_485_760)) + block_on(response.body().limit(10_485_760)) } /// Connect to websocket server at a given path @@ -326,7 +246,9 @@ impl TestServerRuntime { { let url = self.url(path); let connect = self.client.ws(url).connect(); - block_on(lazy(move || connect.map(|(_, framed)| framed))) + block_on(async move { + connect.await.map(|(_, framed)| framed) + }) } /// Connect to a websocket server From 687884fb940e7d4bf535f718cc3a0bab907b27ef Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Tue, 19 Nov 2019 11:08:03 +0600 Subject: [PATCH 048/176] update test-server tests --- actix-http/Cargo.toml | 7 ++- actix-http/src/client/h1proto.rs | 3 +- actix-http/src/h1/dispatcher.rs | 10 ++--- actix-http/src/h2/dispatcher.rs | 2 +- actix-http/tests/test_server.rs | 76 +++++++++++++++----------------- awc/Cargo.toml | 4 +- test-server/Cargo.toml | 2 +- test-server/src/lib.rs | 2 +- 8 files changed, 54 insertions(+), 52 deletions(-) diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 742938d5e..36f3cef47 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -109,17 +109,22 @@ webpki-roots = { version = "0.18", optional = true } actix-rt = "1.0.0-alpha.1" actix-server = { version = "0.8.0-alpha.1", features=["openssl"] } actix-connect = { version = "1.0.0-alpha.1", features=["openssl"] } -#actix-http-test = { version = "0.2.4", features=["ssl"] } +actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } env_logger = "0.6" serde_derive = "1.0" open-ssl = { version="0.10", package="openssl" } [patch.crates-io] +awc = { path = "../awc" } +actix-http = { path = "." } +actix-http-test = { path = "../test-server" } + actix-codec = { path = "../../actix-net/actix-codec" } actix-connect = { path = "../../actix-net/actix-connect" } actix-rt = { path = "../../actix-net/actix-rt" } actix-server = { path = "../../actix-net/actix-server" } actix-server-config = { path = "../../actix-net/actix-server-config" } actix-service = { path = "../../actix-net/actix-service" } +actix-testing = { path = "../../actix-net/actix-testing" } actix-threadpool = { path = "../../actix-net/actix-threadpool" } actix-utils = { path = "../../actix-net/actix-utils" } diff --git a/actix-http/src/client/h1proto.rs b/actix-http/src/client/h1proto.rs index f8902a0ef..041a36856 100644 --- a/actix-http/src/client/h1proto.rs +++ b/actix-http/src/client/h1proto.rs @@ -76,7 +76,8 @@ where }; // read response and init read body - let (head, framed) = if let (Some(result), framed) = framed.into_future().await { + let res = framed.into_future().await; + let (head, framed) = if let (Some(result), framed) = res { let item = result.map_err(SendRequestError::from)?; (item, framed) } else { diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 16e36447d..a06b997ec 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -331,12 +331,10 @@ where Poll::Ready(Err(err)) => return Err(DispatchError::Io(err)), } } - if written > 0 { - if written == self.write_buf.len() { - unsafe { self.write_buf.set_len(0) } - } else { - let _ = self.write_buf.split_to(written); - } + if written == self.write_buf.len() { + unsafe { self.write_buf.set_len(0) } + } else { + let _ = self.write_buf.split_to(written); } Ok(false) } diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index 2a44c83f9..9b5b7e83a 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -122,7 +122,7 @@ where match Pin::new(&mut this.connection).poll_accept(cx) { Poll::Ready(None) => return Poll::Ready(Ok(())), Poll::Ready(Some(Err(err))) => return Poll::Ready(Err(err.into())), - Poll::Ready(Some(Ok((req, res)))) => { + Poll::Ready(Some(Ok((req, _)))) => { // update keep-alive expire if this.ka_timer.is_some() { if let Some(expire) = this.config.keep_alive_expire() { diff --git a/actix-http/tests/test_server.rs b/actix-http/tests/test_server.rs index 51ee9f2d6..ef861b309 100644 --- a/actix-http/tests/test_server.rs +++ b/actix-http/tests/test_server.rs @@ -2,14 +2,14 @@ use std::io::{Read, Write}; use std::time::Duration; use std::{net, thread}; -use actix_http_test::TestServer; +use actix_http_test::{block_fn, TestServer}; use actix_server_config::ServerConfig; use actix_service::{factory_fn_cfg, pipeline, service_fn, ServiceFactory}; use bytes::Bytes; -use futures::future::{self, err, ok, ready, Future, FutureExt}; -use futures::stream::{once, Stream, StreamExt}; +use futures::future::{self, err, ok, ready, FutureExt}; +use futures::stream::{once, StreamExt}; use regex::Regex; -use tokio_timer::sleep; +use tokio_timer::delay_for; use actix_http::httpmessage::HttpMessage; use actix_http::{ @@ -18,7 +18,7 @@ use actix_http::{ #[test] fn test_h1() { - let mut srv = TestServer::new(|| { + let srv = TestServer::new(|| { HttpService::build() .keep_alive(KeepAlive::Disabled) .client_timeout(1000) @@ -29,13 +29,13 @@ fn test_h1() { }) }); - let response = srv.block_on(srv.get("/").send()).unwrap(); + let response = block_fn(|| srv.get("/").send()).unwrap(); assert!(response.status().is_success()); } #[test] fn test_h1_2() { - let mut srv = TestServer::new(|| { + let srv = TestServer::new(|| { HttpService::build() .keep_alive(KeepAlive::Disabled) .client_timeout(1000) @@ -48,7 +48,7 @@ fn test_h1_2() { .map(|_| ()) }); - let response = srv.block_on(srv.get("/").send()).unwrap(); + let response = block_fn(|| srv.get("/").send()).unwrap(); assert!(response.status().is_success()); } @@ -84,11 +84,11 @@ fn test_expect_continue_h1() { let srv = TestServer::new(|| { HttpService::build() .expect(service_fn(|req: Request| { - sleep(Duration::from_millis(20)).then(move |_| { + delay_for(Duration::from_millis(20)).then(move |_| { if req.head().uri.query() == Some("yes=") { - Ok(req) + ok(req) } else { - Err(error::ErrorPreconditionFailed("error")) + err(error::ErrorPreconditionFailed("error")) } }) })) @@ -114,7 +114,7 @@ fn test_chunked_payload() { let total_size: usize = chunk_sizes.iter().sum(); let srv = TestServer::new(|| { - HttpService::build().h1(|mut request: Request| { + HttpService::build().h1(service_fn(|mut request: Request| { request .take_payload() .map(|res| match res { @@ -122,8 +122,10 @@ fn test_chunked_payload() { Err(e) => panic!(format!("Error reading payload: {}", e)), }) .fold(0usize, |acc, chunk| ready(acc + chunk.len())) - .map(|req_size| ok(Response::Ok().body(format!("size={}", req_size)))) - }) + .map(|req_size| { + Ok::<_, Error>(Response::Ok().body(format!("size={}", req_size))) + }) + })) }); let returned_size = { @@ -310,7 +312,7 @@ fn test_content_length() { StatusCode, }; - let mut srv = TestServer::new(|| { + let srv = TestServer::new(|| { HttpService::build().h1(|req: Request| { let indx: usize = req.uri().path()[1..].parse().unwrap(); let statuses = [ @@ -330,24 +332,18 @@ fn test_content_length() { { for i in 0..4 { - let req = srv - .request(http::Method::GET, srv.url(&format!("/{}", i))) - .send(); - let response = srv.block_on(req).unwrap(); + let req = srv.request(http::Method::GET, srv.url(&format!("/{}", i))); + let response = block_fn(move || req.send()).unwrap(); assert_eq!(response.headers().get(&header), None); - let req = srv - .request(http::Method::HEAD, srv.url(&format!("/{}", i))) - .send(); - let response = srv.block_on(req).unwrap(); + let req = srv.request(http::Method::HEAD, srv.url(&format!("/{}", i))); + let response = block_fn(move || req.send()).unwrap(); assert_eq!(response.headers().get(&header), None); } for i in 4..6 { - let req = srv - .request(http::Method::GET, srv.url(&format!("/{}", i))) - .send(); - let response = srv.block_on(req).unwrap(); + let req = srv.request(http::Method::GET, srv.url(&format!("/{}", i))); + let response = block_fn(move || req.send()).unwrap(); assert_eq!(response.headers().get(&header), Some(&value)); } } @@ -384,7 +380,7 @@ fn test_h1_headers() { }) }); - let response = srv.block_on(srv.get("/").send()).unwrap(); + let response = block_fn(|| srv.get("/").send()).unwrap(); assert!(response.status().is_success()); // read response @@ -420,7 +416,7 @@ fn test_h1_body() { HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) }); - let response = srv.block_on(srv.get("/").send()).unwrap(); + let response = block_fn(|| srv.get("/").send()).unwrap(); assert!(response.status().is_success()); // read response @@ -434,7 +430,7 @@ fn test_h1_head_empty() { HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) }); - let response = srv.block_on(srv.head("/").send()).unwrap(); + let response = block_fn(|| srv.head("/").send()).unwrap(); assert!(response.status().is_success()); { @@ -458,7 +454,7 @@ fn test_h1_head_binary() { }) }); - let response = srv.block_on(srv.head("/").send()).unwrap(); + let response = block_fn(|| srv.head("/").send()).unwrap(); assert!(response.status().is_success()); { @@ -476,11 +472,11 @@ fn test_h1_head_binary() { #[test] fn test_h1_head_binary2() { - let mut srv = TestServer::new(|| { + let srv = TestServer::new(|| { HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) }); - let response = srv.block_on(srv.head("/").send()).unwrap(); + let response = block_fn(|| srv.head("/").send()).unwrap(); assert!(response.status().is_success()); { @@ -503,7 +499,7 @@ fn test_h1_body_length() { }) }); - let response = srv.block_on(srv.get("/").send()).unwrap(); + let response = block_fn(|| srv.get("/").send()).unwrap(); assert!(response.status().is_success()); // read response @@ -524,7 +520,7 @@ fn test_h1_body_chunked_explicit() { }) }); - let response = srv.block_on(srv.get("/").send()).unwrap(); + let response = block_fn(|| srv.get("/").send()).unwrap(); assert!(response.status().is_success()); assert_eq!( response @@ -552,7 +548,7 @@ fn test_h1_body_chunked_implicit() { }) }); - let response = srv.block_on(srv.get("/").send()).unwrap(); + let response = block_fn(|| srv.get("/").send()).unwrap(); assert!(response.status().is_success()); assert_eq!( response @@ -584,7 +580,7 @@ fn test_h1_response_http_error_handling() { })) }); - let response = srv.block_on(srv.get("/").send()).unwrap(); + let response = block_fn(|| srv.get("/").send()).unwrap(); assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR); // read response @@ -599,7 +595,7 @@ fn test_h1_service_error() { .h1(|_| future::err::(error::ErrorBadRequest("error"))) }); - let response = srv.block_on(srv.get("/").send()).unwrap(); + let response = block_fn(|| srv.get("/").send()).unwrap(); assert_eq!(response.status(), http::StatusCode::BAD_REQUEST); // read response @@ -609,7 +605,7 @@ fn test_h1_service_error() { #[test] fn test_h1_on_connect() { - let mut srv = TestServer::new(|| { + let srv = TestServer::new(|| { HttpService::build() .on_connect(|_| 10usize) .h1(|req: Request| { @@ -618,6 +614,6 @@ fn test_h1_on_connect() { }) }); - let response = srv.block_on(srv.get("/").send()).unwrap(); + let response = block_fn(|| srv.get("/").send()).unwrap(); assert!(response.status().is_success()); } diff --git a/awc/Cargo.toml b/awc/Cargo.toml index ea181237e..f8f9a7eb8 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -65,7 +65,7 @@ rust-tls = { version = "0.16.0", package="rustls", optional = true } actix-rt = "1.0.0-alpha.1" #actix-web = { version = "1.0.8", features=["ssl"] } actix-http = { version = "0.3.0-alpha.1", features=["openssl"] } -#actix-http-test = { version = "0.2.0", features=["ssl"] } +actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } actix-utils = "0.5.0-alpha.1" actix-server = { version = "0.8.0-alpha.1", features=["openssl", "rustls"] } brotli2 = { version="0.3.2" } @@ -77,7 +77,9 @@ webpki = { version = "0.21" } rus-tls = { version = "0.16.0", package="rustls", features = ["dangerous_configuration"] } [patch.crates-io] +awc = { path = "." } actix-http = { path = "../actix-http" } +actix-http-test = { path = "../test-server" } actix-codec = { path = "../../actix-net/actix-codec" } actix-connect = { path = "../../actix-net/actix-connect" } diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 35bf1a0ed..3333e0486 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http-test" -version = "0.2.5" +version = "0.3.0-alpha.1" authors = ["Nikolay Kim "] description = "Actix http test server" readme = "README.md" diff --git a/test-server/src/lib.rs b/test-server/src/lib.rs index 17acfe291..4ba123aa1 100644 --- a/test-server/src/lib.rs +++ b/test-server/src/lib.rs @@ -109,7 +109,7 @@ impl TestServer { block_on(lazy(|_| { Ok::<_, ()>(actix_connect::start_default_resolver()) })) - .unwrap(); + .unwrap(); TestServerRuntime { addr, From 1ffa7d18d37e293e7659562d77059b44de38d122 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Tue, 19 Nov 2019 18:54:19 +0600 Subject: [PATCH 049/176] drop unpin constraint --- actix-http/Cargo.toml | 3 +- actix-http/examples/echo.rs | 34 +- actix-http/examples/echo2.rs | 29 +- actix-http/src/body.rs | 73 ++- actix-http/src/builder.rs | 55 +- actix-http/src/client/connection.rs | 60 +- actix-http/src/client/connector.rs | 129 +--- actix-http/src/client/error.rs | 8 +- actix-http/src/client/pool.rs | 35 +- actix-http/src/config.rs | 2 +- actix-http/src/error.rs | 20 +- actix-http/src/h1/dispatcher.rs | 103 +-- actix-http/src/h1/payload.rs | 27 +- actix-http/src/h1/service.rs | 95 +-- actix-http/src/h1/utils.rs | 3 +- actix-http/src/h2/dispatcher.rs | 93 ++- actix-http/src/h2/service.rs | 59 +- actix-http/src/request.rs | 5 + actix-http/src/response.rs | 4 +- actix-http/src/service.rs | 262 ++++---- actix-http/src/ws/transport.rs | 14 +- actix-http/tests/test_client.rs | 88 +-- actix-http/tests/test_openssl.rs | 545 ++++++++++++++++ actix-http/tests/test_rustls.rs | 474 ++++++++++++++ actix-http/tests/test_rustls_server.rs | 462 ------------- actix-http/tests/test_server.rs | 855 +++++++++++++------------ actix-http/tests/test_ssl_server.rs | 480 -------------- actix-http/tests/test_ws.rs | 100 +-- test-server/src/lib.rs | 37 +- tests/cert.pem | 47 +- tests/key.pem | 76 +-- 31 files changed, 2136 insertions(+), 2141 deletions(-) create mode 100644 actix-http/tests/test_openssl.rs create mode 100644 actix-http/tests/test_rustls.rs delete mode 100644 actix-http/tests/test_rustls_server.rs delete mode 100644 actix-http/tests/test_ssl_server.rs diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 36f3cef47..edc93f09b 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -74,6 +74,7 @@ language-tags = "0.2" log = "0.4" mime = "0.3" percent-encoding = "2.1" +pin-project = "0.4.5" rand = "0.7" regex = "1.0" serde = "1.0" @@ -107,7 +108,7 @@ webpki-roots = { version = "0.18", optional = true } [dev-dependencies] actix-rt = "1.0.0-alpha.1" -actix-server = { version = "0.8.0-alpha.1", features=["openssl"] } +actix-server = { version = "0.8.0-alpha.1", features=["openssl", "rustls"] } actix-connect = { version = "1.0.0-alpha.1", features=["openssl"] } actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } env_logger = "0.6" diff --git a/actix-http/examples/echo.rs b/actix-http/examples/echo.rs index c36292c44..ba81020ca 100644 --- a/actix-http/examples/echo.rs +++ b/actix-http/examples/echo.rs @@ -1,9 +1,9 @@ use std::{env, io}; -use actix_http::{error::PayloadError, HttpService, Request, Response}; +use actix_http::{Error, HttpService, Request, Response}; use actix_server::Server; use bytes::BytesMut; -use futures::{Future, Stream}; +use futures::StreamExt; use http::header::HeaderValue; use log::info; @@ -17,20 +17,22 @@ fn main() -> io::Result<()> { .client_timeout(1000) .client_disconnect(1000) .finish(|mut req: Request| { - req.take_payload() - .fold(BytesMut::new(), move |mut body, chunk| { - body.extend_from_slice(&chunk); - Ok::<_, PayloadError>(body) - }) - .and_then(|bytes| { - info!("request body: {:?}", bytes); - let mut res = Response::Ok(); - res.header( - "x-head", - HeaderValue::from_static("dummy value!"), - ); - Ok(res.body(bytes)) - }) + async move { + let mut body = BytesMut::new(); + while let Some(item) = req.payload().next().await { + body.extend_from_slice(&item?); + } + + info!("request body: {:?}", body); + Ok::<_, Error>( + Response::Ok() + .header( + "x-head", + HeaderValue::from_static("dummy value!"), + ) + .body(body), + ) + } }) })? .run() diff --git a/actix-http/examples/echo2.rs b/actix-http/examples/echo2.rs index b239796b4..3776c7d58 100644 --- a/actix-http/examples/echo2.rs +++ b/actix-http/examples/echo2.rs @@ -1,25 +1,22 @@ use std::{env, io}; use actix_http::http::HeaderValue; -use actix_http::{error::PayloadError, Error, HttpService, Request, Response}; +use actix_http::{Error, HttpService, Request, Response}; use actix_server::Server; use bytes::BytesMut; -use futures::{Future, Stream}; +use futures::StreamExt; use log::info; -fn handle_request(mut req: Request) -> impl Future { - req.take_payload() - .fold(BytesMut::new(), move |mut body, chunk| { - body.extend_from_slice(&chunk); - Ok::<_, PayloadError>(body) - }) - .from_err() - .and_then(|bytes| { - info!("request body: {:?}", bytes); - let mut res = Response::Ok(); - res.header("x-head", HeaderValue::from_static("dummy value!")); - Ok(res.body(bytes)) - }) +async fn handle_request(mut req: Request) -> Result { + let mut body = BytesMut::new(); + while let Some(item) = req.payload().next().await { + body.extend_from_slice(&item?) + } + + info!("request body: {:?}", body); + Ok(Response::Ok() + .header("x-head", HeaderValue::from_static("dummy value!")) + .body(body)) } fn main() -> io::Result<()> { @@ -28,7 +25,7 @@ fn main() -> io::Result<()> { Server::build() .bind("echo", "127.0.0.1:8080", || { - HttpService::build().finish(|_req: Request| handle_request(_req)) + HttpService::build().finish(handle_request) })? .run() } diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs index 44e76ae0e..1d3a43fe5 100644 --- a/actix-http/src/body.rs +++ b/actix-http/src/body.rs @@ -5,6 +5,7 @@ use std::{fmt, mem}; use bytes::{Bytes, BytesMut}; use futures::Stream; +use pin_project::{pin_project, project}; use crate::error::Error; @@ -31,7 +32,7 @@ impl BodySize { } /// Type that provides this trait can be streamed to a peer. -pub trait MessageBody: Unpin { +pub trait MessageBody { fn size(&self) -> BodySize; fn poll_next(&mut self, cx: &mut Context) -> Poll>>; @@ -57,6 +58,7 @@ impl MessageBody for Box { } } +#[pin_project] pub enum ResponseBody { Body(B), Other(Body), @@ -106,8 +108,13 @@ impl MessageBody for ResponseBody { impl Stream for ResponseBody { type Item = Result; + #[project] fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - self.get_mut().poll_next(cx) + #[project] + match self.project() { + ResponseBody::Body(ref mut body) => body.poll_next(cx), + ResponseBody::Other(ref mut body) => body.poll_next(cx), + } } } @@ -243,7 +250,7 @@ impl From for Body { impl From> for Body where - S: Stream> + Unpin + 'static, + S: Stream> + 'static, { fn from(s: SizedStream) -> Body { Body::from_message(s) @@ -252,7 +259,7 @@ where impl From> for Body where - S: Stream> + Unpin + 'static, + S: Stream> + 'static, E: Into + 'static, { fn from(s: BodyStream) -> Body { @@ -350,7 +357,9 @@ impl MessageBody for String { /// Type represent streaming body. /// Response does not contain `content-length` header and appropriate transfer encoding is used. +#[pin_project] pub struct BodyStream { + #[pin] stream: S, _t: PhantomData, } @@ -368,16 +377,9 @@ where } } -impl Unpin for BodyStream -where - S: Stream> + Unpin, - E: Into, -{ -} - impl MessageBody for BodyStream where - S: Stream> + Unpin, + S: Stream>, E: Into, { fn size(&self) -> BodySize { @@ -385,7 +387,9 @@ where } fn poll_next(&mut self, cx: &mut Context) -> Poll>> { - Pin::new(&mut self.stream) + unsafe { Pin::new_unchecked(self) } + .project() + .stream .poll_next(cx) .map(|res| res.map(|res| res.map_err(std::convert::Into::into))) } @@ -393,8 +397,10 @@ where /// Type represent streaming body. This body implementation should be used /// if total size of stream is known. Data get sent as is without using transfer encoding. +#[pin_project] pub struct SizedStream { size: u64, + #[pin] stream: S, } @@ -409,20 +415,25 @@ where impl MessageBody for SizedStream where - S: Stream> + Unpin, + S: Stream>, { fn size(&self) -> BodySize { BodySize::Sized64(self.size) } fn poll_next(&mut self, cx: &mut Context) -> Poll>> { - Pin::new(&mut self.stream).poll_next(cx) + unsafe { Pin::new_unchecked(self) } + .project() + .stream + .poll_next(cx) } } #[cfg(test)] mod tests { use super::*; + use actix_http_test::block_on; + use futures::future::{lazy, poll_fn}; impl Body { pub(crate) fn get_ref(&self) -> &[u8] { @@ -450,8 +461,8 @@ mod tests { assert_eq!("test".size(), BodySize::Sized(4)); assert_eq!( - "test".poll_next().unwrap(), - Async::Ready(Some(Bytes::from("test"))) + block_on(poll_fn(|cx| "test".poll_next(cx))).unwrap().ok(), + Some(Bytes::from("test")) ); } @@ -467,8 +478,10 @@ mod tests { assert_eq!((&b"test"[..]).size(), BodySize::Sized(4)); assert_eq!( - (&b"test"[..]).poll_next().unwrap(), - Async::Ready(Some(Bytes::from("test"))) + block_on(poll_fn(|cx| (&b"test"[..]).poll_next(cx))) + .unwrap() + .ok(), + Some(Bytes::from("test")) ); } @@ -479,8 +492,10 @@ mod tests { assert_eq!(Vec::from("test").size(), BodySize::Sized(4)); assert_eq!( - Vec::from("test").poll_next().unwrap(), - Async::Ready(Some(Bytes::from("test"))) + block_on(poll_fn(|cx| Vec::from("test").poll_next(cx))) + .unwrap() + .ok(), + Some(Bytes::from("test")) ); } @@ -492,8 +507,8 @@ mod tests { assert_eq!(b.size(), BodySize::Sized(4)); assert_eq!( - b.poll_next().unwrap(), - Async::Ready(Some(Bytes::from("test"))) + block_on(poll_fn(|cx| b.poll_next(cx))).unwrap().ok(), + Some(Bytes::from("test")) ); } @@ -505,8 +520,8 @@ mod tests { assert_eq!(b.size(), BodySize::Sized(4)); assert_eq!( - b.poll_next().unwrap(), - Async::Ready(Some(Bytes::from("test"))) + block_on(poll_fn(|cx| b.poll_next(cx))).unwrap().ok(), + Some(Bytes::from("test")) ); } @@ -520,22 +535,22 @@ mod tests { assert_eq!(b.size(), BodySize::Sized(4)); assert_eq!( - b.poll_next().unwrap(), - Async::Ready(Some(Bytes::from("test"))) + block_on(poll_fn(|cx| b.poll_next(cx))).unwrap().ok(), + Some(Bytes::from("test")) ); } #[test] fn test_unit() { assert_eq!(().size(), BodySize::Empty); - assert_eq!(().poll_next().unwrap(), Async::Ready(None)); + assert!(block_on(poll_fn(|cx| ().poll_next(cx))).is_none()); } #[test] fn test_box() { let mut val = Box::new(()); assert_eq!(val.size(), BodySize::Empty); - assert_eq!(val.poll_next().unwrap(), Async::Ready(None)); + assert!(block_on(poll_fn(|cx| val.poll_next(cx))).is_none()); } #[test] diff --git a/actix-http/src/builder.rs b/actix-http/src/builder.rs index 8997d720c..8fa35feab 100644 --- a/actix-http/src/builder.rs +++ b/actix-http/src/builder.rs @@ -33,11 +33,9 @@ pub struct HttpServiceBuilder> { impl HttpServiceBuilder> where S: ServiceFactory, - S::Error: Into + Unpin + 'static, + S::Error: Into + 'static, S::InitError: fmt::Debug, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin + 'static, + ::Future: 'static, { /// Create instance of `ServiceConfigBuilder` pub fn new() -> Self { @@ -56,17 +54,13 @@ where impl HttpServiceBuilder where S: ServiceFactory, - S::Error: Into + Unpin + 'static, + S::Error: Into + 'static, S::InitError: fmt::Debug, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin + 'static, + ::Future: 'static, X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, - X::Future: Unpin, - X::Service: Unpin, - ::Future: Unpin + 'static, + ::Future: 'static, U: ServiceFactory< Config = SrvConfig, Request = (Request, Framed), @@ -74,9 +68,7 @@ where >, U::Error: fmt::Display, U::InitError: fmt::Debug, - U::Future: Unpin, - U::Service: Unpin, - ::Future: Unpin + 'static, + ::Future: 'static, { /// Set server keep-alive setting. /// @@ -124,9 +116,7 @@ where X1: ServiceFactory, X1::Error: Into, X1::InitError: fmt::Debug, - X1::Future: Unpin, - X1::Service: Unpin, - ::Future: Unpin + 'static, + ::Future: 'static, { HttpServiceBuilder { keep_alive: self.keep_alive, @@ -153,9 +143,7 @@ where >, U1::Error: fmt::Display, U1::InitError: fmt::Debug, - U1::Future: Unpin, - U1::Service: Unpin, - ::Future: Unpin + 'static, + ::Future: 'static, { HttpServiceBuilder { keep_alive: self.keep_alive, @@ -186,13 +174,10 @@ where where B: MessageBody + 'static, F: IntoServiceFactory, - S::Future: Unpin, - S::Error: Into + Unpin + 'static, + S::Error: Into + 'static, S::InitError: fmt::Debug, - S::Response: Into> + Unpin + 'static, - S::Service: Unpin, - ::Future: Unpin + 'static, - P: Unpin, + S::Response: Into> + 'static, + ::Future: 'static, { let cfg = ServiceConfig::new( self.keep_alive, @@ -210,13 +195,10 @@ where where B: MessageBody + 'static, F: IntoServiceFactory, - S::Error: Into + Unpin + 'static, + S::Error: Into + 'static, S::InitError: fmt::Debug, - S::Response: Into> + Unpin + 'static, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin + 'static, - P: Unpin, + S::Response: Into> + 'static, + ::Future: 'static, { let cfg = ServiceConfig::new( self.keep_alive, @@ -231,13 +213,10 @@ where where B: MessageBody + 'static, F: IntoServiceFactory, - S::Error: Into + Unpin + 'static, + S::Error: Into + 'static, S::InitError: fmt::Debug, - S::Response: Into> + Unpin + 'static, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin + 'static, - P: Unpin, + S::Response: Into> + 'static, + ::Future: 'static, { let cfg = ServiceConfig::new( self.keep_alive, diff --git a/actix-http/src/client/connection.rs b/actix-http/src/client/connection.rs index 0901fdb2d..75d393b1b 100644 --- a/actix-http/src/client/connection.rs +++ b/actix-http/src/client/connection.rs @@ -6,6 +6,7 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed}; use bytes::{Buf, Bytes}; use futures::future::{err, Either, Future, FutureExt, LocalBoxFuture, Ready}; use h2::client::SendRequest; +use pin_project::{pin_project, project}; use crate::body::MessageBody; use crate::h1::ClientCodec; @@ -42,9 +43,7 @@ pub trait Connection { fn open_tunnel>(self, head: H) -> Self::TunnelFuture; } -pub(crate) trait ConnectionLifetime: - AsyncRead + AsyncWrite + Unpin + 'static -{ +pub(crate) trait ConnectionLifetime: AsyncRead + AsyncWrite + 'static { /// Close connection fn close(&mut self); @@ -73,7 +72,7 @@ where } } -impl IoConnection { +impl IoConnection { pub(crate) fn new( io: ConnectionType, created: time::Instant, @@ -205,24 +204,27 @@ where } } +#[pin_project] pub enum EitherIo { - A(A), - B(B), + A(#[pin] A), + B(#[pin] B), } impl AsyncRead for EitherIo where - A: AsyncRead + Unpin, - B: AsyncRead + Unpin, + A: AsyncRead, + B: AsyncRead, { + #[project] fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - match self.get_mut() { - EitherIo::A(ref mut val) => Pin::new(val).poll_read(cx, buf), - EitherIo::B(ref mut val) => Pin::new(val).poll_read(cx, buf), + #[project] + match self.project() { + EitherIo::A(val) => val.poll_read(cx, buf), + EitherIo::B(val) => val.poll_read(cx, buf), } } @@ -236,37 +238,44 @@ where impl AsyncWrite for EitherIo where - A: AsyncWrite + Unpin, - B: AsyncWrite + Unpin, + A: AsyncWrite, + B: AsyncWrite, { + #[project] fn poll_write( self: Pin<&mut Self>, cx: &mut Context, buf: &[u8], ) -> Poll> { - match self.get_mut() { - EitherIo::A(ref mut val) => Pin::new(val).poll_write(cx, buf), - EitherIo::B(ref mut val) => Pin::new(val).poll_write(cx, buf), + #[project] + match self.project() { + EitherIo::A(val) => val.poll_write(cx, buf), + EitherIo::B(val) => val.poll_write(cx, buf), } } + #[project] fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.get_mut() { - EitherIo::A(ref mut val) => Pin::new(val).poll_flush(cx), - EitherIo::B(ref mut val) => Pin::new(val).poll_flush(cx), + #[project] + match self.project() { + EitherIo::A(val) => val.poll_flush(cx), + EitherIo::B(val) => val.poll_flush(cx), } } + #[project] fn poll_shutdown( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - match self.get_mut() { - EitherIo::A(ref mut val) => Pin::new(val).poll_shutdown(cx), - EitherIo::B(ref mut val) => Pin::new(val).poll_shutdown(cx), + #[project] + match self.project() { + EitherIo::A(val) => val.poll_shutdown(cx), + EitherIo::B(val) => val.poll_shutdown(cx), } } + #[project] fn poll_write_buf( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -275,9 +284,10 @@ where where Self: Sized, { - match self.get_mut() { - EitherIo::A(ref mut val) => Pin::new(val).poll_write_buf(cx, buf), - EitherIo::B(ref mut val) => Pin::new(val).poll_write_buf(cx, buf), + #[project] + match self.project() { + EitherIo::A(val) => val.poll_write_buf(cx, buf), + EitherIo::B(val) => val.poll_write_buf(cx, buf), } } } diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index 45625ca9f..1895f5306 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -62,8 +62,8 @@ pub struct Connector { _t: PhantomData, } -trait Io: AsyncRead + AsyncWrite {} -impl Io for T {} +trait Io: AsyncRead + AsyncWrite + Unpin {} +impl Io for T {} impl Connector<(), ()> { #[allow(clippy::new_ret_no_self)] @@ -123,7 +123,6 @@ impl Connector { Response = TcpConnection, Error = actix_connect::ConnectError, > + Clone, - T1::Future: Unpin, { Connector { connector, @@ -222,7 +221,7 @@ where { let connector = TimeoutService::new( self.timeout, - apply_fn(UnpinWrapper(self.connector), |msg: Connect, srv| { + apply_fn(self.connector, |msg: Connect, srv| { srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr)) }) .map_err(ConnectError::from) @@ -257,35 +256,33 @@ where let ssl_service = TimeoutService::new( self.timeout, pipeline( - apply_fn( - UnpinWrapper(self.connector.clone()), - |msg: Connect, srv| { - srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr)) - }, - ) + apply_fn(self.connector.clone(), |msg: Connect, srv| { + srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr)) + }) .map_err(ConnectError::from), ) .and_then(match self.ssl { #[cfg(feature = "openssl")] - SslConnector::Openssl(ssl) => OpensslConnector::service(ssl) - .map(|stream| { - let sock = stream.into_parts().0; - let h2 = sock - .ssl() - .selected_alpn_protocol() - .map(|protos| protos.windows(2).any(|w| w == H2)) - .unwrap_or(false); - if h2 { - (Box::new(sock) as Box, Protocol::Http2) - } else { - (Box::new(sock) as Box, Protocol::Http1) - } - }) - .map_err(ConnectError::from), - + SslConnector::Openssl(ssl) => service( + OpensslConnector::service(ssl) + .map(|stream| { + let sock = stream.into_parts().0; + let h2 = sock + .ssl() + .selected_alpn_protocol() + .map(|protos| protos.windows(2).any(|w| w == H2)) + .unwrap_or(false); + if h2 { + (Box::new(sock) as Box, Protocol::Http2) + } else { + (Box::new(sock) as Box, Protocol::Http1) + } + }) + .map_err(ConnectError::from), + ), #[cfg(feature = "rustls")] SslConnector::Rustls(ssl) => service( - UnpinWrapper(RustlsConnector::service(ssl)) + RustlsConnector::service(ssl) .map_err(ConnectError::from) .map(|stream| { let sock = stream.into_parts().0; @@ -296,15 +293,9 @@ where .map(|protos| protos.windows(2).any(|w| w == H2)) .unwrap_or(false); if h2 { - ( - Box::new(sock) as Box, - Protocol::Http2, - ) + (Box::new(sock) as Box, Protocol::Http2) } else { - ( - Box::new(sock) as Box, - Protocol::Http1, - ) + (Box::new(sock) as Box, Protocol::Http1) } }), ), @@ -317,7 +308,7 @@ where let tcp_service = TimeoutService::new( self.timeout, - apply_fn(UnpinWrapper(self.connector), |msg: Connect, srv| { + apply_fn(self.connector, |msg: Connect, srv| { srv.call(TcpConnect::new(msg.uri).set_addr(msg.addr)) }) .map_err(ConnectError::from) @@ -348,42 +339,6 @@ where } } -#[derive(Clone)] -struct UnpinWrapper(T); - -impl Unpin for UnpinWrapper {} - -impl Service for UnpinWrapper { - type Request = T::Request; - type Response = T::Response; - type Error = T::Error; - type Future = UnpinWrapperFut; - - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { - self.0.poll_ready(cx) - } - - fn call(&mut self, req: T::Request) -> Self::Future { - UnpinWrapperFut { - fut: self.0.call(req), - } - } -} - -struct UnpinWrapperFut { - fut: T::Future, -} - -impl Unpin for UnpinWrapperFut {} - -impl Future for UnpinWrapperFut { - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - unsafe { Pin::new_unchecked(&mut self.get_mut().fut) }.poll(cx) - } -} - #[cfg(not(any(feature = "openssl", feature = "rustls")))] mod connect_impl { use std::task::{Context, Poll}; @@ -396,9 +351,8 @@ mod connect_impl { pub(crate) struct InnerConnector where - Io: AsyncRead + AsyncWrite + 'static, + Io: AsyncRead + AsyncWrite + Unpin + 'static, T: Service - + Unpin + 'static, { pub(crate) tcp_pool: ConnectionPool, @@ -406,9 +360,8 @@ mod connect_impl { impl Clone for InnerConnector where - Io: AsyncRead + AsyncWrite + 'static, + Io: AsyncRead + AsyncWrite + Unpin + 'static, T: Service - + Unpin + 'static, { fn clone(&self) -> Self { @@ -422,9 +375,7 @@ mod connect_impl { where Io: AsyncRead + AsyncWrite + Unpin + 'static, T: Service - + Unpin + 'static, - T::Future: Unpin, { type Request = Connect; type Response = IoConnection; @@ -465,8 +416,6 @@ mod connect_impl { Io2: AsyncRead + AsyncWrite + Unpin + 'static, T1: Service, T2: Service, - T1::Future: Unpin, - T2::Future: Unpin, { pub(crate) tcp_pool: ConnectionPool, pub(crate) ssl_pool: ConnectionPool, @@ -477,13 +426,9 @@ mod connect_impl { Io1: AsyncRead + AsyncWrite + Unpin + 'static, Io2: AsyncRead + AsyncWrite + Unpin + 'static, T1: Service - + Unpin + 'static, T2: Service - + Unpin + 'static, - T1::Future: Unpin, - T2::Future: Unpin, { fn clone(&self) -> Self { InnerConnector { @@ -498,13 +443,9 @@ mod connect_impl { Io1: AsyncRead + AsyncWrite + Unpin + 'static, Io2: AsyncRead + AsyncWrite + Unpin + 'static, T1: Service - + Unpin + 'static, T2: Service - + Unpin + 'static, - T1::Future: Unpin, - T2::Future: Unpin, { type Request = Connect; type Response = EitherConnection; @@ -532,14 +473,14 @@ mod connect_impl { } } + #[pin_project::pin_project] pub(crate) struct InnerConnectorResponseA where Io1: AsyncRead + AsyncWrite + Unpin + 'static, T: Service - + Unpin + 'static, - T::Future: Unpin, { + #[pin] fut: as Service>::Future, _t: PhantomData, } @@ -547,9 +488,7 @@ mod connect_impl { impl Future for InnerConnectorResponseA where T: Service - + Unpin + 'static, - T::Future: Unpin, Io1: AsyncRead + AsyncWrite + Unpin + 'static, Io2: AsyncRead + AsyncWrite + Unpin + 'static, { @@ -563,14 +502,14 @@ mod connect_impl { } } + #[pin_project::pin_project] pub(crate) struct InnerConnectorResponseB where Io2: AsyncRead + AsyncWrite + Unpin + 'static, T: Service - + Unpin + 'static, - T::Future: Unpin, { + #[pin] fut: as Service>::Future, _t: PhantomData, } @@ -578,9 +517,7 @@ mod connect_impl { impl Future for InnerConnectorResponseB where T: Service - + Unpin + 'static, - T::Future: Unpin, Io1: AsyncRead + AsyncWrite + Unpin + 'static, Io2: AsyncRead + AsyncWrite + Unpin + 'static, { diff --git a/actix-http/src/client/error.rs b/actix-http/src/client/error.rs index 0ac5f30f5..75f7935f2 100644 --- a/actix-http/src/client/error.rs +++ b/actix-http/src/client/error.rs @@ -3,8 +3,8 @@ use std::io; use derive_more::{Display, From}; use trust_dns_resolver::error::ResolveError; -#[cfg(feature = "ssl")] -use openssl::ssl::{Error as SslError, HandshakeError}; +#[cfg(feature = "openssl")] +use open_ssl::ssl::{Error as SslError, HandshakeError}; use crate::error::{Error, ParseError, ResponseError}; use crate::http::Error as HttpError; @@ -18,7 +18,7 @@ pub enum ConnectError { SslIsNotSupported, /// SSL error - #[cfg(feature = "ssl")] + #[cfg(feature = "openssl")] #[display(fmt = "{}", _0)] SslError(SslError), @@ -63,7 +63,7 @@ impl From for ConnectError { } } -#[cfg(feature = "ssl")] +#[cfg(feature = "openssl")] impl From> for ConnectError { fn from(err: HandshakeError) -> ConnectError { match err { diff --git a/actix-http/src/client/pool.rs b/actix-http/src/client/pool.rs index ee4c4ab9f..1952dca59 100644 --- a/actix-http/src/client/pool.rs +++ b/actix-http/src/client/pool.rs @@ -46,11 +46,9 @@ pub(crate) struct ConnectionPool(Rc>, Rc ConnectionPool where - Io: AsyncRead + AsyncWrite + 'static, + Io: AsyncRead + AsyncWrite + Unpin + 'static, T: Service - + Unpin + 'static, - T::Future: Unpin, { pub(crate) fn new( connector: T, @@ -89,9 +87,7 @@ impl Service for ConnectionPool where Io: AsyncRead + AsyncWrite + Unpin + 'static, T: Service - + Unpin + 'static, - T::Future: Unpin, { type Request = Connect; type Response = IoConnection; @@ -400,7 +396,7 @@ struct CloseConnection { impl CloseConnection where - T: AsyncWrite, + T: AsyncWrite + Unpin, { fn new(io: T, timeout: Duration) -> Self { CloseConnection { @@ -416,10 +412,12 @@ where { type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> { - match Pin::new(&mut self.timeout).poll(cx) { + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> { + let this = self.get_mut(); + + match Pin::new(&mut this.timeout).poll(cx) { Poll::Ready(_) => Poll::Ready(()), - Poll::Pending => match Pin::new(&mut self.io).poll_shutdown(cx) { + Poll::Pending => match Pin::new(&mut this.io).poll_shutdown(cx) { Poll::Ready(_) => Poll::Ready(()), Poll::Pending => Poll::Pending, }, @@ -429,7 +427,7 @@ where struct ConnectorPoolSupport where - Io: AsyncRead + AsyncWrite + 'static, + Io: AsyncRead + AsyncWrite + Unpin + 'static, { connector: T, inner: Rc>>, @@ -438,14 +436,13 @@ where impl Future for ConnectorPoolSupport where Io: AsyncRead + AsyncWrite + Unpin + 'static, - T: Service - + Unpin, - T::Future: Unpin + 'static, + T: Service, + T::Future: 'static, { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let this = self.get_mut(); + let this = unsafe { self.get_unchecked_mut() }; let mut inner = this.inner.as_ref().borrow_mut(); inner.waker.register(cx.waker()); @@ -512,7 +509,7 @@ where impl OpenWaitingConnection where - F: Future> + Unpin + 'static, + F: Future> + 'static, Io: AsyncRead + AsyncWrite + Unpin + 'static, { fn spawn( @@ -546,13 +543,13 @@ where impl Future for OpenWaitingConnection where - F: Future> + Unpin, + F: Future>, Io: AsyncRead + AsyncWrite + Unpin, { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let this = self.get_mut(); + let this = unsafe { self.get_unchecked_mut() }; if let Some(ref mut h2) = this.h2 { return match Pin::new(h2).poll(cx) { @@ -577,7 +574,7 @@ where }; } - match Pin::new(&mut this.fut).poll(cx) { + match unsafe { Pin::new_unchecked(&mut this.fut) }.poll(cx) { Poll::Ready(Err(err)) => { let _ = this.inner.take(); if let Some(rx) = this.rx.take() { @@ -596,7 +593,7 @@ where Poll::Ready(()) } else { this.h2 = Some(handshake(io).boxed_local()); - Pin::new(this).poll(cx) + unsafe { Pin::new_unchecked(this) }.poll(cx) } } Poll::Pending => Poll::Pending, diff --git a/actix-http/src/config.rs b/actix-http/src/config.rs index a2dab8f04..488e4d98a 100644 --- a/actix-http/src/config.rs +++ b/actix-http/src/config.rs @@ -277,7 +277,7 @@ mod tests { fn test_date() { let mut rt = System::new("test"); - let _ = rt.block_on(future::lazy(|| { + let _ = rt.block_on(future::lazy(|_| { let settings = ServiceConfig::new(KeepAlive::Os, 0, 0); let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10); settings.set_date(&mut buf1); diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index 82027dbe3..2a6833995 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -181,13 +181,13 @@ impl ResponseError for FormError {} /// `InternalServerError` for `TimerError` impl ResponseError for TimerError {} -#[cfg(feature = "ssl")] +#[cfg(feature = "openssl")] /// `InternalServerError` for `openssl::ssl::Error` -impl ResponseError for openssl::ssl::Error {} +impl ResponseError for open_ssl::ssl::Error {} -#[cfg(feature = "ssl")] +#[cfg(feature = "openssl")] /// `InternalServerError` for `openssl::ssl::HandshakeError` -impl ResponseError for openssl::ssl::HandshakeError {} +impl ResponseError for open_ssl::ssl::HandshakeError {} /// Return `BAD_REQUEST` for `de::value::Error` impl ResponseError for DeError { @@ -383,12 +383,12 @@ impl ResponseError for PayloadError { } } -// /// Return `BadRequest` for `cookie::ParseError` -// impl ResponseError for crate::cookie::ParseError { -// fn error_response(&self) -> Response { -// Response::new(StatusCode::BAD_REQUEST) -// } -// } +/// Return `BadRequest` for `cookie::ParseError` +impl ResponseError for crate::cookie::ParseError { + fn error_response(&self) -> Response { + Response::new(StatusCode::BAD_REQUEST) + } +} #[derive(Debug, Display, From)] /// A set of errors that can occur during dispatching http requests diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index a06b997ec..8c0896029 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -48,14 +48,11 @@ pub struct Dispatcher where S: Service, S::Error: Into, - S::Future: Unpin, B: MessageBody, X: Service, X::Error: Into, - X::Future: Unpin, U: Service), Response = ()>, U::Error: fmt::Display, - U::Future: Unpin, { inner: DispatcherState, } @@ -64,14 +61,11 @@ enum DispatcherState where S: Service, S::Error: Into, - S::Future: Unpin, B: MessageBody, X: Service, X::Error: Into, - X::Future: Unpin, U: Service), Response = ()>, U::Error: fmt::Display, - U::Future: Unpin, { Normal(InnerDispatcher), Upgrade(U::Future), @@ -82,14 +76,11 @@ struct InnerDispatcher where S: Service, S::Error: Into, - S::Future: Unpin, B: MessageBody, X: Service, X::Error: Into, - X::Future: Unpin, U: Service), Response = ()>, U::Error: fmt::Display, - U::Future: Unpin, { service: CloneableService, expect: CloneableService, @@ -181,14 +172,11 @@ where S: Service, S::Error: Into, S::Response: Into>, - S::Future: Unpin, B: MessageBody, X: Service, X::Error: Into, - X::Future: Unpin, U: Service), Response = ()>, U::Error: fmt::Display, - U::Future: Unpin, { /// Create http/1 dispatcher. pub(crate) fn new( @@ -269,14 +257,11 @@ where S: Service, S::Error: Into, S::Response: Into>, - S::Future: Unpin, B: MessageBody, X: Service, X::Error: Into, - X::Future: Unpin, U: Service), Response = ()>, U::Error: fmt::Display, - U::Future: Unpin, { fn can_read(&self, cx: &mut Context) -> bool { if self @@ -312,7 +297,9 @@ where let len = self.write_buf.len(); let mut written = 0; while written < len { - match Pin::new(&mut self.io).poll_write(cx, &self.write_buf[written..]) { + match unsafe { Pin::new_unchecked(&mut self.io) } + .poll_write(cx, &self.write_buf[written..]) + { Poll::Ready(Ok(0)) => { return Err(DispatchError::Io(io::Error::new( io::ErrorKind::WriteZero, @@ -383,32 +370,36 @@ where } None => None, }, - State::ExpectCall(ref mut fut) => match Pin::new(fut).poll(cx) { - Poll::Ready(Ok(req)) => { - self.send_continue(); - self.state = State::ServiceCall(self.service.call(req)); - continue; + State::ExpectCall(ref mut fut) => { + match unsafe { Pin::new_unchecked(fut) }.poll(cx) { + Poll::Ready(Ok(req)) => { + self.send_continue(); + self.state = State::ServiceCall(self.service.call(req)); + continue; + } + Poll::Ready(Err(e)) => { + let res: Response = e.into().into(); + let (res, body) = res.replace_body(()); + Some(self.send_response(res, body.into_body())?) + } + Poll::Pending => None, } - Poll::Ready(Err(e)) => { - let res: Response = e.into().into(); - let (res, body) = res.replace_body(()); - Some(self.send_response(res, body.into_body())?) + } + State::ServiceCall(ref mut fut) => { + match unsafe { Pin::new_unchecked(fut) }.poll(cx) { + Poll::Ready(Ok(res)) => { + let (res, body) = res.into().replace_body(()); + self.state = self.send_response(res, body)?; + continue; + } + Poll::Ready(Err(e)) => { + let res: Response = e.into().into(); + let (res, body) = res.replace_body(()); + Some(self.send_response(res, body.into_body())?) + } + Poll::Pending => None, } - Poll::Pending => None, - }, - State::ServiceCall(ref mut fut) => match Pin::new(fut).poll(cx) { - Poll::Ready(Ok(res)) => { - let (res, body) = res.into().replace_body(()); - self.state = self.send_response(res, body)?; - continue; - } - Poll::Ready(Err(e)) => { - let res: Response = e.into().into(); - let (res, body) = res.replace_body(()); - Some(self.send_response(res, body.into_body())?) - } - Poll::Pending => None, - }, + } State::SendPayload(ref mut stream) => { loop { if self.write_buf.len() < HW_BUFFER_SIZE { @@ -472,7 +463,7 @@ where // Handle `EXPECT: 100-Continue` header let req = if req.head().expect() { let mut task = self.expect.call(req); - match Pin::new(&mut task).poll(cx) { + match unsafe { Pin::new_unchecked(&mut task) }.poll(cx) { Poll::Ready(Ok(req)) => { self.send_continue(); req @@ -491,7 +482,7 @@ where // Call service let mut task = self.service.call(req); - match Pin::new(&mut task).poll(cx) { + match unsafe { Pin::new_unchecked(&mut task) }.poll(cx) { Poll::Ready(Ok(res)) => { let (res, body) = res.into().replace_body(()); self.send_response(res, body) @@ -689,26 +680,37 @@ where } } +impl Unpin for Dispatcher +where + T: IoStream, + S: Service, + S::Error: Into, + S::Response: Into>, + B: MessageBody, + X: Service, + X::Error: Into, + U: Service), Response = ()>, + U::Error: fmt::Display, +{ +} + impl Future for Dispatcher where T: IoStream, S: Service, S::Error: Into, S::Response: Into>, - S::Future: Unpin, B: MessageBody, X: Service, X::Error: Into, - X::Future: Unpin, U: Service), Response = ()>, U::Error: fmt::Display, - U::Future: Unpin, { type Output = Result<(), DispatchError>; #[inline] fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { - match self.inner { + match self.as_mut().inner { DispatcherState::Normal(ref mut inner) => { inner.poll_keepalive(cx)?; @@ -818,7 +820,7 @@ where } } DispatcherState::Upgrade(ref mut fut) => { - Pin::new(fut).poll(cx).map_err(|e| { + unsafe { Pin::new_unchecked(fut) }.poll(cx).map_err(|e| { error!("Upgrade handler error: {}", e); DispatchError::Upgrade }) @@ -894,7 +896,7 @@ mod tests { #[test] fn test_req_parse_err() { let mut sys = actix_rt::System::new("test"); - let _ = sys.block_on(lazy(|| { + let _ = sys.block_on(lazy(|cx| { let buf = TestBuffer::new("GET /test HTTP/1\r\n\r\n"); let mut h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new( @@ -907,7 +909,10 @@ mod tests { None, None, ); - assert!(h1.poll().is_err()); + match Pin::new(&mut h1).poll(cx) { + Poll::Pending => panic!(), + Poll::Ready(res) => assert!(res.is_err()), + } if let DispatcherState::Normal(ref inner) = h1.inner { assert!(inner.flags.contains(Flags::READ_DISCONNECT)); diff --git a/actix-http/src/h1/payload.rs b/actix-http/src/h1/payload.rs index 20ff830e7..2b52cfd86 100644 --- a/actix-http/src/h1/payload.rs +++ b/actix-http/src/h1/payload.rs @@ -228,26 +228,23 @@ impl Inner { mod tests { use super::*; use actix_rt::Runtime; - use futures::future::{lazy, result}; + use futures::future::{poll_fn, ready}; #[test] fn test_unread_data() { - Runtime::new() - .unwrap() - .block_on(async { - let (_, mut payload) = Payload::create(false); + Runtime::new().unwrap().block_on(async { + let (_, mut payload) = Payload::create(false); - payload.unread_data(Bytes::from("data")); - assert!(!payload.is_empty()); - assert_eq!(payload.len(), 4); + payload.unread_data(Bytes::from("data")); + assert!(!payload.is_empty()); + assert_eq!(payload.len(), 4); - assert_eq!( - Poll::Ready(Some(Bytes::from("data"))), - payload.next_item().await.ok().unwrap() - ); + assert_eq!( + Bytes::from("data"), + poll_fn(|cx| payload.readany(cx)).await.unwrap().unwrap() + ); - result(()) - }) - .unwrap(); + ready(()) + }); } } diff --git a/actix-http/src/h1/service.rs b/actix-http/src/h1/service.rs index 95596af76..ce8ff6626 100644 --- a/actix-http/src/h1/service.rs +++ b/actix-http/src/h1/service.rs @@ -39,11 +39,7 @@ where S::Error: Into, S::InitError: fmt::Debug, S::Response: Into>, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin, B: MessageBody, - P: Unpin, { /// Create new `HttpService` instance with default config. pub fn new>(service: F) -> Self { @@ -81,20 +77,13 @@ where S::Error: Into, S::Response: Into>, S::InitError: fmt::Debug, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin, B: MessageBody, - P: Unpin, { pub fn expect(self, expect: X1) -> H1Service where X1: ServiceFactory, X1::Error: Into, X1::InitError: fmt::Debug, - X1::Future: Unpin, - X1::Service: Unpin, - ::Future: Unpin, { H1Service { expect, @@ -111,9 +100,6 @@ where U1: ServiceFactory), Response = ()>, U1::Error: fmt::Display, U1::InitError: fmt::Debug, - U1::Future: Unpin, - U1::Service: Unpin, - ::Future: Unpin, { H1Service { upgrade, @@ -139,20 +125,13 @@ impl ServiceFactory for H1Service where T: IoStream, S: ServiceFactory, - S::Service: Unpin, S::Error: Into, S::Response: Into>, S::InitError: fmt::Debug, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin, B: MessageBody, X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, - X::Future: Unpin, - X::Service: Unpin, - ::Future: Unpin, U: ServiceFactory< Config = SrvConfig, Request = (Request, Framed), @@ -160,10 +139,6 @@ where >, U::Error: fmt::Display, U::InitError: fmt::Debug, - U::Future: Unpin, - U::Service: Unpin, - ::Future: Unpin, - P: Unpin, { type Config = SrvConfig; type Request = Io; @@ -188,30 +163,24 @@ where } #[doc(hidden)] +#[pin_project::pin_project] pub struct H1ServiceResponse where S: ServiceFactory, S::Error: Into, S::InitError: fmt::Debug, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin, X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, - X::Future: Unpin, - X::Service: Unpin, - ::Future: Unpin, U: ServiceFactory), Response = ()>, U::Error: fmt::Display, U::InitError: fmt::Debug, - U::Future: Unpin, - U::Service: Unpin, - ::Future: Unpin, - P: Unpin, { + #[pin] fut: S::Future, + #[pin] fut_ex: Option, + #[pin] fut_upg: Option, expect: Option, upgrade: Option, @@ -227,51 +196,45 @@ where S::Error: Into, S::Response: Into>, S::InitError: fmt::Debug, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin, B: MessageBody, X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, - X::Future: Unpin, - X::Service: Unpin, - ::Future: Unpin, U: ServiceFactory), Response = ()>, U::Error: fmt::Display, U::InitError: fmt::Debug, - U::Future: Unpin, - U::Service: Unpin, - ::Future: Unpin, - P: Unpin, { type Output = Result, ()>; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let this = self.get_mut(); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let mut this = self.as_mut().project(); - if let Some(ref mut fut) = this.fut_ex { - let expect = ready!(Pin::new(fut) + if let Some(fut) = this.fut_ex.as_pin_mut() { + let expect = ready!(fut .poll(cx) .map_err(|e| log::error!("Init http service error: {:?}", e)))?; - this.expect = Some(expect); - this.fut_ex.take(); + this = self.as_mut().project(); + *this.expect = Some(expect); + this.fut_ex.set(None); } - if let Some(ref mut fut) = this.fut_upg { - let upgrade = ready!(Pin::new(fut) + if let Some(fut) = this.fut_upg.as_pin_mut() { + let upgrade = ready!(fut .poll(cx) .map_err(|e| log::error!("Init http service error: {:?}", e)))?; - this.upgrade = Some(upgrade); - this.fut_ex.take(); + this = self.as_mut().project(); + *this.upgrade = Some(upgrade); + this.fut_ex.set(None); } - let result = ready!(Pin::new(&mut this.fut) + let result = ready!(this + .fut .poll(cx) .map_err(|e| log::error!("Init http service error: {:?}", e))); Poll::Ready(result.map(|service| { + let this = self.as_mut().project(); H1ServiceHandler::new( this.cfg.take().unwrap(), service, @@ -295,18 +258,14 @@ pub struct H1ServiceHandler { impl H1ServiceHandler where - S: Service + Unpin, + S: Service, S::Error: Into, S::Response: Into>, - S::Future: Unpin, B: MessageBody, - X: Service + Unpin, - X::Future: Unpin, + X: Service, X::Error: Into, - U: Service), Response = ()> + Unpin, - U::Future: Unpin, + U: Service), Response = ()>, U::Error: fmt::Display, - P: Unpin, { fn new( cfg: ServiceConfig, @@ -329,18 +288,14 @@ where impl Service for H1ServiceHandler where T: IoStream, - S: Service + Unpin, + S: Service, S::Error: Into, S::Response: Into>, - S::Future: Unpin, B: MessageBody, - X: Service + Unpin, + X: Service, X::Error: Into, - X::Future: Unpin, - U: Service), Response = ()> + Unpin, + U: Service), Response = ()>, U::Error: fmt::Display, - U::Future: Unpin, - P: Unpin, { type Request = Io; type Response = (); diff --git a/actix-http/src/h1/utils.rs b/actix-http/src/h1/utils.rs index a992089c0..7057bf1ca 100644 --- a/actix-http/src/h1/utils.rs +++ b/actix-http/src/h1/utils.rs @@ -11,6 +11,7 @@ use crate::h1::{Codec, Message}; use crate::response::Response; /// Send http/1 response +#[pin_project::pin_project] pub struct SendResponse { res: Option, BodySize)>>, body: Option>, @@ -34,7 +35,7 @@ where impl Future for SendResponse where - T: AsyncRead + AsyncWrite + Unpin, + T: AsyncRead + AsyncWrite, B: MessageBody, { type Output = Result, Error>; diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index 9b5b7e83a..96775b989 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -35,6 +35,7 @@ use crate::response::Response; const CHUNK_SIZE: usize = 16_384; /// Dispatcher for HTTP/2 protocol +#[pin_project::pin_project] pub struct Dispatcher, B: MessageBody> { service: CloneableService, connection: Connection, @@ -46,24 +47,13 @@ pub struct Dispatcher, B: MessageBody _t: PhantomData, } -impl Unpin for Dispatcher -where - T: IoStream, - S: Service, - S::Error: Into + Unpin + 'static, - S::Future: Unpin + 'static, - S::Response: Into> + Unpin + 'static, - B: MessageBody + 'static, -{ -} - impl Dispatcher where T: IoStream, S: Service, - S::Error: Into + Unpin + 'static, - S::Future: Unpin + 'static, - S::Response: Into> + Unpin + 'static, + S::Error: Into + 'static, + S::Future: 'static, + S::Response: Into> + 'static, B: MessageBody + 'static, { pub(crate) fn new( @@ -107,9 +97,9 @@ impl Future for Dispatcher where T: IoStream, S: Service, - S::Error: Into + Unpin + 'static, - S::Future: Unpin + 'static, - S::Response: Into> + Unpin + 'static, + S::Error: Into + 'static, + S::Future: 'static, + S::Response: Into> + 'static, B: MessageBody + 'static, { type Output = Result<(), DispatchError>; @@ -122,7 +112,7 @@ where match Pin::new(&mut this.connection).poll_accept(cx) { Poll::Ready(None) => return Poll::Ready(Ok(())), Poll::Ready(Some(Err(err))) => return Poll::Ready(Err(err.into())), - Poll::Ready(Some(Ok((req, _)))) => { + Poll::Ready(Some(Ok((req, res)))) => { // update keep-alive expire if this.ka_timer.is_some() { if let Some(expire) = this.config.keep_alive_expire() { @@ -131,7 +121,6 @@ where } let (parts, body) = req.into_parts(); - // let b: () = body; let mut req = Request::with_payload(Payload::< crate::payload::PayloadStream, >::H2( @@ -150,20 +139,20 @@ where on_connect.set(&mut req.extensions_mut()); } - // tokio_executor::current_thread::spawn(ServiceResponse::< - // S::Future, - // S::Response, - // S::Error, - // B, - // > { - // state: ServiceResponseState::ServiceCall( - // this.service.call(req), - // Some(res), - // ), - // config: this.config.clone(), - // buffer: None, - // _t: PhantomData, - // }); + tokio_executor::current_thread::spawn(ServiceResponse::< + S::Future, + S::Response, + S::Error, + B, + > { + state: ServiceResponseState::ServiceCall( + this.service.call(req), + Some(res), + ), + config: this.config.clone(), + buffer: None, + _t: PhantomData, + }); } Poll::Pending => return Poll::Pending, } @@ -171,6 +160,7 @@ where } } +#[pin_project::pin_project] struct ServiceResponse { state: ServiceResponseState, config: ServiceConfig, @@ -185,9 +175,9 @@ enum ServiceResponseState { impl ServiceResponse where - F: Future> + Unpin, - E: Into + Unpin + 'static, - I: Into> + Unpin + 'static, + F: Future>, + E: Into + 'static, + I: Into> + 'static, B: MessageBody + 'static, { fn prepare_response( @@ -253,25 +243,27 @@ where impl Future for ServiceResponse where - F: Future> + Unpin, - E: Into + Unpin + 'static, - I: Into> + Unpin + 'static, + F: Future>, + E: Into + 'static, + I: Into> + 'static, B: MessageBody + 'static, { type Output = (); - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let this = self.get_mut(); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let mut this = self.as_mut().project(); match this.state { ServiceResponseState::ServiceCall(ref mut call, ref mut send) => { - match Pin::new(call).poll(cx) { + match unsafe { Pin::new_unchecked(call) }.poll(cx) { Poll::Ready(Ok(res)) => { let (res, body) = res.into().replace_body(()); let mut send = send.take().unwrap(); let mut size = body.size(); - let h2_res = this.prepare_response(res.head(), &mut size); + let h2_res = + self.as_mut().prepare_response(res.head(), &mut size); + this = self.as_mut().project(); let stream = match send.send_response(h2_res, size.is_eof()) { Err(e) => { @@ -284,8 +276,9 @@ where if size.is_eof() { Poll::Ready(()) } else { - this.state = ServiceResponseState::SendPayload(stream, body); - Pin::new(this).poll(cx) + *this.state = + ServiceResponseState::SendPayload(stream, body); + self.poll(cx) } } Poll::Pending => Poll::Pending, @@ -295,7 +288,9 @@ where let mut send = send.take().unwrap(); let mut size = body.size(); - let h2_res = this.prepare_response(res.head(), &mut size); + let h2_res = + self.as_mut().prepare_response(res.head(), &mut size); + this = self.as_mut().project(); let stream = match send.send_response(h2_res, size.is_eof()) { Err(e) => { @@ -308,11 +303,11 @@ where if size.is_eof() { Poll::Ready(()) } else { - this.state = ServiceResponseState::SendPayload( + *this.state = ServiceResponseState::SendPayload( stream, body.into_body(), ); - Pin::new(this).poll(cx) + self.poll(cx) } } } @@ -356,7 +351,7 @@ where chunk.len(), CHUNK_SIZE, )); - this.buffer = Some(chunk); + *this.buffer = Some(chunk); } Poll::Ready(Some(Err(e))) => { error!("Response payload stream error: {:?}", e); diff --git a/actix-http/src/h2/service.rs b/actix-http/src/h2/service.rs index 559c99308..860a61f73 100644 --- a/actix-http/src/h2/service.rs +++ b/actix-http/src/h2/service.rs @@ -37,12 +37,10 @@ pub struct H2Service { impl H2Service where S: ServiceFactory, - S::Error: Into + Unpin + 'static, - S::Response: Into> + Unpin + 'static, - S::Future: Unpin, - ::Future: Unpin + 'static, + S::Error: Into + 'static, + S::Response: Into> + 'static, + ::Future: 'static, B: MessageBody + 'static, - P: Unpin, { /// Create new `HttpService` instance. pub fn new>(service: F) -> Self { @@ -83,12 +81,10 @@ impl ServiceFactory for H2Service where T: IoStream, S: ServiceFactory, - S::Error: Into + Unpin + 'static, - S::Response: Into> + Unpin + 'static, - S::Future: Unpin, - ::Future: Unpin + 'static, + S::Error: Into + 'static, + S::Response: Into> + 'static, + ::Future: 'static, B: MessageBody + 'static, - P: Unpin, { type Config = SrvConfig; type Request = Io; @@ -109,7 +105,9 @@ where } #[doc(hidden)] +#[pin_project::pin_project] pub struct H2ServiceResponse { + #[pin] fut: S::Future, cfg: Option, on_connect: Option Box>>, @@ -120,19 +118,18 @@ impl Future for H2ServiceResponse where T: IoStream, S: ServiceFactory, - S::Error: Into + Unpin + 'static, - S::Response: Into> + Unpin + 'static, - S::Future: Unpin, - ::Future: Unpin + 'static, + S::Error: Into + 'static, + S::Response: Into> + 'static, + ::Future: 'static, B: MessageBody + 'static, - P: Unpin, { type Output = Result, S::InitError>; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let this = self.get_mut(); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.as_mut().project(); - Poll::Ready(ready!(Pin::new(&mut this.fut).poll(cx)).map(|service| { + Poll::Ready(ready!(this.fut.poll(cx)).map(|service| { + let this = self.as_mut().project(); H2ServiceHandler::new( this.cfg.take().unwrap(), this.on_connect.clone(), @@ -153,11 +150,10 @@ pub struct H2ServiceHandler { impl H2ServiceHandler where S: Service, - S::Error: Into + Unpin + 'static, - S::Future: Unpin + 'static, - S::Response: Into> + Unpin + 'static, + S::Error: Into + 'static, + S::Future: 'static, + S::Response: Into> + 'static, B: MessageBody + 'static, - P: Unpin, { fn new( cfg: ServiceConfig, @@ -177,11 +173,10 @@ impl Service for H2ServiceHandler where T: IoStream, S: Service, - S::Error: Into + Unpin + 'static, - S::Future: Unpin + 'static, - S::Response: Into> + Unpin + 'static, + S::Error: Into + 'static, + S::Future: 'static, + S::Response: Into> + 'static, B: MessageBody + 'static, - P: Unpin, { type Request = Io; type Response = (); @@ -235,9 +230,9 @@ pub struct H2ServiceHandlerResponse where T: IoStream, S: Service, - S::Error: Into + Unpin + 'static, - S::Future: Unpin + 'static, - S::Response: Into> + Unpin + 'static, + S::Error: Into + 'static, + S::Future: 'static, + S::Response: Into> + 'static, B: MessageBody + 'static, { state: State, @@ -247,9 +242,9 @@ impl Future for H2ServiceHandlerResponse where T: IoStream, S: Service, - S::Error: Into + Unpin + 'static, - S::Future: Unpin + 'static, - S::Response: Into> + Unpin + 'static, + S::Error: Into + 'static, + S::Future: 'static, + S::Response: Into> + 'static, B: MessageBody, { type Output = Result<(), DispatchError>; diff --git a/actix-http/src/request.rs b/actix-http/src/request.rs index e9252a829..0afa45cbf 100644 --- a/actix-http/src/request.rs +++ b/actix-http/src/request.rs @@ -80,6 +80,11 @@ impl

Request

{ ) } + /// Get request's payload + pub fn payload(&mut self) -> &mut Payload

{ + &mut self.payload + } + /// Get request's payload pub fn take_payload(&mut self) -> Payload

{ std::mem::replace(&mut self.payload, Payload::None) diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index 5b3d17cb4..d05505d80 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -635,8 +635,8 @@ impl ResponseBuilder { /// `ResponseBuilder` can not be used after this call. pub fn streaming(&mut self, stream: S) -> Response where - S: Stream> + Unpin + 'static, - E: Into + Unpin + 'static, + S: Stream> + 'static, + E: Into + 'static, { self.body(Body::from_message(BodyStream::new(stream))) } diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index 65a0c7bd4..e18b10130 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -11,6 +11,7 @@ use actix_service::{IntoServiceFactory, Service, ServiceFactory}; use bytes::{Buf, BufMut, Bytes, BytesMut}; use futures::{ready, Future}; use h2::server::{self, Handshake}; +use pin_project::{pin_project, project}; use crate::body::MessageBody; use crate::builder::HttpServiceBuilder; @@ -35,12 +36,10 @@ pub struct HttpService HttpService where S: ServiceFactory, - S::Error: Into + Unpin + 'static, + S::Error: Into + 'static, S::InitError: fmt::Debug, - S::Response: Into> + Unpin + 'static, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin + 'static, + S::Response: Into> + 'static, + ::Future: 'static, B: MessageBody + 'static, { /// Create builder for `HttpService` instance. @@ -52,14 +51,11 @@ where impl HttpService where S: ServiceFactory, - S::Error: Into + Unpin + 'static, + S::Error: Into + 'static, S::InitError: fmt::Debug, - S::Response: Into> + Unpin + 'static, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin + 'static, + S::Response: Into> + 'static, + ::Future: 'static, B: MessageBody + 'static, - P: Unpin, { /// Create new `HttpService` instance. pub fn new>(service: F) -> Self { @@ -94,14 +90,11 @@ where impl HttpService where S: ServiceFactory, - S::Error: Into + Unpin + 'static, + S::Error: Into + 'static, S::InitError: fmt::Debug, - S::Response: Into> + Unpin + 'static, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin + 'static, + S::Response: Into> + 'static, + ::Future: 'static, B: MessageBody, - P: Unpin, { /// Provide service for `EXPECT: 100-Continue` support. /// @@ -113,9 +106,7 @@ where X1: ServiceFactory, X1::Error: Into, X1::InitError: fmt::Debug, - X1::Future: Unpin, - X1::Service: Unpin, - ::Future: Unpin + 'static, + ::Future: 'static, { HttpService { expect, @@ -140,9 +131,7 @@ where >, U1::Error: fmt::Display, U1::InitError: fmt::Debug, - U1::Future: Unpin, - U1::Service: Unpin, - ::Future: Unpin + 'static, + ::Future: 'static, { HttpService { upgrade, @@ -166,22 +155,17 @@ where impl ServiceFactory for HttpService where - T: IoStream + Unpin, + T: IoStream, S: ServiceFactory, - S::Service: Unpin, - S::Error: Into + Unpin + 'static, + S::Error: Into + 'static, S::InitError: fmt::Debug, - S::Response: Into> + Unpin + 'static, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin + 'static, + S::Response: Into> + 'static, + ::Future: 'static, B: MessageBody + 'static, X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, - X::Future: Unpin, - X::Service: Unpin, - ::Future: Unpin + 'static, + ::Future: 'static, U: ServiceFactory< Config = SrvConfig, Request = (Request, Framed), @@ -189,10 +173,7 @@ where >, U::Error: fmt::Display, U::InitError: fmt::Debug, - U::Future: Unpin, - U::Service: Unpin, - ::Future: Unpin + 'static, - P: Unpin, + ::Future: 'static, { type Config = SrvConfig; type Request = ServerIo; @@ -217,6 +198,7 @@ where } #[doc(hidden)] +#[pin_project] pub struct HttpServiceResponse< T, P, @@ -225,8 +207,11 @@ pub struct HttpServiceResponse< X: ServiceFactory, U: ServiceFactory, > { + #[pin] fut: S::Future, + #[pin] fut_ex: Option, + #[pin] fut_upg: Option, expect: Option, upgrade: Option, @@ -239,53 +224,50 @@ impl Future for HttpServiceResponse where T: IoStream, S: ServiceFactory, - S::Error: Into + Unpin + 'static, + S::Error: Into + 'static, S::InitError: fmt::Debug, - S::Response: Into> + Unpin + 'static, - S::Future: Unpin, - S::Service: Unpin, - ::Future: Unpin + 'static, + S::Response: Into> + 'static, + ::Future: 'static, B: MessageBody + 'static, X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, - X::Future: Unpin, - X::Service: Unpin, - ::Future: Unpin + 'static, + ::Future: 'static, U: ServiceFactory), Response = ()>, U::Error: fmt::Display, U::InitError: fmt::Debug, - U::Future: Unpin, - U::Service: Unpin, - ::Future: Unpin + 'static, - P: Unpin, + ::Future: 'static, { type Output = Result, ()>; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let this = self.get_mut(); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let mut this = self.as_mut().project(); - if let Some(ref mut fut) = this.fut_ex { - let expect = ready!(Pin::new(fut) + if let Some(fut) = this.fut_ex.as_pin_mut() { + let expect = ready!(fut .poll(cx) .map_err(|e| log::error!("Init http service error: {:?}", e)))?; - this.expect = Some(expect); - this.fut_ex.take(); + this = self.as_mut().project(); + *this.expect = Some(expect); + this.fut_ex.set(None); } - if let Some(ref mut fut) = this.fut_upg { - let upgrade = ready!(Pin::new(fut) + if let Some(fut) = this.fut_upg.as_pin_mut() { + let upgrade = ready!(fut .poll(cx) .map_err(|e| log::error!("Init http service error: {:?}", e)))?; - this.upgrade = Some(upgrade); - this.fut_ex.take(); + this = self.as_mut().project(); + *this.upgrade = Some(upgrade); + this.fut_ex.set(None); } - let result = ready!(Pin::new(&mut this.fut) + let result = ready!(this + .fut .poll(cx) .map_err(|e| log::error!("Init http service error: {:?}", e))); Poll::Ready(result.map(|service| { + let this = self.as_mut().project(); HttpServiceHandler::new( this.cfg.take().unwrap(), service, @@ -309,19 +291,15 @@ pub struct HttpServiceHandler { impl HttpServiceHandler where - S: Service + Unpin, - S::Error: Into + Unpin + 'static, + S: Service, + S::Error: Into + 'static, S::Future: 'static, - S::Response: Into> + Unpin + 'static, - S::Future: Unpin, + S::Response: Into> + 'static, B: MessageBody + 'static, - X: Service + Unpin, - X::Future: Unpin, + X: Service, X::Error: Into, - U: Service), Response = ()> + Unpin, - U::Future: Unpin, + U: Service), Response = ()>, U::Error: fmt::Display, - P: Unpin, { fn new( cfg: ServiceConfig, @@ -343,19 +321,16 @@ where impl Service for HttpServiceHandler where - T: IoStream + Unpin, - S: Service + Unpin, - S::Error: Into + Unpin + 'static, - S::Future: Unpin + 'static, - S::Response: Into> + Unpin + 'static, + T: IoStream, + S: Service, + S::Error: Into + 'static, + S::Future: 'static, + S::Response: Into> + 'static, B: MessageBody + 'static, - X: Service + Unpin, + X: Service, X::Error: Into, - X::Future: Unpin, - U: Service), Response = ()> + Unpin, + U: Service), Response = ()>, U::Error: fmt::Display, - U::Future: Unpin, - P: Unpin, { type Request = ServerIo; type Response = (); @@ -442,22 +417,21 @@ where } } +#[pin_project] enum State where - S: Service + Unpin, - S::Future: Unpin + 'static, + S: Service, + S::Future: 'static, S::Error: Into, - T: IoStream + Unpin, + T: IoStream, B: MessageBody, - X: Service + Unpin, + X: Service, X::Error: Into, - X::Future: Unpin, - U: Service), Response = ()> + Unpin, + U: Service), Response = ()>, U::Error: fmt::Display, - U::Future: Unpin, { - H1(h1::Dispatcher), - H2(Dispatcher, S, B>), + H1(#[pin] h1::Dispatcher), + H2(#[pin] Dispatcher, S, B>), Unknown( Option<( T, @@ -480,21 +454,21 @@ where ), } +#[pin_project] pub struct HttpServiceHandlerResponse where - T: IoStream + Unpin, - S: Service + Unpin, - S::Error: Into + Unpin + 'static, - S::Future: Unpin + 'static, - S::Response: Into> + Unpin + 'static, + T: IoStream, + S: Service, + S::Error: Into + 'static, + S::Future: 'static, + S::Response: Into> + 'static, B: MessageBody + 'static, - X: Service + Unpin, + X: Service, X::Error: Into, - X::Future: Unpin, - U: Service), Response = ()> + Unpin, + U: Service), Response = ()>, U::Error: fmt::Display, - U::Future: Unpin, { + #[pin] state: State, } @@ -502,25 +476,45 @@ const HTTP2_PREFACE: [u8; 14] = *b"PRI * HTTP/2.0"; impl Future for HttpServiceHandlerResponse where - T: IoStream + Unpin, - S: Service + Unpin, - S::Error: Into + Unpin + 'static, - S::Future: Unpin + 'static, - S::Response: Into> + Unpin + 'static, + T: IoStream, + S: Service, + S::Error: Into + 'static, + S::Future: 'static, + S::Response: Into> + 'static, B: MessageBody, - X: Service + Unpin, - X::Future: Unpin, + X: Service, X::Error: Into, - U: Service), Response = ()> + Unpin, - U::Future: Unpin, + U: Service), Response = ()>, U::Error: fmt::Display, { type Output = Result<(), DispatchError>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { - match self.state { - State::H1(ref mut disp) => Pin::new(disp).poll(cx), - State::H2(ref mut disp) => Pin::new(disp).poll(cx), + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + self.project().state.poll(cx) + } +} + +impl State +where + T: IoStream, + S: Service, + S::Error: Into + 'static, + S::Response: Into> + 'static, + B: MessageBody + 'static, + X: Service, + X::Error: Into, + U: Service), Response = ()>, + U::Error: fmt::Display, +{ + #[project] + fn poll( + mut self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll> { + #[project] + match self.as_mut().project() { + State::H1(disp) => disp.poll(cx), + State::H2(disp) => disp.poll(cx), State::Unknown(ref mut data) => { if let Some(ref mut item) = data { loop { @@ -549,15 +543,15 @@ where inner: io, unread: Some(buf), }; - self.state = State::Handshake(Some(( + self.set(State::Handshake(Some(( server::handshake(io), cfg, srv, peer_addr, on_connect, - ))); + )))); } else { - self.state = State::H1(h1::Dispatcher::with_timeout( + self.set(State::H1(h1::Dispatcher::with_timeout( io, h1::Codec::new(cfg.clone()), cfg, @@ -567,7 +561,7 @@ where expect, upgrade, on_connect, - )) + ))) } self.poll(cx) } @@ -585,9 +579,9 @@ where panic!() }; let (_, cfg, srv, peer_addr, on_connect) = data.take().unwrap(); - self.state = State::H2(Dispatcher::new( + self.set(State::H2(Dispatcher::new( srv, conn, on_connect, cfg, None, peer_addr, - )); + ))); self.poll(cx) } } @@ -595,13 +589,13 @@ where } /// Wrapper for `AsyncRead + AsyncWrite` types +#[pin_project::pin_project] struct Io { unread: Option, + #[pin] inner: T, } -impl Unpin for Io {} - impl io::Read for Io { fn read(&mut self, buf: &mut [u8]) -> io::Result { if let Some(mut bytes) = self.unread.take() { @@ -627,7 +621,7 @@ impl io::Write for Io { } } -impl AsyncRead for Io { +impl AsyncRead for Io { // unsafe fn initializer(&self) -> io::Initializer { // self.get_mut().inner.initializer() // } @@ -641,7 +635,19 @@ impl AsyncRead for Io { cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - Pin::new(&mut self.get_mut().inner).poll_read(cx, buf) + let this = self.project(); + + if let Some(mut bytes) = this.unread.take() { + let size = std::cmp::min(buf.len(), bytes.len()); + buf[..size].copy_from_slice(&bytes[..size]); + if bytes.len() > size { + bytes.split_to(size); + *this.unread = Some(bytes); + } + Poll::Ready(Ok(size)) + } else { + this.inner.poll_read(cx, buf) + } } // fn poll_read_vectored( @@ -653,32 +659,24 @@ impl AsyncRead for Io { // } } -impl tokio_io::AsyncWrite for Io { +impl tokio_io::AsyncWrite for Io { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - Pin::new(&mut self.get_mut().inner).poll_write(cx, buf) + self.project().inner.poll_write(cx, buf) } - // fn poll_write_vectored( - // self: Pin<&mut Self>, - // cx: &mut Context<'_>, - // bufs: &[io::IoSlice<'_>], - // ) -> Poll> { - // self.get_mut().inner.poll_write_vectored(cx, bufs) - // } - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut self.get_mut().inner).poll_flush(cx) + self.project().inner.poll_flush(cx) } fn poll_shutdown( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - Pin::new(&mut self.get_mut().inner).poll_shutdown(cx) + self.project().inner.poll_shutdown(cx) } } diff --git a/actix-http/src/ws/transport.rs b/actix-http/src/ws/transport.rs index c55e2eebd..58ba3160f 100644 --- a/actix-http/src/ws/transport.rs +++ b/actix-http/src/ws/transport.rs @@ -11,17 +11,17 @@ use super::{Codec, Frame, Message}; pub struct Transport where S: Service + 'static, - T: AsyncRead + AsyncWrite + Unpin, + T: AsyncRead + AsyncWrite, { inner: FramedTransport, } impl Transport where - T: AsyncRead + AsyncWrite + Unpin, - S: Service + Unpin, + T: AsyncRead + AsyncWrite, + S: Service, S::Future: 'static, - S::Error: Unpin + 'static, + S::Error: 'static, { pub fn new>(io: T, service: F) -> Self { Transport { @@ -38,10 +38,10 @@ where impl Future for Transport where - T: AsyncRead + AsyncWrite + Unpin, - S: Service + Unpin, + T: AsyncRead + AsyncWrite, + S: Service, S::Future: 'static, - S::Error: Unpin + 'static, + S::Error: 'static, { type Output = Result<(), FramedTransportError>; diff --git a/actix-http/tests/test_client.rs b/actix-http/tests/test_client.rs index a4f1569cc..05248966a 100644 --- a/actix-http/tests/test_client.rs +++ b/actix-http/tests/test_client.rs @@ -1,9 +1,9 @@ -use actix_service::NewService; +use actix_service::ServiceFactory; use bytes::Bytes; use futures::future::{self, ok}; use actix_http::{http, HttpService, Request, Response}; -use actix_http_test::TestServer; +use actix_http_test::{block_on, TestServer}; const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World \ @@ -29,55 +29,63 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[test] fn test_h1_v2() { - env_logger::init(); - let mut srv = TestServer::new(move || { - HttpService::build().finish(|_| future::ok::<_, ()>(Response::Ok().body(STR))) - }); - let response = srv.block_on(srv.get("/").send()).unwrap(); - assert!(response.status().is_success()); + block_on(async { + let srv = TestServer::start(move || { + HttpService::build() + .finish(|_| future::ok::<_, ()>(Response::Ok().body(STR))) + }); - let request = srv.get("/").header("x-test", "111").send(); - let response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + let request = srv.get("/").header("x-test", "111").send(); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - let response = srv.block_on(srv.post("/").send()).unwrap(); - assert!(response.status().is_success()); + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + let mut response = srv.post("/").send().await.unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) } #[test] fn test_connection_close() { - let mut srv = TestServer::new(move || { - HttpService::build() - .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map(|_| ()) - }); - let response = srv.block_on(srv.get("/").force_close().send()).unwrap(); - assert!(response.status().is_success()); + block_on(async { + let srv = TestServer::start(move || { + HttpService::build() + .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) + .map(|_| ()) + }); + + let response = srv.get("/").force_close().send().await.unwrap(); + assert!(response.status().is_success()); + }) } #[test] fn test_with_query_parameter() { - let mut srv = TestServer::new(move || { - HttpService::build() - .finish(|req: Request| { - if req.uri().query().unwrap().contains("qp=") { - ok::<_, ()>(Response::Ok().finish()) - } else { - ok::<_, ()>(Response::BadRequest().finish()) - } - }) - .map(|_| ()) - }); + block_on(async { + let srv = TestServer::start(move || { + HttpService::build() + .finish(|req: Request| { + if req.uri().query().unwrap().contains("qp=") { + ok::<_, ()>(Response::Ok().finish()) + } else { + ok::<_, ()>(Response::BadRequest().finish()) + } + }) + .map(|_| ()) + }); - let request = srv.request(http::Method::GET, srv.url("/?qp=5")).send(); - let response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + let request = srv.request(http::Method::GET, srv.url("/?qp=5")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); + }) } diff --git a/actix-http/tests/test_openssl.rs b/actix-http/tests/test_openssl.rs new file mode 100644 index 000000000..7eaa8e2a2 --- /dev/null +++ b/actix-http/tests/test_openssl.rs @@ -0,0 +1,545 @@ +#![cfg(feature = "openssl")] +use std::io; + +use actix_codec::{AsyncRead, AsyncWrite}; +use actix_http_test::{block_on, TestServer}; +use actix_server::ssl::OpensslAcceptor; +use actix_server_config::ServerConfig; +use actix_service::{factory_fn_cfg, pipeline_factory, service_fn2, ServiceFactory}; + +use bytes::{Bytes, BytesMut}; +use futures::future::{err, ok, ready}; +use futures::stream::{once, Stream, StreamExt}; +use open_ssl::ssl::{AlpnError, SslAcceptor, SslFiletype, SslMethod}; + +use actix_http::error::{ErrorBadRequest, PayloadError}; +use actix_http::http::header::{self, HeaderName, HeaderValue}; +use actix_http::http::{Method, StatusCode, Version}; +use actix_http::httpmessage::HttpMessage; +use actix_http::{body, Error, HttpService, Request, Response}; + +async fn load_body(stream: S) -> Result +where + S: Stream>, +{ + let body = stream + .map(|res| match res { + Ok(chunk) => chunk, + Err(_) => panic!(), + }) + .fold(BytesMut::new(), move |mut body, chunk| { + body.extend_from_slice(&chunk); + ready(body) + }) + .await; + + Ok(body) +} + +fn ssl_acceptor() -> io::Result> { + // load ssl keys + let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); + builder + .set_private_key_file("../tests/key.pem", SslFiletype::PEM) + .unwrap(); + builder + .set_certificate_chain_file("../tests/cert.pem") + .unwrap(); + builder.set_alpn_select_callback(|_, protos| { + const H2: &[u8] = b"\x02h2"; + if protos.windows(3).any(|window| window == H2) { + Ok(b"h2") + } else { + Err(AlpnError::NOACK) + } + }); + builder.set_alpn_protos(b"\x02h2")?; + Ok(OpensslAcceptor::new(builder.build())) +} + +#[test] +fn test_h2() -> io::Result<()> { + block_on(async { + let openssl = ssl_acceptor()?; + let srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|_| ok::<_, Error>(Response::Ok().finish())) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + Ok(()) + }) +} + +#[test] +fn test_h2_1() -> io::Result<()> { + block_on(async { + let openssl = ssl_acceptor()?; + let srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .finish(|req: Request| { + assert!(req.peer_addr().is_some()); + assert_eq!(req.version(), Version::HTTP_2); + ok::<_, Error>(Response::Ok().finish()) + }) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + Ok(()) + }) +} + +#[test] +fn test_h2_body() -> io::Result<()> { + block_on(async { + let data = "HELLOWORLD".to_owned().repeat(64 * 1024); + let openssl = ssl_acceptor()?; + let mut srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|mut req: Request<_>| { + async move { + let body = load_body(req.take_payload()).await?; + Ok::<_, Error>(Response::Ok().body(body)) + } + }) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send_body(data.clone()).await.unwrap(); + assert!(response.status().is_success()); + + let body = srv.load_body(response).await.unwrap(); + assert_eq!(&body, data.as_bytes()); + Ok(()) + }) +} + +#[test] +fn test_h2_content_length() { + block_on(async { + let openssl = ssl_acceptor().unwrap(); + + let srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|req: Request| { + let indx: usize = req.uri().path()[1..].parse().unwrap(); + let statuses = [ + StatusCode::NO_CONTENT, + StatusCode::CONTINUE, + StatusCode::SWITCHING_PROTOCOLS, + StatusCode::PROCESSING, + StatusCode::OK, + StatusCode::NOT_FOUND, + ]; + ok::<_, ()>(Response::new(statuses[indx])) + }) + .map_err(|_| ()), + ) + }); + + let header = HeaderName::from_static("content-length"); + let value = HeaderValue::from_static("0"); + + { + for i in 0..4 { + let req = srv + .request(Method::GET, srv.surl(&format!("/{}", i))) + .send(); + let response = req.await.unwrap(); + assert_eq!(response.headers().get(&header), None); + + let req = srv + .request(Method::HEAD, srv.surl(&format!("/{}", i))) + .send(); + let response = req.await.unwrap(); + assert_eq!(response.headers().get(&header), None); + } + + for i in 4..6 { + let req = srv + .request(Method::GET, srv.surl(&format!("/{}", i))) + .send(); + let response = req.await.unwrap(); + assert_eq!(response.headers().get(&header), Some(&value)); + } + } + }) +} + +#[test] +fn test_h2_headers() { + block_on(async { + let data = STR.repeat(10); + let data2 = data.clone(); + let openssl = ssl_acceptor().unwrap(); + + let mut srv = TestServer::start(move || { + let data = data.clone(); + pipeline_factory(openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e))) + .and_then( + HttpService::build().h2(move |_| { + let mut builder = Response::Ok(); + for idx in 0..90 { + builder.header( + format!("X-TEST-{}", idx).as_str(), + "TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ", + ); + } + ok::<_, ()>(builder.body(data.clone())) + }).map_err(|_| ())) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from(data2)); + }) +} + +const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World"; + +#[test] +fn test_h2_body2() { + block_on(async { + let openssl = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) +} + +#[test] +fn test_h2_head_empty() { + block_on(async { + let openssl = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) + .map_err(|_| ()), + ) + }); + + let response = srv.shead("/").send().await.unwrap(); + assert!(response.status().is_success()); + assert_eq!(response.version(), Version::HTTP_2); + + { + let len = response.headers().get(header::CONTENT_LENGTH).unwrap(); + assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); + } + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert!(bytes.is_empty()); + }) +} + +#[test] +fn test_h2_head_binary() { + block_on(async { + let openssl = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|_| { + ok::<_, ()>( + Response::Ok().content_length(STR.len() as u64).body(STR), + ) + }) + .map_err(|_| ()), + ) + }); + + let response = srv.shead("/").send().await.unwrap(); + assert!(response.status().is_success()); + + { + let len = response.headers().get(header::CONTENT_LENGTH).unwrap(); + assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); + } + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert!(bytes.is_empty()); + }) +} + +#[test] +fn test_h2_head_binary2() { + block_on(async { + let openssl = ssl_acceptor().unwrap(); + let srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) + .map_err(|_| ()), + ) + }); + + let response = srv.shead("/").send().await.unwrap(); + assert!(response.status().is_success()); + + { + let len = response.headers().get(header::CONTENT_LENGTH).unwrap(); + assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); + } + }) +} + +#[test] +fn test_h2_body_length() { + block_on(async { + let openssl = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|_| { + let body = once(ok(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok() + .body(body::SizedStream::new(STR.len() as u64, body)), + ) + }) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) +} + +#[test] +fn test_h2_body_chunked_explicit() { + block_on(async { + let openssl = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|_| { + let body = + once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok() + .header(header::TRANSFER_ENCODING, "chunked") + .streaming(body), + ) + }) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + assert!(!response.headers().contains_key(header::TRANSFER_ENCODING)); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + + // decode + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) +} + +#[test] +fn test_h2_response_http_error_handling() { + block_on(async { + let openssl = ssl_acceptor().unwrap(); + + let mut srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(factory_fn_cfg(|_: &ServerConfig| { + ok::<_, ()>(service_fn2(|_| { + let broken_header = Bytes::from_static(b"\0\0\0"); + ok::<_, ()>( + Response::Ok() + .header(header::CONTENT_TYPE, broken_header) + .body(STR), + ) + })) + })) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(b"failed to parse header value")); + }) +} + +#[test] +fn test_h2_service_error() { + block_on(async { + let openssl = ssl_acceptor().unwrap(); + + let mut srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|_| err::(ErrorBadRequest("error"))) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert_eq!(response.status(), StatusCode::BAD_REQUEST); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(b"error")); + }) +} + +#[test] +fn test_h2_on_connect() { + block_on(async { + let openssl = ssl_acceptor().unwrap(); + + let srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .on_connect(|_| 10usize) + .h2(|req: Request| { + assert!(req.extensions().contains::()); + ok::<_, ()>(Response::Ok().finish()) + }) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + }) +} diff --git a/actix-http/tests/test_rustls.rs b/actix-http/tests/test_rustls.rs new file mode 100644 index 000000000..c36d05794 --- /dev/null +++ b/actix-http/tests/test_rustls.rs @@ -0,0 +1,474 @@ +#![cfg(feature = "rustls")] +use actix_codec::{AsyncRead, AsyncWrite}; +use actix_http::error::PayloadError; +use actix_http::http::header::{self, HeaderName, HeaderValue}; +use actix_http::http::{Method, StatusCode, Version}; +use actix_http::{body, error, Error, HttpService, Request, Response}; +use actix_http_test::{block_on, TestServer}; +use actix_server::ssl::RustlsAcceptor; +use actix_server_config::ServerConfig; +use actix_service::{factory_fn_cfg, pipeline_factory, service_fn2, ServiceFactory}; + +use bytes::{Bytes, BytesMut}; +use futures::future::{self, err, ok}; +use futures::stream::{once, Stream, StreamExt}; +use rust_tls::{ + internal::pemfile::{certs, pkcs8_private_keys}, + NoClientAuth, ServerConfig as RustlsServerConfig, +}; + +use std::fs::File; +use std::io::{self, BufReader}; + +async fn load_body(mut stream: S) -> Result +where + S: Stream> + Unpin, +{ + let mut body = BytesMut::new(); + while let Some(item) = stream.next().await { + body.extend_from_slice(&item?) + } + Ok(body) +} + +fn ssl_acceptor() -> io::Result> { + // load ssl keys + let mut config = RustlsServerConfig::new(NoClientAuth::new()); + let cert_file = &mut BufReader::new(File::open("../tests/cert.pem").unwrap()); + let key_file = &mut BufReader::new(File::open("../tests/key.pem").unwrap()); + let cert_chain = certs(cert_file).unwrap(); + let mut keys = pkcs8_private_keys(key_file).unwrap(); + config.set_single_cert(cert_chain, keys.remove(0)).unwrap(); + + let protos = vec![b"h2".to_vec()]; + config.set_protocols(&protos); + Ok(RustlsAcceptor::new(config)) +} + +#[test] +fn test_h2() -> io::Result<()> { + block_on(async { + let rustls = ssl_acceptor()?; + let srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(|_| future::ok::<_, Error>(Response::Ok().finish())) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + Ok(()) + }) +} + +#[test] +fn test_h2_1() -> io::Result<()> { + block_on(async { + let rustls = ssl_acceptor()?; + let srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .finish(|req: Request| { + assert!(req.peer_addr().is_some()); + assert_eq!(req.version(), Version::HTTP_2); + future::ok::<_, Error>(Response::Ok().finish()) + }) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + Ok(()) + }) +} + +#[test] +fn test_h2_body1() -> io::Result<()> { + block_on(async { + let data = "HELLOWORLD".to_owned().repeat(64 * 1024); + let rustls = ssl_acceptor()?; + let mut srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(|mut req: Request<_>| { + async move { + let body = load_body(req.take_payload()).await?; + Ok::<_, Error>(Response::Ok().body(body)) + } + }) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send_body(data.clone()).await.unwrap(); + assert!(response.status().is_success()); + + let body = srv.load_body(response).await.unwrap(); + assert_eq!(&body, data.as_bytes()); + Ok(()) + }) +} + +#[test] +fn test_h2_content_length() { + block_on(async { + let rustls = ssl_acceptor().unwrap(); + + let srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(|req: Request| { + let indx: usize = req.uri().path()[1..].parse().unwrap(); + let statuses = [ + StatusCode::NO_CONTENT, + StatusCode::CONTINUE, + StatusCode::SWITCHING_PROTOCOLS, + StatusCode::PROCESSING, + StatusCode::OK, + StatusCode::NOT_FOUND, + ]; + future::ok::<_, ()>(Response::new(statuses[indx])) + }) + .map_err(|_| ()), + ) + }); + + let header = HeaderName::from_static("content-length"); + let value = HeaderValue::from_static("0"); + + { + for i in 0..4 { + let req = srv + .request(Method::GET, srv.surl(&format!("/{}", i))) + .send(); + let response = req.await.unwrap(); + assert_eq!(response.headers().get(&header), None); + + let req = srv + .request(Method::HEAD, srv.surl(&format!("/{}", i))) + .send(); + let response = req.await.unwrap(); + assert_eq!(response.headers().get(&header), None); + } + + for i in 4..6 { + let req = srv + .request(Method::GET, srv.surl(&format!("/{}", i))) + .send(); + let response = req.await.unwrap(); + assert_eq!(response.headers().get(&header), Some(&value)); + } + } + }) +} + +#[test] +fn test_h2_headers() { + block_on(async { + let data = STR.repeat(10); + let data2 = data.clone(); + let rustls = ssl_acceptor().unwrap(); + + let mut srv = TestServer::start(move || { + let data = data.clone(); + pipeline_factory(rustls + .clone() + .map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build().h2(move |_| { + let mut config = Response::Ok(); + for idx in 0..90 { + config.header( + format!("X-TEST-{}", idx).as_str(), + "TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ + TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ", + ); + } + future::ok::<_, ()>(config.body(data.clone())) + }).map_err(|_| ())) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from(data2)); + }) +} + +const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World \ + Hello World Hello World Hello World Hello World Hello World"; + +#[test] +fn test_h2_body2() { + block_on(async { + let rustls = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(|_| future::ok::<_, ()>(Response::Ok().body(STR))) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) +} + +#[test] +fn test_h2_head_empty() { + block_on(async { + let rustls = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) + .map_err(|_| ()), + ) + }); + + let response = srv.shead("/").send().await.unwrap(); + assert!(response.status().is_success()); + assert_eq!(response.version(), Version::HTTP_2); + + { + let len = response + .headers() + .get(http::header::CONTENT_LENGTH) + .unwrap(); + assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); + } + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert!(bytes.is_empty()); + }) +} + +#[test] +fn test_h2_head_binary() { + block_on(async { + let rustls = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(|_| { + ok::<_, ()>( + Response::Ok() + .content_length(STR.len() as u64) + .body(STR), + ) + }) + .map_err(|_| ()), + ) + }); + + let response = srv.shead("/").send().await.unwrap(); + assert!(response.status().is_success()); + + { + let len = response + .headers() + .get(http::header::CONTENT_LENGTH) + .unwrap(); + assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); + } + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert!(bytes.is_empty()); + }) +} + +#[test] +fn test_h2_head_binary2() { + block_on(async { + let rustls = ssl_acceptor().unwrap(); + let srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) + .map_err(|_| ()), + ) + }); + + let response = srv.shead("/").send().await.unwrap(); + assert!(response.status().is_success()); + + { + let len = response + .headers() + .get(http::header::CONTENT_LENGTH) + .unwrap(); + assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); + } + }) +} + +#[test] +fn test_h2_body_length() { + block_on(async { + let rustls = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(|_| { + let body = once(ok(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok().body(body::SizedStream::new( + STR.len() as u64, + body, + )), + ) + }) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) +} + +#[test] +fn test_h2_body_chunked_explicit() { + block_on(async { + let rustls = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(|_| { + let body = + once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok() + .header(header::TRANSFER_ENCODING, "chunked") + .streaming(body), + ) + }) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + assert!(!response.headers().contains_key(header::TRANSFER_ENCODING)); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + + // decode + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) +} + +#[test] +fn test_h2_response_http_error_handling() { + block_on(async { + let rustls = ssl_acceptor().unwrap(); + + let mut srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(factory_fn_cfg(|_: &ServerConfig| { + ok::<_, ()>(service_fn2(|_| { + let broken_header = Bytes::from_static(b"\0\0\0"); + ok::<_, ()>( + Response::Ok() + .header( + http::header::CONTENT_TYPE, + broken_header, + ) + .body(STR), + ) + })) + })) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(b"failed to parse header value")); + }) +} + +#[test] +fn test_h2_service_error() { + block_on(async { + let rustls = ssl_acceptor().unwrap(); + + let mut srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(|_| err::(error::ErrorBadRequest("error"))) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert_eq!(response.status(), http::StatusCode::BAD_REQUEST); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(b"error")); + }) +} diff --git a/actix-http/tests/test_rustls_server.rs b/actix-http/tests/test_rustls_server.rs deleted file mode 100644 index b74fd07bf..000000000 --- a/actix-http/tests/test_rustls_server.rs +++ /dev/null @@ -1,462 +0,0 @@ -#![cfg(feature = "rust-tls")] -use actix_codec::{AsyncRead, AsyncWrite}; -use actix_http::error::PayloadError; -use actix_http::http::header::{self, HeaderName, HeaderValue}; -use actix_http::http::{Method, StatusCode, Version}; -use actix_http::{body, error, Error, HttpService, Request, Response}; -use actix_http_test::TestServer; -use actix_server::ssl::RustlsAcceptor; -use actix_server_config::ServerConfig; -use actix_service::{new_service_cfg, NewService}; - -use bytes::{Bytes, BytesMut}; -use futures::future::{self, ok, Future}; -use futures::stream::{once, Stream}; -use rustls::{ - internal::pemfile::{certs, pkcs8_private_keys}, - NoClientAuth, ServerConfig as RustlsServerConfig, -}; - -use std::fs::File; -use std::io::{BufReader, Result}; - -fn load_body(stream: S) -> impl Future -where - S: Stream, -{ - stream.fold(BytesMut::new(), move |mut body, chunk| { - body.extend_from_slice(&chunk); - Ok::<_, PayloadError>(body) - }) -} - -fn ssl_acceptor() -> Result> { - // load ssl keys - let mut config = RustlsServerConfig::new(NoClientAuth::new()); - let cert_file = &mut BufReader::new(File::open("../tests/cert.pem").unwrap()); - let key_file = &mut BufReader::new(File::open("../tests/key.pem").unwrap()); - let cert_chain = certs(cert_file).unwrap(); - let mut keys = pkcs8_private_keys(key_file).unwrap(); - config.set_single_cert(cert_chain, keys.remove(0)).unwrap(); - - let protos = vec![b"h2".to_vec()]; - config.set_protocols(&protos); - Ok(RustlsAcceptor::new(config)) -} - -#[test] -fn test_h2() -> Result<()> { - let rustls = ssl_acceptor()?; - let mut srv = TestServer::new(move || { - rustls - .clone() - .map_err(|e| println!("Rustls error: {}", e)) - .and_then( - HttpService::build() - .h2(|_| future::ok::<_, Error>(Response::Ok().finish())) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.sget("/").send()).unwrap(); - assert!(response.status().is_success()); - Ok(()) -} - -#[test] -fn test_h2_1() -> Result<()> { - let rustls = ssl_acceptor()?; - let mut srv = TestServer::new(move || { - rustls - .clone() - .map_err(|e| println!("Rustls error: {}", e)) - .and_then( - HttpService::build() - .finish(|req: Request| { - assert!(req.peer_addr().is_some()); - assert_eq!(req.version(), Version::HTTP_2); - future::ok::<_, Error>(Response::Ok().finish()) - }) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.sget("/").send()).unwrap(); - assert!(response.status().is_success()); - Ok(()) -} - -#[test] -fn test_h2_body() -> Result<()> { - let data = "HELLOWORLD".to_owned().repeat(64 * 1024); - let rustls = ssl_acceptor()?; - let mut srv = TestServer::new(move || { - rustls - .clone() - .map_err(|e| println!("Rustls error: {}", e)) - .and_then( - HttpService::build() - .h2(|mut req: Request<_>| { - load_body(req.take_payload()) - .and_then(|body| Ok(Response::Ok().body(body))) - }) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.sget("/").send_body(data.clone())).unwrap(); - assert!(response.status().is_success()); - - let body = srv.load_body(response).unwrap(); - assert_eq!(&body, data.as_bytes()); - Ok(()) -} - -#[test] -fn test_h2_content_length() { - let rustls = ssl_acceptor().unwrap(); - - let mut srv = TestServer::new(move || { - rustls - .clone() - .map_err(|e| println!("Rustls error: {}", e)) - .and_then( - HttpService::build() - .h2(|req: Request| { - let indx: usize = req.uri().path()[1..].parse().unwrap(); - let statuses = [ - StatusCode::NO_CONTENT, - StatusCode::CONTINUE, - StatusCode::SWITCHING_PROTOCOLS, - StatusCode::PROCESSING, - StatusCode::OK, - StatusCode::NOT_FOUND, - ]; - future::ok::<_, ()>(Response::new(statuses[indx])) - }) - .map_err(|_| ()), - ) - }); - - let header = HeaderName::from_static("content-length"); - let value = HeaderValue::from_static("0"); - - { - for i in 0..4 { - let req = srv - .request(Method::GET, srv.surl(&format!("/{}", i))) - .send(); - let response = srv.block_on(req).unwrap(); - assert_eq!(response.headers().get(&header), None); - - let req = srv - .request(Method::HEAD, srv.surl(&format!("/{}", i))) - .send(); - let response = srv.block_on(req).unwrap(); - assert_eq!(response.headers().get(&header), None); - } - - for i in 4..6 { - let req = srv - .request(Method::GET, srv.surl(&format!("/{}", i))) - .send(); - let response = srv.block_on(req).unwrap(); - assert_eq!(response.headers().get(&header), Some(&value)); - } - } -} - -#[test] -fn test_h2_headers() { - let data = STR.repeat(10); - let data2 = data.clone(); - let rustls = ssl_acceptor().unwrap(); - - let mut srv = TestServer::new(move || { - let data = data.clone(); - rustls - .clone() - .map_err(|e| println!("Rustls error: {}", e)) - .and_then( - HttpService::build().h2(move |_| { - let mut config = Response::Ok(); - for idx in 0..90 { - config.header( - format!("X-TEST-{}", idx).as_str(), - "TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ", - ); - } - future::ok::<_, ()>(config.body(data.clone())) - }).map_err(|_| ())) - }); - - let response = srv.block_on(srv.sget("/").send()).unwrap(); - assert!(response.status().is_success()); - - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from(data2)); -} - -const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World"; - -#[test] -fn test_h2_body2() { - let rustls = ssl_acceptor().unwrap(); - let mut srv = TestServer::new(move || { - rustls - .clone() - .map_err(|e| println!("Rustls error: {}", e)) - .and_then( - HttpService::build() - .h2(|_| future::ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.sget("/").send()).unwrap(); - assert!(response.status().is_success()); - - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); -} - -#[test] -fn test_h2_head_empty() { - let rustls = ssl_acceptor().unwrap(); - let mut srv = TestServer::new(move || { - rustls - .clone() - .map_err(|e| println!("Rustls error: {}", e)) - .and_then( - HttpService::build() - .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.shead("/").send()).unwrap(); - assert!(response.status().is_success()); - assert_eq!(response.version(), Version::HTTP_2); - - { - let len = response - .headers() - .get(http::header::CONTENT_LENGTH) - .unwrap(); - assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); - } - - // read response - let bytes = srv.load_body(response).unwrap(); - assert!(bytes.is_empty()); -} - -#[test] -fn test_h2_head_binary() { - let rustls = ssl_acceptor().unwrap(); - let mut srv = TestServer::new(move || { - rustls - .clone() - .map_err(|e| println!("Rustls error: {}", e)) - .and_then( - HttpService::build() - .h2(|_| { - ok::<_, ()>( - Response::Ok().content_length(STR.len() as u64).body(STR), - ) - }) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.shead("/").send()).unwrap(); - assert!(response.status().is_success()); - - { - let len = response - .headers() - .get(http::header::CONTENT_LENGTH) - .unwrap(); - assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); - } - - // read response - let bytes = srv.load_body(response).unwrap(); - assert!(bytes.is_empty()); -} - -#[test] -fn test_h2_head_binary2() { - let rustls = ssl_acceptor().unwrap(); - let mut srv = TestServer::new(move || { - rustls - .clone() - .map_err(|e| println!("Rustls error: {}", e)) - .and_then( - HttpService::build() - .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.shead("/").send()).unwrap(); - assert!(response.status().is_success()); - - { - let len = response - .headers() - .get(http::header::CONTENT_LENGTH) - .unwrap(); - assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); - } -} - -#[test] -fn test_h2_body_length() { - let rustls = ssl_acceptor().unwrap(); - let mut srv = TestServer::new(move || { - rustls - .clone() - .map_err(|e| println!("Rustls error: {}", e)) - .and_then( - HttpService::build() - .h2(|_| { - let body = once(Ok(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok() - .body(body::SizedStream::new(STR.len() as u64, body)), - ) - }) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.sget("/").send()).unwrap(); - assert!(response.status().is_success()); - - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); -} - -#[test] -fn test_h2_body_chunked_explicit() { - let rustls = ssl_acceptor().unwrap(); - let mut srv = TestServer::new(move || { - rustls - .clone() - .map_err(|e| println!("Rustls error: {}", e)) - .and_then( - HttpService::build() - .h2(|_| { - let body = - once::<_, Error>(Ok(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok() - .header(header::TRANSFER_ENCODING, "chunked") - .streaming(body), - ) - }) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.sget("/").send()).unwrap(); - assert!(response.status().is_success()); - assert!(!response.headers().contains_key(header::TRANSFER_ENCODING)); - - // read response - let bytes = srv.load_body(response).unwrap(); - - // decode - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); -} - -#[test] -fn test_h2_response_http_error_handling() { - let rustls = ssl_acceptor().unwrap(); - - let mut srv = TestServer::new(move || { - rustls - .clone() - .map_err(|e| println!("Rustls error: {}", e)) - .and_then( - HttpService::build() - .h2(new_service_cfg(|_: &ServerConfig| { - Ok::<_, ()>(|_| { - let broken_header = Bytes::from_static(b"\0\0\0"); - ok::<_, ()>( - Response::Ok() - .header(http::header::CONTENT_TYPE, broken_header) - .body(STR), - ) - }) - })) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.sget("/").send()).unwrap(); - assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR); - - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(b"failed to parse header value")); -} - -#[test] -fn test_h2_service_error() { - let rustls = ssl_acceptor().unwrap(); - - let mut srv = TestServer::new(move || { - rustls - .clone() - .map_err(|e| println!("Rustls error: {}", e)) - .and_then( - HttpService::build() - .h2(|_| Err::(error::ErrorBadRequest("error"))) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.sget("/").send()).unwrap(); - assert_eq!(response.status(), http::StatusCode::BAD_REQUEST); - - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(b"error")); -} diff --git a/actix-http/tests/test_server.rs b/actix-http/tests/test_server.rs index ef861b309..c37e8fad1 100644 --- a/actix-http/tests/test_server.rs +++ b/actix-http/tests/test_server.rs @@ -2,7 +2,7 @@ use std::io::{Read, Write}; use std::time::Duration; use std::{net, thread}; -use actix_http_test::{block_fn, TestServer}; +use actix_http_test::{block_on, TestServer}; use actix_server_config::ServerConfig; use actix_service::{factory_fn_cfg, pipeline, service_fn, ServiceFactory}; use bytes::Bytes; @@ -18,345 +18,379 @@ use actix_http::{ #[test] fn test_h1() { - let srv = TestServer::new(|| { - HttpService::build() - .keep_alive(KeepAlive::Disabled) - .client_timeout(1000) - .client_disconnect(1000) - .h1(|req: Request| { - assert!(req.peer_addr().is_some()); - future::ok::<_, ()>(Response::Ok().finish()) - }) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build() + .keep_alive(KeepAlive::Disabled) + .client_timeout(1000) + .client_disconnect(1000) + .h1(|req: Request| { + assert!(req.peer_addr().is_some()); + future::ok::<_, ()>(Response::Ok().finish()) + }) + }); - let response = block_fn(|| srv.get("/").send()).unwrap(); - assert!(response.status().is_success()); + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); + }) } #[test] fn test_h1_2() { - let srv = TestServer::new(|| { - HttpService::build() - .keep_alive(KeepAlive::Disabled) - .client_timeout(1000) - .client_disconnect(1000) - .finish(|req: Request| { - assert!(req.peer_addr().is_some()); - assert_eq!(req.version(), http::Version::HTTP_11); - future::ok::<_, ()>(Response::Ok().finish()) - }) - .map(|_| ()) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build() + .keep_alive(KeepAlive::Disabled) + .client_timeout(1000) + .client_disconnect(1000) + .finish(|req: Request| { + assert!(req.peer_addr().is_some()); + assert_eq!(req.version(), http::Version::HTTP_11); + future::ok::<_, ()>(Response::Ok().finish()) + }) + .map(|_| ()) + }); - let response = block_fn(|| srv.get("/").send()).unwrap(); - assert!(response.status().is_success()); + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); + }) } #[test] fn test_expect_continue() { - let srv = TestServer::new(|| { - HttpService::build() - .expect(service_fn(|req: Request| { - if req.head().uri.query() == Some("yes=") { - ok(req) - } else { - err(error::ErrorPreconditionFailed("error")) - } - })) - .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); - - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); - let mut data = String::new(); - let _ = stream.read_to_string(&mut data); - assert!(data.starts_with("HTTP/1.1 412 Precondition Failed\r\ncontent-length")); - - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test?yes= HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); - let mut data = String::new(); - let _ = stream.read_to_string(&mut data); - assert!(data.starts_with("HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\n")); -} - -#[test] -fn test_expect_continue_h1() { - let srv = TestServer::new(|| { - HttpService::build() - .expect(service_fn(|req: Request| { - delay_for(Duration::from_millis(20)).then(move |_| { + block_on(async { + let srv = TestServer::start(|| { + HttpService::build() + .expect(service_fn(|req: Request| { if req.head().uri.query() == Some("yes=") { ok(req) } else { err(error::ErrorPreconditionFailed("error")) } - }) - })) - .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); + })) + .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); - let mut data = String::new(); - let _ = stream.read_to_string(&mut data); - assert!(data.starts_with("HTTP/1.1 412 Precondition Failed\r\ncontent-length")); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); + assert!(data.starts_with("HTTP/1.1 412 Precondition Failed\r\ncontent-length")); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test?yes= HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); - let mut data = String::new(); - let _ = stream.read_to_string(&mut data); - assert!(data.starts_with("HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\n")); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = + stream.write_all(b"GET /test?yes= HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); + assert!(data.starts_with("HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\n")); + }) +} + +#[test] +fn test_expect_continue_h1() { + block_on(async { + let srv = TestServer::start(|| { + HttpService::build() + .expect(service_fn(|req: Request| { + delay_for(Duration::from_millis(20)).then(move |_| { + if req.head().uri.query() == Some("yes=") { + ok(req) + } else { + err(error::ErrorPreconditionFailed("error")) + } + }) + })) + .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); + + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); + assert!(data.starts_with("HTTP/1.1 412 Precondition Failed\r\ncontent-length")); + + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = + stream.write_all(b"GET /test?yes= HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); + assert!(data.starts_with("HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\n")); + }) } #[test] fn test_chunked_payload() { - let chunk_sizes = vec![32768, 32, 32768]; - let total_size: usize = chunk_sizes.iter().sum(); + block_on(async { + let chunk_sizes = vec![32768, 32, 32768]; + let total_size: usize = chunk_sizes.iter().sum(); - let srv = TestServer::new(|| { - HttpService::build().h1(service_fn(|mut request: Request| { - request - .take_payload() - .map(|res| match res { - Ok(pl) => pl, - Err(e) => panic!(format!("Error reading payload: {}", e)), - }) - .fold(0usize, |acc, chunk| ready(acc + chunk.len())) - .map(|req_size| { - Ok::<_, Error>(Response::Ok().body(format!("size={}", req_size))) - }) - })) - }); + let srv = TestServer::start(|| { + HttpService::build().h1(service_fn(|mut request: Request| { + request + .take_payload() + .map(|res| match res { + Ok(pl) => pl, + Err(e) => panic!(format!("Error reading payload: {}", e)), + }) + .fold(0usize, |acc, chunk| ready(acc + chunk.len())) + .map(|req_size| { + Ok::<_, Error>(Response::Ok().body(format!("size={}", req_size))) + }) + })) + }); - let returned_size = { - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream - .write_all(b"POST /test HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"); + let returned_size = { + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream + .write_all(b"POST /test HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"); - for chunk_size in chunk_sizes.iter() { - let mut bytes = Vec::new(); - let random_bytes: Vec = - (0..*chunk_size).map(|_| rand::random::()).collect(); + for chunk_size in chunk_sizes.iter() { + let mut bytes = Vec::new(); + let random_bytes: Vec = + (0..*chunk_size).map(|_| rand::random::()).collect(); - bytes.extend(format!("{:X}\r\n", chunk_size).as_bytes()); - bytes.extend(&random_bytes[..]); - bytes.extend(b"\r\n"); - let _ = stream.write_all(&bytes); - } + bytes.extend(format!("{:X}\r\n", chunk_size).as_bytes()); + bytes.extend(&random_bytes[..]); + bytes.extend(b"\r\n"); + let _ = stream.write_all(&bytes); + } - let _ = stream.write_all(b"0\r\n\r\n"); - stream.shutdown(net::Shutdown::Write).unwrap(); + let _ = stream.write_all(b"0\r\n\r\n"); + stream.shutdown(net::Shutdown::Write).unwrap(); - let mut data = String::new(); - let _ = stream.read_to_string(&mut data); + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); - let re = Regex::new(r"size=(\d+)").unwrap(); - let size: usize = match re.captures(&data) { - Some(caps) => caps.get(1).unwrap().as_str().parse().unwrap(), - None => panic!(format!("Failed to find size in HTTP Response: {}", data)), + let re = Regex::new(r"size=(\d+)").unwrap(); + let size: usize = match re.captures(&data) { + Some(caps) => caps.get(1).unwrap().as_str().parse().unwrap(), + None => { + panic!(format!("Failed to find size in HTTP Response: {}", data)) + } + }; + size }; - size - }; - assert_eq!(returned_size, total_size); + assert_eq!(returned_size, total_size); + }) } #[test] fn test_slow_request() { - let srv = TestServer::new(|| { - HttpService::build() - .client_timeout(100) - .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build() + .client_timeout(100) + .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n"); - let mut data = String::new(); - let _ = stream.read_to_string(&mut data); - assert!(data.starts_with("HTTP/1.1 408 Request Timeout")); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n"); + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); + assert!(data.starts_with("HTTP/1.1 408 Request Timeout")); + }) } #[test] fn test_http1_malformed_request() { - let srv = TestServer::new(|| { - HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test/tests/test HTTP1.1\r\n"); - let mut data = String::new(); - let _ = stream.read_to_string(&mut data); - assert!(data.starts_with("HTTP/1.1 400 Bad Request")); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test/tests/test HTTP1.1\r\n"); + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); + assert!(data.starts_with("HTTP/1.1 400 Bad Request")); + }) } #[test] fn test_http1_keepalive() { - let srv = TestServer::new(|| { - HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); - let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); + }) } #[test] fn test_http1_keepalive_timeout() { - let srv = TestServer::new(|| { - HttpService::build() - .keep_alive(1) - .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build() + .keep_alive(1) + .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); - thread::sleep(Duration::from_millis(1100)); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); + thread::sleep(Duration::from_millis(1100)); - let mut data = vec![0; 1024]; - let res = stream.read(&mut data).unwrap(); - assert_eq!(res, 0); + let mut data = vec![0; 1024]; + let res = stream.read(&mut data).unwrap(); + assert_eq!(res, 0); + }) } #[test] fn test_http1_keepalive_close() { - let srv = TestServer::new(|| { - HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = - stream.write_all(b"GET /test/tests/test HTTP/1.1\r\nconnection: close\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream + .write_all(b"GET /test/tests/test HTTP/1.1\r\nconnection: close\r\n\r\n"); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); - let mut data = vec![0; 1024]; - let res = stream.read(&mut data).unwrap(); - assert_eq!(res, 0); + let mut data = vec![0; 1024]; + let res = stream.read(&mut data).unwrap(); + assert_eq!(res, 0); + }) } #[test] fn test_http10_keepalive_default_close() { - let srv = TestServer::new(|| { - HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test/tests/test HTTP/1.0\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.0 200 OK\r\n"); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.0\r\n\r\n"); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.0 200 OK\r\n"); - let mut data = vec![0; 1024]; - let res = stream.read(&mut data).unwrap(); - assert_eq!(res, 0); + let mut data = vec![0; 1024]; + let res = stream.read(&mut data).unwrap(); + assert_eq!(res, 0); + }) } #[test] fn test_http10_keepalive() { - let srv = TestServer::new(|| { - HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream - .write_all(b"GET /test/tests/test HTTP/1.0\r\nconnection: keep-alive\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.0 200 OK\r\n"); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all( + b"GET /test/tests/test HTTP/1.0\r\nconnection: keep-alive\r\n\r\n", + ); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.0 200 OK\r\n"); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test/tests/test HTTP/1.0\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.0 200 OK\r\n"); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.0\r\n\r\n"); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.0 200 OK\r\n"); - let mut data = vec![0; 1024]; - let res = stream.read(&mut data).unwrap(); - assert_eq!(res, 0); + let mut data = vec![0; 1024]; + let res = stream.read(&mut data).unwrap(); + assert_eq!(res, 0); + }) } #[test] fn test_http1_keepalive_disabled() { - let srv = TestServer::new(|| { - HttpService::build() - .keep_alive(KeepAlive::Disabled) - .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build() + .keep_alive(KeepAlive::Disabled) + .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); - let mut data = vec![0; 1024]; - let res = stream.read(&mut data).unwrap(); - assert_eq!(res, 0); + let mut data = vec![0; 1024]; + let res = stream.read(&mut data).unwrap(); + assert_eq!(res, 0); + }) } #[test] fn test_content_length() { - use actix_http::http::{ - header::{HeaderName, HeaderValue}, - StatusCode, - }; + block_on(async { + use actix_http::http::{ + header::{HeaderName, HeaderValue}, + StatusCode, + }; - let srv = TestServer::new(|| { - HttpService::build().h1(|req: Request| { - let indx: usize = req.uri().path()[1..].parse().unwrap(); - let statuses = [ - StatusCode::NO_CONTENT, - StatusCode::CONTINUE, - StatusCode::SWITCHING_PROTOCOLS, - StatusCode::PROCESSING, - StatusCode::OK, - StatusCode::NOT_FOUND, - ]; - future::ok::<_, ()>(Response::new(statuses[indx])) - }) - }); + let srv = TestServer::start(|| { + HttpService::build().h1(|req: Request| { + let indx: usize = req.uri().path()[1..].parse().unwrap(); + let statuses = [ + StatusCode::NO_CONTENT, + StatusCode::CONTINUE, + StatusCode::SWITCHING_PROTOCOLS, + StatusCode::PROCESSING, + StatusCode::OK, + StatusCode::NOT_FOUND, + ]; + future::ok::<_, ()>(Response::new(statuses[indx])) + }) + }); - let header = HeaderName::from_static("content-length"); - let value = HeaderValue::from_static("0"); + let header = HeaderName::from_static("content-length"); + let value = HeaderValue::from_static("0"); - { - for i in 0..4 { - let req = srv.request(http::Method::GET, srv.url(&format!("/{}", i))); - let response = block_fn(move || req.send()).unwrap(); - assert_eq!(response.headers().get(&header), None); + { + for i in 0..4 { + let req = srv.request(http::Method::GET, srv.url(&format!("/{}", i))); + let response = req.send().await.unwrap(); + assert_eq!(response.headers().get(&header), None); - let req = srv.request(http::Method::HEAD, srv.url(&format!("/{}", i))); - let response = block_fn(move || req.send()).unwrap(); - assert_eq!(response.headers().get(&header), None); + let req = srv.request(http::Method::HEAD, srv.url(&format!("/{}", i))); + let response = req.send().await.unwrap(); + assert_eq!(response.headers().get(&header), None); + } + + for i in 4..6 { + let req = srv.request(http::Method::GET, srv.url(&format!("/{}", i))); + let response = req.send().await.unwrap(); + assert_eq!(response.headers().get(&header), Some(&value)); + } } - - for i in 4..6 { - let req = srv.request(http::Method::GET, srv.url(&format!("/{}", i))); - let response = block_fn(move || req.send()).unwrap(); - assert_eq!(response.headers().get(&header), Some(&value)); - } - } + }) } #[test] fn test_h1_headers() { - let data = STR.repeat(10); - let data2 = data.clone(); + block_on(async { + let data = STR.repeat(10); + let data2 = data.clone(); - let mut srv = TestServer::new(move || { - let data = data.clone(); - HttpService::build().h1(move |_| { + let mut srv = TestServer::start(move || { + let data = data.clone(); + HttpService::build().h1(move |_| { let mut builder = Response::Ok(); for idx in 0..90 { builder.header( @@ -378,14 +412,15 @@ fn test_h1_headers() { } future::ok::<_, ()>(builder.body(data.clone())) }) - }); + }); - let response = block_fn(|| srv.get("/").send()).unwrap(); - assert!(response.status().is_success()); + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from(data2)); + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from(data2)); + }) } const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ @@ -412,208 +447,228 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[test] fn test_h1_body() { - let mut srv = TestServer::new(|| { - HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) - }); + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) + }); - let response = block_fn(|| srv.get("/").send()).unwrap(); - assert!(response.status().is_success()); + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) } #[test] fn test_h1_head_empty() { - let mut srv = TestServer::new(|| { - HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) - }); + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) + }); - let response = block_fn(|| srv.head("/").send()).unwrap(); - assert!(response.status().is_success()); + let response = srv.head("/").send().await.unwrap(); + assert!(response.status().is_success()); - { - let len = response - .headers() - .get(http::header::CONTENT_LENGTH) - .unwrap(); - assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); - } + { + let len = response + .headers() + .get(http::header::CONTENT_LENGTH) + .unwrap(); + assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); + } - // read response - let bytes = srv.load_body(response).unwrap(); - assert!(bytes.is_empty()); + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert!(bytes.is_empty()); + }) } #[test] fn test_h1_head_binary() { - let mut srv = TestServer::new(|| { - HttpService::build().h1(|_| { - ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) - }) - }); + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::build().h1(|_| { + ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) + }) + }); - let response = block_fn(|| srv.head("/").send()).unwrap(); - assert!(response.status().is_success()); + let response = srv.head("/").send().await.unwrap(); + assert!(response.status().is_success()); - { - let len = response - .headers() - .get(http::header::CONTENT_LENGTH) - .unwrap(); - assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); - } + { + let len = response + .headers() + .get(http::header::CONTENT_LENGTH) + .unwrap(); + assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); + } - // read response - let bytes = srv.load_body(response).unwrap(); - assert!(bytes.is_empty()); + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert!(bytes.is_empty()); + }) } #[test] fn test_h1_head_binary2() { - let srv = TestServer::new(|| { - HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) + }); - let response = block_fn(|| srv.head("/").send()).unwrap(); - assert!(response.status().is_success()); + let response = srv.head("/").send().await.unwrap(); + assert!(response.status().is_success()); - { - let len = response - .headers() - .get(http::header::CONTENT_LENGTH) - .unwrap(); - assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); - } + { + let len = response + .headers() + .get(http::header::CONTENT_LENGTH) + .unwrap(); + assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); + } + }) } #[test] fn test_h1_body_length() { - let mut srv = TestServer::new(|| { - HttpService::build().h1(|_| { - let body = once(ok(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok().body(body::SizedStream::new(STR.len() as u64, body)), - ) - }) - }); + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::build().h1(|_| { + let body = once(ok(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok().body(body::SizedStream::new(STR.len() as u64, body)), + ) + }) + }); - let response = block_fn(|| srv.get("/").send()).unwrap(); - assert!(response.status().is_success()); + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) } #[test] fn test_h1_body_chunked_explicit() { - let mut srv = TestServer::new(|| { - HttpService::build().h1(|_| { - let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok() - .header(header::TRANSFER_ENCODING, "chunked") - .streaming(body), - ) - }) - }); + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::build().h1(|_| { + let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok() + .header(header::TRANSFER_ENCODING, "chunked") + .streaming(body), + ) + }) + }); - let response = block_fn(|| srv.get("/").send()).unwrap(); - assert!(response.status().is_success()); - assert_eq!( - response - .headers() - .get(header::TRANSFER_ENCODING) - .unwrap() - .to_str() - .unwrap(), - "chunked" - ); + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); + assert_eq!( + response + .headers() + .get(header::TRANSFER_ENCODING) + .unwrap() + .to_str() + .unwrap(), + "chunked" + ); - // read response - let bytes = srv.load_body(response).unwrap(); + // read response + let bytes = srv.load_body(response).await.unwrap(); - // decode - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // decode + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) } #[test] fn test_h1_body_chunked_implicit() { - let mut srv = TestServer::new(|| { - HttpService::build().h1(|_| { - let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>(Response::Ok().streaming(body)) - }) - }); + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::build().h1(|_| { + let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>(Response::Ok().streaming(body)) + }) + }); - let response = block_fn(|| srv.get("/").send()).unwrap(); - assert!(response.status().is_success()); - assert_eq!( - response - .headers() - .get(header::TRANSFER_ENCODING) - .unwrap() - .to_str() - .unwrap(), - "chunked" - ); + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); + assert_eq!( + response + .headers() + .get(header::TRANSFER_ENCODING) + .unwrap() + .to_str() + .unwrap(), + "chunked" + ); - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) } #[test] fn test_h1_response_http_error_handling() { - let mut srv = TestServer::new(|| { - HttpService::build().h1(factory_fn_cfg(|_: &ServerConfig| { - ok::<_, ()>(pipeline(|_| { - let broken_header = Bytes::from_static(b"\0\0\0"); - ok::<_, ()>( - Response::Ok() - .header(http::header::CONTENT_TYPE, broken_header) - .body(STR), - ) + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::build().h1(factory_fn_cfg(|_: &ServerConfig| { + ok::<_, ()>(pipeline(|_| { + let broken_header = Bytes::from_static(b"\0\0\0"); + ok::<_, ()>( + Response::Ok() + .header(http::header::CONTENT_TYPE, broken_header) + .body(STR), + ) + })) })) - })) - }); + }); - let response = block_fn(|| srv.get("/").send()).unwrap(); - assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR); + let response = srv.get("/").send().await.unwrap(); + assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR); - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(b"failed to parse header value")); + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(b"failed to parse header value")); + }) } #[test] fn test_h1_service_error() { - let mut srv = TestServer::new(|| { - HttpService::build() - .h1(|_| future::err::(error::ErrorBadRequest("error"))) - }); + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::build() + .h1(|_| future::err::(error::ErrorBadRequest("error"))) + }); - let response = block_fn(|| srv.get("/").send()).unwrap(); - assert_eq!(response.status(), http::StatusCode::BAD_REQUEST); + let response = srv.get("/").send().await.unwrap(); + assert_eq!(response.status(), http::StatusCode::BAD_REQUEST); - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(b"error")); + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(b"error")); + }) } #[test] fn test_h1_on_connect() { - let srv = TestServer::new(|| { - HttpService::build() - .on_connect(|_| 10usize) - .h1(|req: Request| { - assert!(req.extensions().contains::()); - future::ok::<_, ()>(Response::Ok().finish()) - }) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::build() + .on_connect(|_| 10usize) + .h1(|req: Request| { + assert!(req.extensions().contains::()); + future::ok::<_, ()>(Response::Ok().finish()) + }) + }); - let response = block_fn(|| srv.get("/").send()).unwrap(); - assert!(response.status().is_success()); + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); + }) } diff --git a/actix-http/tests/test_ssl_server.rs b/actix-http/tests/test_ssl_server.rs deleted file mode 100644 index 897d92b37..000000000 --- a/actix-http/tests/test_ssl_server.rs +++ /dev/null @@ -1,480 +0,0 @@ -#![cfg(feature = "ssl")] -use actix_codec::{AsyncRead, AsyncWrite}; -use actix_http_test::TestServer; -use actix_server::ssl::OpensslAcceptor; -use actix_server_config::ServerConfig; -use actix_service::{new_service_cfg, NewService}; - -use bytes::{Bytes, BytesMut}; -use futures::future::{ok, Future}; -use futures::stream::{once, Stream}; -use openssl::ssl::{AlpnError, SslAcceptor, SslFiletype, SslMethod}; -use std::io::Result; - -use actix_http::error::{ErrorBadRequest, PayloadError}; -use actix_http::http::header::{self, HeaderName, HeaderValue}; -use actix_http::http::{Method, StatusCode, Version}; -use actix_http::httpmessage::HttpMessage; -use actix_http::{body, Error, HttpService, Request, Response}; - -fn load_body(stream: S) -> impl Future -where - S: Stream, -{ - stream.fold(BytesMut::new(), move |mut body, chunk| { - body.extend_from_slice(&chunk); - Ok::<_, PayloadError>(body) - }) -} - -fn ssl_acceptor() -> Result> { - // load ssl keys - let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); - builder - .set_private_key_file("../tests/key.pem", SslFiletype::PEM) - .unwrap(); - builder - .set_certificate_chain_file("../tests/cert.pem") - .unwrap(); - builder.set_alpn_select_callback(|_, protos| { - const H2: &[u8] = b"\x02h2"; - if protos.windows(3).any(|window| window == H2) { - Ok(b"h2") - } else { - Err(AlpnError::NOACK) - } - }); - builder.set_alpn_protos(b"\x02h2")?; - Ok(OpensslAcceptor::new(builder.build())) -} - -#[test] -fn test_h2() -> Result<()> { - let openssl = ssl_acceptor()?; - let mut srv = TestServer::new(move || { - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)) - .and_then( - HttpService::build() - .h2(|_| ok::<_, Error>(Response::Ok().finish())) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.sget("/").send()).unwrap(); - assert!(response.status().is_success()); - Ok(()) -} - -#[test] -fn test_h2_1() -> Result<()> { - let openssl = ssl_acceptor()?; - let mut srv = TestServer::new(move || { - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)) - .and_then( - HttpService::build() - .finish(|req: Request| { - assert!(req.peer_addr().is_some()); - assert_eq!(req.version(), Version::HTTP_2); - ok::<_, Error>(Response::Ok().finish()) - }) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.sget("/").send()).unwrap(); - assert!(response.status().is_success()); - Ok(()) -} - -#[test] -fn test_h2_body() -> Result<()> { - let data = "HELLOWORLD".to_owned().repeat(64 * 1024); - let openssl = ssl_acceptor()?; - let mut srv = TestServer::new(move || { - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)) - .and_then( - HttpService::build() - .h2(|mut req: Request<_>| { - load_body(req.take_payload()) - .and_then(|body| Ok(Response::Ok().body(body))) - }) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.sget("/").send_body(data.clone())).unwrap(); - assert!(response.status().is_success()); - - let body = srv.load_body(response).unwrap(); - assert_eq!(&body, data.as_bytes()); - Ok(()) -} - -#[test] -fn test_h2_content_length() { - let openssl = ssl_acceptor().unwrap(); - - let mut srv = TestServer::new(move || { - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)) - .and_then( - HttpService::build() - .h2(|req: Request| { - let indx: usize = req.uri().path()[1..].parse().unwrap(); - let statuses = [ - StatusCode::NO_CONTENT, - StatusCode::CONTINUE, - StatusCode::SWITCHING_PROTOCOLS, - StatusCode::PROCESSING, - StatusCode::OK, - StatusCode::NOT_FOUND, - ]; - ok::<_, ()>(Response::new(statuses[indx])) - }) - .map_err(|_| ()), - ) - }); - - let header = HeaderName::from_static("content-length"); - let value = HeaderValue::from_static("0"); - - { - for i in 0..4 { - let req = srv - .request(Method::GET, srv.surl(&format!("/{}", i))) - .send(); - let response = srv.block_on(req).unwrap(); - assert_eq!(response.headers().get(&header), None); - - let req = srv - .request(Method::HEAD, srv.surl(&format!("/{}", i))) - .send(); - let response = srv.block_on(req).unwrap(); - assert_eq!(response.headers().get(&header), None); - } - - for i in 4..6 { - let req = srv - .request(Method::GET, srv.surl(&format!("/{}", i))) - .send(); - let response = srv.block_on(req).unwrap(); - assert_eq!(response.headers().get(&header), Some(&value)); - } - } -} - -#[test] -fn test_h2_headers() { - let data = STR.repeat(10); - let data2 = data.clone(); - let openssl = ssl_acceptor().unwrap(); - - let mut srv = TestServer::new(move || { - let data = data.clone(); - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)) - .and_then( - HttpService::build().h2(move |_| { - let mut builder = Response::Ok(); - for idx in 0..90 { - builder.header( - format!("X-TEST-{}", idx).as_str(), - "TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ - TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ", - ); - } - ok::<_, ()>(builder.body(data.clone())) - }).map_err(|_| ())) - }); - - let response = srv.block_on(srv.sget("/").send()).unwrap(); - assert!(response.status().is_success()); - - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from(data2)); -} - -const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World \ - Hello World Hello World Hello World Hello World Hello World"; - -#[test] -fn test_h2_body2() { - let openssl = ssl_acceptor().unwrap(); - let mut srv = TestServer::new(move || { - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)) - .and_then( - HttpService::build() - .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.sget("/").send()).unwrap(); - assert!(response.status().is_success()); - - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); -} - -#[test] -fn test_h2_head_empty() { - let openssl = ssl_acceptor().unwrap(); - let mut srv = TestServer::new(move || { - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)) - .and_then( - HttpService::build() - .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.shead("/").send()).unwrap(); - assert!(response.status().is_success()); - assert_eq!(response.version(), Version::HTTP_2); - - { - let len = response.headers().get(header::CONTENT_LENGTH).unwrap(); - assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); - } - - // read response - let bytes = srv.load_body(response).unwrap(); - assert!(bytes.is_empty()); -} - -#[test] -fn test_h2_head_binary() { - let openssl = ssl_acceptor().unwrap(); - let mut srv = TestServer::new(move || { - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)) - .and_then( - HttpService::build() - .h2(|_| { - ok::<_, ()>( - Response::Ok().content_length(STR.len() as u64).body(STR), - ) - }) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.shead("/").send()).unwrap(); - assert!(response.status().is_success()); - - { - let len = response.headers().get(header::CONTENT_LENGTH).unwrap(); - assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); - } - - // read response - let bytes = srv.load_body(response).unwrap(); - assert!(bytes.is_empty()); -} - -#[test] -fn test_h2_head_binary2() { - let openssl = ssl_acceptor().unwrap(); - let mut srv = TestServer::new(move || { - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)) - .and_then( - HttpService::build() - .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.shead("/").send()).unwrap(); - assert!(response.status().is_success()); - - { - let len = response.headers().get(header::CONTENT_LENGTH).unwrap(); - assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); - } -} - -#[test] -fn test_h2_body_length() { - let openssl = ssl_acceptor().unwrap(); - let mut srv = TestServer::new(move || { - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)) - .and_then( - HttpService::build() - .h2(|_| { - let body = once(Ok(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok() - .body(body::SizedStream::new(STR.len() as u64, body)), - ) - }) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.sget("/").send()).unwrap(); - assert!(response.status().is_success()); - - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); -} - -#[test] -fn test_h2_body_chunked_explicit() { - let openssl = ssl_acceptor().unwrap(); - let mut srv = TestServer::new(move || { - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)) - .and_then( - HttpService::build() - .h2(|_| { - let body = - once::<_, Error>(Ok(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok() - .header(header::TRANSFER_ENCODING, "chunked") - .streaming(body), - ) - }) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.sget("/").send()).unwrap(); - assert!(response.status().is_success()); - assert!(!response.headers().contains_key(header::TRANSFER_ENCODING)); - - // read response - let bytes = srv.load_body(response).unwrap(); - - // decode - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); -} - -#[test] -fn test_h2_response_http_error_handling() { - let openssl = ssl_acceptor().unwrap(); - - let mut srv = TestServer::new(move || { - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)) - .and_then( - HttpService::build() - .h2(new_service_cfg(|_: &ServerConfig| { - Ok::<_, ()>(|_| { - let broken_header = Bytes::from_static(b"\0\0\0"); - ok::<_, ()>( - Response::Ok() - .header(header::CONTENT_TYPE, broken_header) - .body(STR), - ) - }) - })) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.sget("/").send()).unwrap(); - assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR); - - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(b"failed to parse header value")); -} - -#[test] -fn test_h2_service_error() { - let openssl = ssl_acceptor().unwrap(); - - let mut srv = TestServer::new(move || { - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)) - .and_then( - HttpService::build() - .h2(|_| Err::(ErrorBadRequest("error"))) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.sget("/").send()).unwrap(); - assert_eq!(response.status(), StatusCode::BAD_REQUEST); - - // read response - let bytes = srv.load_body(response).unwrap(); - assert_eq!(bytes, Bytes::from_static(b"error")); -} - -#[test] -fn test_h2_on_connect() { - let openssl = ssl_acceptor().unwrap(); - - let mut srv = TestServer::new(move || { - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)) - .and_then( - HttpService::build() - .on_connect(|_| 10usize) - .h2(|req: Request| { - assert!(req.extensions().contains::()); - ok::<_, ()>(Response::Ok().finish()) - }) - .map_err(|_| ()), - ) - }); - - let response = srv.block_on(srv.sget("/").send()).unwrap(); - assert!(response.status().is_success()); -} diff --git a/actix-http/tests/test_ws.rs b/actix-http/tests/test_ws.rs index 65a4d094d..74c1cb405 100644 --- a/actix-http/tests/test_ws.rs +++ b/actix-http/tests/test_ws.rs @@ -1,26 +1,27 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_http::{body, h1, ws, Error, HttpService, Request, Response}; -use actix_http_test::TestServer; +use actix_http_test::{block_on, TestServer}; use actix_utils::framed::FramedTransport; use bytes::{Bytes, BytesMut}; -use futures::future::{self, ok}; -use futures::{Future, Sink, Stream}; +use futures::future; +use futures::{SinkExt, StreamExt}; -fn ws_service( - (req, framed): (Request, Framed), -) -> impl Future { +async fn ws_service( + (req, mut framed): (Request, Framed), +) -> Result<(), Error> { let res = ws::handshake(req.head()).unwrap().message_body(()); framed .send((res, body::BodySize::None).into()) + .await + .unwrap(); + + FramedTransport::new(framed.into_framed(ws::Codec::new()), service) + .await .map_err(|_| panic!()) - .and_then(|framed| { - FramedTransport::new(framed.into_framed(ws::Codec::new()), service) - .map_err(|_| panic!()) - }) } -fn service(msg: ws::Frame) -> impl Future { +async fn service(msg: ws::Frame) -> Result { let msg = match msg { ws::Frame::Ping(msg) => ws::Message::Pong(msg), ws::Frame::Text(text) => { @@ -30,47 +31,56 @@ fn service(msg: ws::Frame) -> impl Future { ws::Frame::Close(reason) => ws::Message::Close(reason), _ => panic!(), }; - ok(msg) + Ok(msg) } #[test] fn test_simple() { - let mut srv = TestServer::new(|| { - HttpService::build() - .upgrade(ws_service) - .finish(|_| future::ok::<_, ()>(Response::NotFound())) - }); + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::build() + .upgrade(actix_service::service_fn(ws_service)) + .finish(|_| future::ok::<_, ()>(Response::NotFound())) + }); - // client service - let framed = srv.ws().unwrap(); - let framed = srv - .block_on(framed.send(ws::Message::Text("text".to_string()))) - .unwrap(); - let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!(item, Some(ws::Frame::Text(Some(BytesMut::from("text"))))); + // client service + let mut framed = srv.ws().await.unwrap(); + framed + .send(ws::Message::Text("text".to_string())) + .await + .unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Text(Some(BytesMut::from("text"))) + ); - let framed = srv - .block_on(framed.send(ws::Message::Binary("text".into()))) - .unwrap(); - let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!( - item, - Some(ws::Frame::Binary(Some(Bytes::from_static(b"text").into()))) - ); + framed + .send(ws::Message::Binary("text".into())) + .await + .unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Binary(Some(Bytes::from_static(b"text").into())) + ); - let framed = srv - .block_on(framed.send(ws::Message::Ping("text".into()))) - .unwrap(); - let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!(item, Some(ws::Frame::Pong("text".to_string().into()))); + framed.send(ws::Message::Ping("text".into())).await.unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Pong("text".to_string().into()) + ); - let framed = srv - .block_on(framed.send(ws::Message::Close(Some(ws::CloseCode::Normal.into())))) - .unwrap(); + framed + .send(ws::Message::Close(Some(ws::CloseCode::Normal.into()))) + .await + .unwrap(); - let (item, _framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!( - item, - Some(ws::Frame::Close(Some(ws::CloseCode::Normal.into()))) - ); + let (item, _framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Close(Some(ws::CloseCode::Normal.into())) + ); + }) } diff --git a/test-server/src/lib.rs b/test-server/src/lib.rs index 4ba123aa1..bf6558b51 100644 --- a/test-server/src/lib.rs +++ b/test-server/src/lib.rs @@ -7,7 +7,7 @@ use actix_rt::{System}; use actix_server::{Server, ServiceFactory}; use awc::{error::PayloadError, ws, Client, ClientRequest, ClientResponse, Connector}; use bytes::Bytes; -use futures::{Stream, future::lazy}; +use futures::Stream; use http::Method; use net2::TcpBuilder; use tokio_net::tcp::TcpStream; @@ -55,7 +55,7 @@ pub struct TestServerRuntime { impl TestServer { #[allow(clippy::new_ret_no_self)] /// Start new test server with application factory - pub fn new>(factory: F) -> TestServerRuntime { + pub fn start>(factory: F) -> TestServerRuntime { let (tx, rx) = mpsc::channel(); // run server in separate thread @@ -76,11 +76,11 @@ impl TestServer { let (system, addr) = rx.recv().unwrap(); - let client = block_on(lazy(move |_| { + let client = { let connector = { - #[cfg(feature = "ssl")] + #[cfg(feature = "openssl")] { - use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; + use open_ssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); builder.set_verify(SslVerifyMode::NONE); @@ -93,7 +93,7 @@ impl TestServer { .ssl(builder.build()) .finish() } - #[cfg(not(feature = "ssl"))] + #[cfg(not(feature = "openssl"))] { Connector::new() .conn_lifetime(time::Duration::from_secs(0)) @@ -102,14 +102,9 @@ impl TestServer { } }; - Ok::(Client::build().connector(connector).finish()) - })) - .unwrap(); - - block_on(lazy(|_| { - Ok::<_, ()>(actix_connect::start_default_resolver()) - })) - .unwrap(); + Client::build().connector(connector).finish() + }; + actix_connect::start_default_resolver(); TestServerRuntime { addr, @@ -228,35 +223,33 @@ impl TestServerRuntime { self.client.request(method, path.as_ref()) } - pub fn load_body( + pub async fn load_body( &mut self, mut response: ClientResponse, ) -> Result where S: Stream> + Unpin + 'static, { - block_on(response.body().limit(10_485_760)) + response.body().limit(10_485_760).await } /// Connect to websocket server at a given path - pub fn ws_at( + pub async fn ws_at( &mut self, path: &str, ) -> Result, awc::error::WsClientError> { let url = self.url(path); let connect = self.client.ws(url).connect(); - block_on(async move { - connect.await.map(|(_, framed)| framed) - }) + connect.await.map(|(_, framed)| framed) } /// Connect to a websocket server - pub fn ws( + pub async fn ws( &mut self, ) -> Result, awc::error::WsClientError> { - self.ws_at("/") + self.ws_at("/").await } /// Stop http server diff --git a/tests/cert.pem b/tests/cert.pem index f9bb05081..0eeb6721d 100644 --- a/tests/cert.pem +++ b/tests/cert.pem @@ -1,32 +1,19 @@ -----BEGIN CERTIFICATE----- -MIIFfjCCA2agAwIBAgIJAOIBvp/w68KrMA0GCSqGSIb3DQEBCwUAMGsxCzAJBgNV -BAYTAlJVMRkwFwYDVQQIDBBTYWludC1QZXRlcnNidXJnMRkwFwYDVQQHDBBTYWlu -dC1QZXRlcnNidXJnMRIwEAYDVQQKDAlLdXBpYmlsZXQxEjAQBgNVBAMMCWxvY2Fs -aG9zdDAgFw0xOTA3MjcxODIzMTJaGA8zMDE5MDcyNzE4MjMxMlowazELMAkGA1UE -BhMCUlUxGTAXBgNVBAgMEFNhaW50LVBldGVyc2J1cmcxGTAXBgNVBAcMEFNhaW50 -LVBldGVyc2J1cmcxEjAQBgNVBAoMCUt1cGliaWxldDESMBAGA1UEAwwJbG9jYWxo -b3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuiQZzTO3gRRPr6ZH -wcmKqkoXig9taCCqx72Qvb9tvCLhQLE1dDPZV8I/r8bx+mM4Yz3r0Hm5LxTIhCM9 -p3/abuiJAZENC/VkxgFzBGg7KGLSFmzU+A8Ft+2mrKmj5MpIPBCxDeVg80TCQOJy -hj+NU3PpBo9nxTgxWNWO6X+ZovZohdp78fYLLtns8rxjug3FVzdPrrLnBvihkGlq -gfImkh+vZxMTj1OgtxyCOhdbO4Ol4jCbn7a5yIw+iixHOEgBQfTQopRP7z1PEUV2 -WIy2VEGzvQDlj2OyzH86T1IOFV5rz5MjdZuW0qNzeS0w3Jzgp/olSbIZLhGAaIk0 -gN7y9XvSHqs7rO0wW+467ico7+uP1ScGgPgJA5fGu7ahp7F7G3ZSoAqAcS60wYsX -kStoA3RWAuqste6aChv1tlgTt+Rhk8qjGhuh0ng2qVnTGyo2T3OCHB/c47Bcsp6L -xiyTCnQIPH3fh2iO/SC7gPw3jihPMCAQZYlkC3MhMk974rn2zs9cKtJ8ubnG2m5F -VFVYmduRqS/YQS/O802jVCFdc8KDmoeAYNuHzgRZjQv9018UUeW3jtWKnopJnWs5 -ae9pbtmYeOtc7OovOxT7J2AaVfUkHRhmlqWZMzEJTcZws0fRPTZDifFJ5LFWbZsC -zW4tCKBKvYM9eAnbb+abiHXlY1MCAwEAAaMjMCEwHwYDVR0RBBgwFoIJbG9jYWxo -b3N0ggkxMjcuMC4wLjEwDQYJKoZIhvcNAQELBQADggIBAC1EU4SVCfUKc7JbhYRf -P87F+8e13bBTLxevJdnTCH3Xw2AN8UPmwQ2vv9Mv2FMulMBQ7yLnQLGtgGUua2OE -XO+EdBBEKnglo9cwXGzU6qHhaiCeXZDM8s53qOOrD42XsDsY0nOoFYqDLW3WixP9 -f1fWbcEf6+ktlvqi/1/3R6QtQR+6LS43enbsYHq8aAP60NrpXxdXxEoUwW6Z/sje -XAQluH8jzledwJcY8bXRskAHZlE4kGlOVuGgnyI3BXyLiwB4g9smFzYIs98iAGmV -7ZBaR5IIiRCtoKBG+SngM7Log0bHphvFPjDDvgqWYiWaOHboYM60Y2Z/gRbcjuMU -WZX64jw29fa8UPFdtGTupt+iuO7iXnHnm0lBBK36rVdOvsZup76p6L4BXmFsRmFK -qJ2Zd8uWNPDq80Am0mYaAqENuIANHHJXX38SesC+QO+G2JZt6vCwkGk/Qune4GIg -1GwhvsDRfTQopSxg1rdPwPM7HWeTfUGHZ34B5p/iILA3o6PfYQU8fNAWIsCDkRX2 -MrgDgCnLZxKb6pjR4DYNAdPwkxyMFACZ2T46z6WvLWFlnkK5nbZoqsOsp+GJHole -llafhrelXEzt3zFR0q4zGcqheJDI+Wy+fBy3XawgAc4eN0T2UCzL/jKxKgzlzSU3 -+xh1SDNjFLRd6sGzZHPMgXN0 +MIIDEDCCAfgCCQCQdmIZc/Ib/jANBgkqhkiG9w0BAQsFADBKMQswCQYDVQQGEwJ1 +czELMAkGA1UECAwCY2ExCzAJBgNVBAcMAnNmMSEwHwYJKoZIhvcNAQkBFhJmYWZo +cmQ5MUBnbWFpbC5jb20wHhcNMTkxMTE5MTEwNjU1WhcNMjkxMTE2MTEwNjU1WjBK +MQswCQYDVQQGEwJ1czELMAkGA1UECAwCY2ExCzAJBgNVBAcMAnNmMSEwHwYJKoZI +hvcNAQkBFhJmYWZocmQ5MUBnbWFpbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDcnaz12CKzUL7248V7Axhms/O9UQXfAdw0yolEfC3P5jADa/1C ++kLWKjAc2coqDSbGsrsR6KiH2g06Kunx+tSGqUO+Sct7HEehmxndiSwx/hfMWezy +XRe/olcHFTeCk/Tllz4xGEplhPua6GLhJygLOhAMiV8cwCYrgyPqsDduExLDFCqc +K2xntIPreumXpiE3QY4+MWyteiJko4IWDFf/UwwsdCY5MlFfw1F/Uv9vz7FfOfvu +GccHd/ex8cOwotUqd6emZb+0bVE24Sv8U+yLnHIVx/tOkxgMAnJEpAnf2G3Wp3zU +b2GJosbmfGaf+xTfnGGhTLLL7kCtva+NvZr5AgMBAAEwDQYJKoZIhvcNAQELBQAD +ggEBANftoL8zDGrjCwWvct8kOOqset2ukK8vjIGwfm88CKsy0IfSochNz2qeIu9R +ZuO7c0pfjmRkir9ZQdq9vXgG3ccL9UstFsferPH9W3YJ83kgXg3fa0EmCiN/0hwz +6Ij1ZBiN1j3+d6+PJPgyYFNu2nGwox5mJ9+aRAGe0/9c63PEOY8P2TI4HsiPmYSl +fFR8k/03vr6e+rTKW85BgctjvYKe/TnFxeCQ7dZ+na7vlEtch4tNmy6O/vEk2kCt +5jW0DUxhmRsv2wGmfFRI0+LotHjoXQQZi6nN5aGL3odaGF3gYwIVlZNd3AdkwDQz +BzG0ZwXuDDV9bSs3MfWEWcy4xuU= -----END CERTIFICATE----- diff --git a/tests/key.pem b/tests/key.pem index 70153c8ae..a6d308168 100644 --- a/tests/key.pem +++ b/tests/key.pem @@ -1,52 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQC6JBnNM7eBFE+v -pkfByYqqSheKD21oIKrHvZC9v228IuFAsTV0M9lXwj+vxvH6YzhjPevQebkvFMiE -Iz2nf9pu6IkBkQ0L9WTGAXMEaDsoYtIWbNT4DwW37aasqaPkykg8ELEN5WDzRMJA -4nKGP41Tc+kGj2fFODFY1Y7pf5mi9miF2nvx9gsu2ezyvGO6DcVXN0+usucG+KGQ -aWqB8iaSH69nExOPU6C3HII6F1s7g6XiMJuftrnIjD6KLEc4SAFB9NCilE/vPU8R -RXZYjLZUQbO9AOWPY7LMfzpPUg4VXmvPkyN1m5bSo3N5LTDcnOCn+iVJshkuEYBo -iTSA3vL1e9Ieqzus7TBb7jruJyjv64/VJwaA+AkDl8a7tqGnsXsbdlKgCoBxLrTB -ixeRK2gDdFYC6qy17poKG/W2WBO35GGTyqMaG6HSeDapWdMbKjZPc4IcH9zjsFyy -novGLJMKdAg8fd+HaI79ILuA/DeOKE8wIBBliWQLcyEyT3viufbOz1wq0ny5ucba -bkVUVViZ25GpL9hBL87zTaNUIV1zwoOah4Bg24fOBFmNC/3TXxRR5beO1Yqeikmd -azlp72lu2Zh461zs6i87FPsnYBpV9SQdGGaWpZkzMQlNxnCzR9E9NkOJ8UnksVZt -mwLNbi0IoEq9gz14Cdtv5puIdeVjUwIDAQABAoICAQCZVVezw+BsAjFKPi1qIv2J -HZOadO7pEc/czflHdUON8SWgxtmDqZpmQmt3/ugiHE284qs4hqzXbcVnpCgLrLRh -HEiP887NhQ3IVjVK8hmZQR5SvsAIv0c0ph3gqbWKqF8sq4tOKR/eBUwHawJwODXR -AvB4KPWQbqOny/P3wNbseRLNAJeNT+MSaw5XPnzgLKvdFoEbJeBNy847Sbsk5DaF -tHgm7n30WS1Q6bkU5VyP//hMBUKNJFaSL4TtCWB5qkbu8B5VbtsR9m0FizTb6L3h -VmYbUXvIzJXjAwMjiDJ1w9wHl+tj3BE33tEmhuVzNf+SH+tLc9xuKJighDWt2vpD -eTpZ1qest26ANLOmNXWVCVTGpcWvOu5yhG/P7En10EzjFruMfHAFdwLm1gMx1rlR -9fyNAk/0ROJ+5BUtuWgDiyytS5f2T9KGiOHni7UbBIkv0CV2H6VL39Twxf+3OHnx -JJ7OWZ8DRuLM/EJfN3C1+3eDsXOvcdvbo2TFBmCCl4Pa2pm4k3g2NBfxy/zSYWIh -ccGPZorFKNMUi29U0Ool6fxeVflbll570pWVBLAB31HdkLSESv9h+2j/IiEJcJXj -nzl2RtYB0Uxzk6SjO0z4WXjz/SXg5tQQkm/dx8kM8TvHICFq68AEnw8t9Hagsdxs -v5jNyOEeI1I5gPgZmKuboQKCAQEA7Hw6s8Xc3UtNaywMcK1Eb1O+kwRdztgtm0uE -uqsHWmGqbBxXN4jiVLh3dILIXFuVbvDSsSZtPLhOj1wqxgsTHg93b4BtvowyNBgo -X4tErMu7/6NRael/hfOEdtpfv2gV+0eQa+8KKqYJPbqpMz/r5L/3RaxS3iXkj3QM -6oC4+cRuwy/flPMIpxhDriH5yjfiMOdDsi3ZfMTJu/58DTrKV7WkJxQZmha4EoZn -IiXeRhzo+2ukMDWrr3GGPyDfjd/NB7rmY8QBdmhB5NSk+6B66JCNTIbKka/pichS -36bwSYFNji4NaHUUlYDUjfKoTNuQMEZknMGhc/433ADO7s17iQKCAQEAyYBYVG7/ -LE2IkvQy9Nwly5tRCNlSvSkloz7PUwRbzG5uF5mweWEa8YECJe9/vrFXvyBW+NR8 -XABFn4eG0POTR9nyb4n2nUlqiGugDIPgkrKCkJws5InifITZ/+Viocd4YZL5UwCU -R1/kMf0UjK2iJjWEeTPS6RmwRI2Iu7kym9BzphDyNYBQSbUE/f+4hNP6nUT/h09c -VH4/sUhubSgVKeK4onOci8bKitAkwVBYCYSyhuBCeCu8fTk2hVRWviRaJPVq2PMB -LHw1FCcfJLIPJG6MZpFAPkMQxpiewdggXIgi46ZlZcsNXEJ81ocT4GU2j+ArQXCf -lgEycyD3mx4k+wKCAQBGneohmKoVYtEheavVUcgnvkggOqOQirlDsE9YNo4hjRyI -4AWjTbrYNaVmI0+VVLvQvxULVUA1a4v5/zm+nbv9s/ykTSN4TQEI0VXtAfdl6gif -k7NR/ynXZBpgK2GAFKLLwFj+Agl1JtOHnV+9MA9O5Yv/QDAWqhYQSEU7GWkjHGc+ -3eLT5ablzrcXHooqunlOxSBP6qURPupGuv1sLewSOOllyfjDLJmW3o+ZgNlY8nUX -7tK+mqhD4ZCG9VgMU5I0BrmZfQQ6yXMz09PYV9mb7N5kxbNjwbXpMOqeYolKSdRQ -6quST7Pv2OKf6KAdI0txPvP4Y1HFA1rG1W71nGKRAoIBAHlDU+T8J3Rx9I77hu70 -zYoKnmnE35YW/R+Q3RQIu3X7vyVUyG9DkQNlr/VEfIw2Dahnve9hcLWtNDkdRnTZ -IPlMoCmfzVo6pHIU0uy1MKEX7Js6YYnnsPVevhLR6NmTQU73NDRPVOzfOGUc+RDw -LXTxIBgQqAy/+ORIiNDwUxSSDgcSi7DG14qD9c0l59WH/HpI276Cc/4lPA9kl4/5 -X0MlvheFm+BCcgG34Wa1A0Y3JXkl3NqU94oktDro1or3NYioaPTGyR4MYaUPJh7f -SV2TacsP/ql5ks7xahkeB9un0ddOfBcWa6PqH1a7U6rnPj63mVB4hpGvhrziSiB/ -s6ECggEAOp2P4Yd9Vm9/CptxS50HFF4adyLscAtsDd3S2hIAXhDovcPbvRek4pLQ -idPhHlRAfqrEztnhaVAmCK9HlhgthtiQGQX62YI4CS4QL2IhzDFo3M1a2snjFEdl -QuFk3XI7kQ0Yp8BLLG7T436JUrUkCXc4gQX2uRNut+ff34RIR2CjcQQjChxuHVeG -sP/3xFFj8OSs7ZoSPbmDBLrMOl64YHwezQUNAZiRYiaGbFiY0QUV6dHq8qX/qE1h -a/0Rq+gTqObDST0TqhMzI8V/i7R8SwVcD5ODHaZp5I2N2P/hV5OWY7ghQXhh89WM -o21xtGh0nP2Fq1TC6jFO+9cpbK8jNA== +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDcnaz12CKzUL72 +48V7Axhms/O9UQXfAdw0yolEfC3P5jADa/1C+kLWKjAc2coqDSbGsrsR6KiH2g06 +Kunx+tSGqUO+Sct7HEehmxndiSwx/hfMWezyXRe/olcHFTeCk/Tllz4xGEplhPua +6GLhJygLOhAMiV8cwCYrgyPqsDduExLDFCqcK2xntIPreumXpiE3QY4+MWyteiJk +o4IWDFf/UwwsdCY5MlFfw1F/Uv9vz7FfOfvuGccHd/ex8cOwotUqd6emZb+0bVE2 +4Sv8U+yLnHIVx/tOkxgMAnJEpAnf2G3Wp3zUb2GJosbmfGaf+xTfnGGhTLLL7kCt +va+NvZr5AgMBAAECggEBAKoU0UwzVgVCQgca8Jt2dnBvWYDhnxIfYAI/BvaKedMm +1ms87OKfB7oOiksjyI0E2JklH72dzZf2jm4CuZt5UjGC+xwPzlTaJ4s6hQVbBHyC +NRyxU1BCXtW5tThbrhD4OjxqjmLRJEIB9OunLtwAEQoeuFLB8Va7+HFhR+Zd9k3f +7aVA93pC5A50NRbZlke4miJ3Q8n7ZF0+UmxkBfm3fbqLk7aMWkoEKwLLTadjRlu1 +bBp0YDStX66I/p1kujqBOdh6VpPvxFOa1sV9pq0jeiGc9YfSkzRSKzIn8GoyviFB +fHeszQdNlcnrSDSNnMABAw+ZpxUO7SCaftjwejEmKZUCgYEA+TY43VpmV95eY7eo +WKwGepiHE0fwQLuKGELmZdZI80tFi73oZMuiB5WzwmkaKGcJmm7KGE9KEvHQCo9j +xvmktBR0VEZH8pmVfun+4h6+0H7m/NKMBBeOyv/IK8jBgHjkkB6e6nmeR7CqTxCw +tf9tbajl1QN8gNzXZSjBDT/lanMCgYEA4qANOKOSiEARtgwyXQeeSJcM2uPv6zF3 +ffM7vjSedtuEOHUSVeyBP/W8KDt7zyPppO/WNbURHS+HV0maS9yyj6zpVS2HGmbs +3fetswsQ+zYVdokW89x4oc2z4XOGHd1LcSlyhRwPt0u2g1E9L0irwTQLWU0npFmG +PRf7sN9+LeMCgYAGkDUDL2ROoB6gRa/7Vdx90hKMoXJkYgwLA4gJ2pDlR3A3c/Lw +5KQJyxmG3zm/IqeQF6be6QesZA30mT4peV2rGHbP2WH/s6fKReNelSy1VQJEWk8x +tGUgV4gwDwN5nLV4TjYlOrq+bJqvpmLhCC8bmj0jVQosYqSRl3cuICasnQKBgGlV +VO/Xb1su1EyWPK5qxRIeSxZOTYw2sMB01nbgxCqge0M2fvA6/hQ5ZlwY0cIEgits +YlcSMsMq/TAAANxz1vbaupUhlSMbZcsBvNV0Nk9c4vr2Wxm7hsJF9u66IEMvQUp2 +pkjiMxfR9CHzF4orr9EcHI5EQ0Grbq5kwFKEfoRbAoGAcWoFPILeJOlp2yW/Ds3E +g2fQdI9BAamtEZEaslJmZMmsDTg5ACPcDkOSFEQIaJ7wLPXeZy74FVk/NrY5F8Gz +bjX9OD/xzwp852yW5L9r62vYJakAlXef5jI6CFdYKDDCcarU0S7W5k6kq9n+wrBR +i1NklYmUAMr2q59uJA5zsic= -----END PRIVATE KEY----- From d081e573167cc139debb00e78333310a18b2cdf2 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Tue, 19 Nov 2019 19:38:42 +0600 Subject: [PATCH 050/176] fix h2 client send body --- actix-http/src/client/h2proto.rs | 2 ++ actix-http/src/request.rs | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/actix-http/src/client/h2proto.rs b/actix-http/src/client/h2proto.rs index 25299fd61..1647abf81 100644 --- a/actix-http/src/client/h2proto.rs +++ b/actix-http/src/client/h2proto.rs @@ -159,6 +159,8 @@ async fn send_body( } else { if !b.is_empty() { send.reserve_capacity(b.len()); + } else { + buf = None; } continue; } diff --git a/actix-http/src/request.rs b/actix-http/src/request.rs index 0afa45cbf..77ece01c5 100644 --- a/actix-http/src/request.rs +++ b/actix-http/src/request.rs @@ -204,7 +204,6 @@ mod tests { assert_eq!(req.uri().query(), Some("q=1")); let s = format!("{:?}", req); - println!("T: {:?}", s); assert!(s.contains("Request HTTP/1.1 GET:/index.html")); } } From 3127dd4db62175e9960b879f7621415b755d9e06 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 20 Nov 2019 23:33:22 +0600 Subject: [PATCH 051/176] migrate actix-web to std::future --- Cargo.toml | 88 ++- actix-cors/Cargo.toml | 10 +- actix-files/Cargo.toml | 12 +- actix-files/src/lib.rs | 5 +- actix-framed/Cargo.toml | 20 +- actix-http/Cargo.toml | 18 +- actix-http/src/builder.rs | 7 +- actix-http/src/h2/dispatcher.rs | 20 +- actix-identity/Cargo.toml | 12 +- actix-multipart/Cargo.toml | 12 +- actix-session/Cargo.toml | 12 +- awc/Cargo.toml | 16 +- awc/tests/test_client.rs | 918 ++++++++++++----------- examples/basic.rs | 6 +- examples/client.rs | 35 +- examples/uds.rs | 8 +- src/app.rs | 414 ++++++----- src/app_service.rs | 131 ++-- src/config.rs | 119 +-- src/data.rs | 139 ++-- src/extract.rs | 101 +-- src/handler.rs | 239 +++--- src/lib.rs | 30 +- src/middleware/compress.rs | 43 +- src/middleware/defaultheaders.rs | 92 ++- src/middleware/logger.rs | 52 +- src/middleware/mod.rs | 10 +- src/request.rs | 114 +-- src/resource.rs | 441 ++++++----- src/responder.rs | 438 +++++------ src/route.rs | 245 +++--- src/scope.rs | 923 +++++++++++++---------- src/server.rs | 59 +- src/service.rs | 80 +- src/test.rs | 420 ++++++----- src/types/form.rs | 252 ++++--- src/types/json.rs | 443 +++++------ src/types/path.rs | 185 ++--- src/types/payload.rs | 158 ++-- src/types/query.rs | 69 +- src/types/readlines.rs | 138 ++-- src/web.rs | 35 +- test-server/Cargo.toml | 16 +- test-server/src/lib.rs | 2 +- tests/test_httpserver.rs | 70 +- tests/test_server.rs | 1197 ++++++++++++++++-------------- 46 files changed, 4134 insertions(+), 3720 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0b5c4f3d1..b1aa79527 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "1.0.9" +version = "2.0.0-alpha.1" authors = ["Nikolay Kim "] description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." readme = "README.md" @@ -16,7 +16,7 @@ exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] edition = "2018" [package.metadata.docs.rs] -features = ["ssl", "brotli", "flate2-zlib", "secure-cookies", "client", "rust-tls", "uds"] +features = ["openssl", "rustls", "brotli", "flate2-zlib", "secure-cookies", "client"] [badges] travis-ci = { repository = "actix/actix-web", branch = "master" } @@ -28,20 +28,19 @@ path = "src/lib.rs" [workspace] members = [ -# ".", -# "awc", -# #"actix-http", -# "actix-cors", -# "actix-files", -# "actix-framed", -# "actix-session", -# "actix-identity", -# "actix-multipart", -# "actix-web-actors", -# "actix-web-codegen", -# "test-server", + ".", + "awc", + "actix-http", + "actix-cors", + "actix-files", + "actix-framed", + "actix-session", + "actix-identity", + "actix-multipart", + "actix-web-actors", + "actix-web-codegen", + "test-server", ] -exclude = ["awc", "actix-http", "test-server"] [features] default = ["brotli", "flate2-zlib", "client", "fail"] @@ -64,37 +63,35 @@ secure-cookies = ["actix-http/secure-cookies"] fail = ["actix-http/fail"] # openssl -ssl = ["openssl", "actix-server/ssl", "awc/ssl"] +openssl = ["open-ssl", "actix-server/openssl", "awc/openssl"] # rustls -rust-tls = ["rustls", "actix-server/rust-tls", "awc/rust-tls"] - -# unix domain sockets support -uds = ["actix-server/uds"] +rustls = ["rust-tls", "actix-server/rustls", "awc/rustls"] [dependencies] -actix-codec = "0.1.2" -actix-service = "0.4.1" -actix-utils = "0.4.4" +actix-codec = "0.2.0-alpha.1" +actix-service = "1.0.0-alpha.1" +actix-utils = "0.5.0-alpha.1" actix-router = "0.1.5" -actix-rt = "0.2.4" +actix-rt = "1.0.0-alpha.1" actix-web-codegen = "0.1.2" -actix-http = "0.2.11" -actix-server = "0.6.1" -actix-server-config = "0.1.2" -actix-testing = "0.1.0" -actix-threadpool = "0.1.1" -awc = { version = "0.2.7", optional = true } +actix-http = "0.3.0-alpha.1" +actix-server = "0.8.0-alpha.1" +actix-server-config = "0.3.0-alpha.1" +actix-testing = "0.3.0-alpha.1" +actix-threadpool = "0.2.0-alpha.1" +awc = { version = "0.3.0-alpha.1", optional = true } bytes = "0.4" derive_more = "0.15.0" encoding_rs = "0.8" -futures = "0.1.25" +futures = "0.3.1" hashbrown = "0.6.3" log = "0.4" mime = "0.3" net2 = "0.2.33" parking_lot = "0.9" +pin-project = "0.4.5" regex = "1.0" serde = { version = "1.0", features=["derive"] } serde_json = "1.0" @@ -103,17 +100,17 @@ time = "0.1.42" url = "2.1" # ssl support -openssl = { version="0.10", optional = true } -rustls = { version = "0.15", optional = true } +open-ssl = { version="0.10", package="openssl", optional = true } +rust-tls = { version = "0.16", package="rustls", optional = true } [dev-dependencies] -actix = "0.8.3" -actix-connect = "0.2.2" -actix-http-test = "0.2.4" +# actix = "0.8.3" +actix-connect = "0.3.0-alpha.1" +actix-http-test = "0.3.0-alpha.1" rand = "0.7" env_logger = "0.6" serde_derive = "1.0" -tokio-timer = "0.2.8" +tokio-timer = "0.3.0-alpha.6" brotli2 = "0.3.2" flate2 = "1.0.2" @@ -123,19 +120,18 @@ opt-level = 3 codegen-units = 1 [patch.crates-io] -# actix-web = { path = "." } -# actix-http = { path = "actix-http" } -# actix-http-test = { path = "test-server" } -# actix-web-codegen = { path = "actix-web-codegen" } +actix-web = { path = "." } +actix-http = { path = "actix-http" } +actix-http-test = { path = "test-server" } +actix-web-codegen = { path = "actix-web-codegen" } # actix-web-actors = { path = "actix-web-actors" } -# actix-session = { path = "actix-session" } -# actix-files = { path = "actix-files" } -# actix-multipart = { path = "actix-multipart" } -# awc = { path = "awc" } +actix-session = { path = "actix-session" } +actix-files = { path = "actix-files" } +actix-multipart = { path = "actix-multipart" } +awc = { path = "awc" } actix-codec = { path = "../actix-net/actix-codec" } actix-connect = { path = "../actix-net/actix-connect" } -actix-ioframe = { path = "../actix-net/actix-ioframe" } actix-rt = { path = "../actix-net/actix-rt" } actix-server = { path = "../actix-net/actix-server" } actix-server-config = { path = "../actix-net/actix-server-config" } diff --git a/actix-cors/Cargo.toml b/actix-cors/Cargo.toml index 091c94044..57aa5833a 100644 --- a/actix-cors/Cargo.toml +++ b/actix-cors/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-cors" -version = "0.1.0" +version = "0.2.0-alpha.1" authors = ["Nikolay Kim "] description = "Cross-origin resource sharing (CORS) for Actix applications." readme = "README.md" @@ -10,14 +10,14 @@ repository = "https://github.com/actix/actix-web.git" documentation = "https://docs.rs/actix-cors/" license = "MIT/Apache-2.0" edition = "2018" -#workspace = ".." +workspace = ".." [lib] name = "actix_cors" path = "src/lib.rs" [dependencies] -actix-web = "1.0.0" -actix-service = "0.4.0" +actix-web = "2.0.0-alpha.1" +actix-service = "1.0.0-alpha.1" derive_more = "0.15.0" -futures = "0.1.25" +futures = "0.3.1" diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 1bc063e55..6e33bb412 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-files" -version = "0.1.7" +version = "0.2.0-alpha.1" authors = ["Nikolay Kim "] description = "Static files support for actix web." readme = "README.md" @@ -18,12 +18,12 @@ name = "actix_files" path = "src/lib.rs" [dependencies] -actix-web = { version = "1.0.8", default-features = false } -actix-http = "0.2.11" -actix-service = "0.4.1" +actix-web = { version = "2.0.0-alpha.1", default-features = false } +actix-http = "0.3.0-alpha.1" +actix-service = "1.0.0-alpha.1" bitflags = "1" bytes = "0.4" -futures = "0.1.25" +futures = "0.3.1" derive_more = "0.15.0" log = "0.4" mime = "0.3" @@ -32,4 +32,4 @@ percent-encoding = "2.1" v_htmlescape = "0.4" [dev-dependencies] -actix-web = { version = "1.0.8", features=["ssl"] } +actix-web = { version = "2.0.0-alpha.1", features=["openssl"] } diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index 16f40a20c..8df7a6aa9 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -303,9 +303,8 @@ impl Files { /// Set custom directory renderer pub fn files_listing_renderer(mut self, f: F) -> Self where - for<'r, 's> F: - Fn(&'r Directory, &'s HttpRequest) -> Result - + 'static, + for<'r, 's> F: Fn(&'r Directory, &'s HttpRequest) -> Result + + 'static, { self.renderer = Rc::new(f); self diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml index 321041c7e..9d32ebed5 100644 --- a/actix-framed/Cargo.toml +++ b/actix-framed/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-framed" -version = "0.2.1" +version = "0.3.0-alpha.1" authors = ["Nikolay Kim "] description = "Actix framed app server" readme = "README.md" @@ -20,19 +20,19 @@ name = "actix_framed" path = "src/lib.rs" [dependencies] -actix-codec = "0.1.2" -actix-service = "0.4.1" +actix-codec = "0.2.0-alpha.1" +actix-service = "1.0.0-alpha.1" actix-router = "0.1.2" -actix-rt = "0.2.2" -actix-http = "0.2.7" -actix-server-config = "0.1.2" +actix-rt = "1.0.0-alpha.1" +actix-http = "0.3.0-alpha.1" +actix-server-config = "0.2.0-alpha.1" bytes = "0.4" futures = "0.1.25" log = "0.4" [dev-dependencies] -actix-server = { version = "0.6.0", features=["ssl"] } -actix-connect = { version = "0.2.0", features=["ssl"] } -actix-http-test = { version = "0.2.4", features=["ssl"] } -actix-utils = "0.4.4" +actix-server = { version = "0.8.0-alpha.1", features=["openssl"] } +actix-connect = { version = "0.3.0-alpha.1", features=["openssl"] } +actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } +actix-utils = "0.5.0-alpha.1" diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index edc93f09b..32af97ad9 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -13,8 +13,7 @@ categories = ["network-programming", "asynchronous", "web-programming::websocket"] license = "MIT/Apache-2.0" edition = "2018" - -# workspace = ".." +workspace = ".." [package.metadata.docs.rs] features = ["openssl", "fail", "brotli", "flate2-zlib", "secure-cookies"] @@ -114,18 +113,3 @@ actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } env_logger = "0.6" serde_derive = "1.0" open-ssl = { version="0.10", package="openssl" } - -[patch.crates-io] -awc = { path = "../awc" } -actix-http = { path = "." } -actix-http-test = { path = "../test-server" } - -actix-codec = { path = "../../actix-net/actix-codec" } -actix-connect = { path = "../../actix-net/actix-connect" } -actix-rt = { path = "../../actix-net/actix-rt" } -actix-server = { path = "../../actix-net/actix-server" } -actix-server-config = { path = "../../actix-net/actix-server-config" } -actix-service = { path = "../../actix-net/actix-service" } -actix-testing = { path = "../../actix-net/actix-testing" } -actix-threadpool = { path = "../../actix-net/actix-threadpool" } -actix-utils = { path = "../../actix-net/actix-utils" } diff --git a/actix-http/src/builder.rs b/actix-http/src/builder.rs index 8fa35feab..7e1dae58f 100644 --- a/actix-http/src/builder.rs +++ b/actix-http/src/builder.rs @@ -172,12 +172,11 @@ where /// Finish service configuration and create *http service* for HTTP/1 protocol. pub fn h1(self, service: F) -> H1Service where - B: MessageBody + 'static, + B: MessageBody, F: IntoServiceFactory, - S::Error: Into + 'static, + S::Error: Into, S::InitError: fmt::Debug, - S::Response: Into> + 'static, - ::Future: 'static, + S::Response: Into>, { let cfg = ServiceConfig::new( self.keep_alive, diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index 96775b989..1a52a60f2 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -51,10 +51,10 @@ impl Dispatcher where T: IoStream, S: Service, - S::Error: Into + 'static, - S::Future: 'static, - S::Response: Into> + 'static, - B: MessageBody + 'static, + S::Error: Into, + // S::Future: 'static, + S::Response: Into>, + B: MessageBody, { pub(crate) fn new( service: CloneableService, @@ -176,9 +176,9 @@ enum ServiceResponseState { impl ServiceResponse where F: Future>, - E: Into + 'static, - I: Into> + 'static, - B: MessageBody + 'static, + E: Into, + I: Into>, + B: MessageBody, { fn prepare_response( &self, @@ -244,9 +244,9 @@ where impl Future for ServiceResponse where F: Future>, - E: Into + 'static, - I: Into> + 'static, - B: MessageBody + 'static, + E: Into, + I: Into>, + B: MessageBody, { type Output = (); diff --git a/actix-identity/Cargo.toml b/actix-identity/Cargo.toml index e645275a2..d05b37685 100644 --- a/actix-identity/Cargo.toml +++ b/actix-identity/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-identity" -version = "0.1.0" +version = "0.2.0-alpha.1" authors = ["Nikolay Kim "] description = "Identity service for actix web framework." readme = "README.md" @@ -17,14 +17,14 @@ name = "actix_identity" path = "src/lib.rs" [dependencies] -actix-web = { version = "1.0.0", default-features = false, features = ["secure-cookies"] } -actix-service = "0.4.0" -futures = "0.1.25" +actix-web = { version = "2.0.0-alpha.1", default-features = false, features = ["secure-cookies"] } +actix-service = "1.0.0-alpha.1" +futures = "0.3.1" serde = "1.0" serde_json = "1.0" time = "0.1.42" [dev-dependencies] -actix-rt = "0.2.2" -actix-http = "0.2.3" +actix-rt = "1.0.0-alpha.1" +actix-http = "0.3.0-alpha.1" bytes = "0.4" \ No newline at end of file diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index 2168c259a..804d1bb67 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-multipart" -version = "0.1.4" +version = "0.2.0-alpha.1" authors = ["Nikolay Kim "] description = "Multipart support for actix web framework." readme = "README.md" @@ -18,17 +18,17 @@ name = "actix_multipart" path = "src/lib.rs" [dependencies] -actix-web = { version = "1.0.0", default-features = false } -actix-service = "0.4.1" +actix-web = { version = "2.0.0-alpha.1", default-features = false } +actix-service = "1.0.0-alpha.1" bytes = "0.4" derive_more = "0.15.0" httparse = "1.3" -futures = "0.1.25" +futures = "0.3.1" log = "0.4" mime = "0.3" time = "0.1" twoway = "0.2" [dev-dependencies] -actix-rt = "0.2.2" -actix-http = "0.2.4" \ No newline at end of file +actix-rt = "1.0.0-alpha.1" +actix-http = "0.3.0-alpha.1" \ No newline at end of file diff --git a/actix-session/Cargo.toml b/actix-session/Cargo.toml index d973661ef..3ce2a8b40 100644 --- a/actix-session/Cargo.toml +++ b/actix-session/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-session" -version = "0.2.0" +version = "0.3.0-alpha.1" authors = ["Nikolay Kim "] description = "Session for actix web framework." readme = "README.md" @@ -24,15 +24,15 @@ default = ["cookie-session"] cookie-session = ["actix-web/secure-cookies"] [dependencies] -actix-web = "1.0.0" -actix-service = "0.4.1" +actix-web = "2.0.0-alpha.1" +actix-service = "1.0.0-alpha.1" bytes = "0.4" derive_more = "0.15.0" -futures = "0.1.25" -hashbrown = "0.5.0" +futures = "0.3.1" +hashbrown = "0.6.3" serde = "1.0" serde_json = "1.0" time = "0.1.42" [dev-dependencies] -actix-rt = "0.2.2" +actix-rt = "1.0.0-alpha.1" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index f8f9a7eb8..70d89d5de 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -63,7 +63,7 @@ rust-tls = { version = "0.16.0", package="rustls", optional = true } [dev-dependencies] actix-rt = "1.0.0-alpha.1" -#actix-web = { version = "1.0.8", features=["ssl"] } +actix-web = { version = "2.0.0-alpha.1", features=["openssl"] } actix-http = { version = "0.3.0-alpha.1", features=["openssl"] } actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } actix-utils = "0.5.0-alpha.1" @@ -75,17 +75,3 @@ rand = "0.7" tokio-tcp = "0.1" webpki = { version = "0.21" } rus-tls = { version = "0.16.0", package="rustls", features = ["dangerous_configuration"] } - -[patch.crates-io] -awc = { path = "." } -actix-http = { path = "../actix-http" } -actix-http-test = { path = "../test-server" } - -actix-codec = { path = "../../actix-net/actix-codec" } -actix-connect = { path = "../../actix-net/actix-connect" } -actix-rt = { path = "../../actix-net/actix-rt" } -actix-server = { path = "../../actix-net/actix-server" } -actix-server-config = { path = "../../actix-net/actix-server-config" } -actix-service = { path = "../../actix-net/actix-service" } -actix-threadpool = { path = "../../actix-net/actix-threadpool" } -actix-utils = { path = "../../actix-net/actix-utils" } diff --git a/awc/tests/test_client.rs b/awc/tests/test_client.rs index cb38c7315..8d7bc2274 100644 --- a/awc/tests/test_client.rs +++ b/awc/tests/test_client.rs @@ -13,8 +13,8 @@ use futures::Future; use rand::Rng; use actix_http::HttpService; -use actix_http_test::TestServer; -use actix_service::{service_fn, NewService}; +use actix_http_test::{bloxk_on, TestServer}; +use actix_service::{service_fn, ServiceFactory}; use actix_web::http::Cookie; use actix_web::middleware::{BodyEncoding, Compress}; use actix_web::{http::header, web, App, Error, HttpMessage, HttpRequest, HttpResponse}; @@ -44,459 +44,497 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[test] fn test_simple() { - let mut srv = - TestServer::new(|| { + block_on(async { + let mut srv = TestServer::start(|| { HttpService::new(App::new().service( web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))), )) }); - let request = srv.get("/").header("x-test", "111").send(); - let mut response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + let request = srv.get("/").header("x-test", "111").send(); + let mut response = srv.block_on(request).unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response + let bytes = srv.block_on(response.body()).unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - let mut response = srv.block_on(srv.post("/").send()).unwrap(); - assert!(response.status().is_success()); + let mut response = srv.block_on(srv.post("/").send()).unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response + let bytes = srv.block_on(response.body()).unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - // camel case - let response = srv.block_on(srv.post("/").camel_case().send()).unwrap(); - assert!(response.status().is_success()); + // camel case + let response = srv.block_on(srv.post("/").camel_case().send()).unwrap(); + assert!(response.status().is_success()); + }) } #[test] fn test_json() { - let mut srv = TestServer::new(|| { - HttpService::new(App::new().service( - web::resource("/").route(web::to(|_: web::Json| HttpResponse::Ok())), - )) - }); + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::new( + App::new().service( + web::resource("/") + .route(web::to(|_: web::Json| HttpResponse::Ok())), + ), + ) + }); - let request = srv - .get("/") - .header("x-test", "111") - .send_json(&"TEST".to_string()); - let response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + let request = srv + .get("/") + .header("x-test", "111") + .send_json(&"TEST".to_string()); + let response = srv.block_on(request).unwrap(); + assert!(response.status().is_success()); + }) } #[test] fn test_form() { - let mut srv = TestServer::new(|| { - HttpService::new(App::new().service(web::resource("/").route(web::to( - |_: web::Form>| HttpResponse::Ok(), - )))) - }); + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::new(App::new().service(web::resource("/").route(web::to( + |_: web::Form>| HttpResponse::Ok(), + )))) + }); - let mut data = HashMap::new(); - let _ = data.insert("key".to_string(), "TEST".to_string()); + let mut data = HashMap::new(); + let _ = data.insert("key".to_string(), "TEST".to_string()); - let request = srv.get("/").header("x-test", "111").send_form(&data); - let response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + let request = srv.get("/").header("x-test", "111").send_form(&data); + let response = srv.block_on(request).unwrap(); + assert!(response.status().is_success()); + }) } #[test] fn test_timeout() { - let mut srv = TestServer::new(|| { - HttpService::new(App::new().service(web::resource("/").route(web::to_async( - || { - tokio_timer::sleep(Duration::from_millis(200)) - .then(|_| Ok::<_, Error>(HttpResponse::Ok().body(STR))) - }, - )))) - }); + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::new(App::new().service(web::resource("/").route( + web::to_async(|| { + tokio_timer::sleep(Duration::from_millis(200)) + .then(|_| Ok::<_, Error>(HttpResponse::Ok().body(STR))) + }), + ))) + }); - let client = srv.execute(|| { - awc::Client::build() - .timeout(Duration::from_millis(50)) - .finish() - }); - let request = client.get(srv.url("/")).send(); - match srv.block_on(request) { - Err(SendRequestError::Timeout) => (), - _ => panic!(), - } + let client = srv.execute(|| { + awc::Client::build() + .timeout(Duration::from_millis(50)) + .finish() + }); + let request = client.get(srv.url("/")).send(); + match srv.block_on(request) { + Err(SendRequestError::Timeout) => (), + _ => panic!(), + } + }) } #[test] fn test_timeout_override() { - let mut srv = TestServer::new(|| { - HttpService::new(App::new().service(web::resource("/").route(web::to_async( - || { - tokio_timer::sleep(Duration::from_millis(200)) - .then(|_| Ok::<_, Error>(HttpResponse::Ok().body(STR))) - }, - )))) - }); + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::new(App::new().service(web::resource("/").route( + web::to_async(|| { + tokio_timer::sleep(Duration::from_millis(200)) + .then(|_| Ok::<_, Error>(HttpResponse::Ok().body(STR))) + }), + ))) + }); - let client = awc::Client::build() - .timeout(Duration::from_millis(50000)) - .finish(); - let request = client - .get(srv.url("/")) - .timeout(Duration::from_millis(50)) - .send(); - match srv.block_on(request) { - Err(SendRequestError::Timeout) => (), - _ => panic!(), - } + let client = awc::Client::build() + .timeout(Duration::from_millis(50000)) + .finish(); + let request = client + .get(srv.url("/")) + .timeout(Duration::from_millis(50)) + .send(); + match srv.block_on(request) { + Err(SendRequestError::Timeout) => (), + _ => panic!(), + } + }) } #[test] fn test_connection_reuse() { - let num = Arc::new(AtomicUsize::new(0)); - let num2 = num.clone(); + block_on(async { + let num = Arc::new(AtomicUsize::new(0)); + let num2 = num.clone(); - let mut srv = TestServer::new(move || { - let num2 = num2.clone(); - service_fn(move |io| { - num2.fetch_add(1, Ordering::Relaxed); - Ok(io) - }) - .and_then(HttpService::new( - App::new().service(web::resource("/").route(web::to(|| HttpResponse::Ok()))), - )) - }); + let mut srv = TestServer::start(move || { + let num2 = num2.clone(); + service_fn(move |io| { + num2.fetch_add(1, Ordering::Relaxed); + Ok(io) + }) + .and_then(HttpService::new(App::new().service( + web::resource("/").route(web::to(|| HttpResponse::Ok())), + ))) + }); - let client = awc::Client::default(); + let client = awc::Client::default(); - // req 1 - let request = client.get(srv.url("/")).send(); - let response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + // req 1 + let request = client.get(srv.url("/")).send(); + let response = srv.block_on(request).unwrap(); + assert!(response.status().is_success()); - // req 2 - let req = client.post(srv.url("/")); - let response = srv.block_on_fn(move || req.send()).unwrap(); - assert!(response.status().is_success()); + // req 2 + let req = client.post(srv.url("/")); + let response = srv.block_on_fn(move || req.send()).unwrap(); + assert!(response.status().is_success()); - // one connection - assert_eq!(num.load(Ordering::Relaxed), 1); + // one connection + assert_eq!(num.load(Ordering::Relaxed), 1); + }) } #[test] fn test_connection_force_close() { - let num = Arc::new(AtomicUsize::new(0)); - let num2 = num.clone(); + block_on(async { + let num = Arc::new(AtomicUsize::new(0)); + let num2 = num.clone(); - let mut srv = TestServer::new(move || { - let num2 = num2.clone(); - service_fn(move |io| { - num2.fetch_add(1, Ordering::Relaxed); - Ok(io) - }) - .and_then(HttpService::new( - App::new().service(web::resource("/").route(web::to(|| HttpResponse::Ok()))), - )) - }); + let mut srv = TestServer::new(move || { + let num2 = num2.clone(); + service_fn(move |io| { + num2.fetch_add(1, Ordering::Relaxed); + Ok(io) + }) + .and_then(HttpService::new(App::new().service( + web::resource("/").route(web::to(|| HttpResponse::Ok())), + ))) + }); - let client = awc::Client::default(); + let client = awc::Client::default(); - // req 1 - let request = client.get(srv.url("/")).force_close().send(); - let response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + // req 1 + let request = client.get(srv.url("/")).force_close().send(); + let response = srv.block_on(request).unwrap(); + assert!(response.status().is_success()); - // req 2 - let req = client.post(srv.url("/")).force_close(); - let response = srv.block_on_fn(move || req.send()).unwrap(); - assert!(response.status().is_success()); + // req 2 + let req = client.post(srv.url("/")).force_close(); + let response = srv.block_on_fn(move || req.send()).unwrap(); + assert!(response.status().is_success()); - // two connection - assert_eq!(num.load(Ordering::Relaxed), 2); + // two connection + assert_eq!(num.load(Ordering::Relaxed), 2); + }) } #[test] fn test_connection_server_close() { - let num = Arc::new(AtomicUsize::new(0)); - let num2 = num.clone(); + block_on(async { + let num = Arc::new(AtomicUsize::new(0)); + let num2 = num.clone(); - let mut srv = TestServer::new(move || { - let num2 = num2.clone(); - service_fn(move |io| { - num2.fetch_add(1, Ordering::Relaxed); - Ok(io) - }) - .and_then(HttpService::new( - App::new().service( - web::resource("/") - .route(web::to(|| HttpResponse::Ok().force_close().finish())), - ), - )) - }); + let mut srv = TestServer::new(move || { + let num2 = num2.clone(); + service_fn(move |io| { + num2.fetch_add(1, Ordering::Relaxed); + Ok(io) + }) + .and_then(HttpService::new( + App::new().service( + web::resource("/") + .route(web::to(|| HttpResponse::Ok().force_close().finish())), + ), + )) + }); - let client = awc::Client::default(); + let client = awc::Client::default(); - // req 1 - let request = client.get(srv.url("/")).send(); - let response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + // req 1 + let request = client.get(srv.url("/")).send(); + let response = srv.block_on(request).unwrap(); + assert!(response.status().is_success()); - // req 2 - let req = client.post(srv.url("/")); - let response = srv.block_on_fn(move || req.send()).unwrap(); - assert!(response.status().is_success()); + // req 2 + let req = client.post(srv.url("/")); + let response = srv.block_on_fn(move || req.send()).unwrap(); + assert!(response.status().is_success()); - // two connection - assert_eq!(num.load(Ordering::Relaxed), 2); + // two connection + assert_eq!(num.load(Ordering::Relaxed), 2); + }) } #[test] fn test_connection_wait_queue() { - let num = Arc::new(AtomicUsize::new(0)); - let num2 = num.clone(); + block_on(async { + let num = Arc::new(AtomicUsize::new(0)); + let num2 = num.clone(); - let mut srv = TestServer::new(move || { - let num2 = num2.clone(); - service_fn(move |io| { - num2.fetch_add(1, Ordering::Relaxed); - Ok(io) - }) - .and_then(HttpService::new(App::new().service( - web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))), - ))) - }); + let mut srv = TestServer::new(move || { + let num2 = num2.clone(); + service_fn(move |io| { + num2.fetch_add(1, Ordering::Relaxed); + Ok(io) + }) + .and_then(HttpService::new(App::new().service( + web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))), + ))) + }); - let client = awc::Client::build() - .connector(awc::Connector::new().limit(1).finish()) - .finish(); + let client = awc::Client::build() + .connector(awc::Connector::new().limit(1).finish()) + .finish(); - // req 1 - let request = client.get(srv.url("/")).send(); - let mut response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + // req 1 + let request = client.get(srv.url("/")).send(); + let mut response = srv.block_on(request).unwrap(); + assert!(response.status().is_success()); - // req 2 - let req2 = client.post(srv.url("/")); - let req2_fut = srv.execute(move || { - let mut fut = req2.send(); - assert!(fut.poll().unwrap().is_not_ready()); - fut - }); + // req 2 + let req2 = client.post(srv.url("/")); + let req2_fut = srv.execute(move || { + let mut fut = req2.send(); + assert!(fut.poll().unwrap().is_not_ready()); + fut + }); - // read response 1 - let bytes = srv.block_on(response.body()).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response 1 + let bytes = srv.block_on(response.body()).unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - // req 2 - let response = srv.block_on(req2_fut).unwrap(); - assert!(response.status().is_success()); + // req 2 + let response = srv.block_on(req2_fut).unwrap(); + assert!(response.status().is_success()); - // two connection - assert_eq!(num.load(Ordering::Relaxed), 1); + // two connection + assert_eq!(num.load(Ordering::Relaxed), 1); + }) } #[test] fn test_connection_wait_queue_force_close() { - let num = Arc::new(AtomicUsize::new(0)); - let num2 = num.clone(); + block_on(async { + let num = Arc::new(AtomicUsize::new(0)); + let num2 = num.clone(); - let mut srv = TestServer::new(move || { - let num2 = num2.clone(); - service_fn(move |io| { - num2.fetch_add(1, Ordering::Relaxed); - Ok(io) - }) - .and_then(HttpService::new( - App::new().service( - web::resource("/") - .route(web::to(|| HttpResponse::Ok().force_close().body(STR))), - ), - )) - }); + let mut srv = TestServer::new(move || { + let num2 = num2.clone(); + service_fn(move |io| { + num2.fetch_add(1, Ordering::Relaxed); + Ok(io) + }) + .and_then(HttpService::new( + App::new().service( + web::resource("/") + .route(web::to(|| HttpResponse::Ok().force_close().body(STR))), + ), + )) + }); - let client = awc::Client::build() - .connector(awc::Connector::new().limit(1).finish()) - .finish(); + let client = awc::Client::build() + .connector(awc::Connector::new().limit(1).finish()) + .finish(); - // req 1 - let request = client.get(srv.url("/")).send(); - let mut response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + // req 1 + let request = client.get(srv.url("/")).send(); + let mut response = srv.block_on(request).unwrap(); + assert!(response.status().is_success()); - // req 2 - let req2 = client.post(srv.url("/")); - let req2_fut = srv.execute(move || { - let mut fut = req2.send(); - assert!(fut.poll().unwrap().is_not_ready()); - fut - }); + // req 2 + let req2 = client.post(srv.url("/")); + let req2_fut = srv.execute(move || { + let mut fut = req2.send(); + assert!(fut.poll().unwrap().is_not_ready()); + fut + }); - // read response 1 - let bytes = srv.block_on(response.body()).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response 1 + let bytes = srv.block_on(response.body()).unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - // req 2 - let response = srv.block_on(req2_fut).unwrap(); - assert!(response.status().is_success()); + // req 2 + let response = srv.block_on(req2_fut).unwrap(); + assert!(response.status().is_success()); - // two connection - assert_eq!(num.load(Ordering::Relaxed), 2); + // two connection + assert_eq!(num.load(Ordering::Relaxed), 2); + }) } #[test] fn test_with_query_parameter() { - let mut srv = TestServer::new(|| { - HttpService::new(App::new().service(web::resource("/").to( - |req: HttpRequest| { - if req.query_string().contains("qp") { - HttpResponse::Ok() - } else { - HttpResponse::BadRequest() - } - }, - ))) - }); + block_on(async { + let mut srv = TestServer::new(|| { + HttpService::new(App::new().service(web::resource("/").to( + |req: HttpRequest| { + if req.query_string().contains("qp") { + HttpResponse::Ok() + } else { + HttpResponse::BadRequest() + } + }, + ))) + }); - let res = srv - .block_on(awc::Client::new().get(srv.url("/?qp=5")).send()) - .unwrap(); - assert!(res.status().is_success()); + let res = srv + .block_on(awc::Client::new().get(srv.url("/?qp=5")).send()) + .unwrap(); + assert!(res.status().is_success()); + }) } #[test] fn test_no_decompress() { - let mut srv = TestServer::new(|| { - HttpService::new(App::new().wrap(Compress::default()).service( - web::resource("/").route(web::to(|| { - let mut res = HttpResponse::Ok().body(STR); - res.encoding(header::ContentEncoding::Gzip); - res - })), - )) - }); + block_on(async { + let mut srv = TestServer::new(|| { + HttpService::new(App::new().wrap(Compress::default()).service( + web::resource("/").route(web::to(|| { + let mut res = HttpResponse::Ok().body(STR); + res.encoding(header::ContentEncoding::Gzip); + res + })), + )) + }); - let mut res = srv - .block_on(awc::Client::new().get(srv.url("/")).no_decompress().send()) - .unwrap(); - assert!(res.status().is_success()); + let mut res = srv + .block_on(awc::Client::new().get(srv.url("/")).no_decompress().send()) + .unwrap(); + assert!(res.status().is_success()); - // read response - let bytes = srv.block_on(res.body()).unwrap(); + // read response + let bytes = srv.block_on(res.body()).unwrap(); - let mut e = GzDecoder::new(&bytes[..]); - let mut dec = Vec::new(); - e.read_to_end(&mut dec).unwrap(); - assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); + let mut e = GzDecoder::new(&bytes[..]); + let mut dec = Vec::new(); + e.read_to_end(&mut dec).unwrap(); + assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); - // POST - let mut res = srv - .block_on(awc::Client::new().post(srv.url("/")).no_decompress().send()) - .unwrap(); - assert!(res.status().is_success()); + // POST + let mut res = srv + .block_on(awc::Client::new().post(srv.url("/")).no_decompress().send()) + .unwrap(); + assert!(res.status().is_success()); - let bytes = srv.block_on(res.body()).unwrap(); - let mut e = GzDecoder::new(&bytes[..]); - let mut dec = Vec::new(); - e.read_to_end(&mut dec).unwrap(); - assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); + let bytes = srv.block_on(res.body()).unwrap(); + let mut e = GzDecoder::new(&bytes[..]); + let mut dec = Vec::new(); + e.read_to_end(&mut dec).unwrap(); + assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); + }) } #[test] fn test_client_gzip_encoding() { - let mut srv = TestServer::new(|| { - HttpService::new(App::new().service(web::resource("/").route(web::to(|| { - let mut e = GzEncoder::new(Vec::new(), Compression::default()); - e.write_all(STR.as_ref()).unwrap(); - let data = e.finish().unwrap(); + block_on(async { + let mut srv = TestServer::new(|| { + HttpService::new(App::new().service(web::resource("/").route(web::to( + || { + let mut e = GzEncoder::new(Vec::new(), Compression::default()); + e.write_all(STR.as_ref()).unwrap(); + let data = e.finish().unwrap(); - HttpResponse::Ok() - .header("content-encoding", "gzip") - .body(data) - })))) - }); + HttpResponse::Ok() + .header("content-encoding", "gzip") + .body(data) + }, + )))) + }); - // client request - let mut response = srv.block_on(srv.post("/").send()).unwrap(); - assert!(response.status().is_success()); + // client request + let mut response = srv.block_on(srv.post("/").send()).unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response + let bytes = srv.block_on(response.body()).unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) } #[test] fn test_client_gzip_encoding_large() { - let mut srv = TestServer::new(|| { - HttpService::new(App::new().service(web::resource("/").route(web::to(|| { - let mut e = GzEncoder::new(Vec::new(), Compression::default()); - e.write_all(STR.repeat(10).as_ref()).unwrap(); - let data = e.finish().unwrap(); + block_on(async { + let mut srv = TestServer::new(|| { + HttpService::new(App::new().service(web::resource("/").route(web::to( + || { + let mut e = GzEncoder::new(Vec::new(), Compression::default()); + e.write_all(STR.repeat(10).as_ref()).unwrap(); + let data = e.finish().unwrap(); - HttpResponse::Ok() - .header("content-encoding", "gzip") - .body(data) - })))) - }); + HttpResponse::Ok() + .header("content-encoding", "gzip") + .body(data) + }, + )))) + }); - // client request - let mut response = srv.block_on(srv.post("/").send()).unwrap(); - assert!(response.status().is_success()); + // client request + let mut response = srv.block_on(srv.post("/").send()).unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); - assert_eq!(bytes, Bytes::from(STR.repeat(10))); + // read response + let bytes = srv.block_on(response.body()).unwrap(); + assert_eq!(bytes, Bytes::from(STR.repeat(10))); + }) } #[test] fn test_client_gzip_encoding_large_random() { - let data = rand::thread_rng() - .sample_iter(&rand::distributions::Alphanumeric) - .take(100_000) - .collect::(); + block_on(async { + let data = rand::thread_rng() + .sample_iter(&rand::distributions::Alphanumeric) + .take(100_000) + .collect::(); - let mut srv = TestServer::new(|| { - HttpService::new(App::new().service(web::resource("/").route(web::to( - |data: Bytes| { - let mut e = GzEncoder::new(Vec::new(), Compression::default()); - e.write_all(&data).unwrap(); - let data = e.finish().unwrap(); - HttpResponse::Ok() - .header("content-encoding", "gzip") - .body(data) - }, - )))) - }); + let mut srv = TestServer::new(|| { + HttpService::new(App::new().service(web::resource("/").route(web::to( + |data: Bytes| { + let mut e = GzEncoder::new(Vec::new(), Compression::default()); + e.write_all(&data).unwrap(); + let data = e.finish().unwrap(); + HttpResponse::Ok() + .header("content-encoding", "gzip") + .body(data) + }, + )))) + }); - // client request - let mut response = srv.block_on(srv.post("/").send_body(data.clone())).unwrap(); - assert!(response.status().is_success()); + // client request + let mut response = srv.block_on(srv.post("/").send_body(data.clone())).unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); - assert_eq!(bytes, Bytes::from(data)); + // read response + let bytes = srv.block_on(response.body()).unwrap(); + assert_eq!(bytes, Bytes::from(data)); + }) } #[test] fn test_client_brotli_encoding() { - let mut srv = TestServer::new(|| { - HttpService::new(App::new().service(web::resource("/").route(web::to( - |data: Bytes| { - let mut e = BrotliEncoder::new(Vec::new(), 5); - e.write_all(&data).unwrap(); - let data = e.finish().unwrap(); - HttpResponse::Ok() - .header("content-encoding", "br") - .body(data) - }, - )))) - }); + block_on(async { + let mut srv = TestServer::new(|| { + HttpService::new(App::new().service(web::resource("/").route(web::to( + |data: Bytes| { + let mut e = BrotliEncoder::new(Vec::new(), 5); + e.write_all(&data).unwrap(); + let data = e.finish().unwrap(); + HttpResponse::Ok() + .header("content-encoding", "br") + .body(data) + }, + )))) + }); - // client request - let mut response = srv.block_on(srv.post("/").send_body(STR)).unwrap(); - assert!(response.status().is_success()); + // client request + let mut response = srv.block_on(srv.post("/").send_body(STR)).unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response + let bytes = srv.block_on(response.body()).unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) } // #[test] @@ -644,65 +682,67 @@ fn test_client_brotli_encoding() { #[test] fn test_client_cookie_handling() { - fn err() -> Error { - use std::io::{Error as IoError, ErrorKind}; - // stub some generic error - Error::from(IoError::from(ErrorKind::NotFound)) - } - let cookie1 = Cookie::build("cookie1", "value1").finish(); - let cookie2 = Cookie::build("cookie2", "value2") - .domain("www.example.org") - .path("/") - .secure(true) - .http_only(true) - .finish(); - // Q: are all these clones really necessary? A: Yes, possibly - let cookie1b = cookie1.clone(); - let cookie2b = cookie2.clone(); + block_on(async { + fn err() -> Error { + use std::io::{Error as IoError, ErrorKind}; + // stub some generic error + Error::from(IoError::from(ErrorKind::NotFound)) + } + let cookie1 = Cookie::build("cookie1", "value1").finish(); + let cookie2 = Cookie::build("cookie2", "value2") + .domain("www.example.org") + .path("/") + .secure(true) + .http_only(true) + .finish(); + // Q: are all these clones really necessary? A: Yes, possibly + let cookie1b = cookie1.clone(); + let cookie2b = cookie2.clone(); - let mut srv = TestServer::new(move || { - let cookie1 = cookie1b.clone(); - let cookie2 = cookie2b.clone(); + let mut srv = TestServer::new(move || { + let cookie1 = cookie1b.clone(); + let cookie2 = cookie2b.clone(); - HttpService::new(App::new().route( - "/", - web::to(move |req: HttpRequest| { - // Check cookies were sent correctly - req.cookie("cookie1") - .ok_or_else(err) - .and_then(|c1| { - if c1.value() == "value1" { - Ok(()) - } else { - Err(err()) - } - }) - .and_then(|()| req.cookie("cookie2").ok_or_else(err)) - .and_then(|c2| { - if c2.value() == "value2" { - Ok(()) - } else { - Err(err()) - } - }) - // Send some cookies back - .map(|_| { - HttpResponse::Ok() - .cookie(cookie1.clone()) - .cookie(cookie2.clone()) - .finish() - }) - }), - )) - }); + HttpService::new(App::new().route( + "/", + web::to(move |req: HttpRequest| { + // Check cookies were sent correctly + req.cookie("cookie1") + .ok_or_else(err) + .and_then(|c1| { + if c1.value() == "value1" { + Ok(()) + } else { + Err(err()) + } + }) + .and_then(|()| req.cookie("cookie2").ok_or_else(err)) + .and_then(|c2| { + if c2.value() == "value2" { + Ok(()) + } else { + Err(err()) + } + }) + // Send some cookies back + .map(|_| { + HttpResponse::Ok() + .cookie(cookie1.clone()) + .cookie(cookie2.clone()) + .finish() + }) + }), + )) + }); - let request = srv.get("/").cookie(cookie1.clone()).cookie(cookie2.clone()); - let response = srv.block_on(request.send()).unwrap(); - assert!(response.status().is_success()); - let c1 = response.cookie("cookie1").expect("Missing cookie1"); - assert_eq!(c1, cookie1); - let c2 = response.cookie("cookie2").expect("Missing cookie2"); - assert_eq!(c2, cookie2); + let request = srv.get("/").cookie(cookie1.clone()).cookie(cookie2.clone()); + let response = srv.block_on(request.send()).unwrap(); + assert!(response.status().is_success()); + let c1 = response.cookie("cookie1").expect("Missing cookie1"); + assert_eq!(c1, cookie1); + let c2 = response.cookie("cookie2").expect("Missing cookie2"); + assert_eq!(c2, cookie2); + }) } // #[test] @@ -737,56 +777,60 @@ fn test_client_cookie_handling() { #[test] fn client_basic_auth() { - let mut srv = TestServer::new(|| { - HttpService::new(App::new().route( - "/", - web::to(|req: HttpRequest| { - if req - .headers() - .get(header::AUTHORIZATION) - .unwrap() - .to_str() - .unwrap() - == "Basic dXNlcm5hbWU6cGFzc3dvcmQ=" - { - HttpResponse::Ok() - } else { - HttpResponse::BadRequest() - } - }), - )) - }); + block_on(async { + let mut srv = TestServer::new(|| { + HttpService::new(App::new().route( + "/", + web::to(|req: HttpRequest| { + if req + .headers() + .get(header::AUTHORIZATION) + .unwrap() + .to_str() + .unwrap() + == "Basic dXNlcm5hbWU6cGFzc3dvcmQ=" + { + HttpResponse::Ok() + } else { + HttpResponse::BadRequest() + } + }), + )) + }); - // set authorization header to Basic - let request = srv.get("/").basic_auth("username", Some("password")); - let response = srv.block_on(request.send()).unwrap(); - assert!(response.status().is_success()); + // set authorization header to Basic + let request = srv.get("/").basic_auth("username", Some("password")); + let response = srv.block_on(request.send()).unwrap(); + assert!(response.status().is_success()); + }) } #[test] fn client_bearer_auth() { - let mut srv = TestServer::new(|| { - HttpService::new(App::new().route( - "/", - web::to(|req: HttpRequest| { - if req - .headers() - .get(header::AUTHORIZATION) - .unwrap() - .to_str() - .unwrap() - == "Bearer someS3cr3tAutht0k3n" - { - HttpResponse::Ok() - } else { - HttpResponse::BadRequest() - } - }), - )) - }); + block_on(async { + let mut srv = TestServer::new(|| { + HttpService::new(App::new().route( + "/", + web::to(|req: HttpRequest| { + if req + .headers() + .get(header::AUTHORIZATION) + .unwrap() + .to_str() + .unwrap() + == "Bearer someS3cr3tAutht0k3n" + { + HttpResponse::Ok() + } else { + HttpResponse::BadRequest() + } + }), + )) + }); - // set authorization header to Bearer - let request = srv.get("/").bearer_auth("someS3cr3tAutht0k3n"); - let response = srv.block_on(request.send()).unwrap(); - assert!(response.status().is_success()); + // set authorization header to Bearer + let request = srv.get("/").bearer_auth("someS3cr3tAutht0k3n"); + let response = srv.block_on(request.send()).unwrap(); + assert!(response.status().is_success()); + }) } diff --git a/examples/basic.rs b/examples/basic.rs index 46440d706..76c977322 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -1,5 +1,3 @@ -use futures::IntoFuture; - use actix_web::{ get, middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer, }; @@ -10,7 +8,7 @@ fn index(req: HttpRequest, name: web::Path) -> String { format!("Hello: {}!\r\n", name) } -fn index_async(req: HttpRequest) -> impl IntoFuture { +async fn index_async(req: HttpRequest) -> Result<&'static str, Error> { println!("REQ: {:?}", req); Ok("Hello world!\r\n") } @@ -28,7 +26,7 @@ fn main() -> std::io::Result<()> { App::new() .wrap(middleware::DefaultHeaders::new().header("X-Version", "0.2")) .wrap(middleware::Compress::default()) - .wrap(middleware::Logger::default()) + // .wrap(middleware::Logger::default()) .service(index) .service(no_params) .service( diff --git a/examples/client.rs b/examples/client.rs index 8a75fd306..90a362fe3 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -1,26 +1,27 @@ use actix_http::Error; use actix_rt::System; -use futures::{future::lazy, Future}; fn main() -> Result<(), Error> { std::env::set_var("RUST_LOG", "actix_http=trace"); env_logger::init(); - System::new("test").block_on(lazy(|| { - awc::Client::new() - .get("https://www.rust-lang.org/") // <- Create request builder - .header("User-Agent", "Actix-web") - .send() // <- Send http request - .from_err() - .and_then(|mut response| { - // <- server http response - println!("Response: {:?}", response); + System::new("test").block_on(async { + let client = awc::Client::new(); - // read response body - response - .body() - .from_err() - .map(|body| println!("Downloaded: {:?} bytes", body.len())) - }) - })) + // Create request builder, configure request and send + let mut response = client + .get("https://www.rust-lang.org/") + .header("User-Agent", "Actix-web") + .send() + .await?; + + // server http response + println!("Response: {:?}", response); + + // read response body + let body = response.body().await?; + println!("Downloaded: {:?} bytes", body.len()); + + Ok(()) + }) } diff --git a/examples/uds.rs b/examples/uds.rs index 9dc82903f..7da41a2c5 100644 --- a/examples/uds.rs +++ b/examples/uds.rs @@ -1,5 +1,3 @@ -use futures::IntoFuture; - use actix_web::{ get, middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer, }; @@ -10,7 +8,7 @@ fn index(req: HttpRequest, name: web::Path) -> String { format!("Hello: {}!\r\n", name) } -fn index_async(req: HttpRequest) -> impl IntoFuture { +async fn index_async(req: HttpRequest) -> Result<&'static str, Error> { println!("REQ: {:?}", req); Ok("Hello world!\r\n") } @@ -29,7 +27,7 @@ fn main() -> std::io::Result<()> { App::new() .wrap(middleware::DefaultHeaders::new().header("X-Version", "0.2")) .wrap(middleware::Compress::default()) - .wrap(middleware::Logger::default()) + // .wrap(middleware::Logger::default()) .service(index) .service(no_params) .service( @@ -38,7 +36,7 @@ fn main() -> std::io::Result<()> { middleware::DefaultHeaders::new().header("X-Version-R2", "0.3"), ) .default_service( - web::route().to(|| HttpResponse::MethodNotAllowed()), + web::route().to(|| ok(HttpResponse::MethodNotAllowed())), ) .route(web::get().to_async(index_async)), ) diff --git a/src/app.rs b/src/app.rs index f93859c7e..288256604 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,14 +1,17 @@ use std::cell::RefCell; use std::fmt; +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; use std::rc::Rc; +use std::task::{Context, Poll}; use actix_http::body::{Body, MessageBody}; use actix_service::boxed::{self, BoxedNewService}; use actix_service::{ - apply_transform, IntoNewService, IntoTransform, NewService, Transform, + apply, apply_fn_factory, IntoServiceFactory, ServiceFactory, Transform, }; -use futures::{Future, IntoFuture}; +use futures::future::{FutureExt, LocalBoxFuture}; use crate::app_service::{AppEntry, AppInit, AppRoutingFactory}; use crate::config::{AppConfig, AppConfigInner, ServiceConfig}; @@ -18,19 +21,19 @@ use crate::error::Error; use crate::resource::Resource; use crate::route::Route; use crate::service::{ - HttpServiceFactory, ServiceFactory, ServiceFactoryWrapper, ServiceRequest, + AppServiceFactory, HttpServiceFactory, ServiceFactoryWrapper, ServiceRequest, ServiceResponse, }; type HttpNewService = BoxedNewService<(), ServiceRequest, ServiceResponse, Error, ()>; type FnDataFactory = - Box Box, Error = ()>>>; + Box LocalBoxFuture<'static, Result, ()>>>; /// Application builder - structure that follows the builder pattern /// for building application instances. pub struct App { endpoint: T, - services: Vec>, + services: Vec>, default: Option>, factory_ref: Rc>>, data: Vec>, @@ -61,7 +64,7 @@ impl App { impl App where B: MessageBody, - T: NewService< + T: ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -107,24 +110,30 @@ where /// Set application data factory. This function is /// similar to `.data()` but it accepts data factory. Data object get /// constructed asynchronously during application initialization. - pub fn data_factory(mut self, data: F) -> Self + pub fn data_factory(mut self, data: F) -> Self where F: Fn() -> Out + 'static, - Out: IntoFuture + 'static, - Out::Error: std::fmt::Debug, + Out: Future> + 'static, + D: 'static, + E: std::fmt::Debug, { self.data_factories.push(Box::new(move || { - Box::new( - data() - .into_future() - .map_err(|e| { - log::error!("Can not construct data instance: {:?}", e); - }) - .map(|data| { - let data: Box = Box::new(Data::new(data)); - data - }), - ) + { + let fut = data(); + async move { + match fut.await { + Err(e) => { + log::error!("Can not construct data instance: {:?}", e); + Err(()) + } + Ok(data) => { + let data: Box = Box::new(Data::new(data)); + Ok(data) + } + } + } + } + .boxed_local() })); self } @@ -267,8 +276,8 @@ where /// ``` pub fn default_service(mut self, f: F) -> Self where - F: IntoNewService, - U: NewService< + F: IntoServiceFactory, + U: ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -277,11 +286,9 @@ where U::InitError: fmt::Debug, { // create and configure default resource - self.default = Some(Rc::new(boxed::new_service( - f.into_new_service().map_init_err(|e| { - log::error!("Can not construct default service: {:?}", e) - }), - ))); + self.default = Some(Rc::new(boxed::factory(f.into_factory().map_init_err( + |e| log::error!("Can not construct default service: {:?}", e), + )))); self } @@ -350,11 +357,11 @@ where /// .route("/index.html", web::get().to(index)); /// } /// ``` - pub fn wrap( + pub fn wrap( self, - mw: F, + mw: M, ) -> App< - impl NewService< + impl ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -372,11 +379,9 @@ where InitError = (), >, B1: MessageBody, - F: IntoTransform, { - let endpoint = apply_transform(mw, self.endpoint); App { - endpoint, + endpoint: apply(mw, self.endpoint), data: self.data, data_factories: self.data_factories, services: self.services, @@ -407,13 +412,16 @@ where /// /// fn main() { /// let app = App::new() - /// .wrap_fn(|req, srv| - /// srv.call(req).map(|mut res| { + /// .wrap_fn(|req, srv| { + /// let fut = srv.call(req); + /// async { + /// let mut res = fut.await?; /// res.headers_mut().insert( /// CONTENT_TYPE, HeaderValue::from_static("text/plain"), /// ); - /// res - /// })) + /// Ok(res) + /// } + /// }) /// .route("/index.html", web::get().to(index)); /// } /// ``` @@ -421,7 +429,7 @@ where self, mw: F, ) -> App< - impl NewService< + impl ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -433,16 +441,26 @@ where where B1: MessageBody, F: FnMut(ServiceRequest, &mut T::Service) -> R + Clone, - R: IntoFuture, Error = Error>, + R: Future, Error>>, { - self.wrap(mw) + App { + endpoint: apply_fn_factory(self.endpoint, mw), + data: self.data, + data_factories: self.data_factories, + services: self.services, + default: self.default, + factory_ref: self.factory_ref, + config: self.config, + external: self.external, + _t: PhantomData, + } } } -impl IntoNewService> for App +impl IntoServiceFactory> for App where B: MessageBody, - T: NewService< + T: ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -450,7 +468,7 @@ where InitError = (), >, { - fn into_new_service(self) -> AppInit { + fn into_factory(self) -> AppInit { AppInit { data: Rc::new(self.data), data_factories: Rc::new(self.data_factories), @@ -468,82 +486,89 @@ where mod tests { use actix_service::Service; use bytes::Bytes; - use futures::{Future, IntoFuture}; + use futures::future::{ok, Future}; use super::*; use crate::http::{header, HeaderValue, Method, StatusCode}; + use crate::middleware::DefaultHeaders; use crate::service::{ServiceRequest, ServiceResponse}; - use crate::test::{ - block_fn, block_on, call_service, init_service, read_body, TestRequest, - }; + use crate::test::{block_on, call_service, init_service, read_body, TestRequest}; use crate::{web, Error, HttpRequest, HttpResponse}; #[test] fn test_default_resource() { - let mut srv = init_service( - App::new().service(web::resource("/test").to(|| HttpResponse::Ok())), - ); - let req = TestRequest::with_uri("/test").to_request(); - let resp = block_fn(|| srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + block_on(async { + let mut srv = init_service( + App::new().service(web::resource("/test").to(|| HttpResponse::Ok())), + ) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); - let req = TestRequest::with_uri("/blah").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); + let req = TestRequest::with_uri("/blah").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); - let mut srv = init_service( - App::new() - .service(web::resource("/test").to(|| HttpResponse::Ok())) - .service( - web::resource("/test2") - .default_service(|r: ServiceRequest| { - r.into_response(HttpResponse::Created()) - }) - .route(web::get().to(|| HttpResponse::Ok())), - ) - .default_service(|r: ServiceRequest| { - r.into_response(HttpResponse::MethodNotAllowed()) - }), - ); + let mut srv = init_service( + App::new() + .service(web::resource("/test").to(|| HttpResponse::Ok())) + .service( + web::resource("/test2") + .default_service(|r: ServiceRequest| { + ok(r.into_response(HttpResponse::Created())) + }) + .route(web::get().to(|| HttpResponse::Ok())), + ) + .default_service(|r: ServiceRequest| { + ok(r.into_response(HttpResponse::MethodNotAllowed())) + }), + ) + .await; - let req = TestRequest::with_uri("/blah").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); + let req = TestRequest::with_uri("/blah").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); - let req = TestRequest::with_uri("/test2").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/test2").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); - let req = TestRequest::with_uri("/test2") - .method(Method::POST) - .to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::CREATED); + let req = TestRequest::with_uri("/test2") + .method(Method::POST) + .to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::CREATED); + }) } #[test] fn test_data_factory() { - let mut srv = - init_service(App::new().data_factory(|| Ok::<_, ()>(10usize)).service( - web::resource("/").to(|_: web::Data| HttpResponse::Ok()), - )); - let req = TestRequest::default().to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + block_on(async { + let mut srv = + init_service(App::new().data_factory(|| ok::<_, ()>(10usize)).service( + web::resource("/").to(|_: web::Data| HttpResponse::Ok()), + )) + .await; + let req = TestRequest::default().to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); - let mut srv = - init_service(App::new().data_factory(|| Ok::<_, ()>(10u32)).service( - web::resource("/").to(|_: web::Data| HttpResponse::Ok()), - )); - let req = TestRequest::default().to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); + let mut srv = + init_service(App::new().data_factory(|| ok::<_, ()>(10u32)).service( + web::resource("/").to(|_: web::Data| HttpResponse::Ok()), + )) + .await; + let req = TestRequest::default().to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); + }) } fn md( req: ServiceRequest, srv: &mut S, - ) -> impl IntoFuture, Error = Error> + ) -> impl Future, Error>> where S: Service< Request = ServiceRequest, @@ -551,112 +576,141 @@ mod tests { Error = Error, >, { - srv.call(req).map(|mut res| { + let fut = srv.call(req); + async move { + let mut res = fut.await?; res.headers_mut() .insert(header::CONTENT_TYPE, HeaderValue::from_static("0001")); - res - }) + Ok(res) + } } #[test] fn test_wrap() { - let mut srv = init_service( - App::new() - .wrap(md) - .route("/test", web::get().to(|| HttpResponse::Ok())), - ); - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - HeaderValue::from_static("0001") - ); + block_on(async { + let mut srv = + init_service( + App::new() + .wrap(DefaultHeaders::new().header( + header::CONTENT_TYPE, + HeaderValue::from_static("0001"), + )) + .route("/test", web::get().to(|| HttpResponse::Ok())), + ) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + HeaderValue::from_static("0001") + ); + }) } #[test] fn test_router_wrap() { - let mut srv = init_service( - App::new() - .route("/test", web::get().to(|| HttpResponse::Ok())) - .wrap(md), - ); - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - HeaderValue::from_static("0001") - ); + block_on(async { + let mut srv = + init_service( + App::new() + .route("/test", web::get().to(|| HttpResponse::Ok())) + .wrap(DefaultHeaders::new().header( + header::CONTENT_TYPE, + HeaderValue::from_static("0001"), + )), + ) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + HeaderValue::from_static("0001") + ); + }) } #[test] fn test_wrap_fn() { - let mut srv = init_service( - App::new() - .wrap_fn(|req, srv| { - srv.call(req).map(|mut res| { - res.headers_mut().insert( - header::CONTENT_TYPE, - HeaderValue::from_static("0001"), - ); - res + block_on(async { + let mut srv = init_service( + App::new() + .wrap_fn(|req, srv| { + let fut = srv.call(req); + async move { + let mut res = fut.await?; + res.headers_mut().insert( + header::CONTENT_TYPE, + HeaderValue::from_static("0001"), + ); + Ok(res) + } }) - }) - .service(web::resource("/test").to(|| HttpResponse::Ok())), - ); - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - HeaderValue::from_static("0001") - ); + .service(web::resource("/test").to(|| HttpResponse::Ok())), + ) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + HeaderValue::from_static("0001") + ); + }) } #[test] fn test_router_wrap_fn() { - let mut srv = init_service( - App::new() - .route("/test", web::get().to(|| HttpResponse::Ok())) - .wrap_fn(|req, srv| { - srv.call(req).map(|mut res| { - res.headers_mut().insert( - header::CONTENT_TYPE, - HeaderValue::from_static("0001"), - ); - res - }) - }), - ); - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - HeaderValue::from_static("0001") - ); + block_on(async { + let mut srv = init_service( + App::new() + .route("/test", web::get().to(|| HttpResponse::Ok())) + .wrap_fn(|req, srv| { + let fut = srv.call(req); + async { + let mut res = fut.await?; + res.headers_mut().insert( + header::CONTENT_TYPE, + HeaderValue::from_static("0001"), + ); + Ok(res) + } + }), + ) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + HeaderValue::from_static("0001") + ); + }) } #[test] fn test_external_resource() { - let mut srv = init_service( - App::new() - .external_resource("youtube", "https://youtube.com/watch/{video_id}") - .route( - "/test", - web::get().to(|req: HttpRequest| { - HttpResponse::Ok().body(format!( - "{}", - req.url_for("youtube", &["12345"]).unwrap() - )) - }), - ), - ); - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); - let body = read_body(resp); - assert_eq!(body, Bytes::from_static(b"https://youtube.com/watch/12345")); + block_on(async { + let mut srv = init_service( + App::new() + .external_resource("youtube", "https://youtube.com/watch/{video_id}") + .route( + "/test", + web::get().to(|req: HttpRequest| { + HttpResponse::Ok().body(format!( + "{}", + req.url_for("youtube", &["12345"]).unwrap() + )) + }), + ), + ) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + let body = read_body(resp).await; + assert_eq!(body, Bytes::from_static(b"https://youtube.com/watch/12345")); + }) } } diff --git a/src/app_service.rs b/src/app_service.rs index 513b4aa4b..7407ee2fb 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -1,14 +1,16 @@ use std::cell::RefCell; +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; use std::rc::Rc; +use std::task::{Context, Poll}; use actix_http::{Extensions, Request, Response}; use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url}; use actix_server_config::ServerConfig; use actix_service::boxed::{self, BoxedNewService, BoxedService}; -use actix_service::{service_fn, NewService, Service}; -use futures::future::{ok, Either, FutureResult}; -use futures::{Async, Future, Poll}; +use actix_service::{service_fn, Service, ServiceFactory}; +use futures::future::{ok, Either, FutureExt, LocalBoxFuture, Ready}; use crate::config::{AppConfig, AppService}; use crate::data::DataFactory; @@ -16,23 +18,20 @@ use crate::error::Error; use crate::guard::Guard; use crate::request::{HttpRequest, HttpRequestPool}; use crate::rmap::ResourceMap; -use crate::service::{ServiceFactory, ServiceRequest, ServiceResponse}; +use crate::service::{AppServiceFactory, ServiceRequest, ServiceResponse}; type Guards = Vec>; type HttpService = BoxedService; type HttpNewService = BoxedNewService<(), ServiceRequest, ServiceResponse, Error, ()>; -type BoxedResponse = Either< - FutureResult, - Box>, ->; +type BoxedResponse = LocalBoxFuture<'static, Result>; type FnDataFactory = - Box Box, Error = ()>>>; + Box LocalBoxFuture<'static, Result, ()>>>; /// Service factory to convert `Request` to a `ServiceRequest`. /// It also executes data factories. pub struct AppInit where - T: NewService< + T: ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -44,15 +43,15 @@ where pub(crate) data: Rc>>, pub(crate) data_factories: Rc>, pub(crate) config: RefCell, - pub(crate) services: Rc>>>, + pub(crate) services: Rc>>>, pub(crate) default: Option>, pub(crate) factory_ref: Rc>>, pub(crate) external: RefCell>, } -impl NewService for AppInit +impl ServiceFactory for AppInit where - T: NewService< + T: ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -71,8 +70,8 @@ where fn new_service(&self, cfg: &ServerConfig) -> Self::Future { // update resource default service let default = self.default.clone().unwrap_or_else(|| { - Rc::new(boxed::new_service(service_fn(|req: ServiceRequest| { - Ok(req.into_response(Response::NotFound().finish())) + Rc::new(boxed::factory(service_fn(|req: ServiceRequest| { + ok(req.into_response(Response::NotFound().finish())) }))) }); @@ -135,23 +134,25 @@ where } } +#[pin_project::pin_project] pub struct AppInitResult where - T: NewService, + T: ServiceFactory, { endpoint: Option, + #[pin] endpoint_fut: T::Future, rmap: Rc, config: AppConfig, data: Rc>>, data_factories: Vec>, - data_factories_fut: Vec, Error = ()>>>, + data_factories_fut: Vec, ()>>>, _t: PhantomData, } impl Future for AppInitResult where - T: NewService< + T: ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -159,48 +160,49 @@ where InitError = (), >, { - type Item = AppInitService; - type Error = (); + type Output = Result, ()>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.project(); - fn poll(&mut self) -> Poll { // async data factories let mut idx = 0; - while idx < self.data_factories_fut.len() { - match self.data_factories_fut[idx].poll()? { - Async::Ready(f) => { - self.data_factories.push(f); - let _ = self.data_factories_fut.remove(idx); + while idx < this.data_factories_fut.len() { + match Pin::new(&mut this.data_factories_fut[idx]).poll(cx)? { + Poll::Ready(f) => { + this.data_factories.push(f); + let _ = this.data_factories_fut.remove(idx); } - Async::NotReady => idx += 1, + Poll::Pending => idx += 1, } } - if self.endpoint.is_none() { - if let Async::Ready(srv) = self.endpoint_fut.poll()? { - self.endpoint = Some(srv); + if this.endpoint.is_none() { + if let Poll::Ready(srv) = this.endpoint_fut.poll(cx)? { + *this.endpoint = Some(srv); } } - if self.endpoint.is_some() && self.data_factories_fut.is_empty() { + if this.endpoint.is_some() && this.data_factories_fut.is_empty() { // create app data container let mut data = Extensions::new(); - for f in self.data.iter() { + for f in this.data.iter() { f.create(&mut data); } - for f in &self.data_factories { + for f in this.data_factories.iter() { f.create(&mut data); } - Ok(Async::Ready(AppInitService { - service: self.endpoint.take().unwrap(), - rmap: self.rmap.clone(), - config: self.config.clone(), + Poll::Ready(Ok(AppInitService { + service: this.endpoint.take().unwrap(), + rmap: this.rmap.clone(), + config: this.config.clone(), data: Rc::new(data), pool: HttpRequestPool::create(), })) } else { - Ok(Async::NotReady) + Poll::Pending } } } @@ -226,8 +228,8 @@ where type Error = T::Error; type Future = T::Future; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.service.poll_ready() + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.service.poll_ready(cx) } fn call(&mut self, req: Request) -> Self::Future { @@ -270,7 +272,7 @@ pub struct AppRoutingFactory { default: Rc, } -impl NewService for AppRoutingFactory { +impl ServiceFactory for AppRoutingFactory { type Config = (); type Request = ServiceRequest; type Response = ServiceResponse; @@ -288,7 +290,7 @@ impl NewService for AppRoutingFactory { CreateAppRoutingItem::Future( Some(path.clone()), guards.borrow_mut().take(), - service.new_service(&()), + service.new_service(&()).boxed_local(), ) }) .collect(), @@ -298,14 +300,14 @@ impl NewService for AppRoutingFactory { } } -type HttpServiceFut = Box>; +type HttpServiceFut = LocalBoxFuture<'static, Result>; /// Create app service #[doc(hidden)] pub struct AppRoutingFactoryResponse { fut: Vec, default: Option, - default_fut: Option>>, + default_fut: Option>>, } enum CreateAppRoutingItem { @@ -314,16 +316,15 @@ enum CreateAppRoutingItem { } impl Future for AppRoutingFactoryResponse { - type Item = AppRouting; - type Error = (); + type Output = Result; - fn poll(&mut self) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { let mut done = true; if let Some(ref mut fut) = self.default_fut { - match fut.poll()? { - Async::Ready(default) => self.default = Some(default), - Async::NotReady => done = false, + match Pin::new(fut).poll(cx)? { + Poll::Ready(default) => self.default = Some(default), + Poll::Pending => done = false, } } @@ -334,11 +335,12 @@ impl Future for AppRoutingFactoryResponse { ref mut path, ref mut guards, ref mut fut, - ) => match fut.poll()? { - Async::Ready(service) => { + ) => match Pin::new(fut).poll(cx) { + Poll::Ready(Ok(service)) => { Some((path.take().unwrap(), guards.take(), service)) } - Async::NotReady => { + Poll::Ready(Err(_)) => return Poll::Ready(Err(())), + Poll::Pending => { done = false; None } @@ -364,13 +366,13 @@ impl Future for AppRoutingFactoryResponse { } router }); - Ok(Async::Ready(AppRouting { + Poll::Ready(Ok(AppRouting { ready: None, router: router.finish(), default: self.default.take(), })) } else { - Ok(Async::NotReady) + Poll::Pending } } } @@ -387,11 +389,11 @@ impl Service for AppRouting { type Error = Error; type Future = BoxedResponse; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { + fn poll_ready(&mut self, _: &mut Context) -> Poll> { if self.ready.is_none() { - Ok(Async::Ready(())) + Poll::Ready(Ok(())) } else { - Ok(Async::NotReady) + Poll::Pending } } @@ -413,7 +415,7 @@ impl Service for AppRouting { default.call(req) } else { let req = req.into_parts().0; - Either::A(ok(ServiceResponse::new(req, Response::NotFound().finish()))) + ok(ServiceResponse::new(req, Response::NotFound().finish())).boxed_local() } } } @@ -429,7 +431,7 @@ impl AppEntry { } } -impl NewService for AppEntry { +impl ServiceFactory for AppEntry { type Config = (); type Request = ServiceRequest; type Response = ServiceResponse; @@ -464,15 +466,16 @@ mod tests { #[test] fn drop_data() { let data = Arc::new(AtomicBool::new(false)); - { + test::block_on(async { let mut app = test::init_service( App::new() .data(DropData(data.clone())) .service(web::resource("/test").to(|| HttpResponse::Ok())), - ); + ) + .await; let req = test::TestRequest::with_uri("/test").to_request(); - let _ = test::block_on(app.call(req)).unwrap(); - } + let _ = app.call(req).await.unwrap(); + }); assert!(data.load(Ordering::Relaxed)); } } diff --git a/src/config.rs b/src/config.rs index 63fd31d27..3ce18f98b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use actix_http::Extensions; use actix_router::ResourceDef; -use actix_service::{boxed, IntoNewService, NewService}; +use actix_service::{boxed, IntoServiceFactory, ServiceFactory}; use crate::data::{Data, DataFactory}; use crate::error::Error; @@ -12,7 +12,7 @@ use crate::resource::Resource; use crate::rmap::ResourceMap; use crate::route::Route; use crate::service::{ - HttpServiceFactory, ServiceFactory, ServiceFactoryWrapper, ServiceRequest, + AppServiceFactory, HttpServiceFactory, ServiceFactoryWrapper, ServiceRequest, ServiceResponse, }; @@ -102,11 +102,11 @@ impl AppService { &mut self, rdef: ResourceDef, guards: Option>>, - service: F, + factory: F, nested: Option>, ) where - F: IntoNewService, - S: NewService< + F: IntoServiceFactory, + S: ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -116,7 +116,7 @@ impl AppService { { self.services.push(( rdef, - boxed::new_service(service.into_new_service()), + boxed::factory(factory.into_factory()), guards, nested, )); @@ -174,7 +174,7 @@ impl Default for AppConfigInner { /// to set of external methods. This could help with /// modularization of big application configuration. pub struct ServiceConfig { - pub(crate) services: Vec>, + pub(crate) services: Vec>, pub(crate) data: Vec>, pub(crate) external: Vec, } @@ -251,17 +251,19 @@ mod tests { #[test] fn test_data() { - let cfg = |cfg: &mut ServiceConfig| { - cfg.data(10usize); - }; + block_on(async { + let cfg = |cfg: &mut ServiceConfig| { + cfg.data(10usize); + }; - let mut srv = - init_service(App::new().configure(cfg).service( + let mut srv = init_service(App::new().configure(cfg).service( web::resource("/").to(|_: web::Data| HttpResponse::Ok()), - )); - let req = TestRequest::default().to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + )) + .await; + let req = TestRequest::default().to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + }) } // #[test] @@ -298,50 +300,57 @@ mod tests { #[test] fn test_external_resource() { - let mut srv = init_service( - App::new() - .configure(|cfg| { - cfg.external_resource( - "youtube", - "https://youtube.com/watch/{video_id}", - ); - }) - .route( - "/test", - web::get().to(|req: HttpRequest| { - HttpResponse::Ok().body(format!( - "{}", - req.url_for("youtube", &["12345"]).unwrap() - )) - }), - ), - ); - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); - let body = read_body(resp); - assert_eq!(body, Bytes::from_static(b"https://youtube.com/watch/12345")); + block_on(async { + let mut srv = init_service( + App::new() + .configure(|cfg| { + cfg.external_resource( + "youtube", + "https://youtube.com/watch/{video_id}", + ); + }) + .route( + "/test", + web::get().to(|req: HttpRequest| { + HttpResponse::Ok().body(format!( + "{}", + req.url_for("youtube", &["12345"]).unwrap() + )) + }), + ), + ) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + let body = read_body(resp).await; + assert_eq!(body, Bytes::from_static(b"https://youtube.com/watch/12345")); + }) } #[test] fn test_service() { - let mut srv = init_service(App::new().configure(|cfg| { - cfg.service( - web::resource("/test").route(web::get().to(|| HttpResponse::Created())), - ) - .route("/index.html", web::get().to(|| HttpResponse::Ok())); - })); + block_on(async { + let mut srv = init_service(App::new().configure(|cfg| { + cfg.service( + web::resource("/test") + .route(web::get().to(|| HttpResponse::Created())), + ) + .route("/index.html", web::get().to(|| HttpResponse::Ok())); + })) + .await; - let req = TestRequest::with_uri("/test") - .method(Method::GET) - .to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::CREATED); + let req = TestRequest::with_uri("/test") + .method(Method::GET) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::CREATED); - let req = TestRequest::with_uri("/index.html") - .method(Method::GET) - .to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/index.html") + .method(Method::GET) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + }) } } diff --git a/src/data.rs b/src/data.rs index 14e293bc2..a11175c12 100644 --- a/src/data.rs +++ b/src/data.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use actix_http::error::{Error, ErrorInternalServerError}; use actix_http::Extensions; +use futures::future::{err, ok, Ready}; use crate::dev::Payload; use crate::extract::FromRequest; @@ -101,19 +102,19 @@ impl Clone for Data { impl FromRequest for Data { type Config = (); type Error = Error; - type Future = Result; + type Future = Ready>; #[inline] fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { if let Some(st) = req.get_app_data::() { - Ok(st) + ok(st) } else { log::debug!( "Failed to construct App-level Data extractor. \ Request path: {:?}", req.path() ); - Err(ErrorInternalServerError( + err(ErrorInternalServerError( "App data is not configured, to configure use App::data()", )) } @@ -142,85 +143,99 @@ mod tests { #[test] fn test_data_extractor() { - let mut srv = - init_service(App::new().data(10usize).service( + block_on(async { + let mut srv = init_service(App::new().data(10usize).service( web::resource("/").to(|_: web::Data| HttpResponse::Ok()), - )); + )) + .await; - let req = TestRequest::default().to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::default().to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); - let mut srv = - init_service(App::new().data(10u32).service( + let mut srv = init_service(App::new().data(10u32).service( web::resource("/").to(|_: web::Data| HttpResponse::Ok()), - )); - let req = TestRequest::default().to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); + )) + .await; + let req = TestRequest::default().to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); + }) } #[test] fn test_register_data_extractor() { - let mut srv = - init_service(App::new().register_data(Data::new(10usize)).service( - web::resource("/").to(|_: web::Data| HttpResponse::Ok()), - )); + block_on(async { + let mut srv = + init_service(App::new().register_data(Data::new(10usize)).service( + web::resource("/").to(|_: web::Data| HttpResponse::Ok()), + )) + .await; - let req = TestRequest::default().to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::default().to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); - let mut srv = - init_service(App::new().register_data(Data::new(10u32)).service( - web::resource("/").to(|_: web::Data| HttpResponse::Ok()), - )); - let req = TestRequest::default().to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); + let mut srv = + init_service(App::new().register_data(Data::new(10u32)).service( + web::resource("/").to(|_: web::Data| HttpResponse::Ok()), + )) + .await; + let req = TestRequest::default().to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); + }) } #[test] fn test_route_data_extractor() { - let mut srv = - init_service(App::new().service(web::resource("/").data(10usize).route( - web::get().to(|data: web::Data| { - let _ = data.clone(); - HttpResponse::Ok() - }), - ))); + block_on(async { + let mut srv = init_service(App::new().service( + web::resource("/").data(10usize).route(web::get().to( + |data: web::Data| { + let _ = data.clone(); + HttpResponse::Ok() + }, + )), + )) + .await; - let req = TestRequest::default().to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::default().to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); - // different type - let mut srv = init_service( - App::new().service( - web::resource("/") - .data(10u32) - .route(web::get().to(|_: web::Data| HttpResponse::Ok())), - ), - ); - let req = TestRequest::default().to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); + // different type + let mut srv = init_service( + App::new().service( + web::resource("/") + .data(10u32) + .route(web::get().to(|_: web::Data| HttpResponse::Ok())), + ), + ) + .await; + let req = TestRequest::default().to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); + }) } #[test] fn test_override_data() { - let mut srv = init_service(App::new().data(1usize).service( - web::resource("/").data(10usize).route(web::get().to( - |data: web::Data| { - assert_eq!(*data, 10); - let _ = data.clone(); - HttpResponse::Ok() - }, - )), - )); + block_on(async { + let mut srv = init_service(App::new().data(1usize).service( + web::resource("/").data(10usize).route(web::get().to( + |data: web::Data| { + assert_eq!(*data, 10); + let _ = data.clone(); + HttpResponse::Ok() + }, + )), + )) + .await; - let req = TestRequest::default().to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::default().to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + }) } } diff --git a/src/extract.rs b/src/extract.rs index 425637311..20a1180ec 100644 --- a/src/extract.rs +++ b/src/extract.rs @@ -1,8 +1,10 @@ //! Request extractors +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; use actix_http::error::Error; -use futures::future::ok; -use futures::{future, Async, Future, IntoFuture, Poll}; +use futures::future::{ok, FutureExt, LocalBoxFuture, Ready}; use crate::dev::Payload; use crate::request::HttpRequest; @@ -15,7 +17,7 @@ pub trait FromRequest: Sized { type Error: Into; /// Future that resolves to a Self - type Future: IntoFuture; + type Future: Future>; /// Configuration for this extractor type Config: Default + 'static; @@ -48,6 +50,7 @@ pub trait FromRequest: Sized { /// ```rust /// use actix_web::{web, dev, App, Error, HttpRequest, FromRequest}; /// use actix_web::error::ErrorBadRequest; +/// use futures::future::{ok, err, Ready}; /// use serde_derive::Deserialize; /// use rand; /// @@ -58,14 +61,14 @@ pub trait FromRequest: Sized { /// /// impl FromRequest for Thing { /// type Error = Error; -/// type Future = Result; +/// type Future = Ready>; /// type Config = (); /// /// fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future { /// if rand::random() { -/// Ok(Thing { name: "thingy".into() }) +/// ok(Thing { name: "thingy".into() }) /// } else { -/// Err(ErrorBadRequest("no luck")) +/// err(ErrorBadRequest("no luck")) /// } /// /// } @@ -94,21 +97,19 @@ where { type Config = T::Config; type Error = Error; - type Future = Box, Error = Error>>; + type Future = LocalBoxFuture<'static, Result, Error>>; #[inline] fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { - Box::new( - T::from_request(req, payload) - .into_future() - .then(|r| match r { - Ok(v) => future::ok(Some(v)), - Err(e) => { - log::debug!("Error for Option extractor: {}", e.into()); - future::ok(None) - } - }), - ) + T::from_request(req, payload) + .then(|r| match r { + Ok(v) => ok(Some(v)), + Err(e) => { + log::debug!("Error for Option extractor: {}", e.into()); + ok(None) + } + }) + .boxed_local() } } @@ -121,6 +122,7 @@ where /// ```rust /// use actix_web::{web, dev, App, Result, Error, HttpRequest, FromRequest}; /// use actix_web::error::ErrorBadRequest; +/// use futures::future::{ok, err, Ready}; /// use serde_derive::Deserialize; /// use rand; /// @@ -131,14 +133,14 @@ where /// /// impl FromRequest for Thing { /// type Error = Error; -/// type Future = Result; +/// type Future = Ready>; /// type Config = (); /// /// fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future { /// if rand::random() { -/// Ok(Thing { name: "thingy".into() }) +/// ok(Thing { name: "thingy".into() }) /// } else { -/// Err(ErrorBadRequest("no luck")) +/// err(ErrorBadRequest("no luck")) /// } /// } /// } @@ -157,26 +159,24 @@ where /// ); /// } /// ``` -impl FromRequest for Result +impl FromRequest for Result where - T: FromRequest, - T::Future: 'static, + T: FromRequest + 'static, T::Error: 'static, + T::Future: 'static, { type Config = T::Config; type Error = Error; - type Future = Box, Error = Error>>; + type Future = LocalBoxFuture<'static, Result, Error>>; #[inline] fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { - Box::new( - T::from_request(req, payload) - .into_future() - .then(|res| match res { - Ok(v) => ok(Ok(v)), - Err(e) => ok(Err(e)), - }), - ) + T::from_request(req, payload) + .then(|res| match res { + Ok(v) => ok(Ok(v)), + Err(e) => ok(Err(e)), + }) + .boxed_local() } } @@ -184,10 +184,10 @@ where impl FromRequest for () { type Config = (); type Error = Error; - type Future = Result<(), Error>; + type Future = Ready>; fn from_request(_: &HttpRequest, _: &mut Payload) -> Self::Future { - Ok(()) + ok(()) } } @@ -204,43 +204,44 @@ macro_rules! tuple_from_req ({$fut_type:ident, $(($n:tt, $T:ident)),+} => { fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { $fut_type { items: <($(Option<$T>,)+)>::default(), - futs: ($($T::from_request(req, payload).into_future(),)+), + futs: ($($T::from_request(req, payload),)+), } } } #[doc(hidden)] + #[pin_project::pin_project] pub struct $fut_type<$($T: FromRequest),+> { items: ($(Option<$T>,)+), - futs: ($(<$T::Future as futures::IntoFuture>::Future,)+), + futs: ($($T::Future,)+), } impl<$($T: FromRequest),+> Future for $fut_type<$($T),+> { - type Item = ($($T,)+); - type Error = Error; + type Output = Result<($($T,)+), Error>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.project(); - fn poll(&mut self) -> Poll { let mut ready = true; - $( - if self.items.$n.is_none() { - match self.futs.$n.poll() { - Ok(Async::Ready(item)) => { - self.items.$n = Some(item); + if this.items.$n.is_none() { + match unsafe { Pin::new_unchecked(&mut this.futs.$n) }.poll(cx) { + Poll::Ready(Ok(item)) => { + this.items.$n = Some(item); } - Ok(Async::NotReady) => ready = false, - Err(e) => return Err(e.into()), + Poll::Pending => ready = false, + Poll::Ready(Err(e)) => return Poll::Ready(Err(e.into())), } } )+ if ready { - Ok(Async::Ready( - ($(self.items.$n.take().unwrap(),)+) + Poll::Ready(Ok( + ($(this.items.$n.take().unwrap(),)+) )) } else { - Ok(Async::NotReady) + Poll::Pending } } } diff --git a/src/handler.rs b/src/handler.rs index 078abbf1d..7f5d52945 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -1,10 +1,14 @@ use std::convert::Infallible; +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; use actix_http::{Error, Response}; -use actix_service::{NewService, Service}; -use futures::future::{ok, FutureResult}; -use futures::{try_ready, Async, Future, IntoFuture, Poll}; +use actix_service::{Service, ServiceFactory}; +use futures::future::{ok, Ready}; +use futures::ready; +use pin_project::pin_project; use crate::extract::FromRequest; use crate::request::HttpRequest; @@ -73,14 +77,14 @@ where type Request = (T, HttpRequest); type Response = ServiceResponse; type Error = Infallible; - type Future = HandlerServiceResponse<::Future>; + type Future = HandlerServiceResponse; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, (param, req): (T, HttpRequest)) -> Self::Future { - let fut = self.hnd.call(param).respond_to(&req).into_future(); + let fut = self.hnd.call(param).respond_to(&req); HandlerServiceResponse { fut, req: Some(req), @@ -88,53 +92,48 @@ where } } -pub struct HandlerServiceResponse { - fut: T, +#[pin_project] +pub struct HandlerServiceResponse { + #[pin] + fut: T::Future, req: Option, } -impl Future for HandlerServiceResponse -where - T: Future, - T::Error: Into, -{ - type Item = ServiceResponse; - type Error = Infallible; +impl Future for HandlerServiceResponse { + type Output = Result; - fn poll(&mut self) -> Poll { - match self.fut.poll() { - Ok(Async::Ready(res)) => Ok(Async::Ready(ServiceResponse::new( - self.req.take().unwrap(), - res, - ))), - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(e) => { + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.project(); + + match this.fut.poll(cx) { + Poll::Ready(Ok(res)) => { + Poll::Ready(Ok(ServiceResponse::new(this.req.take().unwrap(), res))) + } + Poll::Pending => Poll::Pending, + Poll::Ready(Err(e)) => { let res: Response = e.into().into(); - Ok(Async::Ready(ServiceResponse::new( - self.req.take().unwrap(), - res, - ))) + Poll::Ready(Ok(ServiceResponse::new(this.req.take().unwrap(), res))) } } } } /// Async handler converter factory -pub trait AsyncFactory: Clone + 'static +pub trait AsyncFactory: Clone + 'static where - R: IntoFuture, - R::Item: Responder, - R::Error: Into, + R: Future>, + O: Responder, + E: Into, { fn call(&self, param: T) -> R; } -impl AsyncFactory<(), R> for F +impl AsyncFactory<(), R, O, E> for F where F: Fn() -> R + Clone + 'static, - R: IntoFuture, - R::Item: Responder, - R::Error: Into, + R: Future>, + O: Responder, + E: Into, { fn call(&self, _: ()) -> R { (self)() @@ -142,23 +141,23 @@ where } #[doc(hidden)] -pub struct AsyncHandler +pub struct AsyncHandler where - F: AsyncFactory, - R: IntoFuture, - R::Item: Responder, - R::Error: Into, + F: AsyncFactory, + R: Future>, + O: Responder, + E: Into, { hnd: F, - _t: PhantomData<(T, R)>, + _t: PhantomData<(T, R, O, E)>, } -impl AsyncHandler +impl AsyncHandler where - F: AsyncFactory, - R: IntoFuture, - R::Item: Responder, - R::Error: Into, + F: AsyncFactory, + R: Future>, + O: Responder, + E: Into, { pub fn new(hnd: F) -> Self { AsyncHandler { @@ -168,12 +167,12 @@ where } } -impl Clone for AsyncHandler +impl Clone for AsyncHandler where - F: AsyncFactory, - R: IntoFuture, - R::Item: Responder, - R::Error: Into, + F: AsyncFactory, + R: Future>, + O: Responder, + E: Into, { fn clone(&self) -> Self { AsyncHandler { @@ -183,25 +182,25 @@ where } } -impl Service for AsyncHandler +impl Service for AsyncHandler where - F: AsyncFactory, - R: IntoFuture, - R::Item: Responder, - R::Error: Into, + F: AsyncFactory, + R: Future>, + O: Responder, + E: Into, { type Request = (T, HttpRequest); type Response = ServiceResponse; type Error = Infallible; - type Future = AsyncHandlerServiceResponse; + type Future = AsyncHandlerServiceResponse; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, (param, req): (T, HttpRequest)) -> Self::Future { AsyncHandlerServiceResponse { - fut: self.hnd.call(param).into_future(), + fut: self.hnd.call(param), fut2: None, req: Some(req), } @@ -209,56 +208,54 @@ where } #[doc(hidden)] -pub struct AsyncHandlerServiceResponse +#[pin_project] +pub struct AsyncHandlerServiceResponse where - T: Future, - T::Item: Responder, + T: Future>, + R: Responder, + E: Into, { + #[pin] fut: T, - fut2: Option<<::Future as IntoFuture>::Future>, + #[pin] + fut2: Option, req: Option, } -impl Future for AsyncHandlerServiceResponse +impl Future for AsyncHandlerServiceResponse where - T: Future, - T::Item: Responder, - T::Error: Into, + T: Future>, + R: Responder, + E: Into, { - type Item = ServiceResponse; - type Error = Infallible; + type Output = Result; - fn poll(&mut self) -> Poll { - if let Some(ref mut fut) = self.fut2 { - return match fut.poll() { - Ok(Async::Ready(res)) => Ok(Async::Ready(ServiceResponse::new( - self.req.take().unwrap(), - res, - ))), - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(e) => { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.as_mut().project(); + + if let Some(fut) = this.fut2.as_pin_mut() { + return match fut.poll(cx) { + Poll::Ready(Ok(res)) => { + Poll::Ready(Ok(ServiceResponse::new(this.req.take().unwrap(), res))) + } + Poll::Pending => Poll::Pending, + Poll::Ready(Err(e)) => { let res: Response = e.into().into(); - Ok(Async::Ready(ServiceResponse::new( - self.req.take().unwrap(), - res, - ))) + Poll::Ready(Ok(ServiceResponse::new(this.req.take().unwrap(), res))) } }; } - match self.fut.poll() { - Ok(Async::Ready(res)) => { - self.fut2 = - Some(res.respond_to(self.req.as_ref().unwrap()).into_future()); - self.poll() + match this.fut.poll(cx) { + Poll::Ready(Ok(res)) => { + let fut = res.respond_to(this.req.as_ref().unwrap()); + self.as_mut().project().fut2.set(Some(fut)); + self.poll(cx) } - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(e) => { + Poll::Pending => Poll::Pending, + Poll::Ready(Err(e)) => { let res: Response = e.into().into(); - Ok(Async::Ready(ServiceResponse::new( - self.req.take().unwrap(), - res, - ))) + Poll::Ready(Ok(ServiceResponse::new(this.req.take().unwrap(), res))) } } } @@ -279,7 +276,7 @@ impl Extract { } } -impl NewService for Extract +impl ServiceFactory for Extract where S: Service< Request = (T, HttpRequest), @@ -293,7 +290,7 @@ where type Error = (Error, ServiceRequest); type InitError = (); type Service = ExtractService; - type Future = FutureResult; + type Future = Ready>; fn new_service(&self, _: &()) -> Self::Future { ok(ExtractService { @@ -321,13 +318,13 @@ where type Error = (Error, ServiceRequest); type Future = ExtractResponse; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, req: ServiceRequest) -> Self::Future { let (req, mut payload) = req.into_parts(); - let fut = T::from_request(&req, &mut payload).into_future(); + let fut = T::from_request(&req, &mut payload); ExtractResponse { fut, @@ -338,10 +335,13 @@ where } } +#[pin_project] pub struct ExtractResponse { req: HttpRequest, service: S, - fut: ::Future, + #[pin] + fut: T::Future, + #[pin] fut_s: Option, } @@ -353,21 +353,26 @@ where Error = Infallible, >, { - type Item = ServiceResponse; - type Error = (Error, ServiceRequest); + type Output = Result; - fn poll(&mut self) -> Poll { - if let Some(ref mut fut) = self.fut_s { - return fut.poll().map_err(|_| panic!()); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.as_mut().project(); + + if let Some(fut) = this.fut_s.as_pin_mut() { + return fut.poll(cx).map_err(|_| panic!()); } - let item = try_ready!(self.fut.poll().map_err(|e| { - let req = ServiceRequest::new(self.req.clone()); - (e.into(), req) - })); - - self.fut_s = Some(self.service.call((item, self.req.clone()))); - self.poll() + match ready!(this.fut.poll(cx)) { + Err(e) => { + let req = ServiceRequest::new(this.req.clone()); + Poll::Ready(Err((e.into(), req))) + } + Ok(item) => { + let fut = Some(this.service.call((item, this.req.clone()))); + self.as_mut().project().fut_s.set(fut); + self.poll(cx) + } + } } } @@ -382,11 +387,11 @@ macro_rules! factory_tuple ({ $(($n:tt, $T:ident)),+} => { } } - impl AsyncFactory<($($T,)+), Res> for Func + impl AsyncFactory<($($T,)+), Res, O, E1> for Func where Func: Fn($($T,)+) -> Res + Clone + 'static, - Res: IntoFuture, - Res::Item: Responder, - Res::Error: Into, + Res: Future>, + O: Responder, + E1: Into, { fn call(&self, param: ($($T,)+)) -> Res { (self)($(param.$n,)+) diff --git a/src/lib.rs b/src/lib.rs index 60c34489e..1ae81505a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(clippy::borrow_interior_mutable_const)] +#![allow(clippy::borrow_interior_mutable_const, unused_imports, dead_code)] //! Actix web is a small, pragmatic, and extremely fast web framework //! for Rust. //! @@ -68,8 +68,8 @@ //! ## Package feature //! //! * `client` - enables http client (default enabled) -//! * `ssl` - enables ssl support via `openssl` crate, supports `http/2` -//! * `rust-tls` - enables ssl support via `rustls` crate, supports `http/2` +//! * `openssl` - enables ssl support via `openssl` crate, supports `http/2` +//! * `rustls` - enables ssl support via `rustls` crate, supports `http/2` //! * `secure-cookies` - enables secure cookies support, includes `ring` crate as //! dependency //! * `brotli` - enables `brotli` compression support, requires `c` @@ -78,7 +78,6 @@ //! `c` compiler (default enabled) //! * `flate2-rust` - experimental rust based implementation for //! `gzip`, `deflate` compression. -//! * `uds` - Unix domain support, enables `HttpServer::bind_uds()` method. //! #![allow(clippy::type_complexity, clippy::new_without_default)] @@ -143,9 +142,10 @@ pub mod dev { pub use crate::service::{ HttpServiceFactory, ServiceRequest, ServiceResponse, WebService, }; - pub use crate::types::form::UrlEncoded; - pub use crate::types::json::JsonBody; - pub use crate::types::readlines::Readlines; + + //pub use crate::types::form::UrlEncoded; + //pub use crate::types::json::JsonBody; + //pub use crate::types::readlines::Readlines; pub use actix_http::body::{Body, BodySize, MessageBody, ResponseBody, SizedStream}; pub use actix_http::encoding::Decoder as Decompress; @@ -176,18 +176,16 @@ pub mod client { //! use actix_web::client::Client; //! //! fn main() { - //! System::new("test").block_on(lazy(|| { + //! System::new("test").block_on(async { //! let mut client = Client::default(); //! - //! client.get("http://www.rust-lang.org") // <- Create request builder + //! // Create request builder and send request + //! let response = client.get("http://www.rust-lang.org") //! .header("User-Agent", "Actix-web") - //! .send() // <- Send http request - //! .map_err(|_| ()) - //! .and_then(|response| { // <- server http response - //! println!("Response: {:?}", response); - //! Ok(()) - //! }) - //! })); + //! .send().await; // <- Send http request + //! + //! println!("Response: {:?}", response); + //! }); //! } //! ``` pub use awc::error::{ diff --git a/src/middleware/compress.rs b/src/middleware/compress.rs index 86665d824..a697deaec 100644 --- a/src/middleware/compress.rs +++ b/src/middleware/compress.rs @@ -1,15 +1,18 @@ //! `Middleware` for compressing response body. use std::cmp; +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; use std::str::FromStr; +use std::task::{Context, Poll}; use actix_http::body::MessageBody; use actix_http::encoding::Encoder; use actix_http::http::header::{ContentEncoding, ACCEPT_ENCODING}; use actix_http::{Error, Response, ResponseBuilder}; use actix_service::{Service, Transform}; -use futures::future::{ok, FutureResult}; -use futures::{Async, Future, Poll}; +use futures::future::{ok, Ready}; +use pin_project::pin_project; use crate::service::{ServiceRequest, ServiceResponse}; @@ -78,7 +81,7 @@ where type Error = Error; type InitError = (); type Transform = CompressMiddleware; - type Future = FutureResult; + type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { ok(CompressMiddleware { @@ -103,8 +106,8 @@ where type Error = Error; type Future = CompressResponse; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.service.poll_ready() + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.service.poll_ready(cx) } fn call(&mut self, req: ServiceRequest) -> Self::Future { @@ -128,11 +131,13 @@ where } #[doc(hidden)] +#[pin_project] pub struct CompressResponse where S: Service, B: MessageBody, { + #[pin] fut: S::Future, encoding: ContentEncoding, _t: PhantomData<(B)>, @@ -143,21 +148,25 @@ where B: MessageBody, S: Service, Error = Error>, { - type Item = ServiceResponse>; - type Error = Error; + type Output = Result>, Error>; - fn poll(&mut self) -> Poll { - let resp = futures::try_ready!(self.fut.poll()); + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.project(); - let enc = if let Some(enc) = resp.response().extensions().get::() { - enc.0 - } else { - self.encoding - }; + match futures::ready!(this.fut.poll(cx)) { + Ok(resp) => { + let enc = if let Some(enc) = resp.response().extensions().get::() { + enc.0 + } else { + *this.encoding + }; - Ok(Async::Ready(resp.map_body(move |head, body| { - Encoder::response(enc, head, body) - }))) + Poll::Ready(Ok( + resp.map_body(move |head, body| Encoder::response(enc, head, body)) + )) + } + Err(e) => Poll::Ready(Err(e)), + } } } diff --git a/src/middleware/defaultheaders.rs b/src/middleware/defaultheaders.rs index ab2d36c2c..5c995503a 100644 --- a/src/middleware/defaultheaders.rs +++ b/src/middleware/defaultheaders.rs @@ -1,9 +1,11 @@ //! Middleware for setting default response headers +use std::future::Future; +use std::pin::Pin; use std::rc::Rc; +use std::task::{Context, Poll}; use actix_service::{Service, Transform}; -use futures::future::{ok, FutureResult}; -use futures::{Future, Poll}; +use futures::future::{ok, FutureExt, LocalBoxFuture, Ready}; use crate::http::header::{HeaderName, HeaderValue, CONTENT_TYPE}; use crate::http::{HeaderMap, HttpTryFrom}; @@ -96,7 +98,7 @@ where type Error = Error; type InitError = (); type Transform = DefaultHeadersMiddleware; - type Future = FutureResult; + type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { ok(DefaultHeadersMiddleware { @@ -119,16 +121,19 @@ where type Request = ServiceRequest; type Response = ServiceResponse; type Error = Error; - type Future = Box>; + type Future = LocalBoxFuture<'static, Result>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.service.poll_ready() + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.service.poll_ready(cx) } fn call(&mut self, req: ServiceRequest) -> Self::Future { let inner = self.inner.clone(); + let fut = self.service.call(req); + + async move { + let mut res = fut.await?; - Box::new(self.service.call(req).map(move |mut res| { // set response headers for (key, value) in inner.headers.iter() { if !res.headers().contains_key(key) { @@ -142,15 +147,16 @@ where HeaderValue::from_static("application/octet-stream"), ); } - - res - })) + Ok(res) + } + .boxed_local() } } #[cfg(test)] mod tests { use actix_service::IntoService; + use futures::future::ok; use super::*; use crate::dev::ServiceRequest; @@ -160,46 +166,50 @@ mod tests { #[test] fn test_default_headers() { - let mut mw = block_on( - DefaultHeaders::new() + block_on(async { + let mut mw = DefaultHeaders::new() .header(CONTENT_TYPE, "0001") - .new_transform(ok_service()), - ) - .unwrap(); + .new_transform(ok_service()) + .await + .unwrap(); - let req = TestRequest::default().to_srv_request(); - let resp = block_on(mw.call(req)).unwrap(); - assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); + let req = TestRequest::default().to_srv_request(); + let resp = mw.call(req).await.unwrap(); + assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); - let req = TestRequest::default().to_srv_request(); - let srv = |req: ServiceRequest| { - req.into_response(HttpResponse::Ok().header(CONTENT_TYPE, "0002").finish()) - }; - let mut mw = block_on( - DefaultHeaders::new() + let req = TestRequest::default().to_srv_request(); + let srv = |req: ServiceRequest| { + ok(req.into_response( + HttpResponse::Ok().header(CONTENT_TYPE, "0002").finish(), + )) + }; + let mut mw = DefaultHeaders::new() .header(CONTENT_TYPE, "0001") - .new_transform(srv.into_service()), - ) - .unwrap(); - let resp = block_on(mw.call(req)).unwrap(); - assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0002"); + .new_transform(srv.into_service()) + .await + .unwrap(); + let resp = mw.call(req).await.unwrap(); + assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0002"); + }) } #[test] fn test_content_type() { - let srv = |req: ServiceRequest| req.into_response(HttpResponse::Ok().finish()); - let mut mw = block_on( - DefaultHeaders::new() + block_on(async { + let srv = + |req: ServiceRequest| ok(req.into_response(HttpResponse::Ok().finish())); + let mut mw = DefaultHeaders::new() .content_type() - .new_transform(srv.into_service()), - ) - .unwrap(); + .new_transform(srv.into_service()) + .await + .unwrap(); - let req = TestRequest::default().to_srv_request(); - let resp = block_on(mw.call(req)).unwrap(); - assert_eq!( - resp.headers().get(CONTENT_TYPE).unwrap(), - "application/octet-stream" - ); + let req = TestRequest::default().to_srv_request(); + let resp = mw.call(req).await.unwrap(); + assert_eq!( + resp.headers().get(CONTENT_TYPE).unwrap(), + "application/octet-stream" + ); + }) } } diff --git a/src/middleware/logger.rs b/src/middleware/logger.rs index f450f0481..45df4bf34 100644 --- a/src/middleware/logger.rs +++ b/src/middleware/logger.rs @@ -2,13 +2,15 @@ use std::collections::HashSet; use std::env; use std::fmt::{self, Display, Formatter}; +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; use std::rc::Rc; +use std::task::{Context, Poll}; use actix_service::{Service, Transform}; use bytes::Bytes; -use futures::future::{ok, FutureResult}; -use futures::{Async, Future, Poll}; +use futures::future::{ok, Ready}; use log::debug; use regex::Regex; use time; @@ -125,7 +127,7 @@ where type Error = Error; type InitError = (); type Transform = LoggerMiddleware; - type Future = FutureResult; + type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { ok(LoggerMiddleware { @@ -151,8 +153,8 @@ where type Error = Error; type Future = LoggerResponse; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.service.poll_ready() + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.service.poll_ready(cx) } fn call(&mut self, req: ServiceRequest) -> Self::Future { @@ -181,11 +183,13 @@ where } #[doc(hidden)] +#[pin_project::pin_project] pub struct LoggerResponse where B: MessageBody, S: Service, { + #[pin] fut: S::Future, time: time::Tm, format: Option, @@ -197,11 +201,15 @@ where B: MessageBody, S: Service, Error = Error>, { - type Item = ServiceResponse>; - type Error = Error; + type Output = Result>, Error>; - fn poll(&mut self) -> Poll { - let res = futures::try_ready!(self.fut.poll()); + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.project(); + + let res = match futures::ready!(this.fut.poll(cx)) { + Ok(res) => res, + Err(e) => return Poll::Ready(Err(e)), + }; if let Some(error) = res.response().error() { if res.response().head().status != StatusCode::INTERNAL_SERVER_ERROR { @@ -209,18 +217,21 @@ where } } - if let Some(ref mut format) = self.format { + if let Some(ref mut format) = this.format { for unit in &mut format.0 { unit.render_response(res.response()); } } - Ok(Async::Ready(res.map_body(move |_, body| { + let time = *this.time; + let format = this.format.take(); + + Poll::Ready(Ok(res.map_body(move |_, body| { ResponseBody::Body(StreamLog { body, + time, + format, size: 0, - time: self.time, - format: self.format.take(), }) }))) } @@ -252,13 +263,13 @@ impl MessageBody for StreamLog { self.body.size() } - fn poll_next(&mut self) -> Poll, Error> { - match self.body.poll_next()? { - Async::Ready(Some(chunk)) => { + fn poll_next(&mut self, cx: &mut Context) -> Poll>> { + match self.body.poll_next(cx) { + Poll::Ready(Some(Ok(chunk))) => { self.size += chunk.len(); - Ok(Async::Ready(Some(chunk))) + Poll::Ready(Some(Ok(chunk))) } - val => Ok(val), + val => val, } } } @@ -464,6 +475,7 @@ impl<'a> fmt::Display for FormatDisplay<'a> { #[cfg(test)] mod tests { use actix_service::{IntoService, Service, Transform}; + use futures::future::ok; use super::*; use crate::http::{header, StatusCode}; @@ -472,11 +484,11 @@ mod tests { #[test] fn test_logger() { let srv = |req: ServiceRequest| { - req.into_response( + ok(req.into_response( HttpResponse::build(StatusCode::OK) .header("X-Test", "ttt") .finish(), - ) + )) }; let logger = Logger::new("%% %{User-Agent}i %{X-Test}o %{HOME}e %D test"); diff --git a/src/middleware/mod.rs b/src/middleware/mod.rs index 84e0758bf..30acad15a 100644 --- a/src/middleware/mod.rs +++ b/src/middleware/mod.rs @@ -2,13 +2,13 @@ mod compress; pub use self::compress::{BodyEncoding, Compress}; -mod condition; +//mod condition; mod defaultheaders; -pub mod errhandlers; +//pub mod errhandlers; mod logger; -mod normalize; +//mod normalize; -pub use self::condition::Condition; +//pub use self::condition::Condition; pub use self::defaultheaders::DefaultHeaders; pub use self::logger::Logger; -pub use self::normalize::NormalizePath; +//pub use self::normalize::NormalizePath; diff --git a/src/request.rs b/src/request.rs index ea27e303c..84744af28 100644 --- a/src/request.rs +++ b/src/request.rs @@ -5,6 +5,7 @@ use std::{fmt, net}; use actix_http::http::{HeaderMap, Method, Uri, Version}; use actix_http::{Error, Extensions, HttpMessage, Message, Payload, RequestHead}; use actix_router::{Path, Url}; +use futures::future::{ok, Ready}; use crate::config::AppConfig; use crate::data::Data; @@ -289,11 +290,11 @@ impl Drop for HttpRequest { impl FromRequest for HttpRequest { type Config = (); type Error = Error; - type Future = Result; + type Future = Ready>; #[inline] fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { - Ok(req.clone()) + ok(req.clone()) } } @@ -349,7 +350,7 @@ mod tests { use super::*; use crate::dev::{ResourceDef, ResourceMap}; use crate::http::{header, StatusCode}; - use crate::test::{call_service, init_service, TestRequest}; + use crate::test::{block_on, call_service, init_service, TestRequest}; use crate::{web, App, HttpResponse}; #[test] @@ -467,66 +468,73 @@ mod tests { #[test] fn test_app_data() { - let mut srv = init_service(App::new().data(10usize).service( - web::resource("/").to(|req: HttpRequest| { - if req.app_data::().is_some() { - HttpResponse::Ok() - } else { - HttpResponse::BadRequest() - } - }), - )); + block_on(async { + let mut srv = init_service(App::new().data(10usize).service( + web::resource("/").to(|req: HttpRequest| { + if req.app_data::().is_some() { + HttpResponse::Ok() + } else { + HttpResponse::BadRequest() + } + }), + )) + .await; - let req = TestRequest::default().to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::default().to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); - let mut srv = init_service(App::new().data(10u32).service( - web::resource("/").to(|req: HttpRequest| { - if req.app_data::().is_some() { - HttpResponse::Ok() - } else { - HttpResponse::BadRequest() - } - }), - )); + let mut srv = init_service(App::new().data(10u32).service( + web::resource("/").to(|req: HttpRequest| { + if req.app_data::().is_some() { + HttpResponse::Ok() + } else { + HttpResponse::BadRequest() + } + }), + )) + .await; - let req = TestRequest::default().to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::BAD_REQUEST); + let req = TestRequest::default().to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::BAD_REQUEST); + }) } #[test] fn test_extensions_dropped() { - struct Tracker { - pub dropped: bool, - } - struct Foo { - tracker: Rc>, - } - impl Drop for Foo { - fn drop(&mut self) { - self.tracker.borrow_mut().dropped = true; + block_on(async { + struct Tracker { + pub dropped: bool, + } + struct Foo { + tracker: Rc>, + } + impl Drop for Foo { + fn drop(&mut self) { + self.tracker.borrow_mut().dropped = true; + } } - } - let tracker = Rc::new(RefCell::new(Tracker { dropped: false })); - { - let tracker2 = Rc::clone(&tracker); - let mut srv = init_service(App::new().data(10u32).service( - web::resource("/").to(move |req: HttpRequest| { - req.extensions_mut().insert(Foo { - tracker: Rc::clone(&tracker2), - }); - HttpResponse::Ok() - }), - )); + let tracker = Rc::new(RefCell::new(Tracker { dropped: false })); + { + let tracker2 = Rc::clone(&tracker); + let mut srv = init_service(App::new().data(10u32).service( + web::resource("/").to(move |req: HttpRequest| { + req.extensions_mut().insert(Foo { + tracker: Rc::clone(&tracker2), + }); + HttpResponse::Ok() + }), + )) + .await; - let req = TestRequest::default().to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); - } + let req = TestRequest::default().to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + } - assert!(tracker.borrow().dropped); + assert!(tracker.borrow().dropped); + }) } } diff --git a/src/resource.rs b/src/resource.rs index 3ee0167a0..553d41568 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -1,14 +1,17 @@ use std::cell::RefCell; use std::fmt; +use std::future::Future; +use std::pin::Pin; use std::rc::Rc; +use std::task::{Context, Poll}; use actix_http::{Error, Extensions, Response}; use actix_service::boxed::{self, BoxedNewService, BoxedService}; use actix_service::{ - apply_transform, IntoNewService, IntoTransform, NewService, Service, Transform, + apply, apply_fn_factory, IntoServiceFactory, Service, ServiceFactory, Transform, }; -use futures::future::{ok, Either, FutureResult}; -use futures::{Async, Future, IntoFuture, Poll}; +use futures::future::{ok, Either, LocalBoxFuture, Ready}; +use pin_project::pin_project; use crate::data::Data; use crate::dev::{insert_slash, AppService, HttpServiceFactory, ResourceDef}; @@ -74,7 +77,7 @@ impl Resource { impl Resource where - T: NewService< + T: ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -243,8 +246,8 @@ where /// use actix_web::*; /// use futures::future::{ok, Future}; /// - /// fn index(req: HttpRequest) -> impl Future { - /// ok(HttpResponse::Ok().finish()) + /// async fn index(req: HttpRequest) -> Result { + /// Ok(HttpResponse::Ok().finish()) /// } /// /// App::new().service(web::resource("/").to_async(index)); @@ -255,19 +258,19 @@ where /// ```rust /// # use actix_web::*; /// # use futures::future::Future; - /// # fn index(req: HttpRequest) -> Box> { + /// # async fn index(req: HttpRequest) -> Result { /// # unimplemented!() /// # } /// App::new().service(web::resource("/").route(web::route().to_async(index))); /// ``` #[allow(clippy::wrong_self_convention)] - pub fn to_async(mut self, handler: F) -> Self + pub fn to_async(mut self, handler: F) -> Self where - F: AsyncFactory, + F: AsyncFactory, I: FromRequest + 'static, - R: IntoFuture + 'static, - R::Item: Responder, - R::Error: Into, + R: Future> + 'static, + O: Responder + 'static, + E: Into + 'static, { self.routes.push(Route::new().to_async(handler)); self @@ -280,11 +283,11 @@ where /// type (i.e modify response's body). /// /// **Note**: middlewares get called in opposite order of middlewares registration. - pub fn wrap( + pub fn wrap( self, - mw: F, + mw: M, ) -> Resource< - impl NewService< + impl ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -300,11 +303,9 @@ where Error = Error, InitError = (), >, - F: IntoTransform, { - let endpoint = apply_transform(mw, self.endpoint); Resource { - endpoint, + endpoint: apply(mw, self.endpoint), rdef: self.rdef, name: self.name, guards: self.guards, @@ -337,13 +338,16 @@ where /// fn main() { /// let app = App::new().service( /// web::resource("/index.html") - /// .wrap_fn(|req, srv| - /// srv.call(req).map(|mut res| { + /// .wrap_fn(|req, srv| { + /// let fut = srv.call(req); + /// async { + /// let mut res = fut.await?; /// res.headers_mut().insert( /// CONTENT_TYPE, HeaderValue::from_static("text/plain"), /// ); - /// res - /// })) + /// Ok(res) + /// } + /// }) /// .route(web::get().to(index))); /// } /// ``` @@ -351,7 +355,7 @@ where self, mw: F, ) -> Resource< - impl NewService< + impl ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -361,9 +365,18 @@ where > where F: FnMut(ServiceRequest, &mut T::Service) -> R + Clone, - R: IntoFuture, + R: Future>, { - self.wrap(mw) + Resource { + endpoint: apply_fn_factory(self.endpoint, mw), + rdef: self.rdef, + name: self.name, + guards: self.guards, + routes: self.routes, + default: self.default, + data: self.data, + factory_ref: self.factory_ref, + } } /// Default service to be used if no matching route could be found. @@ -371,8 +384,8 @@ where /// default handler from `App` or `Scope`. pub fn default_service(mut self, f: F) -> Self where - F: IntoNewService, - U: NewService< + F: IntoServiceFactory, + U: ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -381,8 +394,8 @@ where U::InitError: fmt::Debug, { // create and configure default resource - self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::new_service( - f.into_new_service().map_init_err(|e| { + self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::factory( + f.into_factory().map_init_err(|e| { log::error!("Can not construct default service: {:?}", e) }), ))))); @@ -393,7 +406,7 @@ where impl HttpServiceFactory for Resource where - T: NewService< + T: ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -423,9 +436,9 @@ where } } -impl IntoNewService for Resource +impl IntoServiceFactory for Resource where - T: NewService< + T: ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -433,7 +446,7 @@ where InitError = (), >, { - fn into_new_service(self) -> T { + fn into_factory(self) -> T { *self.factory_ref.borrow_mut() = Some(ResourceFactory { routes: self.routes, data: self.data.map(Rc::new), @@ -450,7 +463,7 @@ pub struct ResourceFactory { default: Rc>>>, } -impl NewService for ResourceFactory { +impl ServiceFactory for ResourceFactory { type Config = (); type Request = ServiceRequest; type Response = ServiceResponse; @@ -488,31 +501,30 @@ pub struct CreateResourceService { fut: Vec, data: Option>, default: Option, - default_fut: Option>>, + default_fut: Option>>, } impl Future for CreateResourceService { - type Item = ResourceService; - type Error = (); + type Output = Result; - fn poll(&mut self) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { let mut done = true; if let Some(ref mut fut) = self.default_fut { - match fut.poll()? { - Async::Ready(default) => self.default = Some(default), - Async::NotReady => done = false, + match Pin::new(fut).poll(cx)? { + Poll::Ready(default) => self.default = Some(default), + Poll::Pending => done = false, } } // poll http services for item in &mut self.fut { match item { - CreateRouteServiceItem::Future(ref mut fut) => match fut.poll()? { - Async::Ready(route) => { - *item = CreateRouteServiceItem::Service(route) - } - Async::NotReady => { + CreateRouteServiceItem::Future(ref mut fut) => match Pin::new(fut) + .poll(cx)? + { + Poll::Ready(route) => *item = CreateRouteServiceItem::Service(route), + Poll::Pending => { done = false; } }, @@ -529,13 +541,13 @@ impl Future for CreateResourceService { CreateRouteServiceItem::Future(_) => unreachable!(), }) .collect(); - Ok(Async::Ready(ResourceService { + Poll::Ready(Ok(ResourceService { routes, data: self.data.clone(), default: self.default.take(), })) } else { - Ok(Async::NotReady) + Poll::Pending } } } @@ -551,12 +563,12 @@ impl Service for ResourceService { type Response = ServiceResponse; type Error = Error; type Future = Either< - FutureResult, - Box>, + Ready>, + LocalBoxFuture<'static, Result>, >; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, mut req: ServiceRequest) -> Self::Future { @@ -565,14 +577,14 @@ impl Service for ResourceService { if let Some(ref data) = self.data { req.set_data_container(data.clone()); } - return route.call(req); + return Either::Right(route.call(req)); } } if let Some(ref mut default) = self.default { - default.call(req) + Either::Right(default.call(req)) } else { let req = req.into_parts().0; - Either::A(ok(ServiceResponse::new( + Either::Left(ok(ServiceResponse::new( req, Response::MethodNotAllowed().finish(), ))) @@ -591,7 +603,7 @@ impl ResourceEndpoint { } } -impl NewService for ResourceEndpoint { +impl ServiceFactory for ResourceEndpoint { type Config = (); type Request = ServiceRequest; type Response = ServiceResponse; @@ -610,18 +622,19 @@ mod tests { use std::time::Duration; use actix_service::Service; - use futures::{Future, IntoFuture}; - use tokio_timer::sleep; + use futures::future::{ok, Future}; + use tokio_timer::delay_for; use crate::http::{header, HeaderValue, Method, StatusCode}; + use crate::middleware::DefaultHeaders; use crate::service::{ServiceRequest, ServiceResponse}; - use crate::test::{call_service, init_service, TestRequest}; + use crate::test::{block_on, call_service, init_service, TestRequest}; use crate::{guard, web, App, Error, HttpResponse}; fn md( req: ServiceRequest, srv: &mut S, - ) -> impl IntoFuture, Error = Error> + ) -> impl Future, Error>> where S: Service< Request = ServiceRequest, @@ -629,178 +642,210 @@ mod tests { Error = Error, >, { - srv.call(req).map(|mut res| { + let fut = srv.call(req); + async move { + let mut res = fut.await?; res.headers_mut() .insert(header::CONTENT_TYPE, HeaderValue::from_static("0001")); - res - }) + Ok(res) + } } #[test] fn test_middleware() { - let mut srv = init_service( - App::new().service( - web::resource("/test") - .name("test") - .wrap(md) - .route(web::get().to(|| HttpResponse::Ok())), - ), - ); - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - HeaderValue::from_static("0001") - ); + block_on(async { + let mut srv = init_service( + App::new().service( + web::resource("/test") + .name("test") + .wrap(DefaultHeaders::new().header( + header::CONTENT_TYPE, + HeaderValue::from_static("0001"), + )) + .route(web::get().to(|| HttpResponse::Ok())), + ), + ) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + HeaderValue::from_static("0001") + ); + }) } #[test] fn test_middleware_fn() { - let mut srv = init_service( - App::new().service( - web::resource("/test") - .wrap_fn(|req, srv| { - srv.call(req).map(|mut res| { - res.headers_mut().insert( - header::CONTENT_TYPE, - HeaderValue::from_static("0001"), - ); - res + block_on(async { + let mut srv = init_service( + App::new().service( + web::resource("/test") + .wrap_fn(|req, srv| { + let fut = srv.call(req); + async { + fut.await.map(|mut res| { + res.headers_mut().insert( + header::CONTENT_TYPE, + HeaderValue::from_static("0001"), + ); + res + }) + } }) - }) - .route(web::get().to(|| HttpResponse::Ok())), - ), - ); - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - HeaderValue::from_static("0001") - ); + .route(web::get().to(|| HttpResponse::Ok())), + ), + ) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + HeaderValue::from_static("0001") + ); + }) } #[test] fn test_to_async() { - let mut srv = - init_service(App::new().service(web::resource("/test").to_async(|| { - sleep(Duration::from_millis(100)).then(|_| HttpResponse::Ok()) - }))); - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); + block_on(async { + let mut srv = init_service(App::new().service( + web::resource("/test").to_async(|| { + async { + delay_for(Duration::from_millis(100)).await; + Ok::<_, Error>(HttpResponse::Ok()) + } + }), + )) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + }) } #[test] fn test_default_resource() { - let mut srv = init_service( - App::new() - .service( - web::resource("/test").route(web::get().to(|| HttpResponse::Ok())), - ) - .default_service(|r: ServiceRequest| { - r.into_response(HttpResponse::BadRequest()) - }), - ); - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); - - let req = TestRequest::with_uri("/test") - .method(Method::POST) - .to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); - - let mut srv = init_service( - App::new().service( - web::resource("/test") - .route(web::get().to(|| HttpResponse::Ok())) + block_on(async { + let mut srv = init_service( + App::new() + .service( + web::resource("/test") + .route(web::get().to(|| HttpResponse::Ok())), + ) .default_service(|r: ServiceRequest| { - r.into_response(HttpResponse::BadRequest()) + ok(r.into_response(HttpResponse::BadRequest())) }), - ), - ); + ) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/test") + .method(Method::POST) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); - let req = TestRequest::with_uri("/test") - .method(Method::POST) - .to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::BAD_REQUEST); + let mut srv = init_service( + App::new().service( + web::resource("/test") + .route(web::get().to(|| HttpResponse::Ok())) + .default_service(|r: ServiceRequest| { + ok(r.into_response(HttpResponse::BadRequest())) + }), + ), + ) + .await; + + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + + let req = TestRequest::with_uri("/test") + .method(Method::POST) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::BAD_REQUEST); + }) } #[test] fn test_resource_guards() { - let mut srv = init_service( - App::new() - .service( - web::resource("/test/{p}") - .guard(guard::Get()) - .to(|| HttpResponse::Ok()), - ) - .service( - web::resource("/test/{p}") - .guard(guard::Put()) - .to(|| HttpResponse::Created()), - ) - .service( - web::resource("/test/{p}") - .guard(guard::Delete()) - .to(|| HttpResponse::NoContent()), - ), - ); + block_on(async { + let mut srv = init_service( + App::new() + .service( + web::resource("/test/{p}") + .guard(guard::Get()) + .to(|| HttpResponse::Ok()), + ) + .service( + web::resource("/test/{p}") + .guard(guard::Put()) + .to(|| HttpResponse::Created()), + ) + .service( + web::resource("/test/{p}") + .guard(guard::Delete()) + .to(|| HttpResponse::NoContent()), + ), + ) + .await; - let req = TestRequest::with_uri("/test/it") - .method(Method::GET) - .to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/test/it") + .method(Method::GET) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); - let req = TestRequest::with_uri("/test/it") - .method(Method::PUT) - .to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::CREATED); + let req = TestRequest::with_uri("/test/it") + .method(Method::PUT) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::CREATED); - let req = TestRequest::with_uri("/test/it") - .method(Method::DELETE) - .to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::NO_CONTENT); + let req = TestRequest::with_uri("/test/it") + .method(Method::DELETE) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::NO_CONTENT); + }) } #[test] fn test_data() { - let mut srv = init_service( - App::new() - .data(1.0f64) - .data(1usize) - .register_data(web::Data::new('-')) - .service( - web::resource("/test") - .data(10usize) - .register_data(web::Data::new('*')) - .guard(guard::Get()) - .to( - |data1: web::Data, - data2: web::Data, - data3: web::Data| { - assert_eq!(*data1, 10); - assert_eq!(*data2, '*'); - assert_eq!(*data3, 1.0); - HttpResponse::Ok() - }, - ), - ), - ); + block_on(async { + let mut srv = init_service( + App::new() + .data(1.0f64) + .data(1usize) + .register_data(web::Data::new('-')) + .service( + web::resource("/test") + .data(10usize) + .register_data(web::Data::new('*')) + .guard(guard::Get()) + .to( + |data1: web::Data, + data2: web::Data, + data3: web::Data| { + assert_eq!(*data1, 10); + assert_eq!(*data2, '*'); + assert_eq!(*data3, 1.0); + HttpResponse::Ok() + }, + ), + ), + ) + .await; - let req = TestRequest::get().uri("/test").to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::get().uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + }) } } diff --git a/src/responder.rs b/src/responder.rs index 4988ad5bc..2bb422b2e 100644 --- a/src/responder.rs +++ b/src/responder.rs @@ -1,3 +1,8 @@ +use std::future::Future; +use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; + use actix_http::error::InternalError; use actix_http::http::{ header::IntoHeaderValue, Error as HttpError, HeaderMap, HeaderName, HttpTryFrom, @@ -5,8 +10,9 @@ use actix_http::http::{ }; use actix_http::{Error, Response, ResponseBuilder}; use bytes::{Bytes, BytesMut}; -use futures::future::{err, ok, Either as EitherFuture, FutureResult}; -use futures::{try_ready, Async, Future, IntoFuture, Poll}; +use futures::future::{err, ok, Either as EitherFuture, LocalBoxFuture, Ready}; +use futures::ready; +use pin_project::{pin_project, project}; use crate::request::HttpRequest; @@ -18,7 +24,7 @@ pub trait Responder { type Error: Into; /// The future response value. - type Future: IntoFuture; + type Future: Future>; /// Convert itself to `AsyncResult` or `Error`. fn respond_to(self, req: &HttpRequest) -> Self::Future; @@ -71,7 +77,7 @@ pub trait Responder { impl Responder for Response { type Error = Error; - type Future = FutureResult; + type Future = Ready>; #[inline] fn respond_to(self, _: &HttpRequest) -> Self::Future { @@ -84,15 +90,14 @@ where T: Responder, { type Error = T::Error; - type Future = EitherFuture< - ::Future, - FutureResult, - >; + type Future = EitherFuture>>; fn respond_to(self, req: &HttpRequest) -> Self::Future { match self { - Some(t) => EitherFuture::A(t.respond_to(req).into_future()), - None => EitherFuture::B(ok(Response::build(StatusCode::NOT_FOUND).finish())), + Some(t) => EitherFuture::Left(t.respond_to(req)), + None => { + EitherFuture::Right(ok(Response::build(StatusCode::NOT_FOUND).finish())) + } } } } @@ -104,23 +109,21 @@ where { type Error = Error; type Future = EitherFuture< - ResponseFuture<::Future>, - FutureResult, + ResponseFuture, + Ready>, >; fn respond_to(self, req: &HttpRequest) -> Self::Future { match self { - Ok(val) => { - EitherFuture::A(ResponseFuture::new(val.respond_to(req).into_future())) - } - Err(e) => EitherFuture::B(err(e.into())), + Ok(val) => EitherFuture::Left(ResponseFuture::new(val.respond_to(req))), + Err(e) => EitherFuture::Right(err(e.into())), } } } impl Responder for ResponseBuilder { type Error = Error; - type Future = FutureResult; + type Future = Ready>; #[inline] fn respond_to(mut self, _: &HttpRequest) -> Self::Future { @@ -130,7 +133,7 @@ impl Responder for ResponseBuilder { impl Responder for () { type Error = Error; - type Future = FutureResult; + type Future = Ready>; fn respond_to(self, _: &HttpRequest) -> Self::Future { ok(Response::build(StatusCode::OK).finish()) @@ -146,7 +149,7 @@ where fn respond_to(self, req: &HttpRequest) -> Self::Future { CustomResponderFut { - fut: self.0.respond_to(req).into_future(), + fut: self.0.respond_to(req), status: Some(self.1), headers: None, } @@ -155,7 +158,7 @@ where impl Responder for &'static str { type Error = Error; - type Future = FutureResult; + type Future = Ready>; fn respond_to(self, _: &HttpRequest) -> Self::Future { ok(Response::build(StatusCode::OK) @@ -166,7 +169,7 @@ impl Responder for &'static str { impl Responder for &'static [u8] { type Error = Error; - type Future = FutureResult; + type Future = Ready>; fn respond_to(self, _: &HttpRequest) -> Self::Future { ok(Response::build(StatusCode::OK) @@ -177,7 +180,7 @@ impl Responder for &'static [u8] { impl Responder for String { type Error = Error; - type Future = FutureResult; + type Future = Ready>; fn respond_to(self, _: &HttpRequest) -> Self::Future { ok(Response::build(StatusCode::OK) @@ -188,7 +191,7 @@ impl Responder for String { impl<'a> Responder for &'a String { type Error = Error; - type Future = FutureResult; + type Future = Ready>; fn respond_to(self, _: &HttpRequest) -> Self::Future { ok(Response::build(StatusCode::OK) @@ -199,7 +202,7 @@ impl<'a> Responder for &'a String { impl Responder for Bytes { type Error = Error; - type Future = FutureResult; + type Future = Ready>; fn respond_to(self, _: &HttpRequest) -> Self::Future { ok(Response::build(StatusCode::OK) @@ -210,7 +213,7 @@ impl Responder for Bytes { impl Responder for BytesMut { type Error = Error; - type Future = FutureResult; + type Future = Ready>; fn respond_to(self, _: &HttpRequest) -> Self::Future { ok(Response::build(StatusCode::OK) @@ -299,34 +302,40 @@ impl Responder for CustomResponder { fn respond_to(self, req: &HttpRequest) -> Self::Future { CustomResponderFut { - fut: self.responder.respond_to(req).into_future(), + fut: self.responder.respond_to(req), status: self.status, headers: self.headers, } } } +#[pin_project] pub struct CustomResponderFut { - fut: ::Future, + #[pin] + fut: T::Future, status: Option, headers: Option, } impl Future for CustomResponderFut { - type Item = Response; - type Error = T::Error; + type Output = Result; - fn poll(&mut self) -> Poll { - let mut res = try_ready!(self.fut.poll()); - if let Some(status) = self.status { + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.project(); + + let mut res = match ready!(this.fut.poll(cx)) { + Ok(res) => res, + Err(e) => return Poll::Ready(Err(e)), + }; + if let Some(status) = this.status.take() { *res.status_mut() = status; } - if let Some(ref headers) = self.headers { + if let Some(ref headers) = this.headers { for (k, v) in headers { res.headers_mut().insert(k.clone(), v.clone()); } } - Ok(Async::Ready(res)) + Poll::Ready(Ok(res)) } } @@ -336,8 +345,7 @@ impl Future for CustomResponderFut { /// # use futures::future::{ok, Future}; /// use actix_web::{Either, Error, HttpResponse}; /// -/// type RegisterResult = -/// Either>>; +/// type RegisterResult = Either>; /// /// fn index() -> RegisterResult { /// if is_a_variant() { @@ -346,9 +354,9 @@ impl Future for CustomResponderFut { /// } else { /// Either::B( /// // <- Right variant -/// Box::new(ok(HttpResponse::Ok() +/// Ok(HttpResponse::Ok() /// .content_type("text/html") -/// .body("Hello!"))) +/// .body("Hello!")) /// ) /// } /// } @@ -369,97 +377,85 @@ where B: Responder, { type Error = Error; - type Future = EitherResponder< - ::Future, - ::Future, - >; + type Future = EitherResponder; fn respond_to(self, req: &HttpRequest) -> Self::Future { match self { - Either::A(a) => EitherResponder::A(a.respond_to(req).into_future()), - Either::B(b) => EitherResponder::B(b.respond_to(req).into_future()), + Either::A(a) => EitherResponder::A(a.respond_to(req)), + Either::B(b) => EitherResponder::B(b.respond_to(req)), } } } +#[pin_project] pub enum EitherResponder where - A: Future, - A::Error: Into, - B: Future, - B::Error: Into, + A: Responder, + B: Responder, { - A(A), - B(B), + A(#[pin] A::Future), + B(#[pin] B::Future), } impl Future for EitherResponder where - A: Future, - A::Error: Into, - B: Future, - B::Error: Into, + A: Responder, + B: Responder, { - type Item = Response; - type Error = Error; + type Output = Result; - fn poll(&mut self) -> Poll { - match self { - EitherResponder::A(ref mut fut) => Ok(fut.poll().map_err(|e| e.into())?), - EitherResponder::B(ref mut fut) => Ok(fut.poll().map_err(|e| e.into())?), + #[project] + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + #[project] + match self.project() { + EitherResponder::A(fut) => { + Poll::Ready(ready!(fut.poll(cx)).map_err(|e| e.into())) + } + EitherResponder::B(fut) => { + Poll::Ready(ready!(fut.poll(cx).map_err(|e| e.into()))) + } } } } -impl Responder for Box> -where - I: Responder + 'static, - E: Into + 'static, -{ - type Error = Error; - type Future = Box>; - - #[inline] - fn respond_to(self, req: &HttpRequest) -> Self::Future { - let req = req.clone(); - Box::new( - self.map_err(|e| e.into()) - .and_then(move |r| ResponseFuture(r.respond_to(&req).into_future())), - ) - } -} - impl Responder for InternalError where T: std::fmt::Debug + std::fmt::Display + 'static, { type Error = Error; - type Future = Result; + type Future = Ready>; fn respond_to(self, _: &HttpRequest) -> Self::Future { let err: Error = self.into(); - Ok(err.into()) + ok(err.into()) } } -pub struct ResponseFuture(T); +#[pin_project] +pub struct ResponseFuture { + #[pin] + fut: T, + _t: PhantomData, +} -impl ResponseFuture { +impl ResponseFuture { pub fn new(fut: T) -> Self { - ResponseFuture(fut) + ResponseFuture { + fut, + _t: PhantomData, + } } } -impl Future for ResponseFuture +impl Future for ResponseFuture where - T: Future, - T::Error: Into, + T: Future>, + E: Into, { - type Item = Response; - type Error = Error; + type Output = Result; - fn poll(&mut self) -> Poll { - Ok(self.0.poll().map_err(|e| e.into())?) + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + Poll::Ready(ready!(self.project().fut.poll(cx)).map_err(|e| e.into())) } } @@ -476,26 +472,31 @@ pub(crate) mod tests { #[test] fn test_option_responder() { - let mut srv = init_service( - App::new() - .service(web::resource("/none").to(|| -> Option<&'static str> { None })) - .service(web::resource("/some").to(|| Some("some"))), - ); + block_on(async { + let mut srv = init_service( + App::new() + .service( + web::resource("/none").to(|| -> Option<&'static str> { None }), + ) + .service(web::resource("/some").to(|| Some("some"))), + ) + .await; - let req = TestRequest::with_uri("/none").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); + let req = TestRequest::with_uri("/none").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); - let req = TestRequest::with_uri("/some").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - match resp.response().body() { - ResponseBody::Body(Body::Bytes(ref b)) => { - let bytes: Bytes = b.clone().into(); - assert_eq!(bytes, Bytes::from_static(b"some")); + let req = TestRequest::with_uri("/some").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + match resp.response().body() { + ResponseBody::Body(Body::Bytes(ref b)) => { + let bytes: Bytes = b.clone().into(); + assert_eq!(bytes, Bytes::from_static(b"some")); + } + _ => panic!(), } - _ => panic!(), - } + }) } pub(crate) trait BodyTest { @@ -526,142 +527,155 @@ pub(crate) mod tests { #[test] fn test_responder() { - let req = TestRequest::default().to_http_request(); + block_on(async { + let req = TestRequest::default().to_http_request(); - let resp: HttpResponse = block_on(().respond_to(&req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!(*resp.body().body(), Body::Empty); + let resp: HttpResponse = ().respond_to(&req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(*resp.body().body(), Body::Empty); - let resp: HttpResponse = block_on("test".respond_to(&req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!(resp.body().bin_ref(), b"test"); - assert_eq!( - resp.headers().get(CONTENT_TYPE).unwrap(), - HeaderValue::from_static("text/plain; charset=utf-8") - ); + let resp: HttpResponse = "test".respond_to(&req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.body().bin_ref(), b"test"); + assert_eq!( + resp.headers().get(CONTENT_TYPE).unwrap(), + HeaderValue::from_static("text/plain; charset=utf-8") + ); - let resp: HttpResponse = block_on(b"test".respond_to(&req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!(resp.body().bin_ref(), b"test"); - assert_eq!( - resp.headers().get(CONTENT_TYPE).unwrap(), - HeaderValue::from_static("application/octet-stream") - ); + let resp: HttpResponse = b"test".respond_to(&req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.body().bin_ref(), b"test"); + assert_eq!( + resp.headers().get(CONTENT_TYPE).unwrap(), + HeaderValue::from_static("application/octet-stream") + ); - let resp: HttpResponse = block_on("test".to_string().respond_to(&req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!(resp.body().bin_ref(), b"test"); - assert_eq!( - resp.headers().get(CONTENT_TYPE).unwrap(), - HeaderValue::from_static("text/plain; charset=utf-8") - ); + let resp: HttpResponse = "test".to_string().respond_to(&req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.body().bin_ref(), b"test"); + assert_eq!( + resp.headers().get(CONTENT_TYPE).unwrap(), + HeaderValue::from_static("text/plain; charset=utf-8") + ); - let resp: HttpResponse = - block_on((&"test".to_string()).respond_to(&req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!(resp.body().bin_ref(), b"test"); - assert_eq!( - resp.headers().get(CONTENT_TYPE).unwrap(), - HeaderValue::from_static("text/plain; charset=utf-8") - ); + let resp: HttpResponse = + (&"test".to_string()).respond_to(&req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.body().bin_ref(), b"test"); + assert_eq!( + resp.headers().get(CONTENT_TYPE).unwrap(), + HeaderValue::from_static("text/plain; charset=utf-8") + ); - let resp: HttpResponse = - block_on(Bytes::from_static(b"test").respond_to(&req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!(resp.body().bin_ref(), b"test"); - assert_eq!( - resp.headers().get(CONTENT_TYPE).unwrap(), - HeaderValue::from_static("application/octet-stream") - ); + let resp: HttpResponse = + Bytes::from_static(b"test").respond_to(&req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.body().bin_ref(), b"test"); + assert_eq!( + resp.headers().get(CONTENT_TYPE).unwrap(), + HeaderValue::from_static("application/octet-stream") + ); - let resp: HttpResponse = - block_on(BytesMut::from(b"test".as_ref()).respond_to(&req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!(resp.body().bin_ref(), b"test"); - assert_eq!( - resp.headers().get(CONTENT_TYPE).unwrap(), - HeaderValue::from_static("application/octet-stream") - ); - - // InternalError - let resp: HttpResponse = - error::InternalError::new("err", StatusCode::BAD_REQUEST) + let resp: HttpResponse = BytesMut::from(b"test".as_ref()) .respond_to(&req) + .await .unwrap(); - assert_eq!(resp.status(), StatusCode::BAD_REQUEST); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.body().bin_ref(), b"test"); + assert_eq!( + resp.headers().get(CONTENT_TYPE).unwrap(), + HeaderValue::from_static("application/octet-stream") + ); + + // InternalError + let resp: HttpResponse = + error::InternalError::new("err", StatusCode::BAD_REQUEST) + .respond_to(&req) + .await + .unwrap(); + assert_eq!(resp.status(), StatusCode::BAD_REQUEST); + }) } #[test] fn test_result_responder() { - let req = TestRequest::default().to_http_request(); + block_on(async { + let req = TestRequest::default().to_http_request(); - // Result - let resp: HttpResponse = - block_on(Ok::<_, Error>("test".to_string()).respond_to(&req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!(resp.body().bin_ref(), b"test"); - assert_eq!( - resp.headers().get(CONTENT_TYPE).unwrap(), - HeaderValue::from_static("text/plain; charset=utf-8") - ); + // Result + let resp: HttpResponse = Ok::<_, Error>("test".to_string()) + .respond_to(&req) + .await + .unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.body().bin_ref(), b"test"); + assert_eq!( + resp.headers().get(CONTENT_TYPE).unwrap(), + HeaderValue::from_static("text/plain; charset=utf-8") + ); - let res = block_on( - Err::(error::InternalError::new("err", StatusCode::BAD_REQUEST)) - .respond_to(&req), - ); - assert!(res.is_err()); + let res = Err::(error::InternalError::new( + "err", + StatusCode::BAD_REQUEST, + )) + .respond_to(&req) + .await; + assert!(res.is_err()); + }) } #[test] fn test_custom_responder() { - let req = TestRequest::default().to_http_request(); - let res = block_on( - "test" + block_on(async { + let req = TestRequest::default().to_http_request(); + let res = "test" .to_string() .with_status(StatusCode::BAD_REQUEST) - .respond_to(&req), - ) - .unwrap(); - assert_eq!(res.status(), StatusCode::BAD_REQUEST); - assert_eq!(res.body().bin_ref(), b"test"); + .respond_to(&req) + .await + .unwrap(); + assert_eq!(res.status(), StatusCode::BAD_REQUEST); + assert_eq!(res.body().bin_ref(), b"test"); - let res = block_on( - "test" + let res = "test" .to_string() .with_header("content-type", "json") - .respond_to(&req), - ) - .unwrap(); + .respond_to(&req) + .await + .unwrap(); - assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.body().bin_ref(), b"test"); - assert_eq!( - res.headers().get(CONTENT_TYPE).unwrap(), - HeaderValue::from_static("json") - ); + assert_eq!(res.status(), StatusCode::OK); + assert_eq!(res.body().bin_ref(), b"test"); + assert_eq!( + res.headers().get(CONTENT_TYPE).unwrap(), + HeaderValue::from_static("json") + ); + }) } #[test] fn test_tuple_responder_with_status_code() { - let req = TestRequest::default().to_http_request(); - let res = - block_on(("test".to_string(), StatusCode::BAD_REQUEST).respond_to(&req)) + block_on(async { + let req = TestRequest::default().to_http_request(); + let res = ("test".to_string(), StatusCode::BAD_REQUEST) + .respond_to(&req) + .await .unwrap(); - assert_eq!(res.status(), StatusCode::BAD_REQUEST); - assert_eq!(res.body().bin_ref(), b"test"); + assert_eq!(res.status(), StatusCode::BAD_REQUEST); + assert_eq!(res.body().bin_ref(), b"test"); - let req = TestRequest::default().to_http_request(); - let res = block_on( - ("test".to_string(), StatusCode::OK) + let req = TestRequest::default().to_http_request(); + let res = ("test".to_string(), StatusCode::OK) .with_header("content-type", "json") - .respond_to(&req), - ) - .unwrap(); - assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.body().bin_ref(), b"test"); - assert_eq!( - res.headers().get(CONTENT_TYPE).unwrap(), - HeaderValue::from_static("json") - ); + .respond_to(&req) + .await + .unwrap(); + assert_eq!(res.status(), StatusCode::OK); + assert_eq!(res.body().bin_ref(), b"test"); + assert_eq!( + res.headers().get(CONTENT_TYPE).unwrap(), + HeaderValue::from_static("json") + ); + }) } } diff --git a/src/route.rs b/src/route.rs index 35b842944..fb46dbfd2 100644 --- a/src/route.rs +++ b/src/route.rs @@ -1,9 +1,11 @@ +use std::future::Future; +use std::pin::Pin; use std::rc::Rc; +use std::task::{Context, Poll}; use actix_http::{http::Method, Error}; -use actix_service::{NewService, Service}; -use futures::future::{ok, Either, FutureResult}; -use futures::{Async, Future, IntoFuture, Poll}; +use actix_service::{Service, ServiceFactory}; +use futures::future::{ok, Either, FutureExt, LocalBoxFuture, Ready}; use crate::extract::FromRequest; use crate::guard::{self, Guard}; @@ -17,22 +19,19 @@ type BoxedRouteService = Box< Request = Req, Response = Res, Error = Error, - Future = Either< - FutureResult, - Box>, - >, + Future = LocalBoxFuture<'static, Result>, >, >; type BoxedRouteNewService = Box< - dyn NewService< + dyn ServiceFactory< Config = (), Request = Req, Response = Res, Error = Error, InitError = (), Service = BoxedRouteService, - Future = Box, Error = ()>>, + Future = LocalBoxFuture<'static, Result, ()>>, >, >; @@ -61,7 +60,7 @@ impl Route { } } -impl NewService for Route { +impl ServiceFactory for Route { type Config = (); type Request = ServiceRequest; type Response = ServiceResponse; @@ -78,26 +77,30 @@ impl NewService for Route { } } -type RouteFuture = Box< - dyn Future, Error = ()>, +type RouteFuture = LocalBoxFuture< + 'static, + Result, ()>, >; +#[pin_project::pin_project] pub struct CreateRouteService { + #[pin] fut: RouteFuture, guards: Rc>>, } impl Future for CreateRouteService { - type Item = RouteService; - type Error = (); + type Output = Result; - fn poll(&mut self) -> Poll { - match self.fut.poll()? { - Async::Ready(service) => Ok(Async::Ready(RouteService { + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.project(); + + match this.fut.poll(cx)? { + Poll::Ready(service) => Poll::Ready(Ok(RouteService { service, - guards: self.guards.clone(), + guards: this.guards.clone(), })), - Async::NotReady => Ok(Async::NotReady), + Poll::Pending => Poll::Pending, } } } @@ -122,17 +125,14 @@ impl Service for RouteService { type Request = ServiceRequest; type Response = ServiceResponse; type Error = Error; - type Future = Either< - FutureResult, - Box>, - >; + type Future = LocalBoxFuture<'static, Result>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.service.poll_ready() + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.service.poll_ready(cx) } fn call(&mut self, req: ServiceRequest) -> Self::Future { - self.service.call(req) + self.service.call(req).boxed_local() } } @@ -249,8 +249,8 @@ impl Route { /// } /// /// /// extract path info using serde - /// fn index(info: web::Path) -> impl Future { - /// ok("Hello World!") + /// async fn index(info: web::Path) -> Result<&'static str, Error> { + /// Ok("Hello World!") /// } /// /// fn main() { @@ -261,13 +261,13 @@ impl Route { /// } /// ``` #[allow(clippy::wrong_self_convention)] - pub fn to_async(mut self, handler: F) -> Self + pub fn to_async(mut self, handler: F) -> Self where - F: AsyncFactory, + F: AsyncFactory, T: FromRequest + 'static, - R: IntoFuture + 'static, - R::Item: Responder, - R::Error: Into, + R: Future> + 'static, + O: Responder + 'static, + E: Into + 'static, { self.service = Box::new(RouteNewService::new(Extract::new(AsyncHandler::new( handler, @@ -278,14 +278,14 @@ impl Route { struct RouteNewService where - T: NewService, + T: ServiceFactory, { service: T, } impl RouteNewService where - T: NewService< + T: ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -300,9 +300,9 @@ where } } -impl NewService for RouteNewService +impl ServiceFactory for RouteNewService where - T: NewService< + T: ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -318,19 +318,20 @@ where type Error = Error; type InitError = (); type Service = BoxedRouteService; - type Future = Box>; + type Future = LocalBoxFuture<'static, Result>; fn new_service(&self, _: &()) -> Self::Future { - Box::new( - self.service - .new_service(&()) - .map_err(|_| ()) - .and_then(|service| { + self.service + .new_service(&()) + .map(|result| match result { + Ok(service) => { let service: BoxedRouteService<_, _> = Box::new(RouteServiceWrapper { service }); Ok(service) - }), - ) + } + Err(_) => Err(()), + }) + .boxed_local() } } @@ -350,25 +351,30 @@ where type Request = ServiceRequest; type Response = ServiceResponse; type Error = Error; - type Future = Either< - FutureResult, - Box>, - >; + type Future = LocalBoxFuture<'static, Result>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.service.poll_ready().map_err(|(e, _)| e) + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.service.poll_ready(cx).map_err(|(e, _)| e) } fn call(&mut self, req: ServiceRequest) -> Self::Future { - let mut fut = self.service.call(req); - match fut.poll() { - Ok(Async::Ready(res)) => Either::A(ok(res)), - Err((e, req)) => Either::A(ok(req.error_response(e))), - Ok(Async::NotReady) => Either::B(Box::new(fut.then(|res| match res { + // let mut fut = self.service.call(req); + self.service + .call(req) + .map(|res| match res { Ok(res) => Ok(res), Err((err, req)) => Ok(req.error_response(err)), - }))), - } + }) + .boxed_local() + + // match fut.poll() { + // Poll::Ready(Ok(res)) => Either::Left(ok(res)), + // Poll::Ready(Err((e, req))) => Either::Left(ok(req.error_response(e))), + // Poll::Pending => Either::Right(Box::new(fut.then(|res| match res { + // Ok(res) => Ok(res), + // Err((err, req)) => Ok(req.error_response(err)), + // }))), + // } } } @@ -379,11 +385,11 @@ mod tests { use bytes::Bytes; use futures::Future; use serde_derive::Serialize; - use tokio_timer::sleep; + use tokio_timer::delay_for; use crate::http::{Method, StatusCode}; - use crate::test::{call_service, init_service, read_body, TestRequest}; - use crate::{error, web, App, HttpResponse}; + use crate::test::{block_on, call_service, init_service, read_body, TestRequest}; + use crate::{error, web, App, Error, HttpResponse}; #[derive(Serialize, PartialEq, Debug)] struct MyObject { @@ -392,68 +398,75 @@ mod tests { #[test] fn test_route() { - let mut srv = init_service( - App::new() - .service( - web::resource("/test") - .route(web::get().to(|| HttpResponse::Ok())) - .route(web::put().to(|| { - Err::(error::ErrorBadRequest("err")) - })) - .route(web::post().to_async(|| { - sleep(Duration::from_millis(100)) - .then(|_| HttpResponse::Created()) - })) - .route(web::delete().to_async(|| { - sleep(Duration::from_millis(100)).then(|_| { + block_on(async { + let mut srv = init_service( + App::new() + .service( + web::resource("/test") + .route(web::get().to(|| HttpResponse::Ok())) + .route(web::put().to(|| { Err::(error::ErrorBadRequest("err")) - }) - })), - ) - .service(web::resource("/json").route(web::get().to_async(|| { - sleep(Duration::from_millis(25)).then(|_| { - Ok::<_, crate::Error>(web::Json(MyObject { - name: "test".to_string(), - })) - }) - }))), - ); + })) + .route(web::post().to_async(|| { + async { + delay_for(Duration::from_millis(100)).await; + Ok::<_, Error>(HttpResponse::Created()) + } + })) + .route(web::delete().to_async(|| { + async { + delay_for(Duration::from_millis(100)).await; + Err::(error::ErrorBadRequest("err")) + } + })), + ) + .service(web::resource("/json").route(web::get().to_async(|| { + async { + delay_for(Duration::from_millis(25)).await; + Ok::<_, Error>(web::Json(MyObject { + name: "test".to_string(), + })) + } + }))), + ) + .await; - let req = TestRequest::with_uri("/test") - .method(Method::GET) - .to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/test") + .method(Method::GET) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); - let req = TestRequest::with_uri("/test") - .method(Method::POST) - .to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::CREATED); + let req = TestRequest::with_uri("/test") + .method(Method::POST) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::CREATED); - let req = TestRequest::with_uri("/test") - .method(Method::PUT) - .to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::BAD_REQUEST); + let req = TestRequest::with_uri("/test") + .method(Method::PUT) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::BAD_REQUEST); - let req = TestRequest::with_uri("/test") - .method(Method::DELETE) - .to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::BAD_REQUEST); + let req = TestRequest::with_uri("/test") + .method(Method::DELETE) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::BAD_REQUEST); - let req = TestRequest::with_uri("/test") - .method(Method::HEAD) - .to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); + let req = TestRequest::with_uri("/test") + .method(Method::HEAD) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); - let req = TestRequest::with_uri("/json").to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/json").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); - let body = read_body(resp); - assert_eq!(body, Bytes::from_static(b"{\"name\":\"test\"}")); + let body = read_body(resp).await; + assert_eq!(body, Bytes::from_static(b"{\"name\":\"test\"}")); + }) } } diff --git a/src/scope.rs b/src/scope.rs index c152bc334..2e59352d6 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -1,15 +1,16 @@ use std::cell::RefCell; use std::fmt; +use std::pin::Pin; use std::rc::Rc; +use std::task::{Context, Poll}; use actix_http::{Extensions, Response}; use actix_router::{ResourceDef, ResourceInfo, Router}; use actix_service::boxed::{self, BoxedNewService, BoxedService}; use actix_service::{ - apply_transform, IntoNewService, IntoTransform, NewService, Service, Transform, + apply, apply_fn_factory, IntoServiceFactory, Service, ServiceFactory, Transform, }; -use futures::future::{ok, Either, Future, FutureResult}; -use futures::{Async, IntoFuture, Poll}; +use futures::future::{ok, Either, Future, LocalBoxFuture, Ready}; use crate::config::ServiceConfig; use crate::data::Data; @@ -20,16 +21,13 @@ use crate::resource::Resource; use crate::rmap::ResourceMap; use crate::route::Route; use crate::service::{ - ServiceFactory, ServiceFactoryWrapper, ServiceRequest, ServiceResponse, + AppServiceFactory, ServiceFactoryWrapper, ServiceRequest, ServiceResponse, }; type Guards = Vec>; type HttpService = BoxedService; type HttpNewService = BoxedNewService<(), ServiceRequest, ServiceResponse, Error, ()>; -type BoxedResponse = Either< - FutureResult, - Box>, ->; +type BoxedResponse = LocalBoxFuture<'static, Result>; /// Resources scope. /// @@ -64,7 +62,7 @@ pub struct Scope { endpoint: T, rdef: String, data: Option, - services: Vec>, + services: Vec>, guards: Vec>, default: Rc>>>, external: Vec, @@ -90,7 +88,7 @@ impl Scope { impl Scope where - T: NewService< + T: ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -285,8 +283,8 @@ where /// If default resource is not registered, app's default resource is being used. pub fn default_service(mut self, f: F) -> Self where - F: IntoNewService, - U: NewService< + F: IntoServiceFactory, + U: ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -295,8 +293,8 @@ where U::InitError: fmt::Debug, { // create and configure default resource - self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::new_service( - f.into_new_service().map_init_err(|e| { + self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::factory( + f.into_factory().map_init_err(|e| { log::error!("Can not construct default service: {:?}", e) }), ))))); @@ -313,11 +311,11 @@ where /// ServiceResponse. /// /// Use middleware when you need to read or modify *every* request in some way. - pub fn wrap( + pub fn wrap( self, - mw: F, + mw: M, ) -> Scope< - impl NewService< + impl ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -333,11 +331,9 @@ where Error = Error, InitError = (), >, - F: IntoTransform, { - let endpoint = apply_transform(mw, self.endpoint); Scope { - endpoint, + endpoint: apply(mw, self.endpoint), rdef: self.rdef, data: self.data, guards: self.guards, @@ -368,13 +364,16 @@ where /// fn main() { /// let app = App::new().service( /// web::scope("/app") - /// .wrap_fn(|req, srv| - /// srv.call(req).map(|mut res| { + /// .wrap_fn(|req, srv| { + /// let fut = srv.call(req); + /// async { + /// let mut res = fut.await?; /// res.headers_mut().insert( /// CONTENT_TYPE, HeaderValue::from_static("text/plain"), /// ); - /// res - /// })) + /// Ok(res) + /// } + /// }) /// .route("/index.html", web::get().to(index))); /// } /// ``` @@ -382,7 +381,7 @@ where self, mw: F, ) -> Scope< - impl NewService< + impl ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -392,15 +391,24 @@ where > where F: FnMut(ServiceRequest, &mut T::Service) -> R + Clone, - R: IntoFuture, + R: Future>, { - self.wrap(mw) + Scope { + endpoint: apply_fn_factory(self.endpoint, mw), + rdef: self.rdef, + data: self.data, + guards: self.guards, + services: self.services, + default: self.default, + external: self.external, + factory_ref: self.factory_ref, + } } } impl HttpServiceFactory for Scope where - T: NewService< + T: ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -471,7 +479,7 @@ pub struct ScopeFactory { default: Rc>>>, } -impl NewService for ScopeFactory { +impl ServiceFactory for ScopeFactory { type Config = (); type Request = ServiceRequest; type Response = ServiceResponse; @@ -508,14 +516,15 @@ impl NewService for ScopeFactory { /// Create scope service #[doc(hidden)] +#[pin_project::pin_project] pub struct ScopeFactoryResponse { fut: Vec, data: Option>, default: Option, - default_fut: Option>>, + default_fut: Option>>, } -type HttpServiceFut = Box>; +type HttpServiceFut = LocalBoxFuture<'static, Result>; enum CreateScopeServiceItem { Future(Option, Option, HttpServiceFut), @@ -523,16 +532,15 @@ enum CreateScopeServiceItem { } impl Future for ScopeFactoryResponse { - type Item = ScopeService; - type Error = (); + type Output = Result; - fn poll(&mut self) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { let mut done = true; if let Some(ref mut fut) = self.default_fut { - match fut.poll()? { - Async::Ready(default) => self.default = Some(default), - Async::NotReady => done = false, + match Pin::new(fut).poll(cx)? { + Poll::Ready(default) => self.default = Some(default), + Poll::Pending => done = false, } } @@ -543,11 +551,11 @@ impl Future for ScopeFactoryResponse { ref mut path, ref mut guards, ref mut fut, - ) => match fut.poll()? { - Async::Ready(service) => { + ) => match Pin::new(fut).poll(cx)? { + Poll::Ready(service) => { Some((path.take().unwrap(), guards.take(), service)) } - Async::NotReady => { + Poll::Pending => { done = false; None } @@ -573,14 +581,14 @@ impl Future for ScopeFactoryResponse { } router }); - Ok(Async::Ready(ScopeService { + Poll::Ready(Ok(ScopeService { data: self.data.clone(), router: router.finish(), default: self.default.take(), _ready: None, })) } else { - Ok(Async::NotReady) + Poll::Pending } } } @@ -596,10 +604,10 @@ impl Service for ScopeService { type Request = ServiceRequest; type Response = ServiceResponse; type Error = Error; - type Future = Either>; + type Future = Either>>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, mut req: ServiceRequest) -> Self::Future { @@ -618,12 +626,12 @@ impl Service for ScopeService { if let Some(ref data) = self.data { req.set_data_container(data.clone()); } - Either::A(srv.call(req)) + Either::Left(srv.call(req)) } else if let Some(ref mut default) = self.default { - Either::A(default.call(req)) + Either::Left(default.call(req)) } else { let req = req.into_parts().0; - Either::B(ok(ServiceResponse::new(req, Response::NotFound().finish()))) + Either::Right(ok(ServiceResponse::new(req, Response::NotFound().finish()))) } } } @@ -639,7 +647,7 @@ impl ScopeEndpoint { } } -impl NewService for ScopeEndpoint { +impl ServiceFactory for ScopeEndpoint { type Config = (); type Request = ServiceRequest; type Response = ServiceResponse; @@ -657,367 +665,416 @@ impl NewService for ScopeEndpoint { mod tests { use actix_service::Service; use bytes::Bytes; - use futures::{Future, IntoFuture}; + use futures::future::ok; + use futures::Future; use crate::dev::{Body, ResponseBody}; use crate::http::{header, HeaderValue, Method, StatusCode}; + use crate::middleware::DefaultHeaders; use crate::service::{ServiceRequest, ServiceResponse}; use crate::test::{block_on, call_service, init_service, read_body, TestRequest}; use crate::{guard, web, App, Error, HttpRequest, HttpResponse}; #[test] fn test_scope() { - let mut srv = init_service( - App::new().service( - web::scope("/app") - .service(web::resource("/path1").to(|| HttpResponse::Ok())), - ), - ); + block_on(async { + let mut srv = init_service( + App::new().service( + web::scope("/app") + .service(web::resource("/path1").to(|| HttpResponse::Ok())), + ), + ) + .await; - let req = TestRequest::with_uri("/app/path1").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/app/path1").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + }) } #[test] fn test_scope_root() { - let mut srv = init_service( - App::new().service( - web::scope("/app") - .service(web::resource("").to(|| HttpResponse::Ok())) - .service(web::resource("/").to(|| HttpResponse::Created())), - ), - ); + block_on(async { + let mut srv = init_service( + App::new().service( + web::scope("/app") + .service(web::resource("").to(|| HttpResponse::Ok())) + .service(web::resource("/").to(|| HttpResponse::Created())), + ), + ) + .await; - let req = TestRequest::with_uri("/app").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/app").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); - let req = TestRequest::with_uri("/app/").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::CREATED); + let req = TestRequest::with_uri("/app/").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::CREATED); + }) } #[test] fn test_scope_root2() { - let mut srv = init_service(App::new().service( - web::scope("/app/").service(web::resource("").to(|| HttpResponse::Ok())), - )); + block_on(async { + let mut srv = init_service(App::new().service( + web::scope("/app/").service(web::resource("").to(|| HttpResponse::Ok())), + )) + .await; - let req = TestRequest::with_uri("/app").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); + let req = TestRequest::with_uri("/app").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); - let req = TestRequest::with_uri("/app/").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/app/").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + }) } #[test] fn test_scope_root3() { - let mut srv = init_service(App::new().service( - web::scope("/app/").service(web::resource("/").to(|| HttpResponse::Ok())), - )); + block_on(async { + let mut srv = init_service( + App::new().service( + web::scope("/app/") + .service(web::resource("/").to(|| HttpResponse::Ok())), + ), + ) + .await; - let req = TestRequest::with_uri("/app").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); + let req = TestRequest::with_uri("/app").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); - let req = TestRequest::with_uri("/app/").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); + let req = TestRequest::with_uri("/app/").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + }) } #[test] fn test_scope_route() { - let mut srv = init_service( - App::new().service( - web::scope("app") - .route("/path1", web::get().to(|| HttpResponse::Ok())) - .route("/path1", web::delete().to(|| HttpResponse::Ok())), - ), - ); + block_on(async { + let mut srv = init_service( + App::new().service( + web::scope("app") + .route("/path1", web::get().to(|| HttpResponse::Ok())) + .route("/path1", web::delete().to(|| HttpResponse::Ok())), + ), + ) + .await; - let req = TestRequest::with_uri("/app/path1").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/app/path1").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); - let req = TestRequest::with_uri("/app/path1") - .method(Method::DELETE) - .to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/app/path1") + .method(Method::DELETE) + .to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); - let req = TestRequest::with_uri("/app/path1") - .method(Method::POST) - .to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); + let req = TestRequest::with_uri("/app/path1") + .method(Method::POST) + .to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + }) } #[test] fn test_scope_route_without_leading_slash() { - let mut srv = init_service( - App::new().service( - web::scope("app").service( - web::resource("path1") - .route(web::get().to(|| HttpResponse::Ok())) - .route(web::delete().to(|| HttpResponse::Ok())), + block_on(async { + let mut srv = init_service( + App::new().service( + web::scope("app").service( + web::resource("path1") + .route(web::get().to(|| HttpResponse::Ok())) + .route(web::delete().to(|| HttpResponse::Ok())), + ), ), - ), - ); + ) + .await; - let req = TestRequest::with_uri("/app/path1").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/app/path1").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); - let req = TestRequest::with_uri("/app/path1") - .method(Method::DELETE) - .to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/app/path1") + .method(Method::DELETE) + .to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); - let req = TestRequest::with_uri("/app/path1") - .method(Method::POST) - .to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); + let req = TestRequest::with_uri("/app/path1") + .method(Method::POST) + .to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); + }) } #[test] fn test_scope_guard() { - let mut srv = init_service( - App::new().service( - web::scope("/app") - .guard(guard::Get()) - .service(web::resource("/path1").to(|| HttpResponse::Ok())), - ), - ); + block_on(async { + let mut srv = init_service( + App::new().service( + web::scope("/app") + .guard(guard::Get()) + .service(web::resource("/path1").to(|| HttpResponse::Ok())), + ), + ) + .await; - let req = TestRequest::with_uri("/app/path1") - .method(Method::POST) - .to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); + let req = TestRequest::with_uri("/app/path1") + .method(Method::POST) + .to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); - let req = TestRequest::with_uri("/app/path1") - .method(Method::GET) - .to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/app/path1") + .method(Method::GET) + .to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + }) } #[test] fn test_scope_variable_segment() { - let mut srv = - init_service(App::new().service(web::scope("/ab-{project}").service( - web::resource("/path1").to(|r: HttpRequest| { - HttpResponse::Ok() - .body(format!("project: {}", &r.match_info()["project"])) - }), - ))); + block_on(async { + let mut srv = + init_service(App::new().service(web::scope("/ab-{project}").service( + web::resource("/path1").to(|r: HttpRequest| { + HttpResponse::Ok() + .body(format!("project: {}", &r.match_info()["project"])) + }), + ))) + .await; - let req = TestRequest::with_uri("/ab-project1/path1").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/ab-project1/path1").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); - match resp.response().body() { - ResponseBody::Body(Body::Bytes(ref b)) => { - let bytes: Bytes = b.clone().into(); - assert_eq!(bytes, Bytes::from_static(b"project: project1")); + match resp.response().body() { + ResponseBody::Body(Body::Bytes(ref b)) => { + let bytes: Bytes = b.clone().into(); + assert_eq!(bytes, Bytes::from_static(b"project: project1")); + } + _ => panic!(), } - _ => panic!(), - } - let req = TestRequest::with_uri("/aa-project1/path1").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); + let req = TestRequest::with_uri("/aa-project1/path1").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + }) } #[test] fn test_nested_scope() { - let mut srv = init_service( - App::new().service( - web::scope("/app") - .service(web::scope("/t1").service( + block_on(async { + let mut srv = + init_service(App::new().service( + web::scope("/app").service(web::scope("/t1").service( web::resource("/path1").to(|| HttpResponse::Created()), )), - ), - ); + )) + .await; - let req = TestRequest::with_uri("/app/t1/path1").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::CREATED); + let req = TestRequest::with_uri("/app/t1/path1").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::CREATED); + }) } #[test] fn test_nested_scope_no_slash() { - let mut srv = init_service( - App::new().service( - web::scope("/app") - .service(web::scope("t1").service( + block_on(async { + let mut srv = + init_service(App::new().service( + web::scope("/app").service(web::scope("t1").service( web::resource("/path1").to(|| HttpResponse::Created()), )), - ), - ); + )) + .await; - let req = TestRequest::with_uri("/app/t1/path1").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::CREATED); + let req = TestRequest::with_uri("/app/t1/path1").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::CREATED); + }) } #[test] fn test_nested_scope_root() { - let mut srv = init_service( - App::new().service( - web::scope("/app").service( - web::scope("/t1") - .service(web::resource("").to(|| HttpResponse::Ok())) - .service(web::resource("/").to(|| HttpResponse::Created())), + block_on(async { + let mut srv = init_service( + App::new().service( + web::scope("/app").service( + web::scope("/t1") + .service(web::resource("").to(|| HttpResponse::Ok())) + .service(web::resource("/").to(|| HttpResponse::Created())), + ), ), - ), - ); + ) + .await; - let req = TestRequest::with_uri("/app/t1").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/app/t1").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); - let req = TestRequest::with_uri("/app/t1/").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::CREATED); + let req = TestRequest::with_uri("/app/t1/").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::CREATED); + }) } #[test] fn test_nested_scope_filter() { - let mut srv = init_service( - App::new().service( - web::scope("/app").service( - web::scope("/t1") - .guard(guard::Get()) - .service(web::resource("/path1").to(|| HttpResponse::Ok())), + block_on(async { + let mut srv = init_service( + App::new().service( + web::scope("/app").service( + web::scope("/t1") + .guard(guard::Get()) + .service(web::resource("/path1").to(|| HttpResponse::Ok())), + ), ), - ), - ); + ) + .await; - let req = TestRequest::with_uri("/app/t1/path1") - .method(Method::POST) - .to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); + let req = TestRequest::with_uri("/app/t1/path1") + .method(Method::POST) + .to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); - let req = TestRequest::with_uri("/app/t1/path1") - .method(Method::GET) - .to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/app/t1/path1") + .method(Method::GET) + .to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + }) } #[test] fn test_nested_scope_with_variable_segment() { - let mut srv = init_service(App::new().service(web::scope("/app").service( - web::scope("/{project_id}").service(web::resource("/path1").to( - |r: HttpRequest| { - HttpResponse::Created() - .body(format!("project: {}", &r.match_info()["project_id"])) - }, - )), - ))); + block_on(async { + let mut srv = init_service(App::new().service(web::scope("/app").service( + web::scope("/{project_id}").service(web::resource("/path1").to( + |r: HttpRequest| { + HttpResponse::Created() + .body(format!("project: {}", &r.match_info()["project_id"])) + }, + )), + ))) + .await; - let req = TestRequest::with_uri("/app/project_1/path1").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::CREATED); + let req = TestRequest::with_uri("/app/project_1/path1").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::CREATED); - match resp.response().body() { - ResponseBody::Body(Body::Bytes(ref b)) => { - let bytes: Bytes = b.clone().into(); - assert_eq!(bytes, Bytes::from_static(b"project: project_1")); + match resp.response().body() { + ResponseBody::Body(Body::Bytes(ref b)) => { + let bytes: Bytes = b.clone().into(); + assert_eq!(bytes, Bytes::from_static(b"project: project_1")); + } + _ => panic!(), } - _ => panic!(), - } + }) } #[test] fn test_nested2_scope_with_variable_segment() { - let mut srv = init_service(App::new().service(web::scope("/app").service( - web::scope("/{project}").service(web::scope("/{id}").service( - web::resource("/path1").to(|r: HttpRequest| { - HttpResponse::Created().body(format!( - "project: {} - {}", - &r.match_info()["project"], - &r.match_info()["id"], - )) - }), - )), - ))); + block_on(async { + let mut srv = init_service(App::new().service(web::scope("/app").service( + web::scope("/{project}").service(web::scope("/{id}").service( + web::resource("/path1").to(|r: HttpRequest| { + HttpResponse::Created().body(format!( + "project: {} - {}", + &r.match_info()["project"], + &r.match_info()["id"], + )) + }), + )), + ))) + .await; - let req = TestRequest::with_uri("/app/test/1/path1").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::CREATED); + let req = TestRequest::with_uri("/app/test/1/path1").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::CREATED); - match resp.response().body() { - ResponseBody::Body(Body::Bytes(ref b)) => { - let bytes: Bytes = b.clone().into(); - assert_eq!(bytes, Bytes::from_static(b"project: test - 1")); + match resp.response().body() { + ResponseBody::Body(Body::Bytes(ref b)) => { + let bytes: Bytes = b.clone().into(); + assert_eq!(bytes, Bytes::from_static(b"project: test - 1")); + } + _ => panic!(), } - _ => panic!(), - } - let req = TestRequest::with_uri("/app/test/1/path2").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); + let req = TestRequest::with_uri("/app/test/1/path2").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + }) } #[test] fn test_default_resource() { - let mut srv = init_service( - App::new().service( - web::scope("/app") - .service(web::resource("/path1").to(|| HttpResponse::Ok())) - .default_service(|r: ServiceRequest| { - r.into_response(HttpResponse::BadRequest()) - }), - ), - ); + block_on(async { + let mut srv = init_service( + App::new().service( + web::scope("/app") + .service(web::resource("/path1").to(|| HttpResponse::Ok())) + .default_service(|r: ServiceRequest| { + ok(r.into_response(HttpResponse::BadRequest())) + }), + ), + ) + .await; - let req = TestRequest::with_uri("/app/path2").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::BAD_REQUEST); + let req = TestRequest::with_uri("/app/path2").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::BAD_REQUEST); - let req = TestRequest::with_uri("/path2").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); + let req = TestRequest::with_uri("/path2").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + }) } #[test] fn test_default_resource_propagation() { - let mut srv = init_service( - App::new() - .service(web::scope("/app1").default_service( - web::resource("").to(|| HttpResponse::BadRequest()), - )) - .service(web::scope("/app2")) - .default_service(|r: ServiceRequest| { - r.into_response(HttpResponse::MethodNotAllowed()) - }), - ); + block_on(async { + let mut srv = init_service( + App::new() + .service(web::scope("/app1").default_service( + web::resource("").to(|| HttpResponse::BadRequest()), + )) + .service(web::scope("/app2")) + .default_service(|r: ServiceRequest| { + ok(r.into_response(HttpResponse::MethodNotAllowed())) + }), + ) + .await; - let req = TestRequest::with_uri("/non-exist").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); + let req = TestRequest::with_uri("/non-exist").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); - let req = TestRequest::with_uri("/app1/non-exist").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::BAD_REQUEST); + let req = TestRequest::with_uri("/app1/non-exist").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::BAD_REQUEST); - let req = TestRequest::with_uri("/app2/non-exist").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); + let req = TestRequest::with_uri("/app2/non-exist").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); + }) } fn md( req: ServiceRequest, srv: &mut S, - ) -> impl IntoFuture, Error = Error> + ) -> impl Future, Error>> where S: Service< Request = ServiceRequest, @@ -1025,165 +1082,207 @@ mod tests { Error = Error, >, { - srv.call(req).map(|mut res| { + let fut = srv.call(req); + async move { + let mut res = fut.await?; res.headers_mut() .insert(header::CONTENT_TYPE, HeaderValue::from_static("0001")); - res - }) + Ok(res) + } } #[test] fn test_middleware() { - let mut srv = - init_service(App::new().service(web::scope("app").wrap(md).service( - web::resource("/test").route(web::get().to(|| HttpResponse::Ok())), - ))); - let req = TestRequest::with_uri("/app/test").to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - HeaderValue::from_static("0001") - ); + block_on(async { + let mut srv = init_service( + App::new().service( + web::scope("app") + .wrap(DefaultHeaders::new().header( + header::CONTENT_TYPE, + HeaderValue::from_static("0001"), + )) + .service( + web::resource("/test") + .route(web::get().to(|| HttpResponse::Ok())), + ), + ), + ) + .await; + + let req = TestRequest::with_uri("/app/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + HeaderValue::from_static("0001") + ); + }) } #[test] fn test_middleware_fn() { - let mut srv = init_service( - App::new().service( - web::scope("app") - .wrap_fn(|req, srv| { - srv.call(req).map(|mut res| { - res.headers_mut().insert( - header::CONTENT_TYPE, - HeaderValue::from_static("0001"), - ); - res + block_on(async { + let mut srv = init_service( + App::new().service( + web::scope("app") + .wrap_fn(|req, srv| { + let fut = srv.call(req); + async move { + let mut res = fut.await?; + res.headers_mut().insert( + header::CONTENT_TYPE, + HeaderValue::from_static("0001"), + ); + Ok(res) + } }) - }) - .route("/test", web::get().to(|| HttpResponse::Ok())), - ), - ); - let req = TestRequest::with_uri("/app/test").to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - HeaderValue::from_static("0001") - ); + .route("/test", web::get().to(|| HttpResponse::Ok())), + ), + ) + .await; + + let req = TestRequest::with_uri("/app/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + HeaderValue::from_static("0001") + ); + }) } #[test] fn test_override_data() { - let mut srv = init_service(App::new().data(1usize).service( - web::scope("app").data(10usize).route( - "/t", - web::get().to(|data: web::Data| { - assert_eq!(*data, 10); - let _ = data.clone(); - HttpResponse::Ok() - }), - ), - )); + block_on(async { + let mut srv = init_service(App::new().data(1usize).service( + web::scope("app").data(10usize).route( + "/t", + web::get().to(|data: web::Data| { + assert_eq!(*data, 10); + let _ = data.clone(); + HttpResponse::Ok() + }), + ), + )) + .await; - let req = TestRequest::with_uri("/app/t").to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/app/t").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + }) } #[test] fn test_override_register_data() { - let mut srv = init_service( - App::new().register_data(web::Data::new(1usize)).service( - web::scope("app") - .register_data(web::Data::new(10usize)) - .route( - "/t", - web::get().to(|data: web::Data| { - assert_eq!(*data, 10); - let _ = data.clone(); - HttpResponse::Ok() - }), - ), - ), - ); + block_on(async { + let mut srv = init_service( + App::new().register_data(web::Data::new(1usize)).service( + web::scope("app") + .register_data(web::Data::new(10usize)) + .route( + "/t", + web::get().to(|data: web::Data| { + assert_eq!(*data, 10); + let _ = data.clone(); + HttpResponse::Ok() + }), + ), + ), + ) + .await; - let req = TestRequest::with_uri("/app/t").to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/app/t").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + }) } #[test] fn test_scope_config() { - let mut srv = - init_service(App::new().service(web::scope("/app").configure(|s| { - s.route("/path1", web::get().to(|| HttpResponse::Ok())); - }))); + block_on(async { + let mut srv = + init_service(App::new().service(web::scope("/app").configure(|s| { + s.route("/path1", web::get().to(|| HttpResponse::Ok())); + }))) + .await; - let req = TestRequest::with_uri("/app/path1").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/app/path1").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + }) } #[test] fn test_scope_config_2() { - let mut srv = - init_service(App::new().service(web::scope("/app").configure(|s| { - s.service(web::scope("/v1").configure(|s| { - s.route("/", web::get().to(|| HttpResponse::Ok())); - })); - }))); + block_on(async { + let mut srv = + init_service(App::new().service(web::scope("/app").configure(|s| { + s.service(web::scope("/v1").configure(|s| { + s.route("/", web::get().to(|| HttpResponse::Ok())); + })); + }))) + .await; - let req = TestRequest::with_uri("/app/v1/").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/app/v1/").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + }) } #[test] fn test_url_for_external() { - let mut srv = - init_service(App::new().service(web::scope("/app").configure(|s| { - s.service(web::scope("/v1").configure(|s| { - s.external_resource( - "youtube", - "https://youtube.com/watch/{video_id}", - ); - s.route( - "/", - web::get().to(|req: HttpRequest| { - HttpResponse::Ok().body(format!( - "{}", - req.url_for("youtube", &["xxxxxx"]).unwrap().as_str() - )) - }), - ); - })); - }))); + block_on(async { + let mut srv = + init_service(App::new().service(web::scope("/app").configure(|s| { + s.service(web::scope("/v1").configure(|s| { + s.external_resource( + "youtube", + "https://youtube.com/watch/{video_id}", + ); + s.route( + "/", + web::get().to(|req: HttpRequest| { + HttpResponse::Ok().body(format!( + "{}", + req.url_for("youtube", &["xxxxxx"]) + .unwrap() + .as_str() + )) + }), + ); + })); + }))) + .await; - let req = TestRequest::with_uri("/app/v1/").to_request(); - let resp = block_on(srv.call(req)).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - let body = read_body(resp); - assert_eq!(body, &b"https://youtube.com/watch/xxxxxx"[..]); + let req = TestRequest::with_uri("/app/v1/").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + let body = read_body(resp).await; + assert_eq!(body, &b"https://youtube.com/watch/xxxxxx"[..]); + }) } #[test] fn test_url_for_nested() { - let mut srv = init_service(App::new().service(web::scope("/a").service( - web::scope("/b").service(web::resource("/c/{stuff}").name("c").route( - web::get().to(|req: HttpRequest| { - HttpResponse::Ok() - .body(format!("{}", req.url_for("c", &["12345"]).unwrap())) - }), - )), - ))); - let req = TestRequest::with_uri("/a/b/c/test").to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); - let body = read_body(resp); - assert_eq!( - body, - Bytes::from_static(b"http://localhost:8080/a/b/c/12345") - ); + block_on(async { + let mut srv = init_service(App::new().service(web::scope("/a").service( + web::scope("/b").service(web::resource("/c/{stuff}").name("c").route( + web::get().to(|req: HttpRequest| { + HttpResponse::Ok() + .body(format!("{}", req.url_for("c", &["12345"]).unwrap())) + }), + )), + ))) + .await; + + let req = TestRequest::with_uri("/a/b/c/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + let body = read_body(resp).await; + assert_eq!( + body, + Bytes::from_static(b"http://localhost:8080/a/b/c/12345") + ); + }) } } diff --git a/src/server.rs b/src/server.rs index 51492eb01..a98d06275 100644 --- a/src/server.rs +++ b/src/server.rs @@ -6,15 +6,15 @@ use actix_http::{body::MessageBody, Error, HttpService, KeepAlive, Request, Resp use actix_rt::System; use actix_server::{Server, ServerBuilder}; use actix_server_config::ServerConfig; -use actix_service::{IntoNewService, NewService}; +use actix_service::{IntoServiceFactory, Service, ServiceFactory}; use parking_lot::Mutex; use net2::TcpBuilder; -#[cfg(feature = "ssl")] -use openssl::ssl::{SslAcceptor, SslAcceptorBuilder}; -#[cfg(feature = "rust-tls")] -use rustls::ServerConfig as RustlsServerConfig; +#[cfg(feature = "openssl")] +use open_ssl::ssl::{SslAcceptor, SslAcceptorBuilder}; +#[cfg(feature = "rustls")] +use rust_tls::ServerConfig as RustlsServerConfig; struct Socket { scheme: &'static str, @@ -51,12 +51,11 @@ struct Config { pub struct HttpServer where F: Fn() -> I + Send + Clone + 'static, - I: IntoNewService, - S: NewService, + I: IntoServiceFactory, + S: ServiceFactory, S::Error: Into, S::InitError: fmt::Debug, S::Response: Into>, - S::Service: 'static, B: MessageBody, { pub(super) factory: F, @@ -71,12 +70,12 @@ where impl HttpServer where F: Fn() -> I + Send + Clone + 'static, - I: IntoNewService, - S: NewService, - S::Error: Into, + I: IntoServiceFactory, + S: ServiceFactory, + S::Error: Into + 'static, S::InitError: fmt::Debug, - S::Response: Into>, - S::Service: 'static, + S::Response: Into> + 'static, + ::Future: 'static, B: MessageBody + 'static, { /// Create new http server with application factory @@ -254,11 +253,11 @@ where Ok(self) } - #[cfg(feature = "ssl")] + #[cfg(feature = "openssl")] /// Use listener for accepting incoming tls connection requests /// /// This method sets alpn protocols to "h2" and "http/1.1" - pub fn listen_ssl( + pub fn listen_openssl( self, lst: net::TcpListener, builder: SslAcceptorBuilder, @@ -266,13 +265,14 @@ where self.listen_ssl_inner(lst, openssl_acceptor(builder)?) } - #[cfg(feature = "ssl")] + #[cfg(feature = "openssl")] fn listen_ssl_inner( mut self, lst: net::TcpListener, acceptor: SslAcceptor, ) -> io::Result { use actix_server::ssl::{OpensslAcceptor, SslError}; + use actix_service::pipeline_factory; let acceptor = OpensslAcceptor::new(acceptor); let factory = self.factory.clone(); @@ -288,7 +288,7 @@ where lst, move || { let c = cfg.lock(); - acceptor.clone().map_err(SslError::Ssl).and_then( + pipeline_factory(acceptor.clone().map_err(SslError::Ssl)).and_then( HttpService::build() .keep_alive(c.keep_alive) .client_timeout(c.client_timeout) @@ -302,7 +302,7 @@ where Ok(self) } - #[cfg(feature = "rust-tls")] + #[cfg(feature = "rustls")] /// Use listener for accepting incoming tls connection requests /// /// This method sets alpn protocols to "h2" and "http/1.1" @@ -314,13 +314,14 @@ where self.listen_rustls_inner(lst, config) } - #[cfg(feature = "rust-tls")] + #[cfg(feature = "rustls")] fn listen_rustls_inner( mut self, lst: net::TcpListener, mut config: RustlsServerConfig, ) -> io::Result { use actix_server::ssl::{RustlsAcceptor, SslError}; + use actix_service::pipeline_factory; let protos = vec!["h2".to_string().into(), "http/1.1".to_string().into()]; config.set_protocols(&protos); @@ -339,7 +340,7 @@ where lst, move || { let c = cfg.lock(); - acceptor.clone().map_err(SslError::Ssl).and_then( + pipeline_factory(acceptor.clone().map_err(SslError::Ssl)).and_then( HttpService::build() .keep_alive(c.keep_alive) .client_timeout(c.client_timeout) @@ -397,11 +398,11 @@ where } } - #[cfg(feature = "ssl")] + #[cfg(feature = "openssl")] /// Start listening for incoming tls connections. /// /// This method sets alpn protocols to "h2" and "http/1.1" - pub fn bind_ssl( + pub fn bind_openssl( mut self, addr: A, builder: SslAcceptorBuilder, @@ -419,7 +420,7 @@ where Ok(self) } - #[cfg(feature = "rust-tls")] + #[cfg(feature = "rustls")] /// Start listening for incoming tls connections. /// /// This method sets alpn protocols to "h2" and "http/1.1" @@ -435,7 +436,7 @@ where Ok(self) } - #[cfg(feature = "uds")] + #[cfg(unix)] /// Start listening for unix domain connections on existing listener. /// /// This method is available with `uds` feature. @@ -466,7 +467,7 @@ where Ok(self) } - #[cfg(feature = "uds")] + #[cfg(unix)] /// Start listening for incoming unix domain connections. /// /// This method is available with `uds` feature. @@ -502,8 +503,8 @@ where impl HttpServer where F: Fn() -> I + Send + Clone + 'static, - I: IntoNewService, - S: NewService, + I: IntoServiceFactory, + S: ServiceFactory, S::Error: Into, S::InitError: fmt::Debug, S::Response: Into>, @@ -577,10 +578,10 @@ fn create_tcp_listener( Ok(builder.listen(backlog)?) } -#[cfg(feature = "ssl")] +#[cfg(feature = "openssl")] /// Configure `SslAcceptorBuilder` with custom server flags. fn openssl_acceptor(mut builder: SslAcceptorBuilder) -> io::Result { - use openssl::ssl::AlpnError; + use open_ssl::ssl::AlpnError; builder.set_alpn_select_callback(|_, protos| { const H2: &[u8] = b"\x02h2"; diff --git a/src/service.rs b/src/service.rs index 8b94dd284..9c4e6b4aa 100644 --- a/src/service.rs +++ b/src/service.rs @@ -9,8 +9,8 @@ use actix_http::{ ResponseHead, }; use actix_router::{Path, Resource, ResourceDef, Url}; -use actix_service::{IntoNewService, NewService}; -use futures::future::{ok, FutureResult, IntoFuture}; +use actix_service::{IntoServiceFactory, ServiceFactory}; +use futures::future::{ok, Ready}; use crate::config::{AppConfig, AppService}; use crate::data::Data; @@ -24,7 +24,7 @@ pub trait HttpServiceFactory { fn register(self, config: &mut AppService); } -pub(crate) trait ServiceFactory { +pub(crate) trait AppServiceFactory { fn register(&mut self, config: &mut AppService); } @@ -40,7 +40,7 @@ impl ServiceFactoryWrapper { } } -impl ServiceFactory for ServiceFactoryWrapper +impl AppServiceFactory for ServiceFactoryWrapper where T: HttpServiceFactory, { @@ -404,16 +404,6 @@ impl Into> for ServiceResponse { } } -impl IntoFuture for ServiceResponse { - type Item = ServiceResponse; - type Error = Error; - type Future = FutureResult, Error>; - - fn into_future(self) -> Self::Future { - ok(self) - } -} - impl fmt::Debug for ServiceResponse { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let res = writeln!( @@ -459,10 +449,11 @@ impl WebService { /// Add match guard to a web service. /// /// ```rust - /// use actix_web::{web, guard, dev, App, HttpResponse}; + /// use futures::future::{ok, Ready}; + /// use actix_web::{web, guard, dev, App, Error, HttpResponse}; /// - /// fn index(req: dev::ServiceRequest) -> dev::ServiceResponse { - /// req.into_response(HttpResponse::Ok().finish()) + /// fn index(req: dev::ServiceRequest) -> Ready> { + /// ok(req.into_response(HttpResponse::Ok().finish())) /// } /// /// fn main() { @@ -482,8 +473,8 @@ impl WebService { /// Set a service factory implementation and generate web service. pub fn finish(self, service: F) -> impl HttpServiceFactory where - F: IntoNewService, - T: NewService< + F: IntoServiceFactory, + T: ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -492,7 +483,7 @@ impl WebService { > + 'static, { WebServiceImpl { - srv: service.into_new_service(), + srv: service.into_factory(), rdef: self.rdef, name: self.name, guards: self.guards, @@ -509,7 +500,7 @@ struct WebServiceImpl { impl HttpServiceFactory for WebServiceImpl where - T: NewService< + T: ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -539,8 +530,9 @@ where #[cfg(test)] mod tests { use super::*; - use crate::test::{call_service, init_service, TestRequest}; + use crate::test::{block_on, init_service, TestRequest}; use crate::{guard, http, web, App, HttpResponse}; + use actix_service::Service; #[test] fn test_service_request() { @@ -565,25 +557,33 @@ mod tests { #[test] fn test_service() { - let mut srv = init_service( - App::new().service(web::service("/test").name("test").finish( - |req: ServiceRequest| req.into_response(HttpResponse::Ok().finish()), - )), - ); - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), http::StatusCode::OK); + block_on(async { + let mut srv = init_service( + App::new().service(web::service("/test").name("test").finish( + |req: ServiceRequest| { + ok(req.into_response(HttpResponse::Ok().finish())) + }, + )), + ) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), http::StatusCode::OK); - let mut srv = init_service( - App::new().service(web::service("/test").guard(guard::Get()).finish( - |req: ServiceRequest| req.into_response(HttpResponse::Ok().finish()), - )), - ); - let req = TestRequest::with_uri("/test") - .method(http::Method::PUT) - .to_request(); - let resp = call_service(&mut srv, req); - assert_eq!(resp.status(), http::StatusCode::NOT_FOUND); + let mut srv = init_service(App::new().service( + web::service("/test").guard(guard::Get()).finish( + |req: ServiceRequest| { + ok(req.into_response(HttpResponse::Ok().finish())) + }, + ), + )) + .await; + let req = TestRequest::with_uri("/test") + .method(http::Method::PUT) + .to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), http::StatusCode::NOT_FOUND); + }) } #[test] diff --git a/src/test.rs b/src/test.rs index 6563253cc..8cee3bc6a 100644 --- a/src/test.rs +++ b/src/test.rs @@ -7,10 +7,10 @@ use actix_http::test::TestRequest as HttpTestRequest; use actix_http::{cookie::Cookie, Extensions, Request}; use actix_router::{Path, ResourceDef, Url}; use actix_server_config::ServerConfig; -use actix_service::{IntoNewService, IntoService, NewService, Service}; +use actix_service::{IntoService, IntoServiceFactory, Service, ServiceFactory}; use bytes::{Bytes, BytesMut}; -use futures::future::{ok, Future}; -use futures::Stream; +use futures::future::{ok, Future, FutureExt}; +use futures::stream::{Stream, StreamExt}; use serde::de::DeserializeOwned; use serde::Serialize; use serde_json; @@ -39,7 +39,7 @@ pub fn default_service( ) -> impl Service, Error = Error> { (move |req: ServiceRequest| { - req.into_response(HttpResponse::build(status_code).finish()) + ok(req.into_response(HttpResponse::build(status_code).finish())) }) .into_service() } @@ -66,12 +66,12 @@ pub fn default_service( /// assert_eq!(resp.status(), StatusCode::OK); /// } /// ``` -pub fn init_service( +pub async fn init_service( app: R, ) -> impl Service, Error = E> where - R: IntoNewService, - S: NewService< + R: IntoServiceFactory, + S: ServiceFactory< Config = ServerConfig, Request = Request, Response = ServiceResponse, @@ -80,9 +80,8 @@ where S::InitError: std::fmt::Debug, { let cfg = ServerConfig::new("127.0.0.1:8080".parse().unwrap()); - let srv = app.into_new_service(); - let fut = run_on(move || srv.new_service(&cfg)); - block_on(fut).unwrap() + let srv = app.into_factory(); + srv.new_service(&cfg).await.unwrap() } /// Calls service and waits for response future completion. @@ -106,12 +105,12 @@ where /// assert_eq!(resp.status(), StatusCode::OK); /// } /// ``` -pub fn call_service(app: &mut S, req: R) -> S::Response +pub async fn call_service(app: &mut S, req: R) -> S::Response where S: Service, Error = E>, E: std::fmt::Debug, { - block_on(run_on(move || app.call(req))).unwrap() + app.call(req).await.unwrap() } /// Helper function that returns a response body of a TestRequest @@ -138,22 +137,22 @@ where /// assert_eq!(result, Bytes::from_static(b"welcome!")); /// } /// ``` -pub fn read_response(app: &mut S, req: Request) -> Bytes +pub async fn read_response(app: &mut S, req: Request) -> Bytes where S: Service, Error = Error>, B: MessageBody, { - block_on(run_on(move || { - app.call(req).and_then(|mut resp: ServiceResponse| { - resp.take_body() - .fold(BytesMut::new(), move |mut body, chunk| { - body.extend_from_slice(&chunk); - Ok::<_, Error>(body) - }) - .map(|body: BytesMut| body.freeze()) - }) - })) - .unwrap_or_else(|_| panic!("read_response failed at block_on unwrap")) + let mut resp = app + .call(req) + .await + .unwrap_or_else(|_| panic!("read_response failed at block_on unwrap")); + + let mut body = resp.take_body(); + let mut bytes = BytesMut::new(); + while let Some(item) = body.next().await { + bytes.extend_from_slice(&item.unwrap()); + } + bytes.freeze() } /// Helper function that returns a response body of a ServiceResponse. @@ -181,19 +180,27 @@ where /// assert_eq!(result, Bytes::from_static(b"welcome!")); /// } /// ``` -pub fn read_body(mut res: ServiceResponse) -> Bytes +pub async fn read_body(mut res: ServiceResponse) -> Bytes where B: MessageBody, { - block_on(run_on(move || { - res.take_body() - .fold(BytesMut::new(), move |mut body, chunk| { - body.extend_from_slice(&chunk); - Ok::<_, Error>(body) - }) - .map(|body: BytesMut| body.freeze()) - })) - .unwrap_or_else(|_| panic!("read_response failed at block_on unwrap")) + let mut body = res.take_body(); + let mut bytes = BytesMut::new(); + while let Some(item) = body.next().await { + bytes.extend_from_slice(&item.unwrap()); + } + bytes.freeze() +} + +pub async fn load_stream(mut stream: S) -> Result +where + S: Stream> + Unpin, +{ + let mut data = BytesMut::new(); + while let Some(item) = stream.next().await { + data.extend_from_slice(&item?); + } + Ok(data.freeze()) } /// Helper function that returns a deserialized response body of a TestRequest @@ -230,27 +237,16 @@ where /// let result: Person = test::read_response_json(&mut app, req); /// } /// ``` -pub fn read_response_json(app: &mut S, req: Request) -> T +pub async fn read_response_json(app: &mut S, req: Request) -> T where S: Service, Error = Error>, B: MessageBody, T: DeserializeOwned, { - block_on(run_on(move || { - app.call(req).and_then(|mut resp: ServiceResponse| { - resp.take_body() - .fold(BytesMut::new(), move |mut body, chunk| { - body.extend_from_slice(&chunk); - Ok::<_, Error>(body) - }) - .and_then(|body: BytesMut| { - ok(serde_json::from_slice(&body).unwrap_or_else(|_| { - panic!("read_response_json failed during deserialization") - })) - }) - }) - })) - .unwrap_or_else(|_| panic!("read_response_json failed at block_on unwrap")) + let body = read_response(app, req).await; + + serde_json::from_slice(&body) + .unwrap_or_else(|_| panic!("read_response_json failed during deserialization")) } /// Test `Request` builder. @@ -511,74 +507,82 @@ mod tests { #[test] fn test_basics() { - let req = TestRequest::with_hdr(header::ContentType::json()) - .version(Version::HTTP_2) - .set(header::Date(SystemTime::now().into())) - .param("test", "123") - .data(10u32) - .to_http_request(); - assert!(req.headers().contains_key(header::CONTENT_TYPE)); - assert!(req.headers().contains_key(header::DATE)); - assert_eq!(&req.match_info()["test"], "123"); - assert_eq!(req.version(), Version::HTTP_2); - let data = req.get_app_data::().unwrap(); - assert!(req.get_app_data::().is_none()); - assert_eq!(*data, 10); - assert_eq!(*data.get_ref(), 10); + block_on(async { + let req = TestRequest::with_hdr(header::ContentType::json()) + .version(Version::HTTP_2) + .set(header::Date(SystemTime::now().into())) + .param("test", "123") + .data(10u32) + .to_http_request(); + assert!(req.headers().contains_key(header::CONTENT_TYPE)); + assert!(req.headers().contains_key(header::DATE)); + assert_eq!(&req.match_info()["test"], "123"); + assert_eq!(req.version(), Version::HTTP_2); + let data = req.get_app_data::().unwrap(); + assert!(req.get_app_data::().is_none()); + assert_eq!(*data, 10); + assert_eq!(*data.get_ref(), 10); - assert!(req.app_data::().is_none()); - let data = req.app_data::().unwrap(); - assert_eq!(*data, 10); + assert!(req.app_data::().is_none()); + let data = req.app_data::().unwrap(); + assert_eq!(*data, 10); + }) } #[test] fn test_request_methods() { - let mut app = init_service( - App::new().service( - web::resource("/index.html") - .route(web::put().to(|| HttpResponse::Ok().body("put!"))) - .route(web::patch().to(|| HttpResponse::Ok().body("patch!"))) - .route(web::delete().to(|| HttpResponse::Ok().body("delete!"))), - ), - ); + block_on(async { + let mut app = init_service( + App::new().service( + web::resource("/index.html") + .route(web::put().to(|| HttpResponse::Ok().body("put!"))) + .route(web::patch().to(|| HttpResponse::Ok().body("patch!"))) + .route(web::delete().to(|| HttpResponse::Ok().body("delete!"))), + ), + ) + .await; - let put_req = TestRequest::put() - .uri("/index.html") - .header(header::CONTENT_TYPE, "application/json") - .to_request(); + let put_req = TestRequest::put() + .uri("/index.html") + .header(header::CONTENT_TYPE, "application/json") + .to_request(); - let result = read_response(&mut app, put_req); - assert_eq!(result, Bytes::from_static(b"put!")); + let result = read_response(&mut app, put_req).await; + assert_eq!(result, Bytes::from_static(b"put!")); - let patch_req = TestRequest::patch() - .uri("/index.html") - .header(header::CONTENT_TYPE, "application/json") - .to_request(); + let patch_req = TestRequest::patch() + .uri("/index.html") + .header(header::CONTENT_TYPE, "application/json") + .to_request(); - let result = read_response(&mut app, patch_req); - assert_eq!(result, Bytes::from_static(b"patch!")); + let result = read_response(&mut app, patch_req).await; + assert_eq!(result, Bytes::from_static(b"patch!")); - let delete_req = TestRequest::delete().uri("/index.html").to_request(); - let result = read_response(&mut app, delete_req); - assert_eq!(result, Bytes::from_static(b"delete!")); + let delete_req = TestRequest::delete().uri("/index.html").to_request(); + let result = read_response(&mut app, delete_req).await; + assert_eq!(result, Bytes::from_static(b"delete!")); + }) } #[test] fn test_response() { - let mut app = init_service( - App::new().service( - web::resource("/index.html") - .route(web::post().to(|| HttpResponse::Ok().body("welcome!"))), - ), - ); + block_on(async { + let mut app = init_service( + App::new().service( + web::resource("/index.html") + .route(web::post().to(|| HttpResponse::Ok().body("welcome!"))), + ), + ) + .await; - let req = TestRequest::post() - .uri("/index.html") - .header(header::CONTENT_TYPE, "application/json") - .to_request(); + let req = TestRequest::post() + .uri("/index.html") + .header(header::CONTENT_TYPE, "application/json") + .to_request(); - let result = read_response(&mut app, req); - assert_eq!(result, Bytes::from_static(b"welcome!")); + let result = read_response(&mut app, req).await; + assert_eq!(result, Bytes::from_static(b"welcome!")); + }) } #[derive(Serialize, Deserialize)] @@ -589,129 +593,147 @@ mod tests { #[test] fn test_response_json() { - let mut app = init_service(App::new().service(web::resource("/people").route( - web::post().to(|person: web::Json| { - HttpResponse::Ok().json(person.into_inner()) - }), - ))); + block_on(async { + let mut app = + init_service(App::new().service(web::resource("/people").route( + web::post().to(|person: web::Json| { + HttpResponse::Ok().json(person.into_inner()) + }), + ))) + .await; - let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes(); + let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes(); - let req = TestRequest::post() - .uri("/people") - .header(header::CONTENT_TYPE, "application/json") - .set_payload(payload) - .to_request(); + let req = TestRequest::post() + .uri("/people") + .header(header::CONTENT_TYPE, "application/json") + .set_payload(payload) + .to_request(); - let result: Person = read_response_json(&mut app, req); - assert_eq!(&result.id, "12345"); + let result: Person = read_response_json(&mut app, req).await; + assert_eq!(&result.id, "12345"); + }) } #[test] fn test_request_response_form() { - let mut app = init_service(App::new().service(web::resource("/people").route( - web::post().to(|person: web::Form| { - HttpResponse::Ok().json(person.into_inner()) - }), - ))); + block_on(async { + let mut app = + init_service(App::new().service(web::resource("/people").route( + web::post().to(|person: web::Form| { + HttpResponse::Ok().json(person.into_inner()) + }), + ))) + .await; - let payload = Person { - id: "12345".to_string(), - name: "User name".to_string(), - }; + let payload = Person { + id: "12345".to_string(), + name: "User name".to_string(), + }; - let req = TestRequest::post() - .uri("/people") - .set_form(&payload) - .to_request(); + let req = TestRequest::post() + .uri("/people") + .set_form(&payload) + .to_request(); - assert_eq!(req.content_type(), "application/x-www-form-urlencoded"); + assert_eq!(req.content_type(), "application/x-www-form-urlencoded"); - let result: Person = read_response_json(&mut app, req); - assert_eq!(&result.id, "12345"); - assert_eq!(&result.name, "User name"); + let result: Person = read_response_json(&mut app, req).await; + assert_eq!(&result.id, "12345"); + assert_eq!(&result.name, "User name"); + }) } #[test] fn test_request_response_json() { - let mut app = init_service(App::new().service(web::resource("/people").route( - web::post().to(|person: web::Json| { - HttpResponse::Ok().json(person.into_inner()) - }), - ))); + block_on(async { + let mut app = + init_service(App::new().service(web::resource("/people").route( + web::post().to(|person: web::Json| { + HttpResponse::Ok().json(person.into_inner()) + }), + ))) + .await; - let payload = Person { - id: "12345".to_string(), - name: "User name".to_string(), - }; + let payload = Person { + id: "12345".to_string(), + name: "User name".to_string(), + }; - let req = TestRequest::post() - .uri("/people") - .set_json(&payload) - .to_request(); + let req = TestRequest::post() + .uri("/people") + .set_json(&payload) + .to_request(); - assert_eq!(req.content_type(), "application/json"); + assert_eq!(req.content_type(), "application/json"); - let result: Person = read_response_json(&mut app, req); - assert_eq!(&result.id, "12345"); - assert_eq!(&result.name, "User name"); + let result: Person = read_response_json(&mut app, req).await; + assert_eq!(&result.id, "12345"); + assert_eq!(&result.name, "User name"); + }) } #[test] fn test_async_with_block() { - fn async_with_block() -> impl Future { - web::block(move || Some(4).ok_or("wrong")).then(|res| match res { - Ok(value) => HttpResponse::Ok() - .content_type("text/plain") - .body(format!("Async with block value: {}", value)), - Err(_) => panic!("Unexpected"), - }) - } + block_on(async { + async fn async_with_block() -> Result { + let res = web::block(move || Some(4usize).ok_or("wrong")).await; - let mut app = init_service( - App::new().service(web::resource("/index.html").to_async(async_with_block)), - ); - - let req = TestRequest::post().uri("/index.html").to_request(); - let res = block_fn(|| app.call(req)).unwrap(); - assert!(res.status().is_success()); - } - - #[test] - fn test_actor() { - use actix::Actor; - - struct MyActor; - - struct Num(usize); - impl actix::Message for Num { - type Result = usize; - } - impl actix::Actor for MyActor { - type Context = actix::Context; - } - impl actix::Handler for MyActor { - type Result = usize; - fn handle(&mut self, msg: Num, _: &mut Self::Context) -> Self::Result { - msg.0 + match res? { + Ok(value) => Ok(HttpResponse::Ok() + .content_type("text/plain") + .body(format!("Async with block value: {}", value))), + Err(_) => panic!("Unexpected"), + } } - } - let addr = run_on(|| MyActor.start()); - let mut app = init_service(App::new().service( - web::resource("/index.html").to_async(move || { - addr.send(Num(1)).from_err().and_then(|res| { - if res == 1 { - HttpResponse::Ok() - } else { - HttpResponse::BadRequest() - } - }) - }), - )); + let mut app = init_service( + App::new() + .service(web::resource("/index.html").to_async(async_with_block)), + ) + .await; - let req = TestRequest::post().uri("/index.html").to_request(); - let res = block_fn(|| app.call(req)).unwrap(); - assert!(res.status().is_success()); + let req = TestRequest::post().uri("/index.html").to_request(); + let res = app.call(req).await.unwrap(); + assert!(res.status().is_success()); + }) } + + // #[test] + // fn test_actor() { + // use actix::Actor; + + // struct MyActor; + + // struct Num(usize); + // impl actix::Message for Num { + // type Result = usize; + // } + // impl actix::Actor for MyActor { + // type Context = actix::Context; + // } + // impl actix::Handler for MyActor { + // type Result = usize; + // fn handle(&mut self, msg: Num, _: &mut Self::Context) -> Self::Result { + // msg.0 + // } + // } + + // let addr = run_on(|| MyActor.start()); + // let mut app = init_service(App::new().service( + // web::resource("/index.html").to_async(move || { + // addr.send(Num(1)).from_err().and_then(|res| { + // if res == 1 { + // HttpResponse::Ok() + // } else { + // HttpResponse::BadRequest() + // } + // }) + // }), + // )); + + // let req = TestRequest::post().uri("/index.html").to_request(); + // let res = block_fn(|| app.call(req)).unwrap(); + // assert!(res.status().is_success()); + // } } diff --git a/src/types/form.rs b/src/types/form.rs index c727ce0e5..694fe6dbd 100644 --- a/src/types/form.rs +++ b/src/types/form.rs @@ -1,12 +1,16 @@ //! Form extractor +use std::future::Future; +use std::pin::Pin; use std::rc::Rc; +use std::task::{Context, Poll}; use std::{fmt, ops}; use actix_http::{Error, HttpMessage, Payload, Response}; use bytes::BytesMut; use encoding_rs::{Encoding, UTF_8}; -use futures::{Future, Poll, Stream}; +use futures::future::{err, ok, FutureExt, LocalBoxFuture, Ready}; +use futures::{Stream, StreamExt}; use serde::de::DeserializeOwned; use serde::Serialize; @@ -110,7 +114,7 @@ where { type Config = FormConfig; type Error = Error; - type Future = Box>; + type Future = LocalBoxFuture<'static, Result>; #[inline] fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { @@ -120,18 +124,19 @@ where .map(|c| (c.limit, c.ehandler.clone())) .unwrap_or((16384, None)); - Box::new( - UrlEncoded::new(req, payload) - .limit(limit) - .map_err(move |e| { + UrlEncoded::new(req, payload) + .limit(limit) + .map(move |res| match res { + Err(e) => { if let Some(err) = err { - (*err)(e, &req2) + Err((*err)(e, &req2)) } else { - e.into() + Err(e.into()) } - }) - .map(Form), - ) + } + Ok(item) => Ok(Form(item)), + }) + .boxed_local() } } @@ -149,15 +154,15 @@ impl fmt::Display for Form { impl Responder for Form { type Error = Error; - type Future = Result; + type Future = Ready>; fn respond_to(self, _: &HttpRequest) -> Self::Future { let body = match serde_urlencoded::to_string(&self.0) { Ok(body) => body, - Err(e) => return Err(e.into()), + Err(e) => return err(e.into()), }; - Ok(Response::build(StatusCode::OK) + ok(Response::build(StatusCode::OK) .set(ContentType::form_url_encoded()) .body(body)) } @@ -240,7 +245,7 @@ pub struct UrlEncoded { length: Option, encoding: &'static Encoding, err: Option, - fut: Option>>, + fut: Option>>, } impl UrlEncoded { @@ -301,45 +306,45 @@ impl Future for UrlEncoded where U: DeserializeOwned + 'static, { - type Item = U; - type Error = UrlencodedError; + type Output = Result; - fn poll(&mut self) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { if let Some(ref mut fut) = self.fut { - return fut.poll(); + return Pin::new(fut).poll(cx); } if let Some(err) = self.err.take() { - return Err(err); + return Poll::Ready(Err(err)); } // payload size let limit = self.limit; if let Some(len) = self.length.take() { if len > limit { - return Err(UrlencodedError::Overflow { size: len, limit }); + return Poll::Ready(Err(UrlencodedError::Overflow { size: len, limit })); } } // future let encoding = self.encoding; - let fut = self - .stream - .take() - .unwrap() - .from_err() - .fold(BytesMut::with_capacity(8192), move |mut body, chunk| { - if (body.len() + chunk.len()) > limit { - Err(UrlencodedError::Overflow { - size: body.len() + chunk.len(), - limit, - }) - } else { - body.extend_from_slice(&chunk); - Ok(body) + let mut stream = self.stream.take().unwrap(); + + self.fut = Some( + async move { + let mut body = BytesMut::with_capacity(8192); + + while let Some(item) = stream.next().await { + let chunk = item?; + if (body.len() + chunk.len()) > limit { + return Err(UrlencodedError::Overflow { + size: body.len() + chunk.len(), + limit, + }); + } else { + body.extend_from_slice(&chunk); + } } - }) - .and_then(move |body| { + if encoding == UTF_8 { serde_urlencoded::from_bytes::(&body) .map_err(|_| UrlencodedError::Parse) @@ -351,9 +356,10 @@ where serde_urlencoded::from_str::(&body) .map_err(|_| UrlencodedError::Parse) } - }); - self.fut = Some(Box::new(fut)); - self.poll() + } + .boxed_local(), + ); + self.poll(cx) } } @@ -374,20 +380,24 @@ mod tests { #[test] fn test_form() { - let (req, mut pl) = - TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded") - .header(CONTENT_LENGTH, "11") - .set_payload(Bytes::from_static(b"hello=world&counter=123")) - .to_http_parts(); + block_on(async { + let (req, mut pl) = TestRequest::with_header( + CONTENT_TYPE, + "application/x-www-form-urlencoded", + ) + .header(CONTENT_LENGTH, "11") + .set_payload(Bytes::from_static(b"hello=world&counter=123")) + .to_http_parts(); - let Form(s) = block_on(Form::::from_request(&req, &mut pl)).unwrap(); - assert_eq!( - s, - Info { - hello: "world".into(), - counter: 123 - } - ); + let Form(s) = Form::::from_request(&req, &mut pl).await.unwrap(); + assert_eq!( + s, + Info { + hello: "world".into(), + counter: 123 + } + ); + }) } fn eq(err: UrlencodedError, other: UrlencodedError) -> bool { @@ -410,81 +420,93 @@ mod tests { #[test] fn test_urlencoded_error() { - let (req, mut pl) = - TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded") - .header(CONTENT_LENGTH, "xxxx") - .to_http_parts(); - let info = block_on(UrlEncoded::::new(&req, &mut pl)); - assert!(eq(info.err().unwrap(), UrlencodedError::UnknownLength)); - - let (req, mut pl) = - TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded") - .header(CONTENT_LENGTH, "1000000") - .to_http_parts(); - let info = block_on(UrlEncoded::::new(&req, &mut pl)); - assert!(eq( - info.err().unwrap(), - UrlencodedError::Overflow { size: 0, limit: 0 } - )); - - let (req, mut pl) = TestRequest::with_header(CONTENT_TYPE, "text/plain") - .header(CONTENT_LENGTH, "10") + block_on(async { + let (req, mut pl) = TestRequest::with_header( + CONTENT_TYPE, + "application/x-www-form-urlencoded", + ) + .header(CONTENT_LENGTH, "xxxx") .to_http_parts(); - let info = block_on(UrlEncoded::::new(&req, &mut pl)); - assert!(eq(info.err().unwrap(), UrlencodedError::ContentType)); + let info = UrlEncoded::::new(&req, &mut pl).await; + assert!(eq(info.err().unwrap(), UrlencodedError::UnknownLength)); + + let (req, mut pl) = TestRequest::with_header( + CONTENT_TYPE, + "application/x-www-form-urlencoded", + ) + .header(CONTENT_LENGTH, "1000000") + .to_http_parts(); + let info = UrlEncoded::::new(&req, &mut pl).await; + assert!(eq( + info.err().unwrap(), + UrlencodedError::Overflow { size: 0, limit: 0 } + )); + + let (req, mut pl) = TestRequest::with_header(CONTENT_TYPE, "text/plain") + .header(CONTENT_LENGTH, "10") + .to_http_parts(); + let info = UrlEncoded::::new(&req, &mut pl).await; + assert!(eq(info.err().unwrap(), UrlencodedError::ContentType)); + }) } #[test] fn test_urlencoded() { - let (req, mut pl) = - TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded") - .header(CONTENT_LENGTH, "11") - .set_payload(Bytes::from_static(b"hello=world&counter=123")) - .to_http_parts(); + block_on(async { + let (req, mut pl) = TestRequest::with_header( + CONTENT_TYPE, + "application/x-www-form-urlencoded", + ) + .header(CONTENT_LENGTH, "11") + .set_payload(Bytes::from_static(b"hello=world&counter=123")) + .to_http_parts(); - let info = block_on(UrlEncoded::::new(&req, &mut pl)).unwrap(); - assert_eq!( - info, - Info { - hello: "world".to_owned(), - counter: 123 - } - ); + let info = UrlEncoded::::new(&req, &mut pl).await.unwrap(); + assert_eq!( + info, + Info { + hello: "world".to_owned(), + counter: 123 + } + ); - let (req, mut pl) = TestRequest::with_header( - CONTENT_TYPE, - "application/x-www-form-urlencoded; charset=utf-8", - ) - .header(CONTENT_LENGTH, "11") - .set_payload(Bytes::from_static(b"hello=world&counter=123")) - .to_http_parts(); + let (req, mut pl) = TestRequest::with_header( + CONTENT_TYPE, + "application/x-www-form-urlencoded; charset=utf-8", + ) + .header(CONTENT_LENGTH, "11") + .set_payload(Bytes::from_static(b"hello=world&counter=123")) + .to_http_parts(); - let info = block_on(UrlEncoded::::new(&req, &mut pl)).unwrap(); - assert_eq!( - info, - Info { - hello: "world".to_owned(), - counter: 123 - } - ); + let info = UrlEncoded::::new(&req, &mut pl).await.unwrap(); + assert_eq!( + info, + Info { + hello: "world".to_owned(), + counter: 123 + } + ); + }) } #[test] fn test_responder() { - let req = TestRequest::default().to_http_request(); + block_on(async { + let req = TestRequest::default().to_http_request(); - let form = Form(Info { - hello: "world".to_string(), - counter: 123, - }); - let resp = form.respond_to(&req).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!( - resp.headers().get(CONTENT_TYPE).unwrap(), - HeaderValue::from_static("application/x-www-form-urlencoded") - ); + let form = Form(Info { + hello: "world".to_string(), + counter: 123, + }); + let resp = form.respond_to(&req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!( + resp.headers().get(CONTENT_TYPE).unwrap(), + HeaderValue::from_static("application/x-www-form-urlencoded") + ); - use crate::responder::tests::BodyTest; - assert_eq!(resp.body().bin_ref(), b"hello=world&counter=123"); + use crate::responder::tests::BodyTest; + assert_eq!(resp.body().bin_ref(), b"hello=world&counter=123"); + }) } } diff --git a/src/types/json.rs b/src/types/json.rs index e80d0a45f..19f8532bd 100644 --- a/src/types/json.rs +++ b/src/types/json.rs @@ -1,10 +1,14 @@ //! Json extractor/responder +use std::future::Future; +use std::pin::Pin; use std::sync::Arc; +use std::task::{Context, Poll}; use std::{fmt, ops}; use bytes::BytesMut; -use futures::{Future, Poll, Stream}; +use futures::future::{err, ok, FutureExt, LocalBoxFuture, Ready}; +use futures::{Stream, StreamExt}; use serde::de::DeserializeOwned; use serde::Serialize; use serde_json; @@ -118,15 +122,15 @@ where impl Responder for Json { type Error = Error; - type Future = Result; + type Future = Ready>; fn respond_to(self, _: &HttpRequest) -> Self::Future { let body = match serde_json::to_string(&self.0) { Ok(body) => body, - Err(e) => return Err(e.into()), + Err(e) => return err(e.into()), }; - Ok(Response::build(StatusCode::OK) + ok(Response::build(StatusCode::OK) .content_type("application/json") .body(body)) } @@ -169,7 +173,7 @@ where T: DeserializeOwned + 'static, { type Error = Error; - type Future = Box>; + type Future = LocalBoxFuture<'static, Result>; type Config = JsonConfig; #[inline] @@ -180,23 +184,24 @@ where .map(|c| (c.limit, c.ehandler.clone(), c.content_type.clone())) .unwrap_or((32768, None, None)); - Box::new( - JsonBody::new(req, payload, ctype) - .limit(limit) - .map_err(move |e| { + JsonBody::new(req, payload, ctype) + .limit(limit) + .map(move |res| match res { + Err(e) => { log::debug!( "Failed to deserialize Json from payload. \ Request path: {}", req2.path() ); if let Some(err) = err { - (*err)(e, &req2) + Err((*err)(e, &req2)) } else { - e.into() + Err(e.into()) } - }) - .map(Json), - ) + } + Ok(data) => Ok(Json(data)), + }) + .boxed_local() } } @@ -290,7 +295,7 @@ pub struct JsonBody { length: Option, stream: Option>, err: Option, - fut: Option>>, + fut: Option>>, } impl JsonBody @@ -349,41 +354,43 @@ impl Future for JsonBody where U: DeserializeOwned + 'static, { - type Item = U; - type Error = JsonPayloadError; + type Output = Result; - fn poll(&mut self) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { if let Some(ref mut fut) = self.fut { - return fut.poll(); + return Pin::new(fut).poll(cx); } if let Some(err) = self.err.take() { - return Err(err); + return Poll::Ready(Err(err)); } let limit = self.limit; if let Some(len) = self.length.take() { if len > limit { - return Err(JsonPayloadError::Overflow); + return Poll::Ready(Err(JsonPayloadError::Overflow)); } } + let mut stream = self.stream.take().unwrap(); - let fut = self - .stream - .take() - .unwrap() - .from_err() - .fold(BytesMut::with_capacity(8192), move |mut body, chunk| { - if (body.len() + chunk.len()) > limit { - Err(JsonPayloadError::Overflow) - } else { - body.extend_from_slice(&chunk); - Ok(body) + self.fut = Some( + async move { + let mut body = BytesMut::with_capacity(8192); + + while let Some(item) = stream.next().await { + let chunk = item?; + if (body.len() + chunk.len()) > limit { + return Err(JsonPayloadError::Overflow); + } else { + body.extend_from_slice(&chunk); + } } - }) - .and_then(|body| Ok(serde_json::from_slice::(&body)?)); - self.fut = Some(Box::new(fut)); - self.poll() + Ok(serde_json::from_slice::(&body)?) + } + .boxed_local(), + ); + + self.poll(cx) } } @@ -395,7 +402,7 @@ mod tests { use super::*; use crate::error::InternalError; use crate::http::header; - use crate::test::{block_on, TestRequest}; + use crate::test::{block_on, load_stream, TestRequest}; use crate::HttpResponse; #[derive(Serialize, Deserialize, PartialEq, Debug)] @@ -419,218 +426,234 @@ mod tests { #[test] fn test_responder() { - let req = TestRequest::default().to_http_request(); + block_on(async { + let req = TestRequest::default().to_http_request(); - let j = Json(MyObject { - name: "test".to_string(), - }); - let resp = j.respond_to(&req).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - header::HeaderValue::from_static("application/json") - ); + let j = Json(MyObject { + name: "test".to_string(), + }); + let resp = j.respond_to(&req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + header::HeaderValue::from_static("application/json") + ); - use crate::responder::tests::BodyTest; - assert_eq!(resp.body().bin_ref(), b"{\"name\":\"test\"}"); + use crate::responder::tests::BodyTest; + assert_eq!(resp.body().bin_ref(), b"{\"name\":\"test\"}"); + }) } #[test] fn test_custom_error_responder() { - let (req, mut pl) = TestRequest::default() - .header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/json"), - ) - .header( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("16"), - ) - .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .data(JsonConfig::default().limit(10).error_handler(|err, _| { - let msg = MyObject { - name: "invalid request".to_string(), - }; - let resp = HttpResponse::BadRequest() - .body(serde_json::to_string(&msg).unwrap()); - InternalError::from_response(err, resp).into() - })) - .to_http_parts(); + block_on(async { + let (req, mut pl) = TestRequest::default() + .header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/json"), + ) + .header( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("16"), + ) + .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) + .data(JsonConfig::default().limit(10).error_handler(|err, _| { + let msg = MyObject { + name: "invalid request".to_string(), + }; + let resp = HttpResponse::BadRequest() + .body(serde_json::to_string(&msg).unwrap()); + InternalError::from_response(err, resp).into() + })) + .to_http_parts(); - let s = block_on(Json::::from_request(&req, &mut pl)); - let mut resp = Response::from_error(s.err().unwrap().into()); - assert_eq!(resp.status(), StatusCode::BAD_REQUEST); + let s = Json::::from_request(&req, &mut pl).await; + let mut resp = Response::from_error(s.err().unwrap().into()); + assert_eq!(resp.status(), StatusCode::BAD_REQUEST); - let body = block_on(resp.take_body().concat2()).unwrap(); - let msg: MyObject = serde_json::from_slice(&body).unwrap(); - assert_eq!(msg.name, "invalid request"); + let body = load_stream(resp.take_body()).await.unwrap(); + let msg: MyObject = serde_json::from_slice(&body).unwrap(); + assert_eq!(msg.name, "invalid request"); + }) } #[test] fn test_extract() { - let (req, mut pl) = TestRequest::default() - .header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/json"), - ) - .header( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("16"), - ) - .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .to_http_parts(); + block_on(async { + let (req, mut pl) = TestRequest::default() + .header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/json"), + ) + .header( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("16"), + ) + .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) + .to_http_parts(); - let s = block_on(Json::::from_request(&req, &mut pl)).unwrap(); - assert_eq!(s.name, "test"); - assert_eq!( - s.into_inner(), - MyObject { - name: "test".to_string() - } - ); + let s = Json::::from_request(&req, &mut pl).await.unwrap(); + assert_eq!(s.name, "test"); + assert_eq!( + s.into_inner(), + MyObject { + name: "test".to_string() + } + ); - let (req, mut pl) = TestRequest::default() - .header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/json"), - ) - .header( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("16"), - ) - .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .data(JsonConfig::default().limit(10)) - .to_http_parts(); + let (req, mut pl) = TestRequest::default() + .header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/json"), + ) + .header( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("16"), + ) + .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) + .data(JsonConfig::default().limit(10)) + .to_http_parts(); - let s = block_on(Json::::from_request(&req, &mut pl)); - assert!(format!("{}", s.err().unwrap()) - .contains("Json payload size is bigger than allowed")); + let s = Json::::from_request(&req, &mut pl).await; + assert!(format!("{}", s.err().unwrap()) + .contains("Json payload size is bigger than allowed")); - let (req, mut pl) = TestRequest::default() - .header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/json"), - ) - .header( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("16"), - ) - .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .data( - JsonConfig::default() - .limit(10) - .error_handler(|_, _| JsonPayloadError::ContentType.into()), - ) - .to_http_parts(); - let s = block_on(Json::::from_request(&req, &mut pl)); - assert!(format!("{}", s.err().unwrap()).contains("Content type error")); + let (req, mut pl) = TestRequest::default() + .header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/json"), + ) + .header( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("16"), + ) + .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) + .data( + JsonConfig::default() + .limit(10) + .error_handler(|_, _| JsonPayloadError::ContentType.into()), + ) + .to_http_parts(); + let s = Json::::from_request(&req, &mut pl).await; + assert!(format!("{}", s.err().unwrap()).contains("Content type error")); + }) } #[test] fn test_json_body() { - let (req, mut pl) = TestRequest::default().to_http_parts(); - let json = block_on(JsonBody::::new(&req, &mut pl, None)); - assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); + block_on(async { + let (req, mut pl) = TestRequest::default().to_http_parts(); + let json = JsonBody::::new(&req, &mut pl, None).await; + assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); - let (req, mut pl) = TestRequest::default() - .header( + let (req, mut pl) = TestRequest::default() + .header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/text"), + ) + .to_http_parts(); + let json = JsonBody::::new(&req, &mut pl, None).await; + assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); + + let (req, mut pl) = TestRequest::default() + .header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/json"), + ) + .header( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("10000"), + ) + .to_http_parts(); + + let json = JsonBody::::new(&req, &mut pl, None) + .limit(100) + .await; + assert!(json_eq(json.err().unwrap(), JsonPayloadError::Overflow)); + + let (req, mut pl) = TestRequest::default() + .header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/json"), + ) + .header( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("16"), + ) + .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) + .to_http_parts(); + + let json = JsonBody::::new(&req, &mut pl, None).await; + assert_eq!( + json.ok().unwrap(), + MyObject { + name: "test".to_owned() + } + ); + }) + } + + #[test] + fn test_with_json_and_bad_content_type() { + block_on(async { + let (req, mut pl) = TestRequest::with_header( header::CONTENT_TYPE, - header::HeaderValue::from_static("application/text"), - ) - .to_http_parts(); - let json = block_on(JsonBody::::new(&req, &mut pl, None)); - assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); - - let (req, mut pl) = TestRequest::default() - .header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/json"), - ) - .header( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("10000"), - ) - .to_http_parts(); - - let json = block_on(JsonBody::::new(&req, &mut pl, None).limit(100)); - assert!(json_eq(json.err().unwrap(), JsonPayloadError::Overflow)); - - let (req, mut pl) = TestRequest::default() - .header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/json"), + header::HeaderValue::from_static("text/plain"), ) .header( header::CONTENT_LENGTH, header::HeaderValue::from_static("16"), ) .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) + .data(JsonConfig::default().limit(4096)) .to_http_parts(); - let json = block_on(JsonBody::::new(&req, &mut pl, None)); - assert_eq!( - json.ok().unwrap(), - MyObject { - name: "test".to_owned() - } - ); - } - - #[test] - fn test_with_json_and_bad_content_type() { - let (req, mut pl) = TestRequest::with_header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("text/plain"), - ) - .header( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("16"), - ) - .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .data(JsonConfig::default().limit(4096)) - .to_http_parts(); - - let s = block_on(Json::::from_request(&req, &mut pl)); - assert!(s.is_err()) + let s = Json::::from_request(&req, &mut pl).await; + assert!(s.is_err()) + }) } #[test] fn test_with_json_and_good_custom_content_type() { - let (req, mut pl) = TestRequest::with_header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("text/plain"), - ) - .header( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("16"), - ) - .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .data(JsonConfig::default().content_type(|mime: mime::Mime| { - mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN - })) - .to_http_parts(); + block_on(async { + let (req, mut pl) = TestRequest::with_header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("text/plain"), + ) + .header( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("16"), + ) + .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) + .data(JsonConfig::default().content_type(|mime: mime::Mime| { + mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN + })) + .to_http_parts(); - let s = block_on(Json::::from_request(&req, &mut pl)); - assert!(s.is_ok()) + let s = Json::::from_request(&req, &mut pl).await; + assert!(s.is_ok()) + }) } #[test] fn test_with_json_and_bad_custom_content_type() { - let (req, mut pl) = TestRequest::with_header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("text/html"), - ) - .header( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("16"), - ) - .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .data(JsonConfig::default().content_type(|mime: mime::Mime| { - mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN - })) - .to_http_parts(); + block_on(async { + let (req, mut pl) = TestRequest::with_header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("text/html"), + ) + .header( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("16"), + ) + .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) + .data(JsonConfig::default().content_type(|mime: mime::Mime| { + mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN + })) + .to_http_parts(); - let s = block_on(Json::::from_request(&req, &mut pl)); - assert!(s.is_err()) + let s = Json::::from_request(&req, &mut pl).await; + assert!(s.is_err()) + }) } } diff --git a/src/types/path.rs b/src/types/path.rs index fa7c6e110..89b9392be 100644 --- a/src/types/path.rs +++ b/src/types/path.rs @@ -5,6 +5,7 @@ use std::{fmt, ops}; use actix_http::error::{Error, ErrorNotFound}; use actix_router::PathDeserializer; +use futures::future::{ready, Ready}; use serde::de; use crate::dev::Payload; @@ -159,7 +160,7 @@ where T: de::DeserializeOwned, { type Error = Error; - type Future = Result; + type Future = Ready>; type Config = PathConfig; #[inline] @@ -169,21 +170,23 @@ where .map(|c| c.ehandler.clone()) .unwrap_or(None); - de::Deserialize::deserialize(PathDeserializer::new(req.match_info())) - .map(|inner| Path { inner }) - .map_err(move |e| { - log::debug!( - "Failed during Path extractor deserialization. \ - Request path: {:?}", - req.path() - ); - if let Some(error_handler) = error_handler { - let e = PathError::Deserialize(e); - (error_handler)(e, req) - } else { - ErrorNotFound(e) - } - }) + ready( + de::Deserialize::deserialize(PathDeserializer::new(req.match_info())) + .map(|inner| Path { inner }) + .map_err(move |e| { + log::debug!( + "Failed during Path extractor deserialization. \ + Request path: {:?}", + req.path() + ); + if let Some(error_handler) = error_handler { + let e = PathError::Deserialize(e); + (error_handler)(e, req) + } else { + ErrorNotFound(e) + } + }), + ) } } @@ -268,100 +271,116 @@ mod tests { #[test] fn test_extract_path_single() { - let resource = ResourceDef::new("/{value}/"); + block_on(async { + let resource = ResourceDef::new("/{value}/"); - let mut req = TestRequest::with_uri("/32/").to_srv_request(); - resource.match_path(req.match_info_mut()); + let mut req = TestRequest::with_uri("/32/").to_srv_request(); + resource.match_path(req.match_info_mut()); - let (req, mut pl) = req.into_parts(); - assert_eq!(*Path::::from_request(&req, &mut pl).unwrap(), 32); - assert!(Path::::from_request(&req, &mut pl).is_err()); + let (req, mut pl) = req.into_parts(); + assert_eq!(*Path::::from_request(&req, &mut pl).await.unwrap(), 32); + assert!(Path::::from_request(&req, &mut pl).await.is_err()); + }) } #[test] fn test_tuple_extract() { - let resource = ResourceDef::new("/{key}/{value}/"); + block_on(async { + let resource = ResourceDef::new("/{key}/{value}/"); - let mut req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request(); - resource.match_path(req.match_info_mut()); + let mut req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request(); + resource.match_path(req.match_info_mut()); - let (req, mut pl) = req.into_parts(); - let res = - block_on(<(Path<(String, String)>,)>::from_request(&req, &mut pl)).unwrap(); - assert_eq!((res.0).0, "name"); - assert_eq!((res.0).1, "user1"); + let (req, mut pl) = req.into_parts(); + let res = <(Path<(String, String)>,)>::from_request(&req, &mut pl) + .await + .unwrap(); + assert_eq!((res.0).0, "name"); + assert_eq!((res.0).1, "user1"); - let res = block_on( - <(Path<(String, String)>, Path<(String, String)>)>::from_request( + let res = <(Path<(String, String)>, Path<(String, String)>)>::from_request( &req, &mut pl, - ), - ) - .unwrap(); - assert_eq!((res.0).0, "name"); - assert_eq!((res.0).1, "user1"); - assert_eq!((res.1).0, "name"); - assert_eq!((res.1).1, "user1"); + ) + .await + .unwrap(); + assert_eq!((res.0).0, "name"); + assert_eq!((res.0).1, "user1"); + assert_eq!((res.1).0, "name"); + assert_eq!((res.1).1, "user1"); - let () = <()>::from_request(&req, &mut pl).unwrap(); + let () = <()>::from_request(&req, &mut pl).await.unwrap(); + }) } #[test] fn test_request_extract() { - let mut req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request(); + block_on(async { + let mut req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request(); - let resource = ResourceDef::new("/{key}/{value}/"); - resource.match_path(req.match_info_mut()); + let resource = ResourceDef::new("/{key}/{value}/"); + resource.match_path(req.match_info_mut()); - let (req, mut pl) = req.into_parts(); - let mut s = Path::::from_request(&req, &mut pl).unwrap(); - assert_eq!(s.key, "name"); - assert_eq!(s.value, "user1"); - s.value = "user2".to_string(); - assert_eq!(s.value, "user2"); - assert_eq!( - format!("{}, {:?}", s, s), - "MyStruct(name, user2), MyStruct { key: \"name\", value: \"user2\" }" - ); - let s = s.into_inner(); - assert_eq!(s.value, "user2"); + let (req, mut pl) = req.into_parts(); + let mut s = Path::::from_request(&req, &mut pl).await.unwrap(); + assert_eq!(s.key, "name"); + assert_eq!(s.value, "user1"); + s.value = "user2".to_string(); + assert_eq!(s.value, "user2"); + assert_eq!( + format!("{}, {:?}", s, s), + "MyStruct(name, user2), MyStruct { key: \"name\", value: \"user2\" }" + ); + let s = s.into_inner(); + assert_eq!(s.value, "user2"); - let s = Path::<(String, String)>::from_request(&req, &mut pl).unwrap(); - assert_eq!(s.0, "name"); - assert_eq!(s.1, "user1"); + let s = Path::<(String, String)>::from_request(&req, &mut pl) + .await + .unwrap(); + assert_eq!(s.0, "name"); + assert_eq!(s.1, "user1"); - let mut req = TestRequest::with_uri("/name/32/").to_srv_request(); - let resource = ResourceDef::new("/{key}/{value}/"); - resource.match_path(req.match_info_mut()); + let mut req = TestRequest::with_uri("/name/32/").to_srv_request(); + let resource = ResourceDef::new("/{key}/{value}/"); + resource.match_path(req.match_info_mut()); - let (req, mut pl) = req.into_parts(); - let s = Path::::from_request(&req, &mut pl).unwrap(); - assert_eq!(s.as_ref().key, "name"); - assert_eq!(s.value, 32); + let (req, mut pl) = req.into_parts(); + let s = Path::::from_request(&req, &mut pl).await.unwrap(); + assert_eq!(s.as_ref().key, "name"); + assert_eq!(s.value, 32); - let s = Path::<(String, u8)>::from_request(&req, &mut pl).unwrap(); - assert_eq!(s.0, "name"); - assert_eq!(s.1, 32); + let s = Path::<(String, u8)>::from_request(&req, &mut pl) + .await + .unwrap(); + assert_eq!(s.0, "name"); + assert_eq!(s.1, 32); - let res = Path::>::from_request(&req, &mut pl).unwrap(); - assert_eq!(res[0], "name".to_owned()); - assert_eq!(res[1], "32".to_owned()); + let res = Path::>::from_request(&req, &mut pl) + .await + .unwrap(); + assert_eq!(res[0], "name".to_owned()); + assert_eq!(res[1], "32".to_owned()); + }) } #[test] fn test_custom_err_handler() { - let (req, mut pl) = TestRequest::with_uri("/name/user1/") - .data(PathConfig::default().error_handler(|err, _| { - error::InternalError::from_response( - err, - HttpResponse::Conflict().finish(), - ) - .into() - })) - .to_http_parts(); + block_on(async { + let (req, mut pl) = TestRequest::with_uri("/name/user1/") + .data(PathConfig::default().error_handler(|err, _| { + error::InternalError::from_response( + err, + HttpResponse::Conflict().finish(), + ) + .into() + })) + .to_http_parts(); - let s = block_on(Path::<(usize,)>::from_request(&req, &mut pl)).unwrap_err(); - let res: HttpResponse = s.into(); + let s = Path::<(usize,)>::from_request(&req, &mut pl) + .await + .unwrap_err(); + let res: HttpResponse = s.into(); - assert_eq!(res.status(), http::StatusCode::CONFLICT); + assert_eq!(res.status(), http::StatusCode::CONFLICT); + }) } } diff --git a/src/types/payload.rs b/src/types/payload.rs index 8fc5f093e..61f7328b4 100644 --- a/src/types/payload.rs +++ b/src/types/payload.rs @@ -1,12 +1,15 @@ //! Payload/Bytes/String extractors +use std::future::Future; +use std::pin::Pin; use std::str; +use std::task::{Context, Poll}; use actix_http::error::{Error, ErrorBadRequest, PayloadError}; use actix_http::HttpMessage; use bytes::{Bytes, BytesMut}; use encoding_rs::UTF_8; -use futures::future::{err, Either, FutureResult}; -use futures::{Future, Poll, Stream}; +use futures::future::{err, ok, Either, FutureExt, LocalBoxFuture, Ready}; +use futures::{Stream, StreamExt}; use mime::Mime; use crate::dev; @@ -19,21 +22,19 @@ use crate::request::HttpRequest; /// ## Example /// /// ```rust -/// use futures::{Future, Stream}; +/// use futures::{Future, Stream, StreamExt}; /// use actix_web::{web, error, App, Error, HttpResponse}; /// /// /// extract binary data from request -/// fn index(body: web::Payload) -> impl Future +/// async fn index(mut body: web::Payload) -> Result /// { -/// body.map_err(Error::from) -/// .fold(web::BytesMut::new(), move |mut body, chunk| { -/// body.extend_from_slice(&chunk); -/// Ok::<_, Error>(body) -/// }) -/// .and_then(|body| { -/// format!("Body {:?}!", body); -/// Ok(HttpResponse::Ok().finish()) -/// }) +/// let mut bytes = web::BytesMut::new(); +/// while let Some(item) = body.next().await { +/// bytes.extend_from_slice(&item?); +/// } +/// +/// format!("Body {:?}!", bytes); +/// Ok(HttpResponse::Ok().finish()) /// } /// /// fn main() { @@ -53,12 +54,14 @@ impl Payload { } impl Stream for Payload { - type Item = Bytes; - type Error = PayloadError; + type Item = Result; #[inline] - fn poll(&mut self) -> Poll, PayloadError> { - self.0.poll() + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll> { + Pin::new(&mut self.0).poll_next(cx) } } @@ -67,21 +70,19 @@ impl Stream for Payload { /// ## Example /// /// ```rust -/// use futures::{Future, Stream}; +/// use futures::{Future, Stream, StreamExt}; /// use actix_web::{web, error, App, Error, HttpResponse}; /// /// /// extract binary data from request -/// fn index(body: web::Payload) -> impl Future +/// async fn index(mut body: web::Payload) -> Result /// { -/// body.map_err(Error::from) -/// .fold(web::BytesMut::new(), move |mut body, chunk| { -/// body.extend_from_slice(&chunk); -/// Ok::<_, Error>(body) -/// }) -/// .and_then(|body| { -/// format!("Body {:?}!", body); -/// Ok(HttpResponse::Ok().finish()) -/// }) +/// let mut bytes = web::BytesMut::new(); +/// while let Some(item) = body.next().await { +/// bytes.extend_from_slice(&item?); +/// } +/// +/// format!("Body {:?}!", bytes); +/// Ok(HttpResponse::Ok().finish()) /// } /// /// fn main() { @@ -94,11 +95,11 @@ impl Stream for Payload { impl FromRequest for Payload { type Config = PayloadConfig; type Error = Error; - type Future = Result; + type Future = Ready>; #[inline] fn from_request(_: &HttpRequest, payload: &mut dev::Payload) -> Self::Future { - Ok(Payload(payload.take())) + ok(Payload(payload.take())) } } @@ -130,8 +131,10 @@ impl FromRequest for Payload { impl FromRequest for Bytes { type Config = PayloadConfig; type Error = Error; - type Future = - Either>, FutureResult>; + type Future = Either< + LocalBoxFuture<'static, Result>, + Ready>, + >; #[inline] fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future { @@ -144,13 +147,12 @@ impl FromRequest for Bytes { }; if let Err(e) = cfg.check_mimetype(req) { - return Either::B(err(e)); + return Either::Right(err(e)); } let limit = cfg.limit; - Either::A(Box::new( - HttpMessageBody::new(req, payload).limit(limit).from_err(), - )) + let fut = HttpMessageBody::new(req, payload).limit(limit); + Either::Left(async move { Ok(fut.await?) }.boxed_local()) } } @@ -185,8 +187,8 @@ impl FromRequest for String { type Config = PayloadConfig; type Error = Error; type Future = Either< - Box>, - FutureResult, + LocalBoxFuture<'static, Result>, + Ready>, >; #[inline] @@ -201,33 +203,34 @@ impl FromRequest for String { // check content-type if let Err(e) = cfg.check_mimetype(req) { - return Either::B(err(e)); + return Either::Right(err(e)); } // check charset let encoding = match req.encoding() { Ok(enc) => enc, - Err(e) => return Either::B(err(e.into())), + Err(e) => return Either::Right(err(e.into())), }; let limit = cfg.limit; + let fut = HttpMessageBody::new(req, payload).limit(limit); - Either::A(Box::new( - HttpMessageBody::new(req, payload) - .limit(limit) - .from_err() - .and_then(move |body| { - if encoding == UTF_8 { - Ok(str::from_utf8(body.as_ref()) - .map_err(|_| ErrorBadRequest("Can not decode body"))? - .to_owned()) - } else { - Ok(encoding - .decode_without_bom_handling_and_without_replacement(&body) - .map(|s| s.into_owned()) - .ok_or_else(|| ErrorBadRequest("Can not decode body"))?) - } - }), - )) + Either::Left( + async move { + let body = fut.await?; + + if encoding == UTF_8 { + Ok(str::from_utf8(body.as_ref()) + .map_err(|_| ErrorBadRequest("Can not decode body"))? + .to_owned()) + } else { + Ok(encoding + .decode_without_bom_handling_and_without_replacement(&body) + .map(|s| s.into_owned()) + .ok_or_else(|| ErrorBadRequest("Can not decode body"))?) + } + } + .boxed_local(), + ) } } /// Payload configuration for request's payload. @@ -300,7 +303,7 @@ pub struct HttpMessageBody { length: Option, stream: Option>, err: Option, - fut: Option>>, + fut: Option>>, } impl HttpMessageBody { @@ -346,42 +349,43 @@ impl HttpMessageBody { } impl Future for HttpMessageBody { - type Item = Bytes; - type Error = PayloadError; + type Output = Result; - fn poll(&mut self) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { if let Some(ref mut fut) = self.fut { - return fut.poll(); + return Pin::new(fut).poll(cx); } if let Some(err) = self.err.take() { - return Err(err); + return Poll::Ready(Err(err)); } if let Some(len) = self.length.take() { if len > self.limit { - return Err(PayloadError::Overflow); + return Poll::Ready(Err(PayloadError::Overflow)); } } // future let limit = self.limit; - self.fut = Some(Box::new( - self.stream - .take() - .unwrap() - .from_err() - .fold(BytesMut::with_capacity(8192), move |mut body, chunk| { - if (body.len() + chunk.len()) > limit { - Err(PayloadError::Overflow) + let mut stream = self.stream.take().unwrap(); + self.fut = Some( + async move { + let mut body = BytesMut::with_capacity(8192); + + while let Some(item) = stream.next().await { + let chunk = item?; + if body.len() + chunk.len() > limit { + return Err(PayloadError::Overflow); } else { body.extend_from_slice(&chunk); - Ok(body) } - }) - .map(|body| body.freeze()), - )); - self.poll() + } + Ok(body.freeze()) + } + .boxed_local(), + ); + self.poll(cx) } } diff --git a/src/types/query.rs b/src/types/query.rs index 817b2ed7b..8061d7233 100644 --- a/src/types/query.rs +++ b/src/types/query.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use std::{fmt, ops}; use actix_http::error::Error; +use futures::future::{err, ok, Ready}; use serde::de; use serde_urlencoded; @@ -132,7 +133,7 @@ where T: de::DeserializeOwned, { type Error = Error; - type Future = Result; + type Future = Ready>; type Config = QueryConfig; #[inline] @@ -143,7 +144,7 @@ where .unwrap_or(None); serde_urlencoded::from_str::(req.query_string()) - .map(|val| Ok(Query(val))) + .map(|val| ok(Query(val))) .unwrap_or_else(move |e| { let e = QueryPayloadError::Deserialize(e); @@ -159,7 +160,7 @@ where e.into() }; - Err(e) + err(e) }) } } @@ -227,7 +228,7 @@ mod tests { use super::*; use crate::error::InternalError; - use crate::test::TestRequest; + use crate::test::{block_on, TestRequest}; use crate::HttpResponse; #[derive(Deserialize, Debug, Display)] @@ -253,42 +254,46 @@ mod tests { #[test] fn test_request_extract() { - let req = TestRequest::with_uri("/name/user1/").to_srv_request(); - let (req, mut pl) = req.into_parts(); - assert!(Query::::from_request(&req, &mut pl).is_err()); + block_on(async { + let req = TestRequest::with_uri("/name/user1/").to_srv_request(); + let (req, mut pl) = req.into_parts(); + assert!(Query::::from_request(&req, &mut pl).await.is_err()); - let req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request(); - let (req, mut pl) = req.into_parts(); + let req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request(); + let (req, mut pl) = req.into_parts(); - let mut s = Query::::from_request(&req, &mut pl).unwrap(); - assert_eq!(s.id, "test"); - assert_eq!(format!("{}, {:?}", s, s), "test, Id { id: \"test\" }"); + let mut s = Query::::from_request(&req, &mut pl).await.unwrap(); + assert_eq!(s.id, "test"); + assert_eq!(format!("{}, {:?}", s, s), "test, Id { id: \"test\" }"); - s.id = "test1".to_string(); - let s = s.into_inner(); - assert_eq!(s.id, "test1"); + s.id = "test1".to_string(); + let s = s.into_inner(); + assert_eq!(s.id, "test1"); + }) } #[test] fn test_custom_error_responder() { - let req = TestRequest::with_uri("/name/user1/") - .data(QueryConfig::default().error_handler(|e, _| { - let resp = HttpResponse::UnprocessableEntity().finish(); - InternalError::from_response(e, resp).into() - })) - .to_srv_request(); + block_on(async { + let req = TestRequest::with_uri("/name/user1/") + .data(QueryConfig::default().error_handler(|e, _| { + let resp = HttpResponse::UnprocessableEntity().finish(); + InternalError::from_response(e, resp).into() + })) + .to_srv_request(); - let (req, mut pl) = req.into_parts(); - let query = Query::::from_request(&req, &mut pl); + let (req, mut pl) = req.into_parts(); + let query = Query::::from_request(&req, &mut pl).await; - assert!(query.is_err()); - assert_eq!( - query - .unwrap_err() - .as_response_error() - .error_response() - .status(), - StatusCode::UNPROCESSABLE_ENTITY - ); + assert!(query.is_err()); + assert_eq!( + query + .unwrap_err() + .as_response_error() + .error_response() + .status(), + StatusCode::UNPROCESSABLE_ENTITY + ); + }) } } diff --git a/src/types/readlines.rs b/src/types/readlines.rs index cea63e43b..e2b3f9c1d 100644 --- a/src/types/readlines.rs +++ b/src/types/readlines.rs @@ -1,9 +1,13 @@ use std::borrow::Cow; +use std::future::Future; +use std::pin::Pin; use std::str; +use std::task::{Context, Poll}; use bytes::{Bytes, BytesMut}; use encoding_rs::{Encoding, UTF_8}; -use futures::{Async, Poll, Stream}; +use futures::Stream; +use pin_project::pin_project; use crate::dev::Payload; use crate::error::{PayloadError, ReadlinesError}; @@ -22,7 +26,7 @@ pub struct Readlines { impl Readlines where T: HttpMessage, - T::Stream: Stream, + T::Stream: Stream> + Unpin, { /// Create a new stream to read request line by line. pub fn new(req: &mut T) -> Self { @@ -62,20 +66,21 @@ where impl Stream for Readlines where T: HttpMessage, - T::Stream: Stream, + T::Stream: Stream> + Unpin, { - type Item = String; - type Error = ReadlinesError; + type Item = Result; - fn poll(&mut self) -> Poll, Self::Error> { - if let Some(err) = self.err.take() { - return Err(err); + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let this = self.get_mut(); + + if let Some(err) = this.err.take() { + return Poll::Ready(Some(Err(err))); } // check if there is a newline in the buffer - if !self.checked_buff { + if !this.checked_buff { let mut found: Option = None; - for (ind, b) in self.buff.iter().enumerate() { + for (ind, b) in this.buff.iter().enumerate() { if *b == b'\n' { found = Some(ind); break; @@ -83,28 +88,28 @@ where } if let Some(ind) = found { // check if line is longer than limit - if ind + 1 > self.limit { - return Err(ReadlinesError::LimitOverflow); + if ind + 1 > this.limit { + return Poll::Ready(Some(Err(ReadlinesError::LimitOverflow))); } - let line = if self.encoding == UTF_8 { - str::from_utf8(&self.buff.split_to(ind + 1)) + let line = if this.encoding == UTF_8 { + str::from_utf8(&this.buff.split_to(ind + 1)) .map_err(|_| ReadlinesError::EncodingError)? .to_owned() } else { - self.encoding + this.encoding .decode_without_bom_handling_and_without_replacement( - &self.buff.split_to(ind + 1), + &this.buff.split_to(ind + 1), ) .map(Cow::into_owned) .ok_or(ReadlinesError::EncodingError)? }; - return Ok(Async::Ready(Some(line))); + return Poll::Ready(Some(Ok(line))); } - self.checked_buff = true; + this.checked_buff = true; } // poll req for more bytes - match self.stream.poll() { - Ok(Async::Ready(Some(mut bytes))) => { + match Pin::new(&mut this.stream).poll_next(cx) { + Poll::Ready(Some(Ok(mut bytes))) => { // check if there is a newline in bytes let mut found: Option = None; for (ind, b) in bytes.iter().enumerate() { @@ -115,15 +120,15 @@ where } if let Some(ind) = found { // check if line is longer than limit - if ind + 1 > self.limit { - return Err(ReadlinesError::LimitOverflow); + if ind + 1 > this.limit { + return Poll::Ready(Some(Err(ReadlinesError::LimitOverflow))); } - let line = if self.encoding == UTF_8 { + let line = if this.encoding == UTF_8 { str::from_utf8(&bytes.split_to(ind + 1)) .map_err(|_| ReadlinesError::EncodingError)? .to_owned() } else { - self.encoding + this.encoding .decode_without_bom_handling_and_without_replacement( &bytes.split_to(ind + 1), ) @@ -131,83 +136,72 @@ where .ok_or(ReadlinesError::EncodingError)? }; // extend buffer with rest of the bytes; - self.buff.extend_from_slice(&bytes); - self.checked_buff = false; - return Ok(Async::Ready(Some(line))); + this.buff.extend_from_slice(&bytes); + this.checked_buff = false; + return Poll::Ready(Some(Ok(line))); } - self.buff.extend_from_slice(&bytes); - Ok(Async::NotReady) + this.buff.extend_from_slice(&bytes); + Poll::Pending } - Ok(Async::NotReady) => Ok(Async::NotReady), - Ok(Async::Ready(None)) => { - if self.buff.is_empty() { - return Ok(Async::Ready(None)); + Poll::Pending => Poll::Pending, + Poll::Ready(None) => { + if this.buff.is_empty() { + return Poll::Ready(None); } - if self.buff.len() > self.limit { - return Err(ReadlinesError::LimitOverflow); + if this.buff.len() > this.limit { + return Poll::Ready(Some(Err(ReadlinesError::LimitOverflow))); } - let line = if self.encoding == UTF_8 { - str::from_utf8(&self.buff) + let line = if this.encoding == UTF_8 { + str::from_utf8(&this.buff) .map_err(|_| ReadlinesError::EncodingError)? .to_owned() } else { - self.encoding - .decode_without_bom_handling_and_without_replacement(&self.buff) + this.encoding + .decode_without_bom_handling_and_without_replacement(&this.buff) .map(Cow::into_owned) .ok_or(ReadlinesError::EncodingError)? }; - self.buff.clear(); - Ok(Async::Ready(Some(line))) + this.buff.clear(); + Poll::Ready(Some(Ok(line))) } - Err(e) => Err(ReadlinesError::from(e)), + Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(ReadlinesError::from(e)))), } } } #[cfg(test)] mod tests { + use futures::stream::StreamExt; + use super::*; use crate::test::{block_on, TestRequest}; #[test] fn test_readlines() { - let mut req = TestRequest::default() + block_on(async { + let mut req = TestRequest::default() .set_payload(Bytes::from_static( b"Lorem Ipsum is simply dummy text of the printing and typesetting\n\ industry. Lorem Ipsum has been the industry's standard dummy\n\ Contrary to popular belief, Lorem Ipsum is not simply random text.", )) .to_request(); - let stream = match block_on(Readlines::new(&mut req).into_future()) { - Ok((Some(s), stream)) => { - assert_eq!( - s, - "Lorem Ipsum is simply dummy text of the printing and typesetting\n" - ); - stream - } - _ => unreachable!("error"), - }; - let stream = match block_on(stream.into_future()) { - Ok((Some(s), stream)) => { - assert_eq!( - s, - "industry. Lorem Ipsum has been the industry's standard dummy\n" - ); - stream - } - _ => unreachable!("error"), - }; + let mut stream = Readlines::new(&mut req); + assert_eq!( + stream.next().await.unwrap().unwrap(), + "Lorem Ipsum is simply dummy text of the printing and typesetting\n" + ); - match block_on(stream.into_future()) { - Ok((Some(s), _)) => { - assert_eq!( - s, - "Contrary to popular belief, Lorem Ipsum is not simply random text." - ); - } - _ => unreachable!("error"), - } + assert_eq!( + stream.next().await.unwrap().unwrap(), + "industry. Lorem Ipsum has been the industry's standard dummy\n" + ); + + assert_eq!( + stream.next().await.unwrap().unwrap(), + "Contrary to popular belief, Lorem Ipsum is not simply random text." + ); + }) } } diff --git a/src/web.rs b/src/web.rs index 5669a1e86..67cfd51a2 100644 --- a/src/web.rs +++ b/src/web.rs @@ -1,11 +1,12 @@ //! Essentials helper functions and types for application registration. use actix_http::http::Method; -use futures::{Future, IntoFuture}; +use futures::Future; pub use actix_http::Response as HttpResponse; pub use bytes::{Bytes, BytesMut}; +pub use futures::channel::oneshot::Canceled; -use crate::error::{BlockingError, Error}; +use crate::error::Error; use crate::extract::FromRequest; use crate::handler::{AsyncFactory, Factory}; use crate::resource::Resource; @@ -256,21 +257,21 @@ where /// # use futures::future::{ok, Future}; /// use actix_web::{web, App, HttpResponse, Error}; /// -/// fn index() -> impl Future { -/// ok(HttpResponse::Ok().finish()) +/// async fn index() -> Result { +/// Ok(HttpResponse::Ok().finish()) /// } /// /// App::new().service(web::resource("/").route( /// web::to_async(index)) /// ); /// ``` -pub fn to_async(handler: F) -> Route +pub fn to_async(handler: F) -> Route where - F: AsyncFactory, + F: AsyncFactory, I: FromRequest + 'static, - R: IntoFuture + 'static, - R::Item: Responder, - R::Error: Into, + R: Future> + 'static, + O: Responder + 'static, + E: Into + 'static, { Route::new().to_async(handler) } @@ -279,10 +280,11 @@ where /// /// ```rust /// # extern crate actix_web; -/// use actix_web::{dev, web, guard, App, HttpResponse}; +/// use futures::future::{ok, Ready}; +/// use actix_web::{dev, web, guard, App, Error, HttpResponse}; /// -/// fn my_service(req: dev::ServiceRequest) -> dev::ServiceResponse { -/// req.into_response(HttpResponse::Ok().finish()) +/// fn my_service(req: dev::ServiceRequest) -> Ready> { +/// ok(req.into_response(HttpResponse::Ok().finish())) /// } /// /// fn main() { @@ -299,11 +301,10 @@ pub fn service(path: &str) -> WebService { /// Execute blocking function on a thread pool, returns future that resolves /// to result of the function execution. -pub fn block(f: F) -> impl Future> +pub fn block(f: F) -> impl Future> where - F: FnOnce() -> Result + Send + 'static, - I: Send + 'static, - E: Send + std::fmt::Debug + 'static, + F: FnOnce() -> R + Send + 'static, + R: Send + 'static, { - actix_threadpool::run(f).from_err() + actix_threadpool::run(f) } diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 3333e0486..e43820290 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -59,19 +59,5 @@ tokio-timer = "0.3.0-alpha.6" open-ssl = { version="0.10", package="openssl", optional = true } [dev-dependencies] -#actix-web = "1.0.7" +actix-web = "2.0.0-alpha.1" actix-http = "0.3.0-alpha.1" - -[patch.crates-io] -actix-http = { path = "../actix-http" } -awc = { path = "../awc" } - -actix-codec = { path = "../../actix-net/actix-codec" } -actix-connect = { path = "../../actix-net/actix-connect" } -actix-rt = { path = "../../actix-net/actix-rt" } -actix-server = { path = "../../actix-net/actix-server" } -actix-server-config = { path = "../../actix-net/actix-server-config" } -actix-service = { path = "../../actix-net/actix-service" } -actix-testing = { path = "../../actix-net/actix-testing" } -actix-threadpool = { path = "../../actix-net/actix-threadpool" } -actix-utils = { path = "../../actix-net/actix-utils" } diff --git a/test-server/src/lib.rs b/test-server/src/lib.rs index bf6558b51..0c24ac907 100644 --- a/test-server/src/lib.rs +++ b/test-server/src/lib.rs @@ -3,7 +3,7 @@ use std::sync::mpsc; use std::{net, thread, time}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; -use actix_rt::{System}; +use actix_rt::System; use actix_server::{Server, ServiceFactory}; use awc::{error::PayloadError, ws, Client, ClientRequest, ClientResponse, Connector}; use bytes::Bytes; diff --git a/tests/test_httpserver.rs b/tests/test_httpserver.rs index c0d2e81c4..122f79baf 100644 --- a/tests/test_httpserver.rs +++ b/tests/test_httpserver.rs @@ -2,8 +2,8 @@ use net2::TcpBuilder; use std::sync::mpsc; use std::{net, thread, time::Duration}; -#[cfg(feature = "ssl")] -use openssl::ssl::SslAcceptorBuilder; +#[cfg(feature = "openssl")] +use open_ssl::ssl::SslAcceptorBuilder; use actix_http::Response; use actix_web::{test, web, App, HttpServer}; @@ -55,22 +55,19 @@ fn test_start() { use actix_http::client; use actix_web::test; - let client = test::run_on(|| { - Ok::<_, ()>( - awc::Client::build() - .connector( - client::Connector::new() - .timeout(Duration::from_millis(100)) - .finish(), - ) - .finish(), - ) - }) - .unwrap(); - let host = format!("http://{}", addr); + test::block_on(async { + let client = awc::Client::build() + .connector( + client::Connector::new() + .timeout(Duration::from_millis(100)) + .finish(), + ) + .finish(); - let response = test::block_on(client.get(host.clone()).send()).unwrap(); - assert!(response.status().is_success()); + let host = format!("http://{}", addr); + let response = client.get(host.clone()).send().await.unwrap(); + assert!(response.status().is_success()); + }); } // stop @@ -80,9 +77,9 @@ fn test_start() { let _ = sys.stop(); } -#[cfg(feature = "ssl")] +#[cfg(feature = "openssl")] fn ssl_acceptor() -> std::io::Result { - use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; + use open_ssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; // load ssl keys let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); builder @@ -95,7 +92,7 @@ fn ssl_acceptor() -> std::io::Result { } #[test] -#[cfg(feature = "ssl")] +#[cfg(feature = "openssl")] fn test_start_ssl() { let addr = unused_addr(); let (tx, rx) = mpsc::channel(); @@ -113,7 +110,7 @@ fn test_start_ssl() { .shutdown_timeout(1) .system_exit() .disable_signals() - .bind_ssl(format!("{}", addr), builder) + .bind_openssl(format!("{}", addr), builder) .unwrap() .start(); @@ -122,30 +119,27 @@ fn test_start_ssl() { }); let (srv, sys) = rx.recv().unwrap(); - let client = test::run_on(|| { - use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; + test::block_on(async move { + use open_ssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); builder.set_verify(SslVerifyMode::NONE); let _ = builder .set_alpn_protos(b"\x02h2\x08http/1.1") .map_err(|e| log::error!("Can not set alpn protocol: {:?}", e)); - Ok::<_, ()>( - awc::Client::build() - .connector( - awc::Connector::new() - .ssl(builder.build()) - .timeout(Duration::from_millis(100)) - .finish(), - ) - .finish(), - ) - }) - .unwrap(); - let host = format!("https://{}", addr); + let client = awc::Client::build() + .connector( + awc::Connector::new() + .ssl(builder.build()) + .timeout(Duration::from_millis(100)) + .finish(), + ) + .finish(); - let response = test::block_on(client.get(host.clone()).send()).unwrap(); - assert!(response.status().is_success()); + let host = format!("https://{}", addr); + let response = client.get(host.clone()).send().await.unwrap(); + assert!(response.status().is_success()); + }); // stop let _ = srv.stop(false); diff --git a/tests/test_server.rs b/tests/test_server.rs index 1623d2ef3..eeaedec05 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -1,18 +1,17 @@ use std::io::{Read, Write}; -use std::sync::mpsc; -use std::thread; use actix_http::http::header::{ ContentEncoding, ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_LENGTH, TRANSFER_ENCODING, }; use actix_http::{h1, Error, HttpService, Response}; -use actix_http_test::TestServer; +use actix_http_test::{block_on, TestServer}; use brotli2::write::{BrotliDecoder, BrotliEncoder}; use bytes::Bytes; use flate2::read::GzDecoder; use flate2::write::{GzEncoder, ZlibDecoder, ZlibEncoder}; use flate2::Compression; +use futures::future::ok; use futures::stream::once; use rand::{distributions::Alphanumeric, Rng}; @@ -44,679 +43,721 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[test] fn test_body() { - let mut srv = TestServer::new(|| { - h1::H1Service::new( - App::new() - .service(web::resource("/").route(web::to(|| Response::Ok().body(STR)))), - ) - }); + block_on(async { + let srv = + TestServer::start(|| { + h1::H1Service::new(App::new().service( + web::resource("/").route(web::to(|| Response::Ok().body(STR))), + )) + }); - let mut response = srv.block_on(srv.get("/").send()).unwrap(); - assert!(response.status().is_success()); + let mut response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) } #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[test] fn test_body_gzip() { - let mut srv = TestServer::new(|| { - h1::H1Service::new( - App::new() - .wrap(Compress::new(ContentEncoding::Gzip)) - .service(web::resource("/").route(web::to(|| Response::Ok().body(STR)))), - ) - }); + block_on(async { + let srv = TestServer::start(|| { + h1::H1Service::new( + App::new() + .wrap(Compress::new(ContentEncoding::Gzip)) + .service( + web::resource("/").route(web::to(|| Response::Ok().body(STR))), + ), + ) + }); - let mut response = srv - .block_on( - srv.get("/") - .no_decompress() - .header(ACCEPT_ENCODING, "gzip") - .send(), - ) - .unwrap(); - assert!(response.status().is_success()); + let mut response = srv + .get("/") + .no_decompress() + .header(ACCEPT_ENCODING, "gzip") + .send() + .await + .unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); + // read response + let bytes = response.body().await.unwrap(); - // decode - let mut e = GzDecoder::new(&bytes[..]); - let mut dec = Vec::new(); - e.read_to_end(&mut dec).unwrap(); - assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); + // decode + let mut e = GzDecoder::new(&bytes[..]); + let mut dec = Vec::new(); + e.read_to_end(&mut dec).unwrap(); + assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); + }) } #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[test] fn test_body_gzip2() { - let mut srv = TestServer::new(|| { - h1::H1Service::new( - App::new() - .wrap(Compress::new(ContentEncoding::Gzip)) - .service(web::resource("/").route(web::to(|| { - Response::Ok().body(STR).into_body::() - }))), - ) - }); + block_on(async { + let srv = TestServer::start(|| { + h1::H1Service::new( + App::new() + .wrap(Compress::new(ContentEncoding::Gzip)) + .service(web::resource("/").route(web::to(|| { + Response::Ok().body(STR).into_body::() + }))), + ) + }); - let mut response = srv - .block_on( - srv.get("/") - .no_decompress() - .header(ACCEPT_ENCODING, "gzip") - .send(), - ) - .unwrap(); - assert!(response.status().is_success()); + let mut response = srv + .get("/") + .no_decompress() + .header(ACCEPT_ENCODING, "gzip") + .send() + .await + .unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); + // read response + let bytes = response.body().await.unwrap(); - // decode - let mut e = GzDecoder::new(&bytes[..]); - let mut dec = Vec::new(); - e.read_to_end(&mut dec).unwrap(); - assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); + // decode + let mut e = GzDecoder::new(&bytes[..]); + let mut dec = Vec::new(); + e.read_to_end(&mut dec).unwrap(); + assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); + }) } #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[test] fn test_body_encoding_override() { - let mut srv = TestServer::new(|| { - h1::H1Service::new( - App::new() - .wrap(Compress::new(ContentEncoding::Gzip)) - .service(web::resource("/").route(web::to(|| { - Response::Ok().encoding(ContentEncoding::Deflate).body(STR) - }))) - .service(web::resource("/raw").route(web::to(|| { - let body = actix_web::dev::Body::Bytes(STR.into()); - let mut response = - Response::with_body(actix_web::http::StatusCode::OK, body); + block_on(async { + let srv = TestServer::start(|| { + h1::H1Service::new( + App::new() + .wrap(Compress::new(ContentEncoding::Gzip)) + .service(web::resource("/").route(web::to(|| { + Response::Ok().encoding(ContentEncoding::Deflate).body(STR) + }))) + .service(web::resource("/raw").route(web::to(|| { + let body = actix_web::dev::Body::Bytes(STR.into()); + let mut response = + Response::with_body(actix_web::http::StatusCode::OK, body); - response.encoding(ContentEncoding::Deflate); + response.encoding(ContentEncoding::Deflate); - response - }))), - ) - }); + response + }))), + ) + }); - // Builder - let mut response = srv - .block_on( - srv.get("/") - .no_decompress() - .header(ACCEPT_ENCODING, "deflate") - .send(), - ) - .unwrap(); - assert!(response.status().is_success()); + // Builder + let mut response = srv + .get("/") + .no_decompress() + .header(ACCEPT_ENCODING, "deflate") + .send() + .await + .unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); + // read response + let bytes = response.body().await.unwrap(); - // decode - let mut e = ZlibDecoder::new(Vec::new()); - e.write_all(bytes.as_ref()).unwrap(); - let dec = e.finish().unwrap(); - assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); + // decode + let mut e = ZlibDecoder::new(Vec::new()); + e.write_all(bytes.as_ref()).unwrap(); + let dec = e.finish().unwrap(); + assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); - // Raw Response - let mut response = srv - .block_on( - srv.request(actix_web::http::Method::GET, srv.url("/raw")) - .no_decompress() - .header(ACCEPT_ENCODING, "deflate") - .send(), - ) - .unwrap(); - assert!(response.status().is_success()); + // Raw Response + let mut response = srv + .request(actix_web::http::Method::GET, srv.url("/raw")) + .no_decompress() + .header(ACCEPT_ENCODING, "deflate") + .send() + .await + .unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); + // read response + let bytes = response.body().await.unwrap(); - // decode - let mut e = ZlibDecoder::new(Vec::new()); - e.write_all(bytes.as_ref()).unwrap(); - let dec = e.finish().unwrap(); - assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); + // decode + let mut e = ZlibDecoder::new(Vec::new()); + e.write_all(bytes.as_ref()).unwrap(); + let dec = e.finish().unwrap(); + assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); + }) } #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[test] fn test_body_gzip_large() { - let data = STR.repeat(10); - let srv_data = data.clone(); + block_on(async { + let data = STR.repeat(10); + let srv_data = data.clone(); - let mut srv = TestServer::new(move || { - let data = srv_data.clone(); - h1::H1Service::new( - App::new() - .wrap(Compress::new(ContentEncoding::Gzip)) - .service( - web::resource("/") - .route(web::to(move || Response::Ok().body(data.clone()))), - ), - ) - }); + let srv = TestServer::start(move || { + let data = srv_data.clone(); + h1::H1Service::new( + App::new() + .wrap(Compress::new(ContentEncoding::Gzip)) + .service( + web::resource("/") + .route(web::to(move || Response::Ok().body(data.clone()))), + ), + ) + }); - let mut response = srv - .block_on( - srv.get("/") - .no_decompress() - .header(ACCEPT_ENCODING, "gzip") - .send(), - ) - .unwrap(); - assert!(response.status().is_success()); + let mut response = srv + .get("/") + .no_decompress() + .header(ACCEPT_ENCODING, "gzip") + .send() + .await + .unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); + // read response + let bytes = response.body().await.unwrap(); - // decode - let mut e = GzDecoder::new(&bytes[..]); - let mut dec = Vec::new(); - e.read_to_end(&mut dec).unwrap(); - assert_eq!(Bytes::from(dec), Bytes::from(data)); + // decode + let mut e = GzDecoder::new(&bytes[..]); + let mut dec = Vec::new(); + e.read_to_end(&mut dec).unwrap(); + assert_eq!(Bytes::from(dec), Bytes::from(data)); + }) } #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[test] fn test_body_gzip_large_random() { - let data = rand::thread_rng() - .sample_iter(&Alphanumeric) - .take(70_000) - .collect::(); - let srv_data = data.clone(); + block_on(async { + let data = rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(70_000) + .collect::(); + let srv_data = data.clone(); - let mut srv = TestServer::new(move || { - let data = srv_data.clone(); - h1::H1Service::new( - App::new() - .wrap(Compress::new(ContentEncoding::Gzip)) - .service( - web::resource("/") - .route(web::to(move || Response::Ok().body(data.clone()))), - ), - ) - }); + let srv = TestServer::start(move || { + let data = srv_data.clone(); + h1::H1Service::new( + App::new() + .wrap(Compress::new(ContentEncoding::Gzip)) + .service( + web::resource("/") + .route(web::to(move || Response::Ok().body(data.clone()))), + ), + ) + }); - let mut response = srv - .block_on( - srv.get("/") - .no_decompress() - .header(ACCEPT_ENCODING, "gzip") - .send(), - ) - .unwrap(); - assert!(response.status().is_success()); + let mut response = srv + .get("/") + .no_decompress() + .header(ACCEPT_ENCODING, "gzip") + .send() + .await + .unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); + // read response + let bytes = response.body().await.unwrap(); - // decode - let mut e = GzDecoder::new(&bytes[..]); - let mut dec = Vec::new(); - e.read_to_end(&mut dec).unwrap(); - assert_eq!(dec.len(), data.len()); - assert_eq!(Bytes::from(dec), Bytes::from(data)); + // decode + let mut e = GzDecoder::new(&bytes[..]); + let mut dec = Vec::new(); + e.read_to_end(&mut dec).unwrap(); + assert_eq!(dec.len(), data.len()); + assert_eq!(Bytes::from(dec), Bytes::from(data)); + }) } #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[test] fn test_body_chunked_implicit() { - let mut srv = TestServer::new(move || { - h1::H1Service::new( - App::new() - .wrap(Compress::new(ContentEncoding::Gzip)) - .service(web::resource("/").route(web::get().to(move || { - Response::Ok().streaming(once(Ok::<_, Error>(Bytes::from_static( - STR.as_ref(), - )))) - }))), - ) - }); + block_on(async { + let srv = TestServer::start(move || { + h1::H1Service::new( + App::new() + .wrap(Compress::new(ContentEncoding::Gzip)) + .service(web::resource("/").route(web::get().to(move || { + Response::Ok().streaming(once(ok::<_, Error>( + Bytes::from_static(STR.as_ref()), + ))) + }))), + ) + }); - let mut response = srv - .block_on( - srv.get("/") - .no_decompress() - .header(ACCEPT_ENCODING, "gzip") - .send(), - ) - .unwrap(); - assert!(response.status().is_success()); - assert_eq!( - response.headers().get(TRANSFER_ENCODING).unwrap(), - &b"chunked"[..] - ); + let mut response = srv + .get("/") + .no_decompress() + .header(ACCEPT_ENCODING, "gzip") + .send() + .await + .unwrap(); + assert!(response.status().is_success()); + assert_eq!( + response.headers().get(TRANSFER_ENCODING).unwrap(), + &b"chunked"[..] + ); - // read response - let bytes = srv.block_on(response.body()).unwrap(); + // read response + let bytes = response.body().await.unwrap(); - // decode - let mut e = GzDecoder::new(&bytes[..]); - let mut dec = Vec::new(); - e.read_to_end(&mut dec).unwrap(); - assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); + // decode + let mut e = GzDecoder::new(&bytes[..]); + let mut dec = Vec::new(); + e.read_to_end(&mut dec).unwrap(); + assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); + }) } #[test] #[cfg(feature = "brotli")] fn test_body_br_streaming() { - let mut srv = TestServer::new(move || { - h1::H1Service::new(App::new().wrap(Compress::new(ContentEncoding::Br)).service( - web::resource("/").route(web::to(move || { - Response::Ok() - .streaming(once(Ok::<_, Error>(Bytes::from_static(STR.as_ref())))) - })), - )) - }); + block_on(async { + let srv = TestServer::start(move || { + h1::H1Service::new( + App::new().wrap(Compress::new(ContentEncoding::Br)).service( + web::resource("/").route(web::to(move || { + Response::Ok().streaming(once(ok::<_, Error>( + Bytes::from_static(STR.as_ref()), + ))) + })), + ), + ) + }); - let mut response = srv - .block_on( - srv.get("/") - .header(ACCEPT_ENCODING, "br") - .no_decompress() - .send(), - ) - .unwrap(); - assert!(response.status().is_success()); + let mut response = srv + .get("/") + .header(ACCEPT_ENCODING, "br") + .no_decompress() + .send() + .await + .unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); + // read response + let bytes = response.body().await.unwrap(); - // decode br - let mut e = BrotliDecoder::new(Vec::with_capacity(2048)); - e.write_all(bytes.as_ref()).unwrap(); - let dec = e.finish().unwrap(); - assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); + // decode br + let mut e = BrotliDecoder::new(Vec::with_capacity(2048)); + e.write_all(bytes.as_ref()).unwrap(); + let dec = e.finish().unwrap(); + assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); + }) } #[test] fn test_head_binary() { - let mut srv = TestServer::new(move || { - h1::H1Service::new(App::new().service(web::resource("/").route( - web::head().to(move || Response::Ok().content_length(100).body(STR)), - ))) - }); + block_on(async { + let srv = TestServer::start(move || { + h1::H1Service::new(App::new().service(web::resource("/").route( + web::head().to(move || Response::Ok().content_length(100).body(STR)), + ))) + }); - let mut response = srv.block_on(srv.head("/").send()).unwrap(); - assert!(response.status().is_success()); + let mut response = srv.head("/").send().await.unwrap(); + assert!(response.status().is_success()); - { - let len = response.headers().get(CONTENT_LENGTH).unwrap(); - assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); - } + { + let len = response.headers().get(CONTENT_LENGTH).unwrap(); + assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); + } - // read response - let bytes = srv.block_on(response.body()).unwrap(); - assert!(bytes.is_empty()); + // read response + let bytes = response.body().await.unwrap(); + assert!(bytes.is_empty()); + }) } #[test] fn test_no_chunking() { - let mut srv = TestServer::new(move || { - h1::H1Service::new(App::new().service(web::resource("/").route(web::to( - move || { - Response::Ok() - .no_chunking() - .content_length(STR.len() as u64) - .streaming(once(Ok::<_, Error>(Bytes::from_static(STR.as_ref())))) - }, - )))) - }); + block_on(async { + let srv = TestServer::start(move || { + h1::H1Service::new(App::new().service(web::resource("/").route(web::to( + move || { + Response::Ok() + .no_chunking() + .content_length(STR.len() as u64) + .streaming(once(ok::<_, Error>(Bytes::from_static( + STR.as_ref(), + )))) + }, + )))) + }); - let mut response = srv.block_on(srv.get("/").send()).unwrap(); - assert!(response.status().is_success()); - assert!(!response.headers().contains_key(TRANSFER_ENCODING)); + let mut response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); + assert!(!response.headers().contains_key(TRANSFER_ENCODING)); - // read response - let bytes = srv.block_on(response.body()).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) } #[test] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] fn test_body_deflate() { - let mut srv = TestServer::new(move || { - h1::H1Service::new( - App::new() - .wrap(Compress::new(ContentEncoding::Deflate)) - .service( - web::resource("/").route(web::to(move || Response::Ok().body(STR))), - ), - ) - }); + block_on(async { + let srv = TestServer::start(move || { + h1::H1Service::new( + App::new() + .wrap(Compress::new(ContentEncoding::Deflate)) + .service( + web::resource("/") + .route(web::to(move || Response::Ok().body(STR))), + ), + ) + }); - // client request - let mut response = srv - .block_on( - srv.get("/") - .header(ACCEPT_ENCODING, "deflate") - .no_decompress() - .send(), - ) - .unwrap(); - assert!(response.status().is_success()); + // client request + let mut response = srv + .get("/") + .header(ACCEPT_ENCODING, "deflate") + .no_decompress() + .send() + .await + .unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); + // read response + let bytes = response.body().await.unwrap(); - let mut e = ZlibDecoder::new(Vec::new()); - e.write_all(bytes.as_ref()).unwrap(); - let dec = e.finish().unwrap(); - assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); + let mut e = ZlibDecoder::new(Vec::new()); + e.write_all(bytes.as_ref()).unwrap(); + let dec = e.finish().unwrap(); + assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); + }) } #[test] #[cfg(any(feature = "brotli"))] fn test_body_brotli() { - let mut srv = TestServer::new(move || { - h1::H1Service::new(App::new().wrap(Compress::new(ContentEncoding::Br)).service( - web::resource("/").route(web::to(move || Response::Ok().body(STR))), - )) - }); + block_on(async { + let srv = TestServer::start(move || { + h1::H1Service::new( + App::new().wrap(Compress::new(ContentEncoding::Br)).service( + web::resource("/").route(web::to(move || Response::Ok().body(STR))), + ), + ) + }); - // client request - let mut response = srv - .block_on( - srv.get("/") - .header(ACCEPT_ENCODING, "br") - .no_decompress() - .send(), - ) - .unwrap(); - assert!(response.status().is_success()); + // client request + let mut response = srv + .get("/") + .header(ACCEPT_ENCODING, "br") + .no_decompress() + .send() + .await + .unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); + // read response + let bytes = response.body().await.unwrap(); - // decode brotli - let mut e = BrotliDecoder::new(Vec::with_capacity(2048)); - e.write_all(bytes.as_ref()).unwrap(); - let dec = e.finish().unwrap(); - assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); + // decode brotli + let mut e = BrotliDecoder::new(Vec::with_capacity(2048)); + e.write_all(bytes.as_ref()).unwrap(); + let dec = e.finish().unwrap(); + assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); + }) } #[test] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] fn test_encoding() { - let mut srv = TestServer::new(move || { - HttpService::new( - App::new().wrap(Compress::default()).service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) - }); + block_on(async { + let srv = TestServer::start(move || { + HttpService::new( + App::new().wrap(Compress::default()).service( + web::resource("/") + .route(web::to(move |body: Bytes| Response::Ok().body(body))), + ), + ) + }); - // client request - let mut e = GzEncoder::new(Vec::new(), Compression::default()); - e.write_all(STR.as_ref()).unwrap(); - let enc = e.finish().unwrap(); + // client request + let mut e = GzEncoder::new(Vec::new(), Compression::default()); + e.write_all(STR.as_ref()).unwrap(); + let enc = e.finish().unwrap(); - let request = srv - .post("/") - .header(CONTENT_ENCODING, "gzip") - .send_body(enc.clone()); - let mut response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + let request = srv + .post("/") + .header(CONTENT_ENCODING, "gzip") + .send_body(enc.clone()); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) } #[test] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] fn test_gzip_encoding() { - let mut srv = TestServer::new(move || { - HttpService::new( - App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) - }); + block_on(async { + let srv = TestServer::start(move || { + HttpService::new( + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| Response::Ok().body(body))), + ), + ) + }); - // client request - let mut e = GzEncoder::new(Vec::new(), Compression::default()); - e.write_all(STR.as_ref()).unwrap(); - let enc = e.finish().unwrap(); + // client request + let mut e = GzEncoder::new(Vec::new(), Compression::default()); + e.write_all(STR.as_ref()).unwrap(); + let enc = e.finish().unwrap(); - let request = srv - .post("/") - .header(CONTENT_ENCODING, "gzip") - .send_body(enc.clone()); - let mut response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + let request = srv + .post("/") + .header(CONTENT_ENCODING, "gzip") + .send_body(enc.clone()); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) } #[test] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] fn test_gzip_encoding_large() { - let data = STR.repeat(10); - let mut srv = TestServer::new(move || { - h1::H1Service::new( - App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) - }); + block_on(async { + let data = STR.repeat(10); + let srv = TestServer::start(move || { + h1::H1Service::new( + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| Response::Ok().body(body))), + ), + ) + }); - // client request - let mut e = GzEncoder::new(Vec::new(), Compression::default()); - e.write_all(data.as_ref()).unwrap(); - let enc = e.finish().unwrap(); + // client request + let mut e = GzEncoder::new(Vec::new(), Compression::default()); + e.write_all(data.as_ref()).unwrap(); + let enc = e.finish().unwrap(); - let request = srv - .post("/") - .header(CONTENT_ENCODING, "gzip") - .send_body(enc.clone()); - let mut response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + let request = srv + .post("/") + .header(CONTENT_ENCODING, "gzip") + .send_body(enc.clone()); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); - assert_eq!(bytes, Bytes::from(data)); + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from(data)); + }) } #[test] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] fn test_reading_gzip_encoding_large_random() { - let data = rand::thread_rng() - .sample_iter(&Alphanumeric) - .take(60_000) - .collect::(); + block_on(async { + let data = rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(60_000) + .collect::(); - let mut srv = TestServer::new(move || { - HttpService::new( - App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) - }); + let srv = TestServer::start(move || { + HttpService::new( + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| Response::Ok().body(body))), + ), + ) + }); - // client request - let mut e = GzEncoder::new(Vec::new(), Compression::default()); - e.write_all(data.as_ref()).unwrap(); - let enc = e.finish().unwrap(); + // client request + let mut e = GzEncoder::new(Vec::new(), Compression::default()); + e.write_all(data.as_ref()).unwrap(); + let enc = e.finish().unwrap(); - let request = srv - .post("/") - .header(CONTENT_ENCODING, "gzip") - .send_body(enc.clone()); - let mut response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + let request = srv + .post("/") + .header(CONTENT_ENCODING, "gzip") + .send_body(enc.clone()); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); - assert_eq!(bytes.len(), data.len()); - assert_eq!(bytes, Bytes::from(data)); + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes.len(), data.len()); + assert_eq!(bytes, Bytes::from(data)); + }) } #[test] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] fn test_reading_deflate_encoding() { - let mut srv = TestServer::new(move || { - h1::H1Service::new( - App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) - }); + block_on(async { + let srv = TestServer::start(move || { + h1::H1Service::new( + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| Response::Ok().body(body))), + ), + ) + }); - let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); - e.write_all(STR.as_ref()).unwrap(); - let enc = e.finish().unwrap(); + let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); + e.write_all(STR.as_ref()).unwrap(); + let enc = e.finish().unwrap(); - // client request - let request = srv - .post("/") - .header(CONTENT_ENCODING, "deflate") - .send_body(enc.clone()); - let mut response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + // client request + let request = srv + .post("/") + .header(CONTENT_ENCODING, "deflate") + .send_body(enc.clone()); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) } #[test] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] fn test_reading_deflate_encoding_large() { - let data = STR.repeat(10); - let mut srv = TestServer::new(move || { - h1::H1Service::new( - App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) - }); + block_on(async { + let data = STR.repeat(10); + let srv = TestServer::start(move || { + h1::H1Service::new( + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| Response::Ok().body(body))), + ), + ) + }); - let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); - e.write_all(data.as_ref()).unwrap(); - let enc = e.finish().unwrap(); + let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); + e.write_all(data.as_ref()).unwrap(); + let enc = e.finish().unwrap(); - // client request - let request = srv - .post("/") - .header(CONTENT_ENCODING, "deflate") - .send_body(enc.clone()); - let mut response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + // client request + let request = srv + .post("/") + .header(CONTENT_ENCODING, "deflate") + .send_body(enc.clone()); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); - assert_eq!(bytes, Bytes::from(data)); + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from(data)); + }) } #[test] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] fn test_reading_deflate_encoding_large_random() { - let data = rand::thread_rng() - .sample_iter(&Alphanumeric) - .take(160_000) - .collect::(); + block_on(async { + let data = rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(160_000) + .collect::(); - let mut srv = TestServer::new(move || { - h1::H1Service::new( - App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) - }); + let srv = TestServer::start(move || { + h1::H1Service::new( + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| Response::Ok().body(body))), + ), + ) + }); - let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); - e.write_all(data.as_ref()).unwrap(); - let enc = e.finish().unwrap(); + let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); + e.write_all(data.as_ref()).unwrap(); + let enc = e.finish().unwrap(); - // client request - let request = srv - .post("/") - .header(CONTENT_ENCODING, "deflate") - .send_body(enc.clone()); - let mut response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + // client request + let request = srv + .post("/") + .header(CONTENT_ENCODING, "deflate") + .send_body(enc.clone()); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); - assert_eq!(bytes.len(), data.len()); - assert_eq!(bytes, Bytes::from(data)); + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes.len(), data.len()); + assert_eq!(bytes, Bytes::from(data)); + }) } #[test] #[cfg(feature = "brotli")] fn test_brotli_encoding() { - let mut srv = TestServer::new(move || { - h1::H1Service::new( - App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) - }); + block_on(async { + let srv = TestServer::start(move || { + h1::H1Service::new( + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| Response::Ok().body(body))), + ), + ) + }); - let mut e = BrotliEncoder::new(Vec::new(), 5); - e.write_all(STR.as_ref()).unwrap(); - let enc = e.finish().unwrap(); + let mut e = BrotliEncoder::new(Vec::new(), 5); + e.write_all(STR.as_ref()).unwrap(); + let enc = e.finish().unwrap(); - // client request - let request = srv - .post("/") - .header(CONTENT_ENCODING, "br") - .send_body(enc.clone()); - let mut response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + // client request + let request = srv + .post("/") + .header(CONTENT_ENCODING, "br") + .send_body(enc.clone()); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + }) } #[cfg(feature = "brotli")] #[test] fn test_brotli_encoding_large() { - let data = STR.repeat(10); - let mut srv = TestServer::new(move || { - h1::H1Service::new( - App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) - }); + block_on(async { + let data = STR.repeat(10); + let srv = TestServer::start(move || { + h1::H1Service::new( + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| Response::Ok().body(body))), + ), + ) + }); - let mut e = BrotliEncoder::new(Vec::new(), 5); - e.write_all(data.as_ref()).unwrap(); - let enc = e.finish().unwrap(); + let mut e = BrotliEncoder::new(Vec::new(), 5); + e.write_all(data.as_ref()).unwrap(); + let enc = e.finish().unwrap(); - // client request - let request = srv - .post("/") - .header(CONTENT_ENCODING, "br") - .send_body(enc.clone()); - let mut response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + // client request + let request = srv + .post("/") + .header(CONTENT_ENCODING, "br") + .send_body(enc.clone()); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.block_on(response.body()).unwrap(); - assert_eq!(bytes, Bytes::from(data)); + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from(data)); + }) } // #[cfg(all(feature = "brotli", feature = "ssl"))] @@ -782,85 +823,87 @@ fn test_brotli_encoding_large() { ))] #[test] fn test_reading_deflate_encoding_large_random_ssl() { - use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; - use rustls::internal::pemfile::{certs, pkcs8_private_keys}; - use rustls::{NoClientAuth, ServerConfig}; - use std::fs::File; - use std::io::BufReader; + block_on(async { + use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; + use rustls::internal::pemfile::{certs, pkcs8_private_keys}; + use rustls::{NoClientAuth, ServerConfig}; + use std::fs::File; + use std::io::BufReader; - let addr = TestServer::unused_addr(); - let (tx, rx) = mpsc::channel(); + let addr = TestServer::unused_addr(); + let (tx, rx) = mpsc::channel(); - let data = rand::thread_rng() - .sample_iter(&Alphanumeric) - .take(160_000) - .collect::(); + let data = rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(160_000) + .collect::(); - thread::spawn(move || { - let sys = actix_rt::System::new("test"); + thread::spawn(move || { + let sys = actix_rt::System::new("test"); - // load ssl keys - let mut config = ServerConfig::new(NoClientAuth::new()); - let cert_file = &mut BufReader::new(File::open("tests/cert.pem").unwrap()); - let key_file = &mut BufReader::new(File::open("tests/key.pem").unwrap()); - let cert_chain = certs(cert_file).unwrap(); - let mut keys = pkcs8_private_keys(key_file).unwrap(); - config.set_single_cert(cert_chain, keys.remove(0)).unwrap(); + // load ssl keys + let mut config = ServerConfig::new(NoClientAuth::new()); + let cert_file = &mut BufReader::new(File::open("tests/cert.pem").unwrap()); + let key_file = &mut BufReader::new(File::open("tests/key.pem").unwrap()); + let cert_chain = certs(cert_file).unwrap(); + let mut keys = pkcs8_private_keys(key_file).unwrap(); + config.set_single_cert(cert_chain, keys.remove(0)).unwrap(); - let srv = HttpServer::new(|| { - App::new().service(web::resource("/").route(web::to(|bytes: Bytes| { - Ok::<_, Error>( - HttpResponse::Ok() - .encoding(http::ContentEncoding::Identity) - .body(bytes), + let srv = HttpServer::new(|| { + App::new().service(web::resource("/").route(web::to(|bytes: Bytes| { + Ok::<_, Error>( + HttpResponse::Ok() + .encoding(http::ContentEncoding::Identity) + .body(bytes), + ) + }))) + }) + .bind_rustls(addr, config) + .unwrap() + .start(); + + let _ = tx.send((srv, actix_rt::System::current())); + let _ = sys.run(); + }); + let (srv, _sys) = rx.recv().unwrap(); + test::block_on(futures::lazy(|| Ok::<_, ()>(start_default_resolver()))).unwrap(); + let client = test::run_on(|| { + let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); + builder.set_verify(SslVerifyMode::NONE); + let _ = builder.set_alpn_protos(b"\x02h2\x08http/1.1").unwrap(); + + awc::Client::build() + .connector( + awc::Connector::new() + .timeout(std::time::Duration::from_millis(500)) + .ssl(builder.build()) + .finish(), ) - }))) - }) - .bind_rustls(addr, config) - .unwrap() - .start(); + .finish() + }); - let _ = tx.send((srv, actix_rt::System::current())); - let _ = sys.run(); - }); - let (srv, _sys) = rx.recv().unwrap(); - test::block_on(futures::lazy(|| Ok::<_, ()>(start_default_resolver()))).unwrap(); - let client = test::run_on(|| { - let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); - builder.set_verify(SslVerifyMode::NONE); - let _ = builder.set_alpn_protos(b"\x02h2\x08http/1.1").unwrap(); + // encode data + let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); + e.write_all(data.as_ref()).unwrap(); + let enc = e.finish().unwrap(); - awc::Client::build() - .connector( - awc::Connector::new() - .timeout(std::time::Duration::from_millis(500)) - .ssl(builder.build()) - .finish(), - ) - .finish() - }); + // client request + let req = client + .post(format!("https://localhost:{}/", addr.port())) + .header(http::header::CONTENT_ENCODING, "deflate") + .send_body(enc); - // encode data - let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); - e.write_all(data.as_ref()).unwrap(); - let enc = e.finish().unwrap(); + let mut response = test::block_on(req).unwrap(); + assert!(response.status().is_success()); - // client request - let req = client - .post(format!("https://localhost:{}/", addr.port())) - .header(http::header::CONTENT_ENCODING, "deflate") - .send_body(enc); + // read response + let bytes = test::block_on(response.body()).unwrap(); + assert_eq!(bytes.len(), data.len()); + assert_eq!(bytes, Bytes::from(data)); - let mut response = test::block_on(req).unwrap(); - assert!(response.status().is_success()); - - // read response - let bytes = test::block_on(response.body()).unwrap(); - assert_eq!(bytes.len(), data.len()); - assert_eq!(bytes, Bytes::from(data)); - - // stop - let _ = srv.stop(false); + // stop + let _ = srv.stop(false); + }) } // #[cfg(all(feature = "tls", feature = "ssl"))] @@ -954,7 +997,7 @@ fn test_reading_deflate_encoding_large_random_ssl() { // fn test_server_cookies() { // use actix_web::http; -// let mut srv = test::TestServer::with_factory(|| { +// let srv = test::TestServer::with_factory(|| { // App::new().resource("/", |r| { // r.f(|_| { // HttpResponse::Ok() From b510527a9fd64926aa7577dd17e050dc3976ab9e Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 21 Nov 2019 00:35:07 +0600 Subject: [PATCH 052/176] update awc tests --- awc/Cargo.toml | 7 +- awc/src/lib.rs | 12 +- awc/src/request.rs | 27 +++-- awc/src/response.rs | 141 +++++++++++----------- awc/src/ws.rs | 56 ++++----- awc/tests/test_client.rs | 202 ++++++++++++++++---------------- awc/tests/test_rustls_client.rs | 145 ++++++++++++----------- awc/tests/test_ssl_client.rs | 96 +++++++-------- awc/tests/test_ws.rs | 101 ++++++++-------- 9 files changed, 404 insertions(+), 383 deletions(-) diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 70d89d5de..e085ea09d 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -14,7 +14,7 @@ categories = ["network-programming", "asynchronous", license = "MIT/Apache-2.0" exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] edition = "2018" -# workspace = ".." +workspace = ".." [lib] name = "awc" @@ -59,7 +59,7 @@ serde_json = "1.0" serde_urlencoded = "0.6.1" tokio-timer = "0.3.0-alpha.6" open-ssl = { version="0.10", package="openssl", optional = true } -rust-tls = { version = "0.16.0", package="rustls", optional = true } +rust-tls = { version = "0.16.0", package="rustls", optional = true, features = ["dangerous_configuration"] } [dev-dependencies] actix-rt = "1.0.0-alpha.1" @@ -67,11 +67,10 @@ actix-web = { version = "2.0.0-alpha.1", features=["openssl"] } actix-http = { version = "0.3.0-alpha.1", features=["openssl"] } actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } actix-utils = "0.5.0-alpha.1" -actix-server = { version = "0.8.0-alpha.1", features=["openssl", "rustls"] } +actix-server = { version = "0.8.0-alpha.1", features=["openssl"] } brotli2 = { version="0.3.2" } flate2 = { version="1.0.2" } env_logger = "0.6" rand = "0.7" tokio-tcp = "0.1" webpki = { version = "0.21" } -rus-tls = { version = "0.16.0", package="rustls", features = ["dangerous_configuration"] } diff --git a/awc/src/lib.rs b/awc/src/lib.rs index 58c9056b2..7bbe42195 100644 --- a/awc/src/lib.rs +++ b/awc/src/lib.rs @@ -7,18 +7,18 @@ //! use awc::Client; //! //! fn main() { -//! System::new("test").block_on(lazy(|| { +//! System::new("test").block_on(async { //! let mut client = Client::default(); //! //! client.get("http://www.rust-lang.org") // <- Create request builder //! .header("User-Agent", "Actix-web") //! .send() // <- Send http request -//! .map_err(|_| ()) +//! .await //! .and_then(|response| { // <- server http response //! println!("Response: {:?}", response); //! Ok(()) //! }) -//! })); +//! }); //! } //! ``` use std::cell::RefCell; @@ -57,18 +57,18 @@ use self::connect::{Connect, ConnectorWrapper}; /// use awc::Client; /// /// fn main() { -/// System::new("test").block_on(lazy(|| { +/// System::new("test").block_on(async { /// let mut client = Client::default(); /// /// client.get("http://www.rust-lang.org") // <- Create request builder /// .header("User-Agent", "Actix-web") /// .send() // <- Send http request -/// .map_err(|_| ()) +/// .await /// .and_then(|response| { // <- server http response /// println!("Response: {:?}", response); /// Ok(()) /// }) -/// })); +/// }); /// } /// ``` #[derive(Clone)] diff --git a/awc/src/request.rs b/awc/src/request.rs index 831234437..5181f1905 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -41,17 +41,18 @@ const HTTPS_ENCODING: &str = "gzip, deflate"; /// use actix_rt::System; /// /// fn main() { -/// System::new("test").block_on(lazy(|| { -/// awc::Client::new() +/// System::new("test").block_on(async { +/// let response = awc::Client::new() /// .get("http://www.rust-lang.org") // <- Create request builder /// .header("User-Agent", "Actix-web") /// .send() // <- Send http request -/// .map_err(|_| ()) -/// .and_then(|response| { // <- server http response -/// println!("Response: {:?}", response); -/// Ok(()) +/// .await; +/// +/// response.and_then(|response| { // <- server http response +/// println!("Response: {:?}", response); +/// Ok(()) /// }) -/// })); +/// }); /// } /// ``` pub struct ClientRequest { @@ -158,7 +159,7 @@ impl ClientRequest { /// /// ```rust /// fn main() { - /// # actix_rt::System::new("test").block_on(futures::future::lazy(|| { + /// # actix_rt::System::new("test").block_on(futures::future::lazy(|_| { /// let req = awc::Client::new() /// .get("http://www.rust-lang.org") /// .set(awc::http::header::Date::now()) @@ -186,13 +187,13 @@ impl ClientRequest { /// use awc::{http, Client}; /// /// fn main() { - /// # actix_rt::System::new("test").block_on(futures::future::lazy(|| { + /// # actix_rt::System::new("test").block_on(async { /// let req = Client::new() /// .get("http://www.rust-lang.org") /// .header("X-TEST", "value") /// .header(http::header::CONTENT_TYPE, "application/json"); /// # Ok::<_, ()>(()) - /// # })); + /// # }); /// } /// ``` pub fn header(mut self, key: K, value: V) -> Self @@ -311,7 +312,7 @@ impl ClientRequest { /// # use actix_rt::System; /// # use futures::future::{lazy, Future}; /// fn main() { - /// System::new("test").block_on(lazy(|| { + /// System::new("test").block_on(async { /// awc::Client::new().get("https://www.rust-lang.org") /// .cookie( /// awc::http::Cookie::build("name", "value") @@ -322,12 +323,12 @@ impl ClientRequest { /// .finish(), /// ) /// .send() - /// .map_err(|_| ()) + /// .await /// .and_then(|response| { /// println!("Response: {:?}", response); /// Ok(()) /// }) - /// })); + /// }); /// } /// ``` pub fn cookie(mut self, cookie: Cookie<'_>) -> Self { diff --git a/awc/src/response.rs b/awc/src/response.rs index a7b08eedc..5ef8e18b5 100644 --- a/awc/src/response.rs +++ b/awc/src/response.rs @@ -359,41 +359,40 @@ where mod tests { use super::*; use actix_http_test::block_on; - use futures::Async; use serde::{Deserialize, Serialize}; use crate::{http::header, test::TestResponse}; #[test] fn test_body() { - let mut req = TestResponse::with_header(header::CONTENT_LENGTH, "xxxx").finish(); - match req.body().poll().err().unwrap() { - PayloadError::UnknownLength => (), - _ => unreachable!("error"), - } + block_on(async { + let mut req = + TestResponse::with_header(header::CONTENT_LENGTH, "xxxx").finish(); + match req.body().await.err().unwrap() { + PayloadError::UnknownLength => (), + _ => unreachable!("error"), + } - let mut req = - TestResponse::with_header(header::CONTENT_LENGTH, "1000000").finish(); - match req.body().poll().err().unwrap() { - PayloadError::Overflow => (), - _ => unreachable!("error"), - } + let mut req = + TestResponse::with_header(header::CONTENT_LENGTH, "1000000").finish(); + match req.body().await.err().unwrap() { + PayloadError::Overflow => (), + _ => unreachable!("error"), + } - let mut req = TestResponse::default() - .set_payload(Bytes::from_static(b"test")) - .finish(); - match req.body().poll().ok().unwrap() { - Async::Ready(bytes) => assert_eq!(bytes, Bytes::from_static(b"test")), - _ => unreachable!("error"), - } + let mut req = TestResponse::default() + .set_payload(Bytes::from_static(b"test")) + .finish(); + assert_eq!(req.body().await.ok().unwrap(), Bytes::from_static(b"test")); - let mut req = TestResponse::default() - .set_payload(Bytes::from_static(b"11111111111111")) - .finish(); - match req.body().limit(5).poll().err().unwrap() { - PayloadError::Overflow => (), - _ => unreachable!("error"), - } + let mut req = TestResponse::default() + .set_payload(Bytes::from_static(b"11111111111111")) + .finish(); + match req.body().limit(5).await.err().unwrap() { + PayloadError::Overflow => (), + _ => unreachable!("error"), + } + }) } #[derive(Serialize, Deserialize, PartialEq, Debug)] @@ -417,54 +416,56 @@ mod tests { #[test] fn test_json_body() { - let mut req = TestResponse::default().finish(); - let json = block_on(JsonBody::<_, MyObject>::new(&mut req)); - assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); + block_on(async { + let mut req = TestResponse::default().finish(); + let json = JsonBody::<_, MyObject>::new(&mut req).await; + assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); - let mut req = TestResponse::default() - .header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/text"), - ) - .finish(); - let json = block_on(JsonBody::<_, MyObject>::new(&mut req)); - assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); + let mut req = TestResponse::default() + .header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/text"), + ) + .finish(); + let json = JsonBody::<_, MyObject>::new(&mut req).await; + assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); - let mut req = TestResponse::default() - .header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/json"), - ) - .header( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("10000"), - ) - .finish(); + let mut req = TestResponse::default() + .header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/json"), + ) + .header( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("10000"), + ) + .finish(); - let json = block_on(JsonBody::<_, MyObject>::new(&mut req).limit(100)); - assert!(json_eq( - json.err().unwrap(), - JsonPayloadError::Payload(PayloadError::Overflow) - )); + let json = JsonBody::<_, MyObject>::new(&mut req).limit(100).await; + assert!(json_eq( + json.err().unwrap(), + JsonPayloadError::Payload(PayloadError::Overflow) + )); - let mut req = TestResponse::default() - .header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/json"), - ) - .header( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("16"), - ) - .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .finish(); + let mut req = TestResponse::default() + .header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/json"), + ) + .header( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("16"), + ) + .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) + .finish(); - let json = block_on(JsonBody::<_, MyObject>::new(&mut req)); - assert_eq!( - json.ok().unwrap(), - MyObject { - name: "test".to_owned() - } - ); + let json = JsonBody::<_, MyObject>::new(&mut req).await; + assert_eq!( + json.ok().unwrap(), + MyObject { + name: "test".to_owned() + } + ); + }) } } diff --git a/awc/src/ws.rs b/awc/src/ws.rs index 979a382af..8819b4990 100644 --- a/awc/src/ws.rs +++ b/awc/src/ws.rs @@ -389,6 +389,8 @@ impl fmt::Debug for WebsocketsRequest { #[cfg(test)] mod tests { + use actix_web::test::block_on; + use super::*; use crate::Client; @@ -463,35 +465,33 @@ mod tests { #[test] fn basics() { - let req = Client::new() - .ws("http://localhost/") - .origin("test-origin") - .max_frame_size(100) - .server_mode() - .protocols(&["v1", "v2"]) - .set_header_if_none(header::CONTENT_TYPE, "json") - .set_header_if_none(header::CONTENT_TYPE, "text") - .cookie(Cookie::build("cookie1", "value1").finish()); - assert_eq!( - req.origin.as_ref().unwrap().to_str().unwrap(), - "test-origin" - ); - assert_eq!(req.max_size, 100); - assert_eq!(req.server_mode, true); - assert_eq!(req.protocols, Some("v1,v2".to_string())); - assert_eq!( - req.head.headers.get(header::CONTENT_TYPE).unwrap(), - header::HeaderValue::from_static("json") - ); + block_on(async { + let req = Client::new() + .ws("http://localhost/") + .origin("test-origin") + .max_frame_size(100) + .server_mode() + .protocols(&["v1", "v2"]) + .set_header_if_none(header::CONTENT_TYPE, "json") + .set_header_if_none(header::CONTENT_TYPE, "text") + .cookie(Cookie::build("cookie1", "value1").finish()); + assert_eq!( + req.origin.as_ref().unwrap().to_str().unwrap(), + "test-origin" + ); + assert_eq!(req.max_size, 100); + assert_eq!(req.server_mode, true); + assert_eq!(req.protocols, Some("v1,v2".to_string())); + assert_eq!( + req.head.headers.get(header::CONTENT_TYPE).unwrap(), + header::HeaderValue::from_static("json") + ); - let _ = actix_http_test::block_fn(move || req.connect()); + let _ = req.connect().await; - assert!(Client::new().ws("/").connect().poll().is_err()); - assert!(Client::new().ws("http:///test").connect().poll().is_err()); - assert!(Client::new() - .ws("hmm://test.com/") - .connect() - .poll() - .is_err()); + assert!(Client::new().ws("/").connect().await.is_err()); + assert!(Client::new().ws("http:///test").connect().await.is_err()); + assert!(Client::new().ws("hmm://test.com/").connect().await.is_err()); + }) } } diff --git a/awc/tests/test_client.rs b/awc/tests/test_client.rs index 8d7bc2274..bcedaf64b 100644 --- a/awc/tests/test_client.rs +++ b/awc/tests/test_client.rs @@ -9,12 +9,12 @@ use bytes::Bytes; use flate2::read::GzDecoder; use flate2::write::GzEncoder; use flate2::Compression; -use futures::Future; +use futures::future::ok; use rand::Rng; use actix_http::HttpService; -use actix_http_test::{bloxk_on, TestServer}; -use actix_service::{service_fn, ServiceFactory}; +use actix_http_test::{block_on, TestServer}; +use actix_service::pipeline_factory; use actix_web::http::Cookie; use actix_web::middleware::{BodyEncoding, Compress}; use actix_web::{http::header, web, App, Error, HttpMessage, HttpRequest, HttpResponse}; @@ -45,29 +45,29 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[test] fn test_simple() { block_on(async { - let mut srv = TestServer::start(|| { + let srv = TestServer::start(|| { HttpService::new(App::new().service( web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))), )) }); let request = srv.get("/").header("x-test", "111").send(); - let mut response = srv.block_on(request).unwrap(); + let mut response = request.await.unwrap(); assert!(response.status().is_success()); // read response - let bytes = srv.block_on(response.body()).unwrap(); + let bytes = response.body().await.unwrap(); assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - let mut response = srv.block_on(srv.post("/").send()).unwrap(); + let mut response = srv.post("/").send().await.unwrap(); assert!(response.status().is_success()); // read response - let bytes = srv.block_on(response.body()).unwrap(); + let bytes = response.body().await.unwrap(); assert_eq!(bytes, Bytes::from_static(STR.as_ref())); // camel case - let response = srv.block_on(srv.post("/").camel_case().send()).unwrap(); + let response = srv.post("/").camel_case().send().await.unwrap(); assert!(response.status().is_success()); }) } @@ -75,7 +75,7 @@ fn test_simple() { #[test] fn test_json() { block_on(async { - let mut srv = TestServer::start(|| { + let srv = TestServer::start(|| { HttpService::new( App::new().service( web::resource("/") @@ -88,7 +88,7 @@ fn test_json() { .get("/") .header("x-test", "111") .send_json(&"TEST".to_string()); - let response = srv.block_on(request).unwrap(); + let response = request.await.unwrap(); assert!(response.status().is_success()); }) } @@ -96,7 +96,7 @@ fn test_json() { #[test] fn test_form() { block_on(async { - let mut srv = TestServer::start(|| { + let srv = TestServer::start(|| { HttpService::new(App::new().service(web::resource("/").route(web::to( |_: web::Form>| HttpResponse::Ok(), )))) @@ -106,7 +106,7 @@ fn test_form() { let _ = data.insert("key".to_string(), "TEST".to_string()); let request = srv.get("/").header("x-test", "111").send_form(&data); - let response = srv.block_on(request).unwrap(); + let response = request.await.unwrap(); assert!(response.status().is_success()); }) } @@ -114,22 +114,22 @@ fn test_form() { #[test] fn test_timeout() { block_on(async { - let mut srv = TestServer::start(|| { + let srv = TestServer::start(|| { HttpService::new(App::new().service(web::resource("/").route( web::to_async(|| { - tokio_timer::sleep(Duration::from_millis(200)) - .then(|_| Ok::<_, Error>(HttpResponse::Ok().body(STR))) + async { + tokio_timer::delay_for(Duration::from_millis(200)).await; + Ok::<_, Error>(HttpResponse::Ok().body(STR)) + } }), ))) }); - let client = srv.execute(|| { - awc::Client::build() - .timeout(Duration::from_millis(50)) - .finish() - }); + let client = awc::Client::build() + .timeout(Duration::from_millis(50)) + .finish(); let request = client.get(srv.url("/")).send(); - match srv.block_on(request) { + match request.await { Err(SendRequestError::Timeout) => (), _ => panic!(), } @@ -139,11 +139,13 @@ fn test_timeout() { #[test] fn test_timeout_override() { block_on(async { - let mut srv = TestServer::start(|| { + let srv = TestServer::start(|| { HttpService::new(App::new().service(web::resource("/").route( web::to_async(|| { - tokio_timer::sleep(Duration::from_millis(200)) - .then(|_| Ok::<_, Error>(HttpResponse::Ok().body(STR))) + async { + tokio_timer::delay_for(Duration::from_millis(200)).await; + Ok::<_, Error>(HttpResponse::Ok().body(STR)) + } }), ))) }); @@ -155,7 +157,7 @@ fn test_timeout_override() { .get(srv.url("/")) .timeout(Duration::from_millis(50)) .send(); - match srv.block_on(request) { + match request.await { Err(SendRequestError::Timeout) => (), _ => panic!(), } @@ -168,11 +170,11 @@ fn test_connection_reuse() { let num = Arc::new(AtomicUsize::new(0)); let num2 = num.clone(); - let mut srv = TestServer::start(move || { + let srv = TestServer::start(move || { let num2 = num2.clone(); - service_fn(move |io| { + pipeline_factory(move |io| { num2.fetch_add(1, Ordering::Relaxed); - Ok(io) + ok(io) }) .and_then(HttpService::new(App::new().service( web::resource("/").route(web::to(|| HttpResponse::Ok())), @@ -183,12 +185,12 @@ fn test_connection_reuse() { // req 1 let request = client.get(srv.url("/")).send(); - let response = srv.block_on(request).unwrap(); + let response = request.await.unwrap(); assert!(response.status().is_success()); // req 2 let req = client.post(srv.url("/")); - let response = srv.block_on_fn(move || req.send()).unwrap(); + let response = req.send().await.unwrap(); assert!(response.status().is_success()); // one connection @@ -202,11 +204,11 @@ fn test_connection_force_close() { let num = Arc::new(AtomicUsize::new(0)); let num2 = num.clone(); - let mut srv = TestServer::new(move || { + let srv = TestServer::start(move || { let num2 = num2.clone(); - service_fn(move |io| { + pipeline_factory(move |io| { num2.fetch_add(1, Ordering::Relaxed); - Ok(io) + ok(io) }) .and_then(HttpService::new(App::new().service( web::resource("/").route(web::to(|| HttpResponse::Ok())), @@ -217,12 +219,12 @@ fn test_connection_force_close() { // req 1 let request = client.get(srv.url("/")).force_close().send(); - let response = srv.block_on(request).unwrap(); + let response = request.await.unwrap(); assert!(response.status().is_success()); // req 2 let req = client.post(srv.url("/")).force_close(); - let response = srv.block_on_fn(move || req.send()).unwrap(); + let response = req.send().await.unwrap(); assert!(response.status().is_success()); // two connection @@ -236,11 +238,11 @@ fn test_connection_server_close() { let num = Arc::new(AtomicUsize::new(0)); let num2 = num.clone(); - let mut srv = TestServer::new(move || { + let srv = TestServer::start(move || { let num2 = num2.clone(); - service_fn(move |io| { + pipeline_factory(move |io| { num2.fetch_add(1, Ordering::Relaxed); - Ok(io) + ok(io) }) .and_then(HttpService::new( App::new().service( @@ -254,12 +256,12 @@ fn test_connection_server_close() { // req 1 let request = client.get(srv.url("/")).send(); - let response = srv.block_on(request).unwrap(); + let response = request.await.unwrap(); assert!(response.status().is_success()); // req 2 let req = client.post(srv.url("/")); - let response = srv.block_on_fn(move || req.send()).unwrap(); + let response = req.send().await.unwrap(); assert!(response.status().is_success()); // two connection @@ -273,11 +275,11 @@ fn test_connection_wait_queue() { let num = Arc::new(AtomicUsize::new(0)); let num2 = num.clone(); - let mut srv = TestServer::new(move || { + let srv = TestServer::start(move || { let num2 = num2.clone(); - service_fn(move |io| { + pipeline_factory(move |io| { num2.fetch_add(1, Ordering::Relaxed); - Ok(io) + ok(io) }) .and_then(HttpService::new(App::new().service( web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))), @@ -290,23 +292,19 @@ fn test_connection_wait_queue() { // req 1 let request = client.get(srv.url("/")).send(); - let mut response = srv.block_on(request).unwrap(); + let mut response = request.await.unwrap(); assert!(response.status().is_success()); // req 2 let req2 = client.post(srv.url("/")); - let req2_fut = srv.execute(move || { - let mut fut = req2.send(); - assert!(fut.poll().unwrap().is_not_ready()); - fut - }); + let req2_fut = req2.send(); // read response 1 - let bytes = srv.block_on(response.body()).unwrap(); + let bytes = response.body().await.unwrap(); assert_eq!(bytes, Bytes::from_static(STR.as_ref())); // req 2 - let response = srv.block_on(req2_fut).unwrap(); + let response = req2_fut.await.unwrap(); assert!(response.status().is_success()); // two connection @@ -320,11 +318,11 @@ fn test_connection_wait_queue_force_close() { let num = Arc::new(AtomicUsize::new(0)); let num2 = num.clone(); - let mut srv = TestServer::new(move || { + let srv = TestServer::start(move || { let num2 = num2.clone(); - service_fn(move |io| { + pipeline_factory(move |io| { num2.fetch_add(1, Ordering::Relaxed); - Ok(io) + ok(io) }) .and_then(HttpService::new( App::new().service( @@ -340,23 +338,19 @@ fn test_connection_wait_queue_force_close() { // req 1 let request = client.get(srv.url("/")).send(); - let mut response = srv.block_on(request).unwrap(); + let mut response = request.await.unwrap(); assert!(response.status().is_success()); // req 2 let req2 = client.post(srv.url("/")); - let req2_fut = srv.execute(move || { - let mut fut = req2.send(); - assert!(fut.poll().unwrap().is_not_ready()); - fut - }); + let req2_fut = req2.send(); // read response 1 - let bytes = srv.block_on(response.body()).unwrap(); + let bytes = response.body().await.unwrap(); assert_eq!(bytes, Bytes::from_static(STR.as_ref())); // req 2 - let response = srv.block_on(req2_fut).unwrap(); + let response = req2_fut.await.unwrap(); assert!(response.status().is_success()); // two connection @@ -367,7 +361,7 @@ fn test_connection_wait_queue_force_close() { #[test] fn test_with_query_parameter() { block_on(async { - let mut srv = TestServer::new(|| { + let srv = TestServer::start(|| { HttpService::new(App::new().service(web::resource("/").to( |req: HttpRequest| { if req.query_string().contains("qp") { @@ -379,8 +373,10 @@ fn test_with_query_parameter() { ))) }); - let res = srv - .block_on(awc::Client::new().get(srv.url("/?qp=5")).send()) + let res = awc::Client::new() + .get(srv.url("/?qp=5")) + .send() + .await .unwrap(); assert!(res.status().is_success()); }) @@ -389,7 +385,7 @@ fn test_with_query_parameter() { #[test] fn test_no_decompress() { block_on(async { - let mut srv = TestServer::new(|| { + let srv = TestServer::start(|| { HttpService::new(App::new().wrap(Compress::default()).service( web::resource("/").route(web::to(|| { let mut res = HttpResponse::Ok().body(STR); @@ -399,13 +395,16 @@ fn test_no_decompress() { )) }); - let mut res = srv - .block_on(awc::Client::new().get(srv.url("/")).no_decompress().send()) + let mut res = awc::Client::new() + .get(srv.url("/")) + .no_decompress() + .send() + .await .unwrap(); assert!(res.status().is_success()); // read response - let bytes = srv.block_on(res.body()).unwrap(); + let bytes = res.body().await.unwrap(); let mut e = GzDecoder::new(&bytes[..]); let mut dec = Vec::new(); @@ -413,12 +412,15 @@ fn test_no_decompress() { assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); // POST - let mut res = srv - .block_on(awc::Client::new().post(srv.url("/")).no_decompress().send()) + let mut res = awc::Client::new() + .post(srv.url("/")) + .no_decompress() + .send() + .await .unwrap(); assert!(res.status().is_success()); - let bytes = srv.block_on(res.body()).unwrap(); + let bytes = res.body().await.unwrap(); let mut e = GzDecoder::new(&bytes[..]); let mut dec = Vec::new(); e.read_to_end(&mut dec).unwrap(); @@ -429,7 +431,7 @@ fn test_no_decompress() { #[test] fn test_client_gzip_encoding() { block_on(async { - let mut srv = TestServer::new(|| { + let srv = TestServer::start(|| { HttpService::new(App::new().service(web::resource("/").route(web::to( || { let mut e = GzEncoder::new(Vec::new(), Compression::default()); @@ -444,11 +446,11 @@ fn test_client_gzip_encoding() { }); // client request - let mut response = srv.block_on(srv.post("/").send()).unwrap(); + let mut response = srv.post("/").send().await.unwrap(); assert!(response.status().is_success()); // read response - let bytes = srv.block_on(response.body()).unwrap(); + let bytes = response.body().await.unwrap(); assert_eq!(bytes, Bytes::from_static(STR.as_ref())); }) } @@ -456,7 +458,7 @@ fn test_client_gzip_encoding() { #[test] fn test_client_gzip_encoding_large() { block_on(async { - let mut srv = TestServer::new(|| { + let srv = TestServer::start(|| { HttpService::new(App::new().service(web::resource("/").route(web::to( || { let mut e = GzEncoder::new(Vec::new(), Compression::default()); @@ -471,11 +473,11 @@ fn test_client_gzip_encoding_large() { }); // client request - let mut response = srv.block_on(srv.post("/").send()).unwrap(); + let mut response = srv.post("/").send().await.unwrap(); assert!(response.status().is_success()); // read response - let bytes = srv.block_on(response.body()).unwrap(); + let bytes = response.body().await.unwrap(); assert_eq!(bytes, Bytes::from(STR.repeat(10))); }) } @@ -488,7 +490,7 @@ fn test_client_gzip_encoding_large_random() { .take(100_000) .collect::(); - let mut srv = TestServer::new(|| { + let srv = TestServer::start(|| { HttpService::new(App::new().service(web::resource("/").route(web::to( |data: Bytes| { let mut e = GzEncoder::new(Vec::new(), Compression::default()); @@ -502,11 +504,11 @@ fn test_client_gzip_encoding_large_random() { }); // client request - let mut response = srv.block_on(srv.post("/").send_body(data.clone())).unwrap(); + let mut response = srv.post("/").send_body(data.clone()).await.unwrap(); assert!(response.status().is_success()); // read response - let bytes = srv.block_on(response.body()).unwrap(); + let bytes = response.body().await.unwrap(); assert_eq!(bytes, Bytes::from(data)); }) } @@ -514,7 +516,7 @@ fn test_client_gzip_encoding_large_random() { #[test] fn test_client_brotli_encoding() { block_on(async { - let mut srv = TestServer::new(|| { + let srv = TestServer::start(|| { HttpService::new(App::new().service(web::resource("/").route(web::to( |data: Bytes| { let mut e = BrotliEncoder::new(Vec::new(), 5); @@ -528,11 +530,11 @@ fn test_client_brotli_encoding() { }); // client request - let mut response = srv.block_on(srv.post("/").send_body(STR)).unwrap(); + let mut response = srv.post("/").send_body(STR).await.unwrap(); assert!(response.status().is_success()); // read response - let bytes = srv.block_on(response.body()).unwrap(); + let bytes = response.body().await.unwrap(); assert_eq!(bytes, Bytes::from_static(STR.as_ref())); }) } @@ -544,7 +546,7 @@ fn test_client_brotli_encoding() { // .take(70_000) // .collect::(); -// let mut srv = test::TestServer::new(|app| { +// let srv = test::TestServer::start(|app| { // app.handler(|req: &HttpRequest| { // req.body() // .and_then(move |bytes: Bytes| { @@ -562,11 +564,11 @@ fn test_client_brotli_encoding() { // .content_encoding(http::ContentEncoding::Br) // .body(data.clone()) // .unwrap(); -// let response = srv.execute(request.send()).unwrap(); +// let response = request.send().await.unwrap(); // assert!(response.status().is_success()); // // read response -// let bytes = srv.execute(response.body()).unwrap(); +// let bytes = response.body().await.unwrap(); // assert_eq!(bytes.len(), data.len()); // assert_eq!(bytes, Bytes::from(data)); // } @@ -574,7 +576,7 @@ fn test_client_brotli_encoding() { // #[cfg(feature = "brotli")] // #[test] // fn test_client_deflate_encoding() { -// let mut srv = test::TestServer::new(|app| { +// let srv = test::TestServer::start(|app| { // app.handler(|req: &HttpRequest| { // req.body() // .and_then(|bytes: Bytes| { @@ -607,7 +609,7 @@ fn test_client_brotli_encoding() { // .take(70_000) // .collect::(); -// let mut srv = test::TestServer::new(|app| { +// let srv = test::TestServer::start(|app| { // app.handler(|req: &HttpRequest| { // req.body() // .and_then(|bytes: Bytes| { @@ -635,7 +637,7 @@ fn test_client_brotli_encoding() { // #[test] // fn test_client_streaming_explicit() { -// let mut srv = test::TestServer::new(|app| { +// let srv = test::TestServer::start(|app| { // app.handler(|req: &HttpRequest| { // req.body() // .map_err(Error::from) @@ -662,7 +664,7 @@ fn test_client_brotli_encoding() { // #[test] // fn test_body_streaming_implicit() { -// let mut srv = test::TestServer::new(|app| { +// let srv = test::TestServer::start(|app| { // app.handler(|_| { // let body = once(Ok(Bytes::from_static(STR.as_ref()))); // HttpResponse::Ok() @@ -699,7 +701,7 @@ fn test_client_cookie_handling() { let cookie1b = cookie1.clone(); let cookie2b = cookie2.clone(); - let mut srv = TestServer::new(move || { + let srv = TestServer::start(move || { let cookie1 = cookie1b.clone(); let cookie2 = cookie2b.clone(); @@ -736,7 +738,7 @@ fn test_client_cookie_handling() { }); let request = srv.get("/").cookie(cookie1.clone()).cookie(cookie2.clone()); - let response = srv.block_on(request.send()).unwrap(); + let response = request.send().await.unwrap(); assert!(response.status().is_success()); let c1 = response.cookie("cookie1").expect("Missing cookie1"); assert_eq!(c1, cookie1); @@ -767,18 +769,18 @@ fn test_client_cookie_handling() { // let req = client::ClientRequest::get(format!("http://{}/", addr).as_str()) // .finish() // .unwrap(); -// let response = sys.block_on(req.send()).unwrap(); +// let response = req.send().await.unwrap(); // assert!(response.status().is_success()); // // read response -// let bytes = sys.block_on(response.body()).unwrap(); +// let bytes = response.body().await.unwrap(); // assert_eq!(bytes, Bytes::from_static(b"welcome!")); // } #[test] fn client_basic_auth() { block_on(async { - let mut srv = TestServer::new(|| { + let srv = TestServer::start(|| { HttpService::new(App::new().route( "/", web::to(|req: HttpRequest| { @@ -800,7 +802,7 @@ fn client_basic_auth() { // set authorization header to Basic let request = srv.get("/").basic_auth("username", Some("password")); - let response = srv.block_on(request.send()).unwrap(); + let response = request.send().await.unwrap(); assert!(response.status().is_success()); }) } @@ -808,7 +810,7 @@ fn client_basic_auth() { #[test] fn client_bearer_auth() { block_on(async { - let mut srv = TestServer::new(|| { + let srv = TestServer::start(|| { HttpService::new(App::new().route( "/", web::to(|req: HttpRequest| { @@ -830,7 +832,7 @@ fn client_bearer_auth() { // set authorization header to Bearer let request = srv.get("/").bearer_auth("someS3cr3tAutht0k3n"); - let response = srv.block_on(request.send()).unwrap(); + let response = request.send().await.unwrap(); assert!(response.status().is_success()); }) } diff --git a/awc/tests/test_rustls_client.rs b/awc/tests/test_rustls_client.rs index e65e4e874..bdfd21031 100644 --- a/awc/tests/test_rustls_client.rs +++ b/awc/tests/test_rustls_client.rs @@ -1,96 +1,109 @@ -#![cfg(feature = "rust-tls")] -use rustls::{ - internal::pemfile::{certs, pkcs8_private_keys}, - ClientConfig, NoClientAuth, -}; +#![cfg(feature = "rustls")] +use rust_tls::ClientConfig; -use std::fs::File; -use std::io::{BufReader, Result}; +use std::io::Result; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use actix_codec::{AsyncRead, AsyncWrite}; use actix_http::HttpService; -use actix_http_test::TestServer; -use actix_server::ssl::RustlsAcceptor; -use actix_service::{service_fn, NewService}; +use actix_http_test::{block_on, TestServer}; +use actix_server::ssl::OpensslAcceptor; +use actix_service::{pipeline_factory, ServiceFactory}; use actix_web::http::Version; use actix_web::{web, App, HttpResponse}; +use futures::future::ok; +use open_ssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslVerifyMode}; -fn ssl_acceptor() -> Result> { - use rustls::ServerConfig; +fn ssl_acceptor() -> Result> { // load ssl keys - let mut config = ServerConfig::new(NoClientAuth::new()); - let cert_file = &mut BufReader::new(File::open("../tests/cert.pem").unwrap()); - let key_file = &mut BufReader::new(File::open("../tests/key.pem").unwrap()); - let cert_chain = certs(cert_file).unwrap(); - let mut keys = pkcs8_private_keys(key_file).unwrap(); - config.set_single_cert(cert_chain, keys.remove(0)).unwrap(); - let protos = vec![b"h2".to_vec()]; - config.set_protocols(&protos); - Ok(RustlsAcceptor::new(config)) + let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); + builder.set_verify_callback(SslVerifyMode::NONE, |_, _| true); + builder + .set_private_key_file("../tests/key.pem", SslFiletype::PEM) + .unwrap(); + builder + .set_certificate_chain_file("../tests/cert.pem") + .unwrap(); + builder.set_alpn_select_callback(|_, protos| { + const H2: &[u8] = b"\x02h2"; + if protos.windows(3).any(|window| window == H2) { + Ok(b"h2") + } else { + Err(open_ssl::ssl::AlpnError::NOACK) + } + }); + builder.set_alpn_protos(b"\x02h2")?; + Ok(actix_server::ssl::OpensslAcceptor::new(builder.build())) } mod danger { pub struct NoCertificateVerification {} - impl rustls::ServerCertVerifier for NoCertificateVerification { + impl rust_tls::ServerCertVerifier for NoCertificateVerification { fn verify_server_cert( &self, - _roots: &rustls::RootCertStore, - _presented_certs: &[rustls::Certificate], + _roots: &rust_tls::RootCertStore, + _presented_certs: &[rust_tls::Certificate], _dns_name: webpki::DNSNameRef<'_>, _ocsp: &[u8], - ) -> Result { - Ok(rustls::ServerCertVerified::assertion()) + ) -> Result { + Ok(rust_tls::ServerCertVerified::assertion()) } } } -#[test] -fn test_connection_reuse_h2() { - let rustls = ssl_acceptor().unwrap(); - let num = Arc::new(AtomicUsize::new(0)); - let num2 = num.clone(); +// #[test] +fn _test_connection_reuse_h2() { + block_on(async { + let openssl = ssl_acceptor().unwrap(); + let num = Arc::new(AtomicUsize::new(0)); + let num2 = num.clone(); - let mut srv = TestServer::new(move || { - let num2 = num2.clone(); - service_fn(move |io| { - num2.fetch_add(1, Ordering::Relaxed); - Ok(io) - }) - .and_then(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(App::new() - .service(web::resource("/").route(web::to(|| HttpResponse::Ok())))) - .map_err(|_| ()), - ) - }); + let srv = TestServer::start(move || { + let num2 = num2.clone(); + pipeline_factory(move |io| { + num2.fetch_add(1, Ordering::Relaxed); + ok(io) + }) + .and_then( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(App::new().service( + web::resource("/").route(web::to(|| HttpResponse::Ok())), + )) + .map_err(|_| ()), + ) + }); - // disable ssl verification - let mut config = ClientConfig::new(); - let protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; - config.set_protocols(&protos); - config - .dangerous() - .set_certificate_verifier(Arc::new(danger::NoCertificateVerification {})); + // disable ssl verification + let mut config = ClientConfig::new(); + let protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; + config.set_protocols(&protos); + config + .dangerous() + .set_certificate_verifier(Arc::new(danger::NoCertificateVerification {})); - let client = awc::Client::build() - .connector(awc::Connector::new().rustls(Arc::new(config)).finish()) - .finish(); + let client = awc::Client::build() + .connector(awc::Connector::new().rustls(Arc::new(config)).finish()) + .finish(); - // req 1 - let request = client.get(srv.surl("/")).send(); - let response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + // req 1 + let request = client.get(srv.surl("/")).send(); + let response = request.await.unwrap(); + assert!(response.status().is_success()); - // req 2 - let req = client.post(srv.surl("/")); - let response = srv.block_on_fn(move || req.send()).unwrap(); - assert!(response.status().is_success()); - assert_eq!(response.version(), Version::HTTP_2); + // req 2 + let req = client.post(srv.surl("/")); + let response = req.send().await.unwrap(); + assert!(response.status().is_success()); + assert_eq!(response.version(), Version::HTTP_2); - // one connection - assert_eq!(num.load(Ordering::Relaxed), 1); + // one connection + assert_eq!(num.load(Ordering::Relaxed), 1); + }) } diff --git a/awc/tests/test_ssl_client.rs b/awc/tests/test_ssl_client.rs index e6b0101b2..d37dba291 100644 --- a/awc/tests/test_ssl_client.rs +++ b/awc/tests/test_ssl_client.rs @@ -1,5 +1,5 @@ -#![cfg(feature = "ssl")] -use openssl::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod, SslVerifyMode}; +#![cfg(feature = "openssl")] +use open_ssl::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod, SslVerifyMode}; use std::io::Result; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -7,11 +7,12 @@ use std::sync::Arc; use actix_codec::{AsyncRead, AsyncWrite}; use actix_http::HttpService; -use actix_http_test::TestServer; +use actix_http_test::{block_on, TestServer}; use actix_server::ssl::OpensslAcceptor; -use actix_service::{service_fn, NewService}; +use actix_service::{pipeline_factory, ServiceFactory}; use actix_web::http::Version; use actix_web::{web, App, HttpResponse}; +use futures::future::ok; fn ssl_acceptor() -> Result> { // load ssl keys @@ -27,7 +28,7 @@ fn ssl_acceptor() -> Result> { if protos.windows(3).any(|window| window == H2) { Ok(b"h2") } else { - Err(openssl::ssl::AlpnError::NOACK) + Err(open_ssl::ssl::AlpnError::NOACK) } }); builder.set_alpn_protos(b"\x02h2")?; @@ -36,51 +37,54 @@ fn ssl_acceptor() -> Result> { #[test] fn test_connection_reuse_h2() { - let openssl = ssl_acceptor().unwrap(); - let num = Arc::new(AtomicUsize::new(0)); - let num2 = num.clone(); + block_on(async { + let openssl = ssl_acceptor().unwrap(); + let num = Arc::new(AtomicUsize::new(0)); + let num2 = num.clone(); - let mut srv = TestServer::new(move || { - let num2 = num2.clone(); - service_fn(move |io| { - num2.fetch_add(1, Ordering::Relaxed); - Ok(io) - }) - .and_then( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(App::new() - .service(web::resource("/").route(web::to(|| HttpResponse::Ok())))) - .map_err(|_| ()), - ) - }); + let srv = TestServer::start(move || { + let num2 = num2.clone(); + pipeline_factory(move |io| { + num2.fetch_add(1, Ordering::Relaxed); + ok(io) + }) + .and_then( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(App::new().service( + web::resource("/").route(web::to(|| HttpResponse::Ok())), + )) + .map_err(|_| ()), + ) + }); - // disable ssl verification - let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); - builder.set_verify(SslVerifyMode::NONE); - let _ = builder - .set_alpn_protos(b"\x02h2\x08http/1.1") - .map_err(|e| log::error!("Can not set alpn protocol: {:?}", e)); + // disable ssl verification + let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); + builder.set_verify(SslVerifyMode::NONE); + let _ = builder + .set_alpn_protos(b"\x02h2\x08http/1.1") + .map_err(|e| log::error!("Can not set alpn protocol: {:?}", e)); - let client = awc::Client::build() - .connector(awc::Connector::new().ssl(builder.build()).finish()) - .finish(); + let client = awc::Client::build() + .connector(awc::Connector::new().ssl(builder.build()).finish()) + .finish(); - // req 1 - let request = client.get(srv.surl("/")).send(); - let response = srv.block_on(request).unwrap(); - assert!(response.status().is_success()); + // req 1 + let request = client.get(srv.surl("/")).send(); + let response = request.await.unwrap(); + assert!(response.status().is_success()); - // req 2 - let req = client.post(srv.surl("/")); - let response = srv.block_on_fn(move || req.send()).unwrap(); - assert!(response.status().is_success()); - assert_eq!(response.version(), Version::HTTP_2); + // req 2 + let req = client.post(srv.surl("/")); + let response = req.send().await.unwrap(); + assert!(response.status().is_success()); + assert_eq!(response.version(), Version::HTTP_2); - // one connection - assert_eq!(num.load(Ordering::Relaxed), 1); + // one connection + assert_eq!(num.load(Ordering::Relaxed), 1); + }) } diff --git a/awc/tests/test_ws.rs b/awc/tests/test_ws.rs index 5abf96355..633e8db51 100644 --- a/awc/tests/test_ws.rs +++ b/awc/tests/test_ws.rs @@ -2,81 +2,82 @@ use std::io; use actix_codec::Framed; use actix_http::{body::BodySize, h1, ws, Error, HttpService, Request, Response}; -use actix_http_test::TestServer; +use actix_http_test::{block_on, TestServer}; use bytes::{Bytes, BytesMut}; use futures::future::ok; -use futures::{Future, Sink, Stream}; +use futures::{SinkExt, StreamExt}; -fn ws_service(req: ws::Frame) -> impl Future { +async fn ws_service(req: ws::Frame) -> Result { match req { - ws::Frame::Ping(msg) => ok(ws::Message::Pong(msg)), + ws::Frame::Ping(msg) => Ok(ws::Message::Pong(msg)), ws::Frame::Text(text) => { let text = if let Some(pl) = text { String::from_utf8(Vec::from(pl.as_ref())).unwrap() } else { String::new() }; - ok(ws::Message::Text(text)) + Ok(ws::Message::Text(text)) } - ws::Frame::Binary(bin) => ok(ws::Message::Binary( + ws::Frame::Binary(bin) => Ok(ws::Message::Binary( bin.map(|e| e.freeze()) .unwrap_or_else(|| Bytes::from("")) .into(), )), - ws::Frame::Close(reason) => ok(ws::Message::Close(reason)), - _ => ok(ws::Message::Close(None)), + ws::Frame::Close(reason) => Ok(ws::Message::Close(reason)), + _ => Ok(ws::Message::Close(None)), } } #[test] fn test_simple() { - let mut srv = TestServer::new(|| { - HttpService::build() - .upgrade(|(req, framed): (Request, Framed<_, _>)| { - let res = ws::handshake_response(req.head()).finish(); - // send handshake response - framed - .send(h1::Message::Item((res.drop_body(), BodySize::None))) - .map_err(|e: io::Error| e.into()) - .and_then(|framed| { + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::build() + .upgrade(|(req, mut framed): (Request, Framed<_, _>)| { + async move { + let res = ws::handshake_response(req.head()).finish(); + // send handshake response + framed + .send(h1::Message::Item((res.drop_body(), BodySize::None))) + .await?; + // start websocket service let framed = framed.into_framed(ws::Codec::new()); - ws::Transport::with(framed, ws_service) - }) - }) - .finish(|_| ok::<_, Error>(Response::NotFound())) - }); + ws::Transport::with(framed, ws_service).await + } + }) + .finish(|_| ok::<_, Error>(Response::NotFound())) + }); - // client service - let framed = srv.ws().unwrap(); - let framed = srv - .block_on(framed.send(ws::Message::Text("text".to_string()))) - .unwrap(); - let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!(item, Some(ws::Frame::Text(Some(BytesMut::from("text"))))); + // client service + let mut framed = srv.ws().await.unwrap(); + framed + .send(ws::Message::Text("text".to_string())) + .await + .unwrap(); + let item = framed.next().await.unwrap().unwrap(); + assert_eq!(item, ws::Frame::Text(Some(BytesMut::from("text")))); - let framed = srv - .block_on(framed.send(ws::Message::Binary("text".into()))) - .unwrap(); - let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!( - item, - Some(ws::Frame::Binary(Some(Bytes::from_static(b"text").into()))) - ); + framed + .send(ws::Message::Binary("text".into())) + .await + .unwrap(); + let item = framed.next().await.unwrap().unwrap(); + assert_eq!( + item, + ws::Frame::Binary(Some(Bytes::from_static(b"text").into())) + ); - let framed = srv - .block_on(framed.send(ws::Message::Ping("text".into()))) - .unwrap(); - let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!(item, Some(ws::Frame::Pong("text".to_string().into()))); + framed.send(ws::Message::Ping("text".into())).await.unwrap(); + let item = framed.next().await.unwrap().unwrap(); + assert_eq!(item, ws::Frame::Pong("text".to_string().into())); - let framed = srv - .block_on(framed.send(ws::Message::Close(Some(ws::CloseCode::Normal.into())))) - .unwrap(); + framed + .send(ws::Message::Close(Some(ws::CloseCode::Normal.into()))) + .await + .unwrap(); - let (item, _framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!( - item, - Some(ws::Frame::Close(Some(ws::CloseCode::Normal.into()))) - ); + let item = framed.next().await.unwrap().unwrap(); + assert_eq!(item, ws::Frame::Close(Some(ws::CloseCode::Normal.into()))); + }) } From ff62facc0d82e1410d1abc78d4191386308b13d8 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 21 Nov 2019 00:52:38 +0600 Subject: [PATCH 053/176] disable unmigrated crates --- Cargo.toml | 48 +++++++++++++++++++++++--------------- actix-cors/Cargo.toml | 4 ++-- actix-files/Cargo.toml | 14 +++++------ actix-framed/Cargo.toml | 18 +++++++------- actix-identity/Cargo.toml | 8 +++---- actix-multipart/Cargo.toml | 10 ++++---- actix-session/Cargo.toml | 8 +++---- test-server/Cargo.toml | 2 +- test-server/src/lib.rs | 28 +++++++++++----------- 9 files changed, 76 insertions(+), 64 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b1aa79527..db983bf63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,13 +31,13 @@ members = [ ".", "awc", "actix-http", - "actix-cors", - "actix-files", - "actix-framed", - "actix-session", - "actix-identity", - "actix-multipart", - "actix-web-actors", + #"actix-cors", + #"actix-files", + #"actix-framed", + #"actix-session", + #"actix-identity", + #"actix-multipart", + #"actix-web-actors", "actix-web-codegen", "test-server", ] @@ -125,17 +125,27 @@ actix-http = { path = "actix-http" } actix-http-test = { path = "test-server" } actix-web-codegen = { path = "actix-web-codegen" } # actix-web-actors = { path = "actix-web-actors" } -actix-session = { path = "actix-session" } -actix-files = { path = "actix-files" } -actix-multipart = { path = "actix-multipart" } +# actix-session = { path = "actix-session" } +# actix-files = { path = "actix-files" } +# actix-multipart = { path = "actix-multipart" } awc = { path = "awc" } -actix-codec = { path = "../actix-net/actix-codec" } -actix-connect = { path = "../actix-net/actix-connect" } -actix-rt = { path = "../actix-net/actix-rt" } -actix-server = { path = "../actix-net/actix-server" } -actix-server-config = { path = "../actix-net/actix-server-config" } -actix-service = { path = "../actix-net/actix-service" } -actix-testing = { path = "../actix-net/actix-testing" } -actix-threadpool = { path = "../actix-net/actix-threadpool" } -actix-utils = { path = "../actix-net/actix-utils" } +actix-codec = { git = "https://github.com/actix/actix-net.git" } +actix-connect = { git = "https://github.com/actix/actix-net.git" } +actix-rt = { git = "https://github.com/actix/actix-net.git" } +actix-server = { git = "https://github.com/actix/actix-net.git" } +actix-server-config = { git = "https://github.com/actix/actix-net.git" } +actix-service = { git = "https://github.com/actix/actix-net.git" } +actix-testing = { git = "https://github.com/actix/actix-net.git" } +actix-threadpool = { git = "https://github.com/actix/actix-net.git" } +actix-utils = { git = "https://github.com/actix/actix-net.git" } + +# actix-codec = { path = "../actix-net/actix-codec" } +# actix-connect = { path = "../actix-net/actix-connect" } +# actix-rt = { path = "../actix-net/actix-rt" } +# actix-server = { path = "../actix-net/actix-server" } +# actix-server-config = { path = "../actix-net/actix-server-config" } +# actix-service = { path = "../actix-net/actix-service" } +# actix-testing = { path = "../actix-net/actix-testing" } +# actix-threadpool = { path = "../actix-net/actix-threadpool" } +# actix-utils = { path = "../actix-net/actix-utils" } diff --git a/actix-cors/Cargo.toml b/actix-cors/Cargo.toml index 57aa5833a..56b6fabd9 100644 --- a/actix-cors/Cargo.toml +++ b/actix-cors/Cargo.toml @@ -17,7 +17,7 @@ name = "actix_cors" path = "src/lib.rs" [dependencies] -actix-web = "2.0.0-alpha.1" -actix-service = "1.0.0-alpha.1" +actix-web = "1.0.9" +actix-service = "0.4.0" derive_more = "0.15.0" futures = "0.3.1" diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 6e33bb412..2f75fb50d 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-files" -version = "0.2.0-alpha.1" +version = "0.1.7" authors = ["Nikolay Kim "] description = "Static files support for actix web." readme = "README.md" @@ -11,19 +11,19 @@ documentation = "https://docs.rs/actix-files/" categories = ["asynchronous", "web-programming::http-server"] license = "MIT/Apache-2.0" edition = "2018" -workspace = ".." +# workspace = ".." [lib] name = "actix_files" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-alpha.1", default-features = false } -actix-http = "0.3.0-alpha.1" -actix-service = "1.0.0-alpha.1" +actix-web = { version = "1.0.9", default-features = false } +actix-http = "0.2.11" +actix-service = "0.4.2" bitflags = "1" bytes = "0.4" -futures = "0.3.1" +futures = "0.1.24" derive_more = "0.15.0" log = "0.4" mime = "0.3" @@ -32,4 +32,4 @@ percent-encoding = "2.1" v_htmlescape = "0.4" [dev-dependencies] -actix-web = { version = "2.0.0-alpha.1", features=["openssl"] } +actix-web = { version = "1.0.9", features=["ssl"] } diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml index 9d32ebed5..232c6ae66 100644 --- a/actix-framed/Cargo.toml +++ b/actix-framed/Cargo.toml @@ -20,19 +20,19 @@ name = "actix_framed" path = "src/lib.rs" [dependencies] -actix-codec = "0.2.0-alpha.1" -actix-service = "1.0.0-alpha.1" +actix-codec = "0.1.2" +actix-service = "0.4.2" actix-router = "0.1.2" -actix-rt = "1.0.0-alpha.1" -actix-http = "0.3.0-alpha.1" -actix-server-config = "0.2.0-alpha.1" +actix-rt = "0.2.2" +actix-http = "0.2.11" +actix-server-config = "0.1.1" bytes = "0.4" futures = "0.1.25" log = "0.4" [dev-dependencies] -actix-server = { version = "0.8.0-alpha.1", features=["openssl"] } -actix-connect = { version = "0.3.0-alpha.1", features=["openssl"] } -actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } -actix-utils = "0.5.0-alpha.1" +actix-server = { version = "0.6.0", features=["openssl"] } +actix-connect = { version = "0.2.0", features=["openssl"] } +actix-http-test = { version = "0.1.0", features=["openssl"] } +actix-utils = "0.4.0" diff --git a/actix-identity/Cargo.toml b/actix-identity/Cargo.toml index d05b37685..a307007ef 100644 --- a/actix-identity/Cargo.toml +++ b/actix-identity/Cargo.toml @@ -17,14 +17,14 @@ name = "actix_identity" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-alpha.1", default-features = false, features = ["secure-cookies"] } -actix-service = "1.0.0-alpha.1" +actix-web = { version = "1.0.9", default-features = false, features = ["secure-cookies"] } +actix-service = "0.4.2" futures = "0.3.1" serde = "1.0" serde_json = "1.0" time = "0.1.42" [dev-dependencies] -actix-rt = "1.0.0-alpha.1" -actix-http = "0.3.0-alpha.1" +actix-rt = "0.2.2" +actix-http = "0.2.11" bytes = "0.4" \ No newline at end of file diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index 804d1bb67..aa4e9be20 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -18,17 +18,17 @@ name = "actix_multipart" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-alpha.1", default-features = false } -actix-service = "1.0.0-alpha.1" +actix-web = { version = "1.0.9", default-features = false } +actix-service = "0.4.2" bytes = "0.4" derive_more = "0.15.0" httparse = "1.3" -futures = "0.3.1" +futures = "0.1.24" log = "0.4" mime = "0.3" time = "0.1" twoway = "0.2" [dev-dependencies] -actix-rt = "1.0.0-alpha.1" -actix-http = "0.3.0-alpha.1" \ No newline at end of file +actix-rt = "0.2.2" +actix-http = "0.2.11" \ No newline at end of file diff --git a/actix-session/Cargo.toml b/actix-session/Cargo.toml index 3ce2a8b40..d2fd5ae50 100644 --- a/actix-session/Cargo.toml +++ b/actix-session/Cargo.toml @@ -24,15 +24,15 @@ default = ["cookie-session"] cookie-session = ["actix-web/secure-cookies"] [dependencies] -actix-web = "2.0.0-alpha.1" -actix-service = "1.0.0-alpha.1" +actix-web = "1.0.9" +actix-service = "0.4.2" bytes = "0.4" derive_more = "0.15.0" -futures = "0.3.1" +futures = "0.1.24" hashbrown = "0.6.3" serde = "1.0" serde_json = "1.0" time = "0.1.42" [dev-dependencies] -actix-rt = "1.0.0-alpha.1" +actix-rt = "0.2.2" diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index e43820290..a2b03ffc2 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -14,7 +14,7 @@ categories = ["network-programming", "asynchronous", license = "MIT/Apache-2.0" exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] edition = "2018" -# workspace = ".." +workspace = ".." [package.metadata.docs.rs] features = [] diff --git a/test-server/src/lib.rs b/test-server/src/lib.rs index 0c24ac907..1ec69b100 100644 --- a/test-server/src/lib.rs +++ b/test-server/src/lib.rs @@ -23,24 +23,26 @@ pub use actix_testing::*; /// /// ```rust /// use actix_http::HttpService; -/// use actix_http_test::TestServer; -/// use actix_web::{web, App, HttpResponse}; +/// use actix_http_test::{block_on, TestServer}; +/// use actix_web::{web, App, HttpResponse, Error}; /// -/// fn my_handler() -> HttpResponse { -/// HttpResponse::Ok().into() +/// async fn my_handler() -> Result { +/// Ok(HttpResponse::Ok().into()) /// } /// /// fn main() { -/// let mut srv = TestServer::new( -/// || HttpService::new( -/// App::new().service( -/// web::resource("/").to(my_handler)) -/// ) -/// ); +/// block_on( async { +/// let mut srv = TestServer::start( +/// || HttpService::new( +/// App::new().service( +/// web::resource("/").to_async(my_handler)) +/// ) +/// ); /// -/// let req = srv.get("/"); -/// let response = srv.block_on(req.send()).unwrap(); -/// assert!(response.status().is_success()); +/// let req = srv.get("/"); +/// let response = req.send().await.unwrap(); +/// assert!(response.status().is_success()); +/// }) /// } /// ``` pub struct TestServer; From 3646725cf634331a0f5162c4f3bb63637dc4e34b Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 21 Nov 2019 10:31:52 +0600 Subject: [PATCH 054/176] migrate actix-identity --- Cargo.toml | 2 +- actix-identity/Cargo.toml | 8 +- actix-identity/src/lib.rs | 625 ++++++++++++++++++++------------------ 3 files changed, 342 insertions(+), 293 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index db983bf63..b80cf3e6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ members = [ #"actix-files", #"actix-framed", #"actix-session", - #"actix-identity", + "actix-identity", #"actix-multipart", #"actix-web-actors", "actix-web-codegen", diff --git a/actix-identity/Cargo.toml b/actix-identity/Cargo.toml index a307007ef..d05b37685 100644 --- a/actix-identity/Cargo.toml +++ b/actix-identity/Cargo.toml @@ -17,14 +17,14 @@ name = "actix_identity" path = "src/lib.rs" [dependencies] -actix-web = { version = "1.0.9", default-features = false, features = ["secure-cookies"] } -actix-service = "0.4.2" +actix-web = { version = "2.0.0-alpha.1", default-features = false, features = ["secure-cookies"] } +actix-service = "1.0.0-alpha.1" futures = "0.3.1" serde = "1.0" serde_json = "1.0" time = "0.1.42" [dev-dependencies] -actix-rt = "0.2.2" -actix-http = "0.2.11" +actix-rt = "1.0.0-alpha.1" +actix-http = "0.3.0-alpha.1" bytes = "0.4" \ No newline at end of file diff --git a/actix-identity/src/lib.rs b/actix-identity/src/lib.rs index 7216104eb..30761d872 100644 --- a/actix-identity/src/lib.rs +++ b/actix-identity/src/lib.rs @@ -47,12 +47,13 @@ //! } //! ``` use std::cell::RefCell; +use std::future::Future; use std::rc::Rc; +use std::task::{Context, Poll}; use std::time::SystemTime; use actix_service::{Service, Transform}; -use futures::future::{ok, Either, FutureResult}; -use futures::{Future, IntoFuture, Poll}; +use futures::future::{ok, FutureExt, LocalBoxFuture, Ready}; use serde::{Deserialize, Serialize}; use time::Duration; @@ -165,21 +166,21 @@ where impl FromRequest for Identity { type Config = (); type Error = Error; - type Future = Result; + type Future = Ready>; #[inline] fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { - Ok(Identity(req.clone())) + ok(Identity(req.clone())) } } /// Identity policy definition. pub trait IdentityPolicy: Sized + 'static { /// The return type of the middleware - type Future: IntoFuture, Error = Error>; + type Future: Future, Error>>; /// The return type of the middleware - type ResponseFuture: IntoFuture; + type ResponseFuture: Future>; /// Parse the session from request and load data from a service identity. fn from_request(&self, request: &mut ServiceRequest) -> Self::Future; @@ -234,7 +235,7 @@ where type Error = Error; type InitError = (); type Transform = IdentityServiceMiddleware; - type Future = FutureResult; + type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { ok(IdentityServiceMiddleware { @@ -261,46 +262,39 @@ where type Request = ServiceRequest; type Response = ServiceResponse; type Error = Error; - type Future = Box>; + type Future = LocalBoxFuture<'static, Result>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.service.borrow_mut().poll_ready() + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.service.borrow_mut().poll_ready(cx) } fn call(&mut self, mut req: ServiceRequest) -> Self::Future { let srv = self.service.clone(); let backend = self.backend.clone(); + let fut = self.backend.from_request(&mut req); - Box::new( - self.backend.from_request(&mut req).into_future().then( - move |res| match res { - Ok(id) => { - req.extensions_mut() - .insert(IdentityItem { id, changed: false }); + async move { + match fut.await { + Ok(id) => { + req.extensions_mut() + .insert(IdentityItem { id, changed: false }); - Either::A(srv.borrow_mut().call(req).and_then(move |mut res| { - let id = - res.request().extensions_mut().remove::(); + let mut res = srv.borrow_mut().call(req).await?; + let id = res.request().extensions_mut().remove::(); - if let Some(id) = id { - Either::A( - backend - .to_response(id.id, id.changed, &mut res) - .into_future() - .then(move |t| match t { - Ok(_) => Ok(res), - Err(e) => Ok(res.error_response(e)), - }), - ) - } else { - Either::B(ok(res)) - } - })) + if let Some(id) = id { + match backend.to_response(id.id, id.changed, &mut res).await { + Ok(_) => Ok(res), + Err(e) => Ok(res.error_response(e)), + } + } else { + Ok(res) } - Err(err) => Either::B(ok(req.error_response(err))), - }, - ), - ) + } + Err(err) => Ok(req.error_response(err)), + } + } + .boxed_local() } } @@ -547,11 +541,11 @@ impl CookieIdentityPolicy { } impl IdentityPolicy for CookieIdentityPolicy { - type Future = Result, Error>; - type ResponseFuture = Result<(), Error>; + type Future = Ready, Error>>; + type ResponseFuture = Ready>; fn from_request(&self, req: &mut ServiceRequest) -> Self::Future { - Ok(self.0.load(req).map( + ok(self.0.load(req).map( |CookieValue { identity, login_timestamp, @@ -603,7 +597,7 @@ impl IdentityPolicy for CookieIdentityPolicy { } else { Ok(()) }; - Ok(()) + ok(()) } } @@ -613,7 +607,7 @@ mod tests { use super::*; use actix_web::http::StatusCode; - use actix_web::test::{self, TestRequest}; + use actix_web::test::{self, block_on, TestRequest}; use actix_web::{web, App, Error, HttpResponse}; const COOKIE_KEY_MASTER: [u8; 32] = [0; 32]; @@ -622,115 +616,138 @@ mod tests { #[test] fn test_identity() { - let mut srv = test::init_service( - App::new() - .wrap(IdentityService::new( - CookieIdentityPolicy::new(&COOKIE_KEY_MASTER) - .domain("www.rust-lang.org") - .name(COOKIE_NAME) - .path("/") - .secure(true), - )) - .service(web::resource("/index").to(|id: Identity| { - if id.identity().is_some() { - HttpResponse::Created() - } else { + block_on(async { + let mut srv = test::init_service( + App::new() + .wrap(IdentityService::new( + CookieIdentityPolicy::new(&COOKIE_KEY_MASTER) + .domain("www.rust-lang.org") + .name(COOKIE_NAME) + .path("/") + .secure(true), + )) + .service(web::resource("/index").to(|id: Identity| { + if id.identity().is_some() { + HttpResponse::Created() + } else { + HttpResponse::Ok() + } + })) + .service(web::resource("/login").to(|id: Identity| { + id.remember(COOKIE_LOGIN.to_string()); HttpResponse::Ok() - } - })) - .service(web::resource("/login").to(|id: Identity| { - id.remember(COOKIE_LOGIN.to_string()); - HttpResponse::Ok() - })) - .service(web::resource("/logout").to(|id: Identity| { - if id.identity().is_some() { - id.forget(); - HttpResponse::Ok() - } else { - HttpResponse::BadRequest() - } - })), - ); - let resp = - test::call_service(&mut srv, TestRequest::with_uri("/index").to_request()); - assert_eq!(resp.status(), StatusCode::OK); + })) + .service(web::resource("/logout").to(|id: Identity| { + if id.identity().is_some() { + id.forget(); + HttpResponse::Ok() + } else { + HttpResponse::BadRequest() + } + })), + ) + .await; + let resp = test::call_service( + &mut srv, + TestRequest::with_uri("/index").to_request(), + ) + .await; + assert_eq!(resp.status(), StatusCode::OK); - let resp = - test::call_service(&mut srv, TestRequest::with_uri("/login").to_request()); - assert_eq!(resp.status(), StatusCode::OK); - let c = resp.response().cookies().next().unwrap().to_owned(); + let resp = test::call_service( + &mut srv, + TestRequest::with_uri("/login").to_request(), + ) + .await; + assert_eq!(resp.status(), StatusCode::OK); + let c = resp.response().cookies().next().unwrap().to_owned(); - let resp = test::call_service( - &mut srv, - TestRequest::with_uri("/index") - .cookie(c.clone()) - .to_request(), - ); - assert_eq!(resp.status(), StatusCode::CREATED); + let resp = test::call_service( + &mut srv, + TestRequest::with_uri("/index") + .cookie(c.clone()) + .to_request(), + ) + .await; + assert_eq!(resp.status(), StatusCode::CREATED); - let resp = test::call_service( - &mut srv, - TestRequest::with_uri("/logout") - .cookie(c.clone()) - .to_request(), - ); - assert_eq!(resp.status(), StatusCode::OK); - assert!(resp.headers().contains_key(header::SET_COOKIE)) + let resp = test::call_service( + &mut srv, + TestRequest::with_uri("/logout") + .cookie(c.clone()) + .to_request(), + ) + .await; + assert_eq!(resp.status(), StatusCode::OK); + assert!(resp.headers().contains_key(header::SET_COOKIE)) + }) } #[test] fn test_identity_max_age_time() { - let duration = Duration::days(1); - let mut srv = test::init_service( - App::new() - .wrap(IdentityService::new( - CookieIdentityPolicy::new(&COOKIE_KEY_MASTER) - .domain("www.rust-lang.org") - .name(COOKIE_NAME) - .path("/") - .max_age_time(duration) - .secure(true), - )) - .service(web::resource("/login").to(|id: Identity| { - id.remember("test".to_string()); - HttpResponse::Ok() - })), - ); - let resp = - test::call_service(&mut srv, TestRequest::with_uri("/login").to_request()); - assert_eq!(resp.status(), StatusCode::OK); - assert!(resp.headers().contains_key(header::SET_COOKIE)); - let c = resp.response().cookies().next().unwrap().to_owned(); - assert_eq!(duration, c.max_age().unwrap()); + block_on(async { + let duration = Duration::days(1); + let mut srv = test::init_service( + App::new() + .wrap(IdentityService::new( + CookieIdentityPolicy::new(&COOKIE_KEY_MASTER) + .domain("www.rust-lang.org") + .name(COOKIE_NAME) + .path("/") + .max_age_time(duration) + .secure(true), + )) + .service(web::resource("/login").to(|id: Identity| { + id.remember("test".to_string()); + HttpResponse::Ok() + })), + ) + .await; + let resp = test::call_service( + &mut srv, + TestRequest::with_uri("/login").to_request(), + ) + .await; + assert_eq!(resp.status(), StatusCode::OK); + assert!(resp.headers().contains_key(header::SET_COOKIE)); + let c = resp.response().cookies().next().unwrap().to_owned(); + assert_eq!(duration, c.max_age().unwrap()); + }) } #[test] fn test_identity_max_age() { - let seconds = 60; - let mut srv = test::init_service( - App::new() - .wrap(IdentityService::new( - CookieIdentityPolicy::new(&COOKIE_KEY_MASTER) - .domain("www.rust-lang.org") - .name(COOKIE_NAME) - .path("/") - .max_age(seconds) - .secure(true), - )) - .service(web::resource("/login").to(|id: Identity| { - id.remember("test".to_string()); - HttpResponse::Ok() - })), - ); - let resp = - test::call_service(&mut srv, TestRequest::with_uri("/login").to_request()); - assert_eq!(resp.status(), StatusCode::OK); - assert!(resp.headers().contains_key(header::SET_COOKIE)); - let c = resp.response().cookies().next().unwrap().to_owned(); - assert_eq!(Duration::seconds(seconds as i64), c.max_age().unwrap()); + block_on(async { + let seconds = 60; + let mut srv = test::init_service( + App::new() + .wrap(IdentityService::new( + CookieIdentityPolicy::new(&COOKIE_KEY_MASTER) + .domain("www.rust-lang.org") + .name(COOKIE_NAME) + .path("/") + .max_age(seconds) + .secure(true), + )) + .service(web::resource("/login").to(|id: Identity| { + id.remember("test".to_string()); + HttpResponse::Ok() + })), + ) + .await; + let resp = test::call_service( + &mut srv, + TestRequest::with_uri("/login").to_request(), + ) + .await; + assert_eq!(resp.status(), StatusCode::OK); + assert!(resp.headers().contains_key(header::SET_COOKIE)); + let c = resp.response().cookies().next().unwrap().to_owned(); + assert_eq!(Duration::seconds(seconds as i64), c.max_age().unwrap()); + }) } - fn create_identity_server< + async fn create_identity_server< F: Fn(CookieIdentityPolicy) -> CookieIdentityPolicy + Sync + Send + Clone + 'static, >( f: F, @@ -754,6 +771,7 @@ mod tests { web::Json(identity) })), ) + .await } fn legacy_login_cookie(identity: &'static str) -> Cookie<'static> { @@ -786,15 +804,8 @@ mod tests { jar.get(COOKIE_NAME).unwrap().clone() } - fn assert_logged_in(response: &mut ServiceResponse, identity: Option<&str>) { - use bytes::BytesMut; - use futures::Stream; - let bytes = - test::block_on(response.take_body().fold(BytesMut::new(), |mut b, c| { - b.extend(c); - Ok::<_, Error>(b) - })) - .unwrap(); + async fn assert_logged_in(response: ServiceResponse, identity: Option<&str>) { + let bytes = test::read_body(response).await; let resp: Option = serde_json::from_slice(&bytes[..]).unwrap(); assert_eq!(resp.as_ref().map(|s| s.borrow()), identity); } @@ -874,183 +885,221 @@ mod tests { #[test] fn test_identity_legacy_cookie_is_set() { - let mut srv = create_identity_server(|c| c); - let mut resp = - test::call_service(&mut srv, TestRequest::with_uri("/").to_request()); - assert_logged_in(&mut resp, None); - assert_legacy_login_cookie(&mut resp, COOKIE_LOGIN); + block_on(async { + let mut srv = create_identity_server(|c| c).await; + let mut resp = + test::call_service(&mut srv, TestRequest::with_uri("/").to_request()) + .await; + assert_legacy_login_cookie(&mut resp, COOKIE_LOGIN); + assert_logged_in(resp, None).await; + }) } #[test] fn test_identity_legacy_cookie_works() { - let mut srv = create_identity_server(|c| c); - let cookie = legacy_login_cookie(COOKIE_LOGIN); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ); - assert_logged_in(&mut resp, Some(COOKIE_LOGIN)); - assert_no_login_cookie(&mut resp); + block_on(async { + let mut srv = create_identity_server(|c| c).await; + let cookie = legacy_login_cookie(COOKIE_LOGIN); + let mut resp = test::call_service( + &mut srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_no_login_cookie(&mut resp); + assert_logged_in(resp, Some(COOKIE_LOGIN)).await; + }) } #[test] fn test_identity_legacy_cookie_rejected_if_visit_timestamp_needed() { - let mut srv = create_identity_server(|c| c.visit_deadline(Duration::days(90))); - let cookie = legacy_login_cookie(COOKIE_LOGIN); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ); - assert_logged_in(&mut resp, None); - assert_login_cookie( - &mut resp, - COOKIE_LOGIN, - LoginTimestampCheck::NoTimestamp, - VisitTimeStampCheck::NewTimestamp, - ); + block_on(async { + let mut srv = + create_identity_server(|c| c.visit_deadline(Duration::days(90))).await; + let cookie = legacy_login_cookie(COOKIE_LOGIN); + let mut resp = test::call_service( + &mut srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_login_cookie( + &mut resp, + COOKIE_LOGIN, + LoginTimestampCheck::NoTimestamp, + VisitTimeStampCheck::NewTimestamp, + ); + assert_logged_in(resp, None).await; + }) } #[test] fn test_identity_legacy_cookie_rejected_if_login_timestamp_needed() { - let mut srv = create_identity_server(|c| c.login_deadline(Duration::days(90))); - let cookie = legacy_login_cookie(COOKIE_LOGIN); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ); - assert_logged_in(&mut resp, None); - assert_login_cookie( - &mut resp, - COOKIE_LOGIN, - LoginTimestampCheck::NewTimestamp, - VisitTimeStampCheck::NoTimestamp, - ); + block_on(async { + let mut srv = + create_identity_server(|c| c.login_deadline(Duration::days(90))).await; + let cookie = legacy_login_cookie(COOKIE_LOGIN); + let mut resp = test::call_service( + &mut srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_login_cookie( + &mut resp, + COOKIE_LOGIN, + LoginTimestampCheck::NewTimestamp, + VisitTimeStampCheck::NoTimestamp, + ); + assert_logged_in(resp, None).await; + }) } #[test] fn test_identity_cookie_rejected_if_login_timestamp_needed() { - let mut srv = create_identity_server(|c| c.login_deadline(Duration::days(90))); - let cookie = login_cookie(COOKIE_LOGIN, None, Some(SystemTime::now())); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ); - assert_logged_in(&mut resp, None); - assert_login_cookie( - &mut resp, - COOKIE_LOGIN, - LoginTimestampCheck::NewTimestamp, - VisitTimeStampCheck::NoTimestamp, - ); + block_on(async { + let mut srv = + create_identity_server(|c| c.login_deadline(Duration::days(90))).await; + let cookie = login_cookie(COOKIE_LOGIN, None, Some(SystemTime::now())); + let mut resp = test::call_service( + &mut srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_login_cookie( + &mut resp, + COOKIE_LOGIN, + LoginTimestampCheck::NewTimestamp, + VisitTimeStampCheck::NoTimestamp, + ); + assert_logged_in(resp, None).await; + }) } #[test] fn test_identity_cookie_rejected_if_visit_timestamp_needed() { - let mut srv = create_identity_server(|c| c.visit_deadline(Duration::days(90))); - let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now()), None); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ); - assert_logged_in(&mut resp, None); - assert_login_cookie( - &mut resp, - COOKIE_LOGIN, - LoginTimestampCheck::NoTimestamp, - VisitTimeStampCheck::NewTimestamp, - ); + block_on(async { + let mut srv = + create_identity_server(|c| c.visit_deadline(Duration::days(90))).await; + let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now()), None); + let mut resp = test::call_service( + &mut srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_login_cookie( + &mut resp, + COOKIE_LOGIN, + LoginTimestampCheck::NoTimestamp, + VisitTimeStampCheck::NewTimestamp, + ); + assert_logged_in(resp, None).await; + }) } #[test] fn test_identity_cookie_rejected_if_login_timestamp_too_old() { - let mut srv = create_identity_server(|c| c.login_deadline(Duration::days(90))); - let cookie = login_cookie( - COOKIE_LOGIN, - Some(SystemTime::now() - Duration::days(180).to_std().unwrap()), - None, - ); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ); - assert_logged_in(&mut resp, None); - assert_login_cookie( - &mut resp, - COOKIE_LOGIN, - LoginTimestampCheck::NewTimestamp, - VisitTimeStampCheck::NoTimestamp, - ); + block_on(async { + let mut srv = + create_identity_server(|c| c.login_deadline(Duration::days(90))).await; + let cookie = login_cookie( + COOKIE_LOGIN, + Some(SystemTime::now() - Duration::days(180).to_std().unwrap()), + None, + ); + let mut resp = test::call_service( + &mut srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_login_cookie( + &mut resp, + COOKIE_LOGIN, + LoginTimestampCheck::NewTimestamp, + VisitTimeStampCheck::NoTimestamp, + ); + assert_logged_in(resp, None).await; + }) } #[test] fn test_identity_cookie_rejected_if_visit_timestamp_too_old() { - let mut srv = create_identity_server(|c| c.visit_deadline(Duration::days(90))); - let cookie = login_cookie( - COOKIE_LOGIN, - None, - Some(SystemTime::now() - Duration::days(180).to_std().unwrap()), - ); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ); - assert_logged_in(&mut resp, None); - assert_login_cookie( - &mut resp, - COOKIE_LOGIN, - LoginTimestampCheck::NoTimestamp, - VisitTimeStampCheck::NewTimestamp, - ); + block_on(async { + let mut srv = + create_identity_server(|c| c.visit_deadline(Duration::days(90))).await; + let cookie = login_cookie( + COOKIE_LOGIN, + None, + Some(SystemTime::now() - Duration::days(180).to_std().unwrap()), + ); + let mut resp = test::call_service( + &mut srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_login_cookie( + &mut resp, + COOKIE_LOGIN, + LoginTimestampCheck::NoTimestamp, + VisitTimeStampCheck::NewTimestamp, + ); + assert_logged_in(resp, None).await; + }) } #[test] fn test_identity_cookie_not_updated_on_login_deadline() { - let mut srv = create_identity_server(|c| c.login_deadline(Duration::days(90))); - let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now()), None); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ); - assert_logged_in(&mut resp, Some(COOKIE_LOGIN)); - assert_no_login_cookie(&mut resp); + block_on(async { + let mut srv = + create_identity_server(|c| c.login_deadline(Duration::days(90))).await; + let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now()), None); + let mut resp = test::call_service( + &mut srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_no_login_cookie(&mut resp); + assert_logged_in(resp, Some(COOKIE_LOGIN)).await; + }) } #[test] fn test_identity_cookie_updated_on_visit_deadline() { - let mut srv = create_identity_server(|c| { - c.visit_deadline(Duration::days(90)) - .login_deadline(Duration::days(90)) - }); - let timestamp = SystemTime::now() - Duration::days(1).to_std().unwrap(); - let cookie = login_cookie(COOKIE_LOGIN, Some(timestamp), Some(timestamp)); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ); - assert_logged_in(&mut resp, Some(COOKIE_LOGIN)); - assert_login_cookie( - &mut resp, - COOKIE_LOGIN, - LoginTimestampCheck::OldTimestamp(timestamp), - VisitTimeStampCheck::NewTimestamp, - ); + block_on(async { + let mut srv = create_identity_server(|c| { + c.visit_deadline(Duration::days(90)) + .login_deadline(Duration::days(90)) + }) + .await; + let timestamp = SystemTime::now() - Duration::days(1).to_std().unwrap(); + let cookie = login_cookie(COOKIE_LOGIN, Some(timestamp), Some(timestamp)); + let mut resp = test::call_service( + &mut srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_login_cookie( + &mut resp, + COOKIE_LOGIN, + LoginTimestampCheck::OldTimestamp(timestamp), + VisitTimeStampCheck::NewTimestamp, + ); + assert_logged_in(resp, Some(COOKIE_LOGIN)).await; + }) } } From 6ac4ac66b96aa73f694d8de7fde2e0a2cffa1af8 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 21 Nov 2019 10:54:07 +0600 Subject: [PATCH 055/176] migrate actix-cors --- Cargo.toml | 2 +- actix-cors/Cargo.toml | 4 +- actix-cors/src/lib.rs | 722 ++++++++++++++++++++++-------------------- 3 files changed, 383 insertions(+), 345 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b80cf3e6e..6827a6196 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ members = [ ".", "awc", "actix-http", - #"actix-cors", + "actix-cors", #"actix-files", #"actix-framed", #"actix-session", diff --git a/actix-cors/Cargo.toml b/actix-cors/Cargo.toml index 56b6fabd9..57aa5833a 100644 --- a/actix-cors/Cargo.toml +++ b/actix-cors/Cargo.toml @@ -17,7 +17,7 @@ name = "actix_cors" path = "src/lib.rs" [dependencies] -actix-web = "1.0.9" -actix-service = "0.4.0" +actix-web = "2.0.0-alpha.1" +actix-service = "1.0.0-alpha.1" derive_more = "0.15.0" futures = "0.3.1" diff --git a/actix-cors/src/lib.rs b/actix-cors/src/lib.rs index c76bae925..40f9fdf99 100644 --- a/actix-cors/src/lib.rs +++ b/actix-cors/src/lib.rs @@ -23,7 +23,8 @@ //! .allowed_methods(vec!["GET", "POST"]) //! .allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT]) //! .allowed_header(http::header::CONTENT_TYPE) -//! .max_age(3600)) +//! .max_age(3600) +//! .finish()) //! .service( //! web::resource("/index.html") //! .route(web::get().to(index)) @@ -41,16 +42,16 @@ use std::collections::HashSet; use std::iter::FromIterator; use std::rc::Rc; +use std::task::{Context, Poll}; -use actix_service::{IntoTransform, Service, Transform}; +use actix_service::{Service, Transform}; use actix_web::dev::{RequestHead, ServiceRequest, ServiceResponse}; use actix_web::error::{Error, ResponseError, Result}; use actix_web::http::header::{self, HeaderName, HeaderValue}; use actix_web::http::{self, HttpTryFrom, Method, StatusCode, Uri}; use actix_web::HttpResponse; use derive_more::Display; -use futures::future::{ok, Either, Future, FutureResult}; -use futures::Poll; +use futures::future::{ok, Either, FutureExt, LocalBoxFuture, Ready}; /// A set of errors that can occur during processing CORS #[derive(Debug, Display)] @@ -456,25 +457,9 @@ impl Cors { } self } -} -fn cors<'a>( - parts: &'a mut Option, - err: &Option, -) -> Option<&'a mut Inner> { - if err.is_some() { - return None; - } - parts.as_mut() -} - -impl IntoTransform for Cors -where - S: Service, Error = Error>, - S::Future: 'static, - B: 'static, -{ - fn into_transform(self) -> CorsFactory { + /// Construct cors middleware + pub fn finish(self) -> CorsFactory { let mut slf = if !self.methods { self.allowed_methods(vec![ Method::GET, @@ -521,6 +506,16 @@ where } } +fn cors<'a>( + parts: &'a mut Option, + err: &Option, +) -> Option<&'a mut Inner> { + if err.is_some() { + return None; + } + parts.as_mut() +} + /// `Middleware` for Cross-origin resource sharing support /// /// The Cors struct contains the settings for CORS requests to be validated and @@ -540,7 +535,7 @@ where type Error = Error; type InitError = (); type Transform = CorsMiddleware; - type Future = FutureResult; + type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { ok(CorsMiddleware { @@ -682,12 +677,12 @@ where type Response = ServiceResponse; type Error = Error; type Future = Either< - FutureResult, - Either>>, + Ready>, + LocalBoxFuture<'static, Result>, >; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.service.poll_ready() + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.service.poll_ready(cx) } fn call(&mut self, req: ServiceRequest) -> Self::Future { @@ -698,7 +693,7 @@ where .and_then(|_| self.inner.validate_allowed_method(req.head())) .and_then(|_| self.inner.validate_allowed_headers(req.head())) { - return Either::A(ok(req.error_response(e))); + return Either::Left(ok(req.error_response(e))); } // allowed headers @@ -751,39 +746,50 @@ where .finish() .into_body(); - Either::A(ok(req.into_response(res))) - } else if req.headers().contains_key(&header::ORIGIN) { - // Only check requests with a origin header. - if let Err(e) = self.inner.validate_origin(req.head()) { - return Either::A(ok(req.error_response(e))); + Either::Left(ok(req.into_response(res))) + } else { + if req.headers().contains_key(&header::ORIGIN) { + // Only check requests with a origin header. + if let Err(e) = self.inner.validate_origin(req.head()) { + return Either::Left(ok(req.error_response(e))); + } } let inner = self.inner.clone(); + let has_origin = req.headers().contains_key(&header::ORIGIN); + let fut = self.service.call(req); - Either::B(Either::B(Box::new(self.service.call(req).and_then( - move |mut res| { - if let Some(origin) = - inner.access_control_allow_origin(res.request().head()) - { - res.headers_mut() - .insert(header::ACCESS_CONTROL_ALLOW_ORIGIN, origin.clone()); - }; + Either::Right( + async move { + let res = fut.await; - if let Some(ref expose) = inner.expose_hdrs { - res.headers_mut().insert( - header::ACCESS_CONTROL_EXPOSE_HEADERS, - HeaderValue::try_from(expose.as_str()).unwrap(), - ); - } - if inner.supports_credentials { - res.headers_mut().insert( - header::ACCESS_CONTROL_ALLOW_CREDENTIALS, - HeaderValue::from_static("true"), - ); - } - if inner.vary_header { - let value = - if let Some(hdr) = res.headers_mut().get(&header::VARY) { + if has_origin { + let mut res = res?; + if let Some(origin) = + inner.access_control_allow_origin(res.request().head()) + { + res.headers_mut().insert( + header::ACCESS_CONTROL_ALLOW_ORIGIN, + origin.clone(), + ); + }; + + if let Some(ref expose) = inner.expose_hdrs { + res.headers_mut().insert( + header::ACCESS_CONTROL_EXPOSE_HEADERS, + HeaderValue::try_from(expose.as_str()).unwrap(), + ); + } + if inner.supports_credentials { + res.headers_mut().insert( + header::ACCESS_CONTROL_ALLOW_CREDENTIALS, + HeaderValue::from_static("true"), + ); + } + if inner.vary_header { + let value = if let Some(hdr) = + res.headers_mut().get(&header::VARY) + { let mut val: Vec = Vec::with_capacity(hdr.as_bytes().len() + 8); val.extend(hdr.as_bytes()); @@ -792,159 +798,153 @@ where } else { HeaderValue::from_static("Origin") }; - res.headers_mut().insert(header::VARY, value); + res.headers_mut().insert(header::VARY, value); + } + Ok(res) + } else { + res } - Ok(res) - }, - )))) - } else { - Either::B(Either::A(self.service.call(req))) + } + .boxed_local(), + ) } } } #[cfg(test)] mod tests { - use actix_service::{IntoService, Transform}; + use actix_service::{service_fn2, Transform}; use actix_web::test::{self, block_on, TestRequest}; use super::*; - impl Cors { - fn finish(self, srv: F) -> CorsMiddleware - where - F: IntoService, - S: Service< - Request = ServiceRequest, - Response = ServiceResponse, - Error = Error, - > + 'static, - S::Future: 'static, - B: 'static, - { - block_on( - IntoTransform::::into_transform(self) - .new_transform(srv.into_service()), - ) - .unwrap() - } - } - #[test] #[should_panic(expected = "Credentials are allowed, but the Origin is set to")] fn cors_validates_illegal_allow_credentials() { - let _cors = Cors::new() - .supports_credentials() - .send_wildcard() - .finish(test::ok_service()); + let _cors = Cors::new().supports_credentials().send_wildcard().finish(); } #[test] fn validate_origin_allows_all_origins() { - let mut cors = Cors::new().finish(test::ok_service()); - let req = TestRequest::with_header("Origin", "https://www.example.com") - .to_srv_request(); + block_on(async { + let mut cors = Cors::new() + .finish() + .new_transform(test::ok_service()) + .await + .unwrap(); + let req = TestRequest::with_header("Origin", "https://www.example.com") + .to_srv_request(); - let resp = test::call_service(&mut cors, req); - assert_eq!(resp.status(), StatusCode::OK); + let resp = test::call_service(&mut cors, req).await; + assert_eq!(resp.status(), StatusCode::OK); + }) } #[test] fn default() { - let mut cors = - block_on(Cors::default().new_transform(test::ok_service())).unwrap(); - let req = TestRequest::with_header("Origin", "https://www.example.com") - .to_srv_request(); + block_on(async { + let mut cors = Cors::default() + .new_transform(test::ok_service()) + .await + .unwrap(); + let req = TestRequest::with_header("Origin", "https://www.example.com") + .to_srv_request(); - let resp = test::call_service(&mut cors, req); - assert_eq!(resp.status(), StatusCode::OK); + let resp = test::call_service(&mut cors, req).await; + assert_eq!(resp.status(), StatusCode::OK); + }) } #[test] fn test_preflight() { - let mut cors = Cors::new() - .send_wildcard() - .max_age(3600) - .allowed_methods(vec![Method::GET, Method::OPTIONS, Method::POST]) - .allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT]) - .allowed_header(header::CONTENT_TYPE) - .finish(test::ok_service()); + block_on(async { + let mut cors = Cors::new() + .send_wildcard() + .max_age(3600) + .allowed_methods(vec![Method::GET, Method::OPTIONS, Method::POST]) + .allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT]) + .allowed_header(header::CONTENT_TYPE) + .finish() + .new_transform(test::ok_service()) + .await + .unwrap(); - let req = TestRequest::with_header("Origin", "https://www.example.com") - .method(Method::OPTIONS) - .header(header::ACCESS_CONTROL_REQUEST_HEADERS, "X-Not-Allowed") - .to_srv_request(); + let req = TestRequest::with_header("Origin", "https://www.example.com") + .method(Method::OPTIONS) + .header(header::ACCESS_CONTROL_REQUEST_HEADERS, "X-Not-Allowed") + .to_srv_request(); - assert!(cors.inner.validate_allowed_method(req.head()).is_err()); - assert!(cors.inner.validate_allowed_headers(req.head()).is_err()); - let resp = test::call_service(&mut cors, req); - assert_eq!(resp.status(), StatusCode::BAD_REQUEST); + assert!(cors.inner.validate_allowed_method(req.head()).is_err()); + assert!(cors.inner.validate_allowed_headers(req.head()).is_err()); + let resp = test::call_service(&mut cors, req).await; + assert_eq!(resp.status(), StatusCode::BAD_REQUEST); - let req = TestRequest::with_header("Origin", "https://www.example.com") - .header(header::ACCESS_CONTROL_REQUEST_METHOD, "put") - .method(Method::OPTIONS) - .to_srv_request(); + let req = TestRequest::with_header("Origin", "https://www.example.com") + .header(header::ACCESS_CONTROL_REQUEST_METHOD, "put") + .method(Method::OPTIONS) + .to_srv_request(); - assert!(cors.inner.validate_allowed_method(req.head()).is_err()); - assert!(cors.inner.validate_allowed_headers(req.head()).is_ok()); + assert!(cors.inner.validate_allowed_method(req.head()).is_err()); + assert!(cors.inner.validate_allowed_headers(req.head()).is_ok()); - let req = TestRequest::with_header("Origin", "https://www.example.com") - .header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST") - .header( - header::ACCESS_CONTROL_REQUEST_HEADERS, - "AUTHORIZATION,ACCEPT", - ) - .method(Method::OPTIONS) - .to_srv_request(); + let req = TestRequest::with_header("Origin", "https://www.example.com") + .header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST") + .header( + header::ACCESS_CONTROL_REQUEST_HEADERS, + "AUTHORIZATION,ACCEPT", + ) + .method(Method::OPTIONS) + .to_srv_request(); - let resp = test::call_service(&mut cors, req); - assert_eq!( - &b"*"[..], - resp.headers() - .get(&header::ACCESS_CONTROL_ALLOW_ORIGIN) + let resp = test::call_service(&mut cors, req).await; + assert_eq!( + &b"*"[..], + resp.headers() + .get(&header::ACCESS_CONTROL_ALLOW_ORIGIN) + .unwrap() + .as_bytes() + ); + assert_eq!( + &b"3600"[..], + resp.headers() + .get(&header::ACCESS_CONTROL_MAX_AGE) + .unwrap() + .as_bytes() + ); + let hdr = resp + .headers() + .get(&header::ACCESS_CONTROL_ALLOW_HEADERS) .unwrap() - .as_bytes() - ); - assert_eq!( - &b"3600"[..], - resp.headers() - .get(&header::ACCESS_CONTROL_MAX_AGE) + .to_str() + .unwrap(); + assert!(hdr.contains("authorization")); + assert!(hdr.contains("accept")); + assert!(hdr.contains("content-type")); + + let methods = resp + .headers() + .get(header::ACCESS_CONTROL_ALLOW_METHODS) .unwrap() - .as_bytes() - ); - let hdr = resp - .headers() - .get(&header::ACCESS_CONTROL_ALLOW_HEADERS) - .unwrap() - .to_str() - .unwrap(); - assert!(hdr.contains("authorization")); - assert!(hdr.contains("accept")); - assert!(hdr.contains("content-type")); + .to_str() + .unwrap(); + assert!(methods.contains("POST")); + assert!(methods.contains("GET")); + assert!(methods.contains("OPTIONS")); - let methods = resp - .headers() - .get(header::ACCESS_CONTROL_ALLOW_METHODS) - .unwrap() - .to_str() - .unwrap(); - assert!(methods.contains("POST")); - assert!(methods.contains("GET")); - assert!(methods.contains("OPTIONS")); + Rc::get_mut(&mut cors.inner).unwrap().preflight = false; - Rc::get_mut(&mut cors.inner).unwrap().preflight = false; + let req = TestRequest::with_header("Origin", "https://www.example.com") + .header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST") + .header( + header::ACCESS_CONTROL_REQUEST_HEADERS, + "AUTHORIZATION,ACCEPT", + ) + .method(Method::OPTIONS) + .to_srv_request(); - let req = TestRequest::with_header("Origin", "https://www.example.com") - .header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST") - .header( - header::ACCESS_CONTROL_REQUEST_HEADERS, - "AUTHORIZATION,ACCEPT", - ) - .method(Method::OPTIONS) - .to_srv_request(); - - let resp = test::call_service(&mut cors, req); - assert_eq!(resp.status(), StatusCode::OK); + let resp = test::call_service(&mut cors, req).await; + assert_eq!(resp.status(), StatusCode::OK); + }) } // #[test] @@ -960,216 +960,254 @@ mod tests { #[test] #[should_panic(expected = "OriginNotAllowed")] fn test_validate_not_allowed_origin() { - let cors = Cors::new() - .allowed_origin("https://www.example.com") - .finish(test::ok_service()); + block_on(async { + let cors = Cors::new() + .allowed_origin("https://www.example.com") + .finish() + .new_transform(test::ok_service()) + .await + .unwrap(); - let req = TestRequest::with_header("Origin", "https://www.unknown.com") - .method(Method::GET) - .to_srv_request(); - cors.inner.validate_origin(req.head()).unwrap(); - cors.inner.validate_allowed_method(req.head()).unwrap(); - cors.inner.validate_allowed_headers(req.head()).unwrap(); + let req = TestRequest::with_header("Origin", "https://www.unknown.com") + .method(Method::GET) + .to_srv_request(); + cors.inner.validate_origin(req.head()).unwrap(); + cors.inner.validate_allowed_method(req.head()).unwrap(); + cors.inner.validate_allowed_headers(req.head()).unwrap(); + }) } #[test] fn test_validate_origin() { - let mut cors = Cors::new() - .allowed_origin("https://www.example.com") - .finish(test::ok_service()); + block_on(async { + let mut cors = Cors::new() + .allowed_origin("https://www.example.com") + .finish() + .new_transform(test::ok_service()) + .await + .unwrap(); - let req = TestRequest::with_header("Origin", "https://www.example.com") - .method(Method::GET) - .to_srv_request(); + let req = TestRequest::with_header("Origin", "https://www.example.com") + .method(Method::GET) + .to_srv_request(); - let resp = test::call_service(&mut cors, req); - assert_eq!(resp.status(), StatusCode::OK); + let resp = test::call_service(&mut cors, req).await; + assert_eq!(resp.status(), StatusCode::OK); + }) } #[test] fn test_no_origin_response() { - let mut cors = Cors::new().disable_preflight().finish(test::ok_service()); + block_on(async { + let mut cors = Cors::new() + .disable_preflight() + .finish() + .new_transform(test::ok_service()) + .await + .unwrap(); - let req = TestRequest::default().method(Method::GET).to_srv_request(); - let resp = test::call_service(&mut cors, req); - assert!(resp - .headers() - .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) - .is_none()); - - let req = TestRequest::with_header("Origin", "https://www.example.com") - .method(Method::OPTIONS) - .to_srv_request(); - let resp = test::call_service(&mut cors, req); - assert_eq!( - &b"https://www.example.com"[..], - resp.headers() + let req = TestRequest::default().method(Method::GET).to_srv_request(); + let resp = test::call_service(&mut cors, req).await; + assert!(resp + .headers() .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) - .unwrap() - .as_bytes() - ); + .is_none()); + + let req = TestRequest::with_header("Origin", "https://www.example.com") + .method(Method::OPTIONS) + .to_srv_request(); + let resp = test::call_service(&mut cors, req).await; + assert_eq!( + &b"https://www.example.com"[..], + resp.headers() + .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) + .unwrap() + .as_bytes() + ); + }) } #[test] fn test_response() { - let exposed_headers = vec![header::AUTHORIZATION, header::ACCEPT]; - let mut cors = Cors::new() - .send_wildcard() - .disable_preflight() - .max_age(3600) - .allowed_methods(vec![Method::GET, Method::OPTIONS, Method::POST]) - .allowed_headers(exposed_headers.clone()) - .expose_headers(exposed_headers.clone()) - .allowed_header(header::CONTENT_TYPE) - .finish(test::ok_service()); + block_on(async { + let exposed_headers = vec![header::AUTHORIZATION, header::ACCEPT]; + let mut cors = Cors::new() + .send_wildcard() + .disable_preflight() + .max_age(3600) + .allowed_methods(vec![Method::GET, Method::OPTIONS, Method::POST]) + .allowed_headers(exposed_headers.clone()) + .expose_headers(exposed_headers.clone()) + .allowed_header(header::CONTENT_TYPE) + .finish() + .new_transform(test::ok_service()) + .await + .unwrap(); - let req = TestRequest::with_header("Origin", "https://www.example.com") - .method(Method::OPTIONS) - .to_srv_request(); + let req = TestRequest::with_header("Origin", "https://www.example.com") + .method(Method::OPTIONS) + .to_srv_request(); - let resp = test::call_service(&mut cors, req); - assert_eq!( - &b"*"[..], - resp.headers() + let resp = test::call_service(&mut cors, req).await; + assert_eq!( + &b"*"[..], + resp.headers() + .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) + .unwrap() + .as_bytes() + ); + assert_eq!( + &b"Origin"[..], + resp.headers().get(header::VARY).unwrap().as_bytes() + ); + + { + let headers = resp + .headers() + .get(header::ACCESS_CONTROL_EXPOSE_HEADERS) + .unwrap() + .to_str() + .unwrap() + .split(',') + .map(|s| s.trim()) + .collect::>(); + + for h in exposed_headers { + assert!(headers.contains(&h.as_str())); + } + } + + let exposed_headers = vec![header::AUTHORIZATION, header::ACCEPT]; + let mut cors = Cors::new() + .send_wildcard() + .disable_preflight() + .max_age(3600) + .allowed_methods(vec![Method::GET, Method::OPTIONS, Method::POST]) + .allowed_headers(exposed_headers.clone()) + .expose_headers(exposed_headers.clone()) + .allowed_header(header::CONTENT_TYPE) + .finish() + .new_transform(service_fn2(|req: ServiceRequest| { + ok(req.into_response( + HttpResponse::Ok().header(header::VARY, "Accept").finish(), + )) + })) + .await + .unwrap(); + let req = TestRequest::with_header("Origin", "https://www.example.com") + .method(Method::OPTIONS) + .to_srv_request(); + let resp = test::call_service(&mut cors, req).await; + assert_eq!( + &b"Accept, Origin"[..], + resp.headers().get(header::VARY).unwrap().as_bytes() + ); + + let mut cors = Cors::new() + .disable_vary_header() + .allowed_origin("https://www.example.com") + .allowed_origin("https://www.google.com") + .finish() + .new_transform(test::ok_service()) + .await + .unwrap(); + + let req = TestRequest::with_header("Origin", "https://www.example.com") + .method(Method::OPTIONS) + .header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST") + .to_srv_request(); + let resp = test::call_service(&mut cors, req).await; + + let origins_str = resp + .headers() .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) .unwrap() - .as_bytes() - ); - assert_eq!( - &b"Origin"[..], - resp.headers().get(header::VARY).unwrap().as_bytes() - ); - - { - let headers = resp - .headers() - .get(header::ACCESS_CONTROL_EXPOSE_HEADERS) - .unwrap() .to_str() - .unwrap() - .split(',') - .map(|s| s.trim()) - .collect::>(); + .unwrap(); - for h in exposed_headers { - assert!(headers.contains(&h.as_str())); - } - } - - let exposed_headers = vec![header::AUTHORIZATION, header::ACCEPT]; - let mut cors = Cors::new() - .send_wildcard() - .disable_preflight() - .max_age(3600) - .allowed_methods(vec![Method::GET, Method::OPTIONS, Method::POST]) - .allowed_headers(exposed_headers.clone()) - .expose_headers(exposed_headers.clone()) - .allowed_header(header::CONTENT_TYPE) - .finish(|req: ServiceRequest| { - req.into_response( - HttpResponse::Ok().header(header::VARY, "Accept").finish(), - ) - }); - let req = TestRequest::with_header("Origin", "https://www.example.com") - .method(Method::OPTIONS) - .to_srv_request(); - let resp = test::call_service(&mut cors, req); - assert_eq!( - &b"Accept, Origin"[..], - resp.headers().get(header::VARY).unwrap().as_bytes() - ); - - let mut cors = Cors::new() - .disable_vary_header() - .allowed_origin("https://www.example.com") - .allowed_origin("https://www.google.com") - .finish(test::ok_service()); - - let req = TestRequest::with_header("Origin", "https://www.example.com") - .method(Method::OPTIONS) - .header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST") - .to_srv_request(); - let resp = test::call_service(&mut cors, req); - - let origins_str = resp - .headers() - .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) - .unwrap() - .to_str() - .unwrap(); - - assert_eq!("https://www.example.com", origins_str); + assert_eq!("https://www.example.com", origins_str); + }) } #[test] fn test_multiple_origins() { - let mut cors = Cors::new() - .allowed_origin("https://example.com") - .allowed_origin("https://example.org") - .allowed_methods(vec![Method::GET]) - .finish(test::ok_service()); + block_on(async { + let mut cors = Cors::new() + .allowed_origin("https://example.com") + .allowed_origin("https://example.org") + .allowed_methods(vec![Method::GET]) + .finish() + .new_transform(test::ok_service()) + .await + .unwrap(); - let req = TestRequest::with_header("Origin", "https://example.com") - .method(Method::GET) - .to_srv_request(); + let req = TestRequest::with_header("Origin", "https://example.com") + .method(Method::GET) + .to_srv_request(); - let resp = test::call_service(&mut cors, req); - assert_eq!( - &b"https://example.com"[..], - resp.headers() - .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) - .unwrap() - .as_bytes() - ); + let resp = test::call_service(&mut cors, req).await; + assert_eq!( + &b"https://example.com"[..], + resp.headers() + .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) + .unwrap() + .as_bytes() + ); - let req = TestRequest::with_header("Origin", "https://example.org") - .method(Method::GET) - .to_srv_request(); + let req = TestRequest::with_header("Origin", "https://example.org") + .method(Method::GET) + .to_srv_request(); - let resp = test::call_service(&mut cors, req); - assert_eq!( - &b"https://example.org"[..], - resp.headers() - .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) - .unwrap() - .as_bytes() - ); + let resp = test::call_service(&mut cors, req).await; + assert_eq!( + &b"https://example.org"[..], + resp.headers() + .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) + .unwrap() + .as_bytes() + ); + }) } #[test] fn test_multiple_origins_preflight() { - let mut cors = Cors::new() - .allowed_origin("https://example.com") - .allowed_origin("https://example.org") - .allowed_methods(vec![Method::GET]) - .finish(test::ok_service()); + block_on(async { + let mut cors = Cors::new() + .allowed_origin("https://example.com") + .allowed_origin("https://example.org") + .allowed_methods(vec![Method::GET]) + .finish() + .new_transform(test::ok_service()) + .await + .unwrap(); - let req = TestRequest::with_header("Origin", "https://example.com") - .header(header::ACCESS_CONTROL_REQUEST_METHOD, "GET") - .method(Method::OPTIONS) - .to_srv_request(); + let req = TestRequest::with_header("Origin", "https://example.com") + .header(header::ACCESS_CONTROL_REQUEST_METHOD, "GET") + .method(Method::OPTIONS) + .to_srv_request(); - let resp = test::call_service(&mut cors, req); - assert_eq!( - &b"https://example.com"[..], - resp.headers() - .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) - .unwrap() - .as_bytes() - ); + let resp = test::call_service(&mut cors, req).await; + assert_eq!( + &b"https://example.com"[..], + resp.headers() + .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) + .unwrap() + .as_bytes() + ); - let req = TestRequest::with_header("Origin", "https://example.org") - .header(header::ACCESS_CONTROL_REQUEST_METHOD, "GET") - .method(Method::OPTIONS) - .to_srv_request(); + let req = TestRequest::with_header("Origin", "https://example.org") + .header(header::ACCESS_CONTROL_REQUEST_METHOD, "GET") + .method(Method::OPTIONS) + .to_srv_request(); - let resp = test::call_service(&mut cors, req); - assert_eq!( - &b"https://example.org"[..], - resp.headers() - .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) - .unwrap() - .as_bytes() - ); + let resp = test::call_service(&mut cors, req).await; + assert_eq!( + &b"https://example.org"[..], + resp.headers() + .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) + .unwrap() + .as_bytes() + ); + }) } } From 69cadcdedb139f6cdae4604dde128161be6d87cb Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 21 Nov 2019 11:31:31 +0600 Subject: [PATCH 056/176] migrate actix-files --- Cargo.toml | 4 +- actix-files/Cargo.toml | 14 +- actix-files/src/lib.rs | 1129 ++++++++++++++++++++------------------ actix-files/src/named.rs | 122 ++-- actix-http/src/error.rs | 2 +- 5 files changed, 664 insertions(+), 607 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6827a6196..32918ee43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ members = [ "awc", "actix-http", "actix-cors", - #"actix-files", + "actix-files", #"actix-framed", #"actix-session", "actix-identity", @@ -126,7 +126,7 @@ actix-http-test = { path = "test-server" } actix-web-codegen = { path = "actix-web-codegen" } # actix-web-actors = { path = "actix-web-actors" } # actix-session = { path = "actix-session" } -# actix-files = { path = "actix-files" } +actix-files = { path = "actix-files" } # actix-multipart = { path = "actix-multipart" } awc = { path = "awc" } diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 2f75fb50d..6e33bb412 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-files" -version = "0.1.7" +version = "0.2.0-alpha.1" authors = ["Nikolay Kim "] description = "Static files support for actix web." readme = "README.md" @@ -11,19 +11,19 @@ documentation = "https://docs.rs/actix-files/" categories = ["asynchronous", "web-programming::http-server"] license = "MIT/Apache-2.0" edition = "2018" -# workspace = ".." +workspace = ".." [lib] name = "actix_files" path = "src/lib.rs" [dependencies] -actix-web = { version = "1.0.9", default-features = false } -actix-http = "0.2.11" -actix-service = "0.4.2" +actix-web = { version = "2.0.0-alpha.1", default-features = false } +actix-http = "0.3.0-alpha.1" +actix-service = "1.0.0-alpha.1" bitflags = "1" bytes = "0.4" -futures = "0.1.24" +futures = "0.3.1" derive_more = "0.15.0" log = "0.4" mime = "0.3" @@ -32,4 +32,4 @@ percent-encoding = "2.1" v_htmlescape = "0.4" [dev-dependencies] -actix-web = { version = "1.0.9", features=["ssl"] } +actix-web = { version = "2.0.0-alpha.1", features=["openssl"] } diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index 8df7a6aa9..72db16957 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -4,25 +4,28 @@ use std::cell::RefCell; use std::fmt::Write; use std::fs::{DirEntry, File}; +use std::future::Future; use std::io::{Read, Seek}; use std::path::{Path, PathBuf}; +use std::pin::Pin; use std::rc::Rc; +use std::task::{Context, Poll}; use std::{cmp, io}; use actix_service::boxed::{self, BoxedNewService, BoxedService}; -use actix_service::{IntoNewService, NewService, Service}; +use actix_service::{IntoServiceFactory, Service, ServiceFactory}; use actix_web::dev::{ AppService, HttpServiceFactory, Payload, ResourceDef, ServiceRequest, ServiceResponse, }; -use actix_web::error::{BlockingError, Error, ErrorInternalServerError}; +use actix_web::error::{Canceled, Error, ErrorInternalServerError}; use actix_web::guard::Guard; use actix_web::http::header::{self, DispositionType}; use actix_web::http::Method; -use actix_web::{web, FromRequest, HttpRequest, HttpResponse, Responder}; +use actix_web::{web, FromRequest, HttpRequest, HttpResponse}; use bytes::Bytes; -use futures::future::{ok, Either, FutureResult}; -use futures::{Async, Future, Poll, Stream}; +use futures::future::{ok, ready, Either, FutureExt, LocalBoxFuture, Ready}; +use futures::Stream; use mime; use mime_guess::from_ext; use percent_encoding::{utf8_percent_encode, CONTROLS}; @@ -54,32 +57,34 @@ pub struct ChunkedReadFile { size: u64, offset: u64, file: Option, - fut: Option>>>, + fut: Option< + LocalBoxFuture<'static, Result, Canceled>>, + >, counter: u64, } -fn handle_error(err: BlockingError) -> Error { - match err { - BlockingError::Error(err) => err.into(), - BlockingError::Canceled => ErrorInternalServerError("Unexpected error"), - } -} - impl Stream for ChunkedReadFile { - type Item = Bytes; - type Error = Error; + type Item = Result; - fn poll(&mut self) -> Poll, Error> { - if self.fut.is_some() { - return match self.fut.as_mut().unwrap().poll().map_err(handle_error)? { - Async::Ready((file, bytes)) => { + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll> { + if let Some(ref mut fut) = self.fut { + return match Pin::new(fut).poll(cx) { + Poll::Ready(Err(_)) => Poll::Ready(Some(Err(ErrorInternalServerError( + "Unexpected error", + ) + .into()))), + Poll::Ready(Ok(Ok((file, bytes)))) => { self.fut.take(); self.file = Some(file); self.offset += bytes.len() as u64; self.counter += bytes.len() as u64; - Ok(Async::Ready(Some(bytes))) + Poll::Ready(Some(Ok(bytes))) } - Async::NotReady => Ok(Async::NotReady), + Poll::Ready(Ok(Err(e))) => Poll::Ready(Some(Err(e.into()))), + Poll::Pending => Poll::Pending, }; } @@ -88,22 +93,25 @@ impl Stream for ChunkedReadFile { let counter = self.counter; if size == counter { - Ok(Async::Ready(None)) + Poll::Ready(None) } else { let mut file = self.file.take().expect("Use after completion"); - self.fut = Some(Box::new(web::block(move || { - let max_bytes: usize; - max_bytes = cmp::min(size.saturating_sub(counter), 65_536) as usize; - let mut buf = Vec::with_capacity(max_bytes); - file.seek(io::SeekFrom::Start(offset))?; - let nbytes = - file.by_ref().take(max_bytes as u64).read_to_end(&mut buf)?; - if nbytes == 0 { - return Err(io::ErrorKind::UnexpectedEof.into()); - } - Ok((file, Bytes::from(buf))) - }))); - self.poll() + self.fut = Some( + web::block(move || { + let max_bytes: usize; + max_bytes = cmp::min(size.saturating_sub(counter), 65_536) as usize; + let mut buf = Vec::with_capacity(max_bytes); + file.seek(io::SeekFrom::Start(offset))?; + let nbytes = + file.by_ref().take(max_bytes as u64).read_to_end(&mut buf)?; + if nbytes == 0 { + return Err(io::ErrorKind::UnexpectedEof.into()); + } + Ok((file, Bytes::from(buf))) + }) + .boxed_local(), + ); + self.poll_next(cx) } } } @@ -367,8 +375,8 @@ impl Files { /// Sets default handler which is used when no matched file could be found. pub fn default_handler(mut self, f: F) -> Self where - F: IntoNewService, - U: NewService< + F: IntoServiceFactory, + U: ServiceFactory< Config = (), Request = ServiceRequest, Response = ServiceResponse, @@ -376,8 +384,8 @@ impl Files { > + 'static, { // create and configure default resource - self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::new_service( - f.into_new_service().map_init_err(|_| ()), + self.default = Rc::new(RefCell::new(Some(Rc::new(boxed::factory( + f.into_factory().map_init_err(|_| ()), ))))); self @@ -398,14 +406,14 @@ impl HttpServiceFactory for Files { } } -impl NewService for Files { +impl ServiceFactory for Files { type Request = ServiceRequest; type Response = ServiceResponse; type Error = Error; type Config = (); type Service = FilesService; type InitError = (); - type Future = Box>; + type Future = LocalBoxFuture<'static, Result>; fn new_service(&self, _: &()) -> Self::Future { let mut srv = FilesService { @@ -421,17 +429,18 @@ impl NewService for Files { }; if let Some(ref default) = *self.default.borrow() { - Box::new( - default - .new_service(&()) - .map(move |default| { + default + .new_service(&()) + .map(move |result| match result { + Ok(default) => { srv.default = Some(default); - srv - }) - .map_err(|_| ()), - ) + Ok(srv) + } + Err(_) => Err(()), + }) + .boxed_local() } else { - Box::new(ok(srv)) + ok(srv).boxed_local() } } } @@ -454,14 +463,14 @@ impl FilesService { e: io::Error, req: ServiceRequest, ) -> Either< - FutureResult, - Box>, + Ready>, + LocalBoxFuture<'static, Result>, > { log::debug!("Files: Failed to handle {}: {}", req.path(), e); if let Some(ref mut default) = self.default { - default.call(req) + Either::Right(default.call(req)) } else { - Either::A(ok(req.error_response(e))) + Either::Left(ok(req.error_response(e))) } } } @@ -471,17 +480,15 @@ impl Service for FilesService { type Response = ServiceResponse; type Error = Error; type Future = Either< - FutureResult, - Box>, + Ready>, + LocalBoxFuture<'static, Result>, >; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, req: ServiceRequest) -> Self::Future { - // let (req, pl) = req.into_parts(); - let is_method_valid = if let Some(guard) = &self.guards { // execute user defined guards (**guard).check(req.head()) @@ -494,7 +501,7 @@ impl Service for FilesService { }; if !is_method_valid { - return Either::A(ok(req.into_response( + return Either::Left(ok(req.into_response( actix_web::HttpResponse::MethodNotAllowed() .header(header::CONTENT_TYPE, "text/plain") .body("Request did not meet this resource's requirements."), @@ -503,7 +510,7 @@ impl Service for FilesService { let real_path = match PathBufWrp::get_pathbuf(req.match_info().path()) { Ok(item) => item, - Err(e) => return Either::A(ok(req.error_response(e))), + Err(e) => return Either::Left(ok(req.error_response(e))), }; // full filepath @@ -516,7 +523,7 @@ impl Service for FilesService { if let Some(ref redir_index) = self.index { if self.redirect_to_slash && !req.path().ends_with('/') { let redirect_to = format!("{}/", req.path()); - return Either::A(ok(req.into_response( + return Either::Left(ok(req.into_response( HttpResponse::Found() .header(header::LOCATION, redirect_to) .body("") @@ -536,7 +543,7 @@ impl Service for FilesService { named_file.flags = self.file_flags; let (req, _) = req.into_parts(); - Either::A(ok(match named_file.respond_to(&req) { + Either::Left(ok(match named_file.into_response(&req) { Ok(item) => ServiceResponse::new(req, item), Err(e) => ServiceResponse::from_err(e, req), })) @@ -548,11 +555,11 @@ impl Service for FilesService { let (req, _) = req.into_parts(); let x = (self.renderer)(&dir, &req); match x { - Ok(resp) => Either::A(ok(resp)), - Err(e) => Either::A(ok(ServiceResponse::from_err(e, req))), + Ok(resp) => Either::Left(ok(resp)), + Err(e) => Either::Left(ok(ServiceResponse::from_err(e, req))), } } else { - Either::A(ok(ServiceResponse::from_err( + Either::Left(ok(ServiceResponse::from_err( FilesError::IsDirectory, req.into_parts().0, ))) @@ -568,11 +575,11 @@ impl Service for FilesService { named_file.flags = self.file_flags; let (req, _) = req.into_parts(); - match named_file.respond_to(&req) { + match named_file.into_response(&req) { Ok(item) => { - Either::A(ok(ServiceResponse::new(req.clone(), item))) + Either::Left(ok(ServiceResponse::new(req.clone(), item))) } - Err(e) => Either::A(ok(ServiceResponse::from_err(e, req))), + Err(e) => Either::Left(ok(ServiceResponse::from_err(e, req))), } } Err(e) => self.handle_err(e, req), @@ -615,11 +622,11 @@ impl PathBufWrp { impl FromRequest for PathBufWrp { type Error = UriSegmentError; - type Future = Result; + type Future = Ready>; type Config = (); fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { - PathBufWrp::get_pathbuf(req.match_info().path()) + ready(PathBufWrp::get_pathbuf(req.match_info().path())) } } @@ -630,8 +637,6 @@ mod tests { use std::ops::Add; use std::time::{Duration, SystemTime}; - use bytes::BytesMut; - use super::*; use actix_web::guard; use actix_web::http::header::{ @@ -639,8 +644,8 @@ mod tests { }; use actix_web::http::{Method, StatusCode}; use actix_web::middleware::Compress; - use actix_web::test::{self, TestRequest}; - use actix_web::App; + use actix_web::test::{self, block_on, TestRequest}; + use actix_web::{App, Responder}; #[test] fn test_file_extension_to_mime() { @@ -656,592 +661,643 @@ mod tests { #[test] fn test_if_modified_since_without_if_none_match() { - let file = NamedFile::open("Cargo.toml").unwrap(); - let since = - header::HttpDate::from(SystemTime::now().add(Duration::from_secs(60))); + block_on(async { + let file = NamedFile::open("Cargo.toml").unwrap(); + let since = + header::HttpDate::from(SystemTime::now().add(Duration::from_secs(60))); - let req = TestRequest::default() - .header(header::IF_MODIFIED_SINCE, since) - .to_http_request(); - let resp = file.respond_to(&req).unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_MODIFIED); + let req = TestRequest::default() + .header(header::IF_MODIFIED_SINCE, since) + .to_http_request(); + let resp = file.respond_to(&req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_MODIFIED); + }) } #[test] fn test_if_modified_since_with_if_none_match() { - let file = NamedFile::open("Cargo.toml").unwrap(); - let since = - header::HttpDate::from(SystemTime::now().add(Duration::from_secs(60))); + block_on(async { + let file = NamedFile::open("Cargo.toml").unwrap(); + let since = + header::HttpDate::from(SystemTime::now().add(Duration::from_secs(60))); - let req = TestRequest::default() - .header(header::IF_NONE_MATCH, "miss_etag") - .header(header::IF_MODIFIED_SINCE, since) - .to_http_request(); - let resp = file.respond_to(&req).unwrap(); - assert_ne!(resp.status(), StatusCode::NOT_MODIFIED); + let req = TestRequest::default() + .header(header::IF_NONE_MATCH, "miss_etag") + .header(header::IF_MODIFIED_SINCE, since) + .to_http_request(); + let resp = file.respond_to(&req).await.unwrap(); + assert_ne!(resp.status(), StatusCode::NOT_MODIFIED); + }) } #[test] fn test_named_file_text() { - assert!(NamedFile::open("test--").is_err()); - let mut file = NamedFile::open("Cargo.toml").unwrap(); - { - file.file(); - let _f: &File = &file; - } - { - let _f: &mut File = &mut file; - } + block_on(async { + assert!(NamedFile::open("test--").is_err()); + let mut file = NamedFile::open("Cargo.toml").unwrap(); + { + file.file(); + let _f: &File = &file; + } + { + let _f: &mut File = &mut file; + } - let req = TestRequest::default().to_http_request(); - let resp = file.respond_to(&req).unwrap(); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - "text/x-toml" - ); - assert_eq!( - resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), - "inline; filename=\"Cargo.toml\"" - ); + let req = TestRequest::default().to_http_request(); + let resp = file.respond_to(&req).await.unwrap(); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + "text/x-toml" + ); + assert_eq!( + resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), + "inline; filename=\"Cargo.toml\"" + ); + }) } #[test] fn test_named_file_content_disposition() { - assert!(NamedFile::open("test--").is_err()); - let mut file = NamedFile::open("Cargo.toml").unwrap(); - { - file.file(); - let _f: &File = &file; - } - { - let _f: &mut File = &mut file; - } + block_on(async { + assert!(NamedFile::open("test--").is_err()); + let mut file = NamedFile::open("Cargo.toml").unwrap(); + { + file.file(); + let _f: &File = &file; + } + { + let _f: &mut File = &mut file; + } - let req = TestRequest::default().to_http_request(); - let resp = file.respond_to(&req).unwrap(); - assert_eq!( - resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), - "inline; filename=\"Cargo.toml\"" - ); + let req = TestRequest::default().to_http_request(); + let resp = file.respond_to(&req).await.unwrap(); + assert_eq!( + resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), + "inline; filename=\"Cargo.toml\"" + ); - let file = NamedFile::open("Cargo.toml") - .unwrap() - .disable_content_disposition(); - let req = TestRequest::default().to_http_request(); - let resp = file.respond_to(&req).unwrap(); - assert!(resp.headers().get(header::CONTENT_DISPOSITION).is_none()); + let file = NamedFile::open("Cargo.toml") + .unwrap() + .disable_content_disposition(); + let req = TestRequest::default().to_http_request(); + let resp = file.respond_to(&req).await.unwrap(); + assert!(resp.headers().get(header::CONTENT_DISPOSITION).is_none()); + }) } #[test] fn test_named_file_non_ascii_file_name() { - let mut file = - NamedFile::from_file(File::open("Cargo.toml").unwrap(), "貨物.toml") - .unwrap(); - { - file.file(); - let _f: &File = &file; - } - { - let _f: &mut File = &mut file; - } + block_on(async { + let mut file = + NamedFile::from_file(File::open("Cargo.toml").unwrap(), "貨物.toml") + .unwrap(); + { + file.file(); + let _f: &File = &file; + } + { + let _f: &mut File = &mut file; + } - let req = TestRequest::default().to_http_request(); - let resp = file.respond_to(&req).unwrap(); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - "text/x-toml" - ); - assert_eq!( + let req = TestRequest::default().to_http_request(); + let resp = file.respond_to(&req).await.unwrap(); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + "text/x-toml" + ); + assert_eq!( resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), "inline; filename=\"貨物.toml\"; filename*=UTF-8''%E8%B2%A8%E7%89%A9.toml" ); + }) } #[test] fn test_named_file_set_content_type() { - let mut file = NamedFile::open("Cargo.toml") - .unwrap() - .set_content_type(mime::TEXT_XML); - { - file.file(); - let _f: &File = &file; - } - { - let _f: &mut File = &mut file; - } + block_on(async { + let mut file = NamedFile::open("Cargo.toml") + .unwrap() + .set_content_type(mime::TEXT_XML); + { + file.file(); + let _f: &File = &file; + } + { + let _f: &mut File = &mut file; + } - let req = TestRequest::default().to_http_request(); - let resp = file.respond_to(&req).unwrap(); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - "text/xml" - ); - assert_eq!( - resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), - "inline; filename=\"Cargo.toml\"" - ); + let req = TestRequest::default().to_http_request(); + let resp = file.respond_to(&req).await.unwrap(); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + "text/xml" + ); + assert_eq!( + resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), + "inline; filename=\"Cargo.toml\"" + ); + }) } #[test] fn test_named_file_image() { - let mut file = NamedFile::open("tests/test.png").unwrap(); - { - file.file(); - let _f: &File = &file; - } - { - let _f: &mut File = &mut file; - } + block_on(async { + let mut file = NamedFile::open("tests/test.png").unwrap(); + { + file.file(); + let _f: &File = &file; + } + { + let _f: &mut File = &mut file; + } - let req = TestRequest::default().to_http_request(); - let resp = file.respond_to(&req).unwrap(); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - "image/png" - ); - assert_eq!( - resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), - "inline; filename=\"test.png\"" - ); + let req = TestRequest::default().to_http_request(); + let resp = file.respond_to(&req).await.unwrap(); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + "image/png" + ); + assert_eq!( + resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), + "inline; filename=\"test.png\"" + ); + }) } #[test] fn test_named_file_image_attachment() { - let cd = ContentDisposition { - disposition: DispositionType::Attachment, - parameters: vec![DispositionParam::Filename(String::from("test.png"))], - }; - let mut file = NamedFile::open("tests/test.png") - .unwrap() - .set_content_disposition(cd); - { - file.file(); - let _f: &File = &file; - } - { - let _f: &mut File = &mut file; - } + block_on(async { + let cd = ContentDisposition { + disposition: DispositionType::Attachment, + parameters: vec![DispositionParam::Filename(String::from("test.png"))], + }; + let mut file = NamedFile::open("tests/test.png") + .unwrap() + .set_content_disposition(cd); + { + file.file(); + let _f: &File = &file; + } + { + let _f: &mut File = &mut file; + } - let req = TestRequest::default().to_http_request(); - let resp = file.respond_to(&req).unwrap(); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - "image/png" - ); - assert_eq!( - resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), - "attachment; filename=\"test.png\"" - ); + let req = TestRequest::default().to_http_request(); + let resp = file.respond_to(&req).await.unwrap(); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + "image/png" + ); + assert_eq!( + resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), + "attachment; filename=\"test.png\"" + ); + }) } #[test] fn test_named_file_binary() { - let mut file = NamedFile::open("tests/test.binary").unwrap(); - { - file.file(); - let _f: &File = &file; - } - { - let _f: &mut File = &mut file; - } + block_on(async { + let mut file = NamedFile::open("tests/test.binary").unwrap(); + { + file.file(); + let _f: &File = &file; + } + { + let _f: &mut File = &mut file; + } - let req = TestRequest::default().to_http_request(); - let resp = file.respond_to(&req).unwrap(); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - "application/octet-stream" - ); - assert_eq!( - resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), - "attachment; filename=\"test.binary\"" - ); + let req = TestRequest::default().to_http_request(); + let resp = file.respond_to(&req).await.unwrap(); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + "application/octet-stream" + ); + assert_eq!( + resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), + "attachment; filename=\"test.binary\"" + ); + }) } #[test] fn test_named_file_status_code_text() { - let mut file = NamedFile::open("Cargo.toml") - .unwrap() - .set_status_code(StatusCode::NOT_FOUND); - { - file.file(); - let _f: &File = &file; - } - { - let _f: &mut File = &mut file; - } + block_on(async { + let mut file = NamedFile::open("Cargo.toml") + .unwrap() + .set_status_code(StatusCode::NOT_FOUND); + { + file.file(); + let _f: &File = &file; + } + { + let _f: &mut File = &mut file; + } - let req = TestRequest::default().to_http_request(); - let resp = file.respond_to(&req).unwrap(); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - "text/x-toml" - ); - assert_eq!( - resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), - "inline; filename=\"Cargo.toml\"" - ); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); + let req = TestRequest::default().to_http_request(); + let resp = file.respond_to(&req).await.unwrap(); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + "text/x-toml" + ); + assert_eq!( + resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), + "inline; filename=\"Cargo.toml\"" + ); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + }) } #[test] fn test_mime_override() { - fn all_attachment(_: &mime::Name) -> DispositionType { - DispositionType::Attachment - } + block_on(async { + fn all_attachment(_: &mime::Name) -> DispositionType { + DispositionType::Attachment + } - let mut srv = test::init_service( - App::new().service( - Files::new("/", ".") - .mime_override(all_attachment) - .index_file("Cargo.toml"), - ), - ); + let mut srv = test::init_service( + App::new().service( + Files::new("/", ".") + .mime_override(all_attachment) + .index_file("Cargo.toml"), + ), + ) + .await; - let request = TestRequest::get().uri("/").to_request(); - let response = test::call_service(&mut srv, request); - assert_eq!(response.status(), StatusCode::OK); + let request = TestRequest::get().uri("/").to_request(); + let response = test::call_service(&mut srv, request).await; + assert_eq!(response.status(), StatusCode::OK); - let content_disposition = response - .headers() - .get(header::CONTENT_DISPOSITION) - .expect("To have CONTENT_DISPOSITION"); - let content_disposition = content_disposition - .to_str() - .expect("Convert CONTENT_DISPOSITION to str"); - assert_eq!(content_disposition, "attachment; filename=\"Cargo.toml\""); + let content_disposition = response + .headers() + .get(header::CONTENT_DISPOSITION) + .expect("To have CONTENT_DISPOSITION"); + let content_disposition = content_disposition + .to_str() + .expect("Convert CONTENT_DISPOSITION to str"); + assert_eq!(content_disposition, "attachment; filename=\"Cargo.toml\""); + }) } #[test] fn test_named_file_ranges_status_code() { - let mut srv = test::init_service( - App::new().service(Files::new("/test", ".").index_file("Cargo.toml")), - ); + block_on(async { + let mut srv = test::init_service( + App::new().service(Files::new("/test", ".").index_file("Cargo.toml")), + ) + .await; - // Valid range header - let request = TestRequest::get() - .uri("/t%65st/Cargo.toml") - .header(header::RANGE, "bytes=10-20") - .to_request(); - let response = test::call_service(&mut srv, request); - assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT); + // Valid range header + let request = TestRequest::get() + .uri("/t%65st/Cargo.toml") + .header(header::RANGE, "bytes=10-20") + .to_request(); + let response = test::call_service(&mut srv, request).await; + assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT); - // Invalid range header - let request = TestRequest::get() - .uri("/t%65st/Cargo.toml") - .header(header::RANGE, "bytes=1-0") - .to_request(); - let response = test::call_service(&mut srv, request); + // Invalid range header + let request = TestRequest::get() + .uri("/t%65st/Cargo.toml") + .header(header::RANGE, "bytes=1-0") + .to_request(); + let response = test::call_service(&mut srv, request).await; - assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE); + assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE); + }) } #[test] fn test_named_file_content_range_headers() { - let mut srv = test::init_service( - App::new().service(Files::new("/test", ".").index_file("tests/test.binary")), - ); + block_on(async { + let mut srv = test::init_service( + App::new() + .service(Files::new("/test", ".").index_file("tests/test.binary")), + ) + .await; - // Valid range header - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") - .header(header::RANGE, "bytes=10-20") - .to_request(); + // Valid range header + let request = TestRequest::get() + .uri("/t%65st/tests/test.binary") + .header(header::RANGE, "bytes=10-20") + .to_request(); - let response = test::call_service(&mut srv, request); - let contentrange = response - .headers() - .get(header::CONTENT_RANGE) - .unwrap() - .to_str() - .unwrap(); + let response = test::call_service(&mut srv, request).await; + let contentrange = response + .headers() + .get(header::CONTENT_RANGE) + .unwrap() + .to_str() + .unwrap(); - assert_eq!(contentrange, "bytes 10-20/100"); + assert_eq!(contentrange, "bytes 10-20/100"); - // Invalid range header - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") - .header(header::RANGE, "bytes=10-5") - .to_request(); - let response = test::call_service(&mut srv, request); + // Invalid range header + let request = TestRequest::get() + .uri("/t%65st/tests/test.binary") + .header(header::RANGE, "bytes=10-5") + .to_request(); + let response = test::call_service(&mut srv, request).await; - let contentrange = response - .headers() - .get(header::CONTENT_RANGE) - .unwrap() - .to_str() - .unwrap(); + let contentrange = response + .headers() + .get(header::CONTENT_RANGE) + .unwrap() + .to_str() + .unwrap(); - assert_eq!(contentrange, "bytes */100"); + assert_eq!(contentrange, "bytes */100"); + }) } #[test] fn test_named_file_content_length_headers() { - // use actix_web::body::{MessageBody, ResponseBody}; + block_on(async { + // use actix_web::body::{MessageBody, ResponseBody}; - let mut srv = test::init_service( - App::new().service(Files::new("test", ".").index_file("tests/test.binary")), - ); + let mut srv = test::init_service( + App::new() + .service(Files::new("test", ".").index_file("tests/test.binary")), + ) + .await; - // Valid range header - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") - .header(header::RANGE, "bytes=10-20") - .to_request(); - let _response = test::call_service(&mut srv, request); + // Valid range header + let request = TestRequest::get() + .uri("/t%65st/tests/test.binary") + .header(header::RANGE, "bytes=10-20") + .to_request(); + let _response = test::call_service(&mut srv, request).await; - // let contentlength = response - // .headers() - // .get(header::CONTENT_LENGTH) - // .unwrap() - // .to_str() - // .unwrap(); - // assert_eq!(contentlength, "11"); + // let contentlength = response + // .headers() + // .get(header::CONTENT_LENGTH) + // .unwrap() + // .to_str() + // .unwrap(); + // assert_eq!(contentlength, "11"); - // Invalid range header - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") - .header(header::RANGE, "bytes=10-8") - .to_request(); - let response = test::call_service(&mut srv, request); - assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE); + // Invalid range header + let request = TestRequest::get() + .uri("/t%65st/tests/test.binary") + .header(header::RANGE, "bytes=10-8") + .to_request(); + let response = test::call_service(&mut srv, request).await; + assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE); - // Without range header - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") - // .no_default_headers() - .to_request(); - let _response = test::call_service(&mut srv, request); + // Without range header + let request = TestRequest::get() + .uri("/t%65st/tests/test.binary") + // .no_default_headers() + .to_request(); + let _response = test::call_service(&mut srv, request).await; - // let contentlength = response - // .headers() - // .get(header::CONTENT_LENGTH) - // .unwrap() - // .to_str() - // .unwrap(); - // assert_eq!(contentlength, "100"); + // let contentlength = response + // .headers() + // .get(header::CONTENT_LENGTH) + // .unwrap() + // .to_str() + // .unwrap(); + // assert_eq!(contentlength, "100"); - // chunked - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") - .to_request(); - let mut response = test::call_service(&mut srv, request); + // chunked + let request = TestRequest::get() + .uri("/t%65st/tests/test.binary") + .to_request(); + let response = test::call_service(&mut srv, request).await; - // with enabled compression - // { - // let te = response - // .headers() - // .get(header::TRANSFER_ENCODING) - // .unwrap() - // .to_str() - // .unwrap(); - // assert_eq!(te, "chunked"); - // } + // with enabled compression + // { + // let te = response + // .headers() + // .get(header::TRANSFER_ENCODING) + // .unwrap() + // .to_str() + // .unwrap(); + // assert_eq!(te, "chunked"); + // } - let bytes = - test::block_on(response.take_body().fold(BytesMut::new(), |mut b, c| { - b.extend(c); - Ok::<_, Error>(b) - })) - .unwrap(); - let data = Bytes::from(fs::read("tests/test.binary").unwrap()); - assert_eq!(bytes.freeze(), data); + let bytes = test::read_body(response).await; + let data = Bytes::from(fs::read("tests/test.binary").unwrap()); + assert_eq!(bytes, data); + }) } #[test] fn test_head_content_length_headers() { - let mut srv = test::init_service( - App::new().service(Files::new("test", ".").index_file("tests/test.binary")), - ); + block_on(async { + let mut srv = test::init_service( + App::new() + .service(Files::new("test", ".").index_file("tests/test.binary")), + ) + .await; - // Valid range header - let request = TestRequest::default() - .method(Method::HEAD) - .uri("/t%65st/tests/test.binary") - .to_request(); - let _response = test::call_service(&mut srv, request); + // Valid range header + let request = TestRequest::default() + .method(Method::HEAD) + .uri("/t%65st/tests/test.binary") + .to_request(); + let _response = test::call_service(&mut srv, request).await; - // TODO: fix check - // let contentlength = response - // .headers() - // .get(header::CONTENT_LENGTH) - // .unwrap() - // .to_str() - // .unwrap(); - // assert_eq!(contentlength, "100"); + // TODO: fix check + // let contentlength = response + // .headers() + // .get(header::CONTENT_LENGTH) + // .unwrap() + // .to_str() + // .unwrap(); + // assert_eq!(contentlength, "100"); + }) } #[test] fn test_static_files_with_spaces() { - let mut srv = test::init_service( - App::new().service(Files::new("/", ".").index_file("Cargo.toml")), - ); - let request = TestRequest::get() - .uri("/tests/test%20space.binary") - .to_request(); - let mut response = test::call_service(&mut srv, request); - assert_eq!(response.status(), StatusCode::OK); + block_on(async { + let mut srv = test::init_service( + App::new().service(Files::new("/", ".").index_file("Cargo.toml")), + ) + .await; + let request = TestRequest::get() + .uri("/tests/test%20space.binary") + .to_request(); + let response = test::call_service(&mut srv, request).await; + assert_eq!(response.status(), StatusCode::OK); - let bytes = - test::block_on(response.take_body().fold(BytesMut::new(), |mut b, c| { - b.extend(c); - Ok::<_, Error>(b) - })) - .unwrap(); - - let data = Bytes::from(fs::read("tests/test space.binary").unwrap()); - assert_eq!(bytes.freeze(), data); + let bytes = test::read_body(response).await; + let data = Bytes::from(fs::read("tests/test space.binary").unwrap()); + assert_eq!(bytes, data); + }) } #[test] fn test_files_not_allowed() { - let mut srv = test::init_service(App::new().service(Files::new("/", "."))); + block_on(async { + let mut srv = + test::init_service(App::new().service(Files::new("/", "."))).await; - let req = TestRequest::default() - .uri("/Cargo.toml") - .method(Method::POST) - .to_request(); + let req = TestRequest::default() + .uri("/Cargo.toml") + .method(Method::POST) + .to_request(); - let resp = test::call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); + let resp = test::call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); - let mut srv = test::init_service(App::new().service(Files::new("/", "."))); - let req = TestRequest::default() - .method(Method::PUT) - .uri("/Cargo.toml") - .to_request(); - let resp = test::call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); + let mut srv = + test::init_service(App::new().service(Files::new("/", "."))).await; + let req = TestRequest::default() + .method(Method::PUT) + .uri("/Cargo.toml") + .to_request(); + let resp = test::call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); + }) } #[test] fn test_files_guards() { - let mut srv = test::init_service( - App::new().service(Files::new("/", ".").use_guards(guard::Post())), - ); + block_on(async { + let mut srv = test::init_service( + App::new().service(Files::new("/", ".").use_guards(guard::Post())), + ) + .await; - let req = TestRequest::default() - .uri("/Cargo.toml") - .method(Method::POST) - .to_request(); + let req = TestRequest::default() + .uri("/Cargo.toml") + .method(Method::POST) + .to_request(); - let resp = test::call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::OK); + let resp = test::call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + }) } #[test] fn test_named_file_content_encoding() { - let mut srv = test::init_service(App::new().wrap(Compress::default()).service( - web::resource("/").to(|| { - NamedFile::open("Cargo.toml") - .unwrap() - .set_content_encoding(header::ContentEncoding::Identity) - }), - )); + block_on(async { + let mut srv = + test::init_service(App::new().wrap(Compress::default()).service( + web::resource("/").to(|| { + NamedFile::open("Cargo.toml") + .unwrap() + .set_content_encoding(header::ContentEncoding::Identity) + }), + )) + .await; - let request = TestRequest::get() - .uri("/") - .header(header::ACCEPT_ENCODING, "gzip") - .to_request(); - let res = test::call_service(&mut srv, request); - assert_eq!(res.status(), StatusCode::OK); - assert!(!res.headers().contains_key(header::CONTENT_ENCODING)); + let request = TestRequest::get() + .uri("/") + .header(header::ACCEPT_ENCODING, "gzip") + .to_request(); + let res = test::call_service(&mut srv, request).await; + assert_eq!(res.status(), StatusCode::OK); + assert!(!res.headers().contains_key(header::CONTENT_ENCODING)); + }) } #[test] fn test_named_file_content_encoding_gzip() { - let mut srv = test::init_service(App::new().wrap(Compress::default()).service( - web::resource("/").to(|| { - NamedFile::open("Cargo.toml") - .unwrap() - .set_content_encoding(header::ContentEncoding::Gzip) - }), - )); + block_on(async { + let mut srv = + test::init_service(App::new().wrap(Compress::default()).service( + web::resource("/").to(|| { + NamedFile::open("Cargo.toml") + .unwrap() + .set_content_encoding(header::ContentEncoding::Gzip) + }), + )) + .await; - let request = TestRequest::get() - .uri("/") - .header(header::ACCEPT_ENCODING, "gzip") - .to_request(); - let res = test::call_service(&mut srv, request); - assert_eq!(res.status(), StatusCode::OK); - assert_eq!( - res.headers() - .get(header::CONTENT_ENCODING) - .unwrap() - .to_str() - .unwrap(), - "gzip" - ); + let request = TestRequest::get() + .uri("/") + .header(header::ACCEPT_ENCODING, "gzip") + .to_request(); + let res = test::call_service(&mut srv, request).await; + assert_eq!(res.status(), StatusCode::OK); + assert_eq!( + res.headers() + .get(header::CONTENT_ENCODING) + .unwrap() + .to_str() + .unwrap(), + "gzip" + ); + }) } #[test] fn test_named_file_allowed_method() { - let req = TestRequest::default().method(Method::GET).to_http_request(); - let file = NamedFile::open("Cargo.toml").unwrap(); - let resp = file.respond_to(&req).unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + block_on(async { + let req = TestRequest::default().method(Method::GET).to_http_request(); + let file = NamedFile::open("Cargo.toml").unwrap(); + let resp = file.respond_to(&req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + }) } #[test] fn test_static_files() { - let mut srv = test::init_service( - App::new().service(Files::new("/", ".").show_files_listing()), - ); - let req = TestRequest::with_uri("/missing").to_request(); + block_on(async { + let mut srv = test::init_service( + App::new().service(Files::new("/", ".").show_files_listing()), + ) + .await; + let req = TestRequest::with_uri("/missing").to_request(); - let resp = test::call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); + let resp = test::call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::NOT_FOUND); - let mut srv = test::init_service(App::new().service(Files::new("/", "."))); + let mut srv = + test::init_service(App::new().service(Files::new("/", "."))).await; - let req = TestRequest::default().to_request(); - let resp = test::call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); + let req = TestRequest::default().to_request(); + let resp = test::call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::NOT_FOUND); - let mut srv = test::init_service( - App::new().service(Files::new("/", ".").show_files_listing()), - ); - let req = TestRequest::with_uri("/tests").to_request(); - let mut resp = test::call_service(&mut srv, req); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - "text/html; charset=utf-8" - ); + let mut srv = test::init_service( + App::new().service(Files::new("/", ".").show_files_listing()), + ) + .await; + let req = TestRequest::with_uri("/tests").to_request(); + let resp = test::call_service(&mut srv, req).await; + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + "text/html; charset=utf-8" + ); - let bytes = - test::block_on(resp.take_body().fold(BytesMut::new(), |mut b, c| { - b.extend(c); - Ok::<_, Error>(b) - })) - .unwrap(); - assert!(format!("{:?}", bytes).contains("/tests/test.png")); + let bytes = test::read_body(resp).await; + assert!(format!("{:?}", bytes).contains("/tests/test.png")); + }) } #[test] fn test_redirect_to_slash_directory() { - // should not redirect if no index - let mut srv = test::init_service( - App::new().service(Files::new("/", ".").redirect_to_slash_directory()), - ); - let req = TestRequest::with_uri("/tests").to_request(); - let resp = test::call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); + block_on(async { + // should not redirect if no index + let mut srv = test::init_service( + App::new().service(Files::new("/", ".").redirect_to_slash_directory()), + ) + .await; + let req = TestRequest::with_uri("/tests").to_request(); + let resp = test::call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::NOT_FOUND); - // should redirect if index present - let mut srv = test::init_service( - App::new().service( - Files::new("/", ".") - .index_file("test.png") - .redirect_to_slash_directory(), - ), - ); - let req = TestRequest::with_uri("/tests").to_request(); - let resp = test::call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::FOUND); + // should redirect if index present + let mut srv = test::init_service( + App::new().service( + Files::new("/", ".") + .index_file("test.png") + .redirect_to_slash_directory(), + ), + ) + .await; + let req = TestRequest::with_uri("/tests").to_request(); + let resp = test::call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::FOUND); - // should not redirect if the path is wrong - let req = TestRequest::with_uri("/not_existing").to_request(); - let resp = test::call_service(&mut srv, req); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); + // should not redirect if the path is wrong + let req = TestRequest::with_uri("/not_existing").to_request(); + let resp = test::call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + }) } #[test] @@ -1252,26 +1308,21 @@ mod tests { #[test] fn test_default_handler_file_missing() { - let mut st = test::block_on( - Files::new("/", ".") + block_on(async { + let mut st = Files::new("/", ".") .default_handler(|req: ServiceRequest| { - Ok(req.into_response(HttpResponse::Ok().body("default content"))) + ok(req.into_response(HttpResponse::Ok().body("default content"))) }) - .new_service(&()), - ) - .unwrap(); - let req = TestRequest::with_uri("/missing").to_srv_request(); + .new_service(&()) + .await + .unwrap(); + let req = TestRequest::with_uri("/missing").to_srv_request(); - let mut resp = test::call_service(&mut st, req); - assert_eq!(resp.status(), StatusCode::OK); - let bytes = - test::block_on(resp.take_body().fold(BytesMut::new(), |mut b, c| { - b.extend(c); - Ok::<_, Error>(b) - })) - .unwrap(); - - assert_eq!(bytes.freeze(), Bytes::from_static(b"default content")); + let resp = test::call_service(&mut st, req).await; + assert_eq!(resp.status(), StatusCode::OK); + let bytes = test::read_body(resp).await; + assert_eq!(bytes, Bytes::from_static(b"default content")); + }) } // #[test] diff --git a/actix-files/src/named.rs b/actix-files/src/named.rs index 955982caf..0dcbd93b8 100644 --- a/actix-files/src/named.rs +++ b/actix-files/src/named.rs @@ -18,6 +18,7 @@ use actix_web::http::header::{ use actix_web::http::{ContentEncoding, StatusCode}; use actix_web::middleware::BodyEncoding; use actix_web::{Error, HttpMessage, HttpRequest, HttpResponse, Responder}; +use futures::future::{ready, Ready}; use crate::range::HttpRange; use crate::ChunkedReadFile; @@ -255,62 +256,8 @@ impl NamedFile { pub(crate) fn last_modified(&self) -> Option { self.modified.map(|mtime| mtime.into()) } -} -impl Deref for NamedFile { - type Target = File; - - fn deref(&self) -> &File { - &self.file - } -} - -impl DerefMut for NamedFile { - fn deref_mut(&mut self) -> &mut File { - &mut self.file - } -} - -/// Returns true if `req` has no `If-Match` header or one which matches `etag`. -fn any_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool { - match req.get_header::() { - None | Some(header::IfMatch::Any) => true, - Some(header::IfMatch::Items(ref items)) => { - if let Some(some_etag) = etag { - for item in items { - if item.strong_eq(some_etag) { - return true; - } - } - } - false - } - } -} - -/// Returns true if `req` doesn't have an `If-None-Match` header matching `req`. -fn none_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool { - match req.get_header::() { - Some(header::IfNoneMatch::Any) => false, - Some(header::IfNoneMatch::Items(ref items)) => { - if let Some(some_etag) = etag { - for item in items { - if item.weak_eq(some_etag) { - return false; - } - } - } - true - } - None => true, - } -} - -impl Responder for NamedFile { - type Error = Error; - type Future = Result; - - fn respond_to(self, req: &HttpRequest) -> Self::Future { + pub fn into_response(self, req: &HttpRequest) -> Result { if self.status_code != StatusCode::OK { let mut resp = HttpResponse::build(self.status_code); resp.set(header::ContentType(self.content_type.clone())) @@ -442,8 +389,67 @@ impl Responder for NamedFile { counter: 0, }; if offset != 0 || length != self.md.len() { - return Ok(resp.status(StatusCode::PARTIAL_CONTENT).streaming(reader)); - }; - Ok(resp.body(SizedStream::new(length, reader))) + Ok(resp.status(StatusCode::PARTIAL_CONTENT).streaming(reader)) + } else { + Ok(resp.body(SizedStream::new(length, reader))) + } + } +} + +impl Deref for NamedFile { + type Target = File; + + fn deref(&self) -> &File { + &self.file + } +} + +impl DerefMut for NamedFile { + fn deref_mut(&mut self) -> &mut File { + &mut self.file + } +} + +/// Returns true if `req` has no `If-Match` header or one which matches `etag`. +fn any_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool { + match req.get_header::() { + None | Some(header::IfMatch::Any) => true, + Some(header::IfMatch::Items(ref items)) => { + if let Some(some_etag) = etag { + for item in items { + if item.strong_eq(some_etag) { + return true; + } + } + } + false + } + } +} + +/// Returns true if `req` doesn't have an `If-None-Match` header matching `req`. +fn none_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool { + match req.get_header::() { + Some(header::IfNoneMatch::Any) => false, + Some(header::IfNoneMatch::Items(ref items)) => { + if let Some(some_etag) = etag { + for item in items { + if item.weak_eq(some_etag) { + return false; + } + } + } + true + } + None => true, + } +} + +impl Responder for NamedFile { + type Error = Error; + type Future = Ready>; + + fn respond_to(self, req: &HttpRequest) -> Self::Future { + ready(self.into_response(req)) } } diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index 2a6833995..a725789a2 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -9,7 +9,7 @@ use std::{fmt, io, result}; use actix_utils::timeout::TimeoutError; use bytes::BytesMut; use derive_more::{Display, From}; -use futures::channel::oneshot::Canceled; +pub use futures::channel::oneshot::Canceled; use http::uri::InvalidUri; use http::{header, Error as HttpError, StatusCode}; use httparse; From 95e2a0ef2e5617264176e67fe9b5769717702a56 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 21 Nov 2019 12:17:01 +0600 Subject: [PATCH 057/176] migrate actix-framed --- Cargo.toml | 2 +- actix-framed/Cargo.toml | 21 +-- actix-framed/src/app.rs | 63 ++++----- actix-framed/src/helpers.rs | 48 ++++--- actix-framed/src/route.rs | 68 +++++----- actix-framed/src/service.rs | 67 +++++----- actix-framed/src/test.rs | 7 +- actix-framed/tests/test_server.rs | 204 +++++++++++++++++------------- 8 files changed, 263 insertions(+), 217 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 32918ee43..3efc058df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ members = [ "actix-http", "actix-cors", "actix-files", - #"actix-framed", + "actix-framed", #"actix-session", "actix-identity", #"actix-multipart", diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml index 232c6ae66..4783daefd 100644 --- a/actix-framed/Cargo.toml +++ b/actix-framed/Cargo.toml @@ -20,19 +20,20 @@ name = "actix_framed" path = "src/lib.rs" [dependencies] -actix-codec = "0.1.2" -actix-service = "0.4.2" +actix-codec = "0.2.0-alpha.1" +actix-service = "1.0.0-alpha.1" actix-router = "0.1.2" -actix-rt = "0.2.2" -actix-http = "0.2.11" -actix-server-config = "0.1.1" +actix-rt = "1.0.0-alpha.1" +actix-http = "0.3.0-alpha.1" +actix-server-config = "0.3.0-alpha.1" bytes = "0.4" -futures = "0.1.25" +futures = "0.3.1" +pin-project = "0.4.6" log = "0.4" [dev-dependencies] -actix-server = { version = "0.6.0", features=["openssl"] } -actix-connect = { version = "0.2.0", features=["openssl"] } -actix-http-test = { version = "0.1.0", features=["openssl"] } -actix-utils = "0.4.0" +actix-server = { version = "0.8.0-alpha.1", features=["openssl"] } +actix-connect = { version = "0.3.0-alpha.1", features=["openssl"] } +actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } +actix-utils = "0.5.0-alpha.1" diff --git a/actix-framed/src/app.rs b/actix-framed/src/app.rs index ad5b1ec26..f3e746e9f 100644 --- a/actix-framed/src/app.rs +++ b/actix-framed/src/app.rs @@ -1,21 +1,24 @@ +use std::future::Future; +use std::pin::Pin; use std::rc::Rc; +use std::task::{Context, Poll}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_http::h1::{Codec, SendResponse}; use actix_http::{Error, Request, Response}; use actix_router::{Path, Router, Url}; use actix_server_config::ServerConfig; -use actix_service::{IntoNewService, NewService, Service}; -use futures::{Async, Future, Poll}; +use actix_service::{IntoServiceFactory, Service, ServiceFactory}; +use futures::future::{ok, FutureExt, LocalBoxFuture}; use crate::helpers::{BoxedHttpNewService, BoxedHttpService, HttpNewService}; use crate::request::FramedRequest; use crate::state::State; -type BoxedResponse = Box>; +type BoxedResponse = LocalBoxFuture<'static, Result<(), Error>>; pub trait HttpServiceFactory { - type Factory: NewService; + type Factory: ServiceFactory; fn path(&self) -> &str; @@ -48,19 +51,19 @@ impl FramedApp { pub fn service(mut self, factory: U) -> Self where U: HttpServiceFactory, - U::Factory: NewService< + U::Factory: ServiceFactory< Config = (), Request = FramedRequest, Response = (), Error = Error, InitError = (), > + 'static, - ::Future: 'static, - ::Service: Service< + ::Future: 'static, + ::Service: Service< Request = FramedRequest, Response = (), Error = Error, - Future = Box>, + Future = LocalBoxFuture<'static, Result<(), Error>>, >, { let path = factory.path().to_string(); @@ -70,12 +73,12 @@ impl FramedApp { } } -impl IntoNewService> for FramedApp +impl IntoServiceFactory> for FramedApp where - T: AsyncRead + AsyncWrite + 'static, + T: AsyncRead + AsyncWrite + Unpin + 'static, S: 'static, { - fn into_new_service(self) -> FramedAppFactory { + fn into_factory(self) -> FramedAppFactory { FramedAppFactory { state: self.state, services: Rc::new(self.services), @@ -89,9 +92,9 @@ pub struct FramedAppFactory { services: Rc>)>>, } -impl NewService for FramedAppFactory +impl ServiceFactory for FramedAppFactory where - T: AsyncRead + AsyncWrite + 'static, + T: AsyncRead + AsyncWrite + Unpin + 'static, S: 'static, { type Config = ServerConfig; @@ -128,28 +131,30 @@ pub struct CreateService { enum CreateServiceItem { Future( Option, - Box>, Error = ()>>, + LocalBoxFuture<'static, Result>, ()>>, ), Service(String, BoxedHttpService>), } impl Future for CreateService where - T: AsyncRead + AsyncWrite, + T: AsyncRead + AsyncWrite + Unpin, { - type Item = FramedAppService; - type Error = (); + type Output = Result, ()>; - fn poll(&mut self) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { let mut done = true; // poll http services for item in &mut self.fut { let res = match item { CreateServiceItem::Future(ref mut path, ref mut fut) => { - match fut.poll()? { - Async::Ready(service) => Some((path.take().unwrap(), service)), - Async::NotReady => { + match Pin::new(fut).poll(cx) { + Poll::Ready(Ok(service)) => { + Some((path.take().unwrap(), service)) + } + Poll::Ready(Err(e)) => return Poll::Ready(Err(e)), + Poll::Pending => { done = false; None } @@ -176,12 +181,12 @@ where } router }); - Ok(Async::Ready(FramedAppService { + Poll::Ready(Ok(FramedAppService { router: router.finish(), state: self.state.clone(), })) } else { - Ok(Async::NotReady) + Poll::Pending } } } @@ -193,15 +198,15 @@ pub struct FramedAppService { impl Service for FramedAppService where - T: AsyncRead + AsyncWrite, + T: AsyncRead + AsyncWrite + Unpin, { type Request = (Request, Framed); type Response = (); type Error = Error; type Future = BoxedResponse; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, (req, framed): (Request, Framed)) -> Self::Future { @@ -210,8 +215,8 @@ where if let Some((srv, _info)) = self.router.recognize_mut(&mut path) { return srv.call(FramedRequest::new(req, framed, path, self.state.clone())); } - Box::new( - SendResponse::new(framed, Response::NotFound().finish()).then(|_| Ok(())), - ) + SendResponse::new(framed, Response::NotFound().finish()) + .then(|_| ok(())) + .boxed_local() } } diff --git a/actix-framed/src/helpers.rs b/actix-framed/src/helpers.rs index b343301f3..b654f9cd7 100644 --- a/actix-framed/src/helpers.rs +++ b/actix-framed/src/helpers.rs @@ -1,36 +1,38 @@ +use std::task::{Context, Poll}; + use actix_http::Error; -use actix_service::{NewService, Service}; -use futures::{Future, Poll}; +use actix_service::{Service, ServiceFactory}; +use futures::future::{FutureExt, LocalBoxFuture}; pub(crate) type BoxedHttpService = Box< dyn Service< Request = Req, Response = (), Error = Error, - Future = Box>, + Future = LocalBoxFuture<'static, Result<(), Error>>, >, >; pub(crate) type BoxedHttpNewService = Box< - dyn NewService< + dyn ServiceFactory< Config = (), Request = Req, Response = (), Error = Error, InitError = (), Service = BoxedHttpService, - Future = Box, Error = ()>>, + Future = LocalBoxFuture<'static, Result, ()>>, >, >; -pub(crate) struct HttpNewService(T); +pub(crate) struct HttpNewService(T); impl HttpNewService where - T: NewService, + T: ServiceFactory, T::Response: 'static, T::Future: 'static, - T::Service: Service>> + 'static, + T::Service: Service>> + 'static, ::Future: 'static, { pub fn new(service: T) -> Self { @@ -38,12 +40,12 @@ where } } -impl NewService for HttpNewService +impl ServiceFactory for HttpNewService where - T: NewService, + T: ServiceFactory, T::Request: 'static, T::Future: 'static, - T::Service: Service>> + 'static, + T::Service: Service>> + 'static, ::Future: 'static, { type Config = (); @@ -52,13 +54,19 @@ where type Error = Error; type InitError = (); type Service = BoxedHttpService; - type Future = Box>; + type Future = LocalBoxFuture<'static, Result>; fn new_service(&self, _: &()) -> Self::Future { - Box::new(self.0.new_service(&()).map_err(|_| ()).and_then(|service| { - let service: BoxedHttpService<_> = Box::new(HttpServiceWrapper { service }); - Ok(service) - })) + let fut = self.0.new_service(&()); + + async move { + fut.await.map_err(|_| ()).map(|service| { + let service: BoxedHttpService<_> = + Box::new(HttpServiceWrapper { service }); + service + }) + } + .boxed_local() } } @@ -70,7 +78,7 @@ impl Service for HttpServiceWrapper where T: Service< Response = (), - Future = Box>, + Future = LocalBoxFuture<'static, Result<(), Error>>, Error = Error, >, T::Request: 'static, @@ -78,10 +86,10 @@ where type Request = T::Request; type Response = (); type Error = Error; - type Future = Box>; + type Future = LocalBoxFuture<'static, Result<(), Error>>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.service.poll_ready() + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.service.poll_ready(cx) } fn call(&mut self, req: Self::Request) -> Self::Future { diff --git a/actix-framed/src/route.rs b/actix-framed/src/route.rs index 5beb24165..783039684 100644 --- a/actix-framed/src/route.rs +++ b/actix-framed/src/route.rs @@ -1,11 +1,12 @@ use std::fmt; +use std::future::Future; use std::marker::PhantomData; +use std::task::{Context, Poll}; use actix_codec::{AsyncRead, AsyncWrite}; use actix_http::{http::Method, Error}; -use actix_service::{NewService, Service}; -use futures::future::{ok, FutureResult}; -use futures::{Async, Future, IntoFuture, Poll}; +use actix_service::{Service, ServiceFactory}; +use futures::future::{ok, FutureExt, LocalBoxFuture, Ready}; use log::error; use crate::app::HttpServiceFactory; @@ -15,11 +16,11 @@ use crate::request::FramedRequest; /// /// Route uses builder-like pattern for configuration. /// If handler is not explicitly set, default *404 Not Found* handler is used. -pub struct FramedRoute { +pub struct FramedRoute { handler: F, pattern: String, methods: Vec, - state: PhantomData<(Io, S, R)>, + state: PhantomData<(Io, S, R, E)>, } impl FramedRoute { @@ -53,12 +54,12 @@ impl FramedRoute { self } - pub fn to(self, handler: F) -> FramedRoute + pub fn to(self, handler: F) -> FramedRoute where F: FnMut(FramedRequest) -> R, - R: IntoFuture, - R::Future: 'static, - R::Error: fmt::Debug, + R: Future> + 'static, + + E: fmt::Debug, { FramedRoute { handler, @@ -69,15 +70,14 @@ impl FramedRoute { } } -impl HttpServiceFactory for FramedRoute +impl HttpServiceFactory for FramedRoute where Io: AsyncRead + AsyncWrite + 'static, F: FnMut(FramedRequest) -> R + Clone, - R: IntoFuture, - R::Future: 'static, - R::Error: fmt::Display, + R: Future> + 'static, + E: fmt::Display, { - type Factory = FramedRouteFactory; + type Factory = FramedRouteFactory; fn path(&self) -> &str { &self.pattern @@ -92,27 +92,26 @@ where } } -pub struct FramedRouteFactory { +pub struct FramedRouteFactory { handler: F, methods: Vec, - _t: PhantomData<(Io, S, R)>, + _t: PhantomData<(Io, S, R, E)>, } -impl NewService for FramedRouteFactory +impl ServiceFactory for FramedRouteFactory where Io: AsyncRead + AsyncWrite + 'static, F: FnMut(FramedRequest) -> R + Clone, - R: IntoFuture, - R::Future: 'static, - R::Error: fmt::Display, + R: Future> + 'static, + E: fmt::Display, { type Config = (); type Request = FramedRequest; type Response = (); type Error = Error; type InitError = (); - type Service = FramedRouteService; - type Future = FutureResult; + type Service = FramedRouteService; + type Future = Ready>; fn new_service(&self, _: &()) -> Self::Future { ok(FramedRouteService { @@ -123,35 +122,38 @@ where } } -pub struct FramedRouteService { +pub struct FramedRouteService { handler: F, methods: Vec, - _t: PhantomData<(Io, S, R)>, + _t: PhantomData<(Io, S, R, E)>, } -impl Service for FramedRouteService +impl Service for FramedRouteService where Io: AsyncRead + AsyncWrite + 'static, F: FnMut(FramedRequest) -> R + Clone, - R: IntoFuture, - R::Future: 'static, - R::Error: fmt::Display, + R: Future> + 'static, + E: fmt::Display, { type Request = FramedRequest; type Response = (); type Error = Error; - type Future = Box>; + type Future = LocalBoxFuture<'static, Result<(), Error>>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, req: FramedRequest) -> Self::Future { - Box::new((self.handler)(req).into_future().then(|res| { + let fut = (self.handler)(req); + + async move { + let res = fut.await; if let Err(e) = res { error!("Error in request handler: {}", e); } Ok(()) - })) + } + .boxed_local() } } diff --git a/actix-framed/src/service.rs b/actix-framed/src/service.rs index fbbc9fbef..ed3a75ff5 100644 --- a/actix-framed/src/service.rs +++ b/actix-framed/src/service.rs @@ -1,4 +1,6 @@ use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_http::body::BodySize; @@ -6,9 +8,9 @@ use actix_http::error::ResponseError; use actix_http::h1::{Codec, Message}; use actix_http::ws::{verify_handshake, HandshakeError}; use actix_http::{Request, Response}; -use actix_service::{NewService, Service}; -use futures::future::{ok, Either, FutureResult}; -use futures::{Async, Future, IntoFuture, Poll, Sink}; +use actix_service::{Service, ServiceFactory}; +use futures::future::{err, ok, Either, Ready}; +use futures::Future; /// Service that verifies incoming request if it is valid websocket /// upgrade request. In case of error returns `HandshakeError` @@ -22,14 +24,14 @@ impl Default for VerifyWebSockets { } } -impl NewService for VerifyWebSockets { +impl ServiceFactory for VerifyWebSockets { type Config = C; type Request = (Request, Framed); type Response = (Request, Framed); type Error = (HandshakeError, Framed); type InitError = (); type Service = VerifyWebSockets; - type Future = FutureResult; + type Future = Ready>; fn new_service(&self, _: &C) -> Self::Future { ok(VerifyWebSockets { _t: PhantomData }) @@ -40,16 +42,16 @@ impl Service for VerifyWebSockets { type Request = (Request, Framed); type Response = (Request, Framed); type Error = (HandshakeError, Framed); - type Future = FutureResult; + type Future = Ready>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, (req, framed): (Request, Framed)) -> Self::Future { match verify_handshake(req.head()) { - Err(e) => Err((e, framed)).into_future(), - Ok(_) => Ok((req, framed)).into_future(), + Err(e) => err((e, framed)), + Ok(_) => ok((req, framed)), } } } @@ -67,9 +69,9 @@ where } } -impl NewService for SendError +impl ServiceFactory for SendError where - T: AsyncRead + AsyncWrite + 'static, + T: AsyncRead + AsyncWrite + Unpin + 'static, R: 'static, E: ResponseError + 'static, { @@ -79,7 +81,7 @@ where type Error = (E, Framed); type InitError = (); type Service = SendError; - type Future = FutureResult; + type Future = Ready>; fn new_service(&self, _: &C) -> Self::Future { ok(SendError(PhantomData)) @@ -88,25 +90,25 @@ where impl Service for SendError where - T: AsyncRead + AsyncWrite + 'static, + T: AsyncRead + AsyncWrite + Unpin + 'static, R: 'static, E: ResponseError + 'static, { type Request = Result)>; type Response = R; type Error = (E, Framed); - type Future = Either)>, SendErrorFut>; + type Future = Either)>>, SendErrorFut>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, req: Result)>) -> Self::Future { match req { - Ok(r) => Either::A(ok(r)), + Ok(r) => Either::Left(ok(r)), Err((e, framed)) => { let res = e.error_response().drop_body(); - Either::B(SendErrorFut { + Either::Right(SendErrorFut { framed: Some(framed), res: Some((res, BodySize::Empty).into()), err: Some(e), @@ -117,6 +119,7 @@ where } } +#[pin_project::pin_project] pub struct SendErrorFut { res: Option, BodySize)>>, framed: Option>, @@ -127,23 +130,27 @@ pub struct SendErrorFut { impl Future for SendErrorFut where E: ResponseError, - T: AsyncRead + AsyncWrite, + T: AsyncRead + AsyncWrite + Unpin, { - type Item = R; - type Error = (E, Framed); + type Output = Result)>; - fn poll(&mut self) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { if let Some(res) = self.res.take() { - if self.framed.as_mut().unwrap().force_send(res).is_err() { - return Err((self.err.take().unwrap(), self.framed.take().unwrap())); + if self.framed.as_mut().unwrap().write(res).is_err() { + return Poll::Ready(Err(( + self.err.take().unwrap(), + self.framed.take().unwrap(), + ))); } } - match self.framed.as_mut().unwrap().poll_complete() { - Ok(Async::Ready(_)) => { - Err((self.err.take().unwrap(), self.framed.take().unwrap())) + match self.framed.as_mut().unwrap().flush(cx) { + Poll::Ready(Ok(_)) => { + Poll::Ready(Err((self.err.take().unwrap(), self.framed.take().unwrap()))) } - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(_) => Err((self.err.take().unwrap(), self.framed.take().unwrap())), + Poll::Ready(Err(_)) => { + Poll::Ready(Err((self.err.take().unwrap(), self.framed.take().unwrap()))) + } + Poll::Pending => Poll::Pending, } } } diff --git a/actix-framed/src/test.rs b/actix-framed/src/test.rs index 3bc828df4..b90a493dc 100644 --- a/actix-framed/src/test.rs +++ b/actix-framed/src/test.rs @@ -1,4 +1,6 @@ //! Various helpers for Actix applications to use during testing. +use std::future::Future; + use actix_codec::Framed; use actix_http::h1::Codec; use actix_http::http::header::{Header, HeaderName, IntoHeaderValue}; @@ -6,7 +8,6 @@ use actix_http::http::{HttpTryFrom, Method, Uri, Version}; use actix_http::test::{TestBuffer, TestRequest as HttpTestRequest}; use actix_router::{Path, Url}; use actix_rt::Runtime; -use futures::IntoFuture; use crate::{FramedRequest, State}; @@ -121,10 +122,10 @@ impl TestRequest { pub fn run(self, f: F) -> Result where F: FnOnce(FramedRequest) -> R, - R: IntoFuture, + R: Future>, { let mut rt = Runtime::new().unwrap(); - rt.block_on(f(self.finish()).into_future()) + rt.block_on(f(self.finish())) } } diff --git a/actix-framed/tests/test_server.rs b/actix-framed/tests/test_server.rs index 00f6a97d8..6e4bb6ada 100644 --- a/actix-framed/tests/test_server.rs +++ b/actix-framed/tests/test_server.rs @@ -1,30 +1,31 @@ use actix_codec::{AsyncRead, AsyncWrite}; use actix_http::{body, http::StatusCode, ws, Error, HttpService, Response}; -use actix_http_test::TestServer; -use actix_service::{IntoNewService, NewService}; +use actix_http_test::{block_on, TestServer}; +use actix_service::{pipeline_factory, IntoServiceFactory, ServiceFactory}; use actix_utils::framed::FramedTransport; use bytes::{Bytes, BytesMut}; -use futures::future::{self, ok}; -use futures::{Future, Sink, Stream}; +use futures::{future, SinkExt, StreamExt}; use actix_framed::{FramedApp, FramedRequest, FramedRoute, SendError, VerifyWebSockets}; -fn ws_service( +async fn ws_service( req: FramedRequest, -) -> impl Future { - let (req, framed, _) = req.into_parts(); +) -> Result<(), Error> { + let (req, mut framed, _) = req.into_parts(); let res = ws::handshake(req.head()).unwrap().message_body(()); framed .send((res, body::BodySize::None).into()) - .map_err(|_| panic!()) - .and_then(|framed| { - FramedTransport::new(framed.into_framed(ws::Codec::new()), service) - .map_err(|_| panic!()) - }) + .await + .unwrap(); + FramedTransport::new(framed.into_framed(ws::Codec::new()), service) + .await + .unwrap(); + + Ok(()) } -fn service(msg: ws::Frame) -> impl Future { +async fn service(msg: ws::Frame) -> Result { let msg = match msg { ws::Frame::Ping(msg) => ws::Message::Pong(msg), ws::Frame::Text(text) => { @@ -34,108 +35,129 @@ fn service(msg: ws::Frame) -> impl Future { ws::Frame::Close(reason) => ws::Message::Close(reason), _ => panic!(), }; - ok(msg) + Ok(msg) } #[test] fn test_simple() { - let mut srv = TestServer::new(|| { - HttpService::build() - .upgrade( - FramedApp::new().service(FramedRoute::get("/index.html").to(ws_service)), - ) - .finish(|_| future::ok::<_, Error>(Response::NotFound())) - }); + block_on(async { + let mut srv = TestServer::start(|| { + HttpService::build() + .upgrade( + FramedApp::new() + .service(FramedRoute::get("/index.html").to(ws_service)), + ) + .finish(|_| future::ok::<_, Error>(Response::NotFound())) + }); - assert!(srv.ws_at("/test").is_err()); + assert!(srv.ws_at("/test").await.is_err()); - // client service - let framed = srv.ws_at("/index.html").unwrap(); - let framed = srv - .block_on(framed.send(ws::Message::Text("text".to_string()))) - .unwrap(); - let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!(item, Some(ws::Frame::Text(Some(BytesMut::from("text"))))); + // client service + let mut framed = srv.ws_at("/index.html").await.unwrap(); + framed + .send(ws::Message::Text("text".to_string())) + .await + .unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Text(Some(BytesMut::from("text"))) + ); - let framed = srv - .block_on(framed.send(ws::Message::Binary("text".into()))) - .unwrap(); - let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!( - item, - Some(ws::Frame::Binary(Some(Bytes::from_static(b"text").into()))) - ); + framed + .send(ws::Message::Binary("text".into())) + .await + .unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Binary(Some(Bytes::from_static(b"text").into())) + ); - let framed = srv - .block_on(framed.send(ws::Message::Ping("text".into()))) - .unwrap(); - let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!(item, Some(ws::Frame::Pong("text".to_string().into()))); + framed.send(ws::Message::Ping("text".into())).await.unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Pong("text".to_string().into()) + ); - let framed = srv - .block_on(framed.send(ws::Message::Close(Some(ws::CloseCode::Normal.into())))) - .unwrap(); + framed + .send(ws::Message::Close(Some(ws::CloseCode::Normal.into()))) + .await + .unwrap(); - let (item, _framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!( - item, - Some(ws::Frame::Close(Some(ws::CloseCode::Normal.into()))) - ); + let (item, _) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Close(Some(ws::CloseCode::Normal.into())) + ); + }) } #[test] fn test_service() { - let mut srv = TestServer::new(|| { - actix_http::h1::OneRequest::new().map_err(|_| ()).and_then( - VerifyWebSockets::default() - .then(SendError::default()) - .map_err(|_| ()) + block_on(async { + let mut srv = TestServer::start(|| { + pipeline_factory(actix_http::h1::OneRequest::new().map_err(|_| ())).and_then( + pipeline_factory( + pipeline_factory(VerifyWebSockets::default()) + .then(SendError::default()) + .map_err(|_| ()), + ) .and_then( FramedApp::new() .service(FramedRoute::get("/index.html").to(ws_service)) - .into_new_service() + .into_factory() .map_err(|_| ()), ), - ) - }); + ) + }); - // non ws request - let res = srv.block_on(srv.get("/index.html").send()).unwrap(); - assert_eq!(res.status(), StatusCode::BAD_REQUEST); + // non ws request + let res = srv.get("/index.html").send().await.unwrap(); + assert_eq!(res.status(), StatusCode::BAD_REQUEST); - // not found - assert!(srv.ws_at("/test").is_err()); + // not found + assert!(srv.ws_at("/test").await.is_err()); - // client service - let framed = srv.ws_at("/index.html").unwrap(); - let framed = srv - .block_on(framed.send(ws::Message::Text("text".to_string()))) - .unwrap(); - let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!(item, Some(ws::Frame::Text(Some(BytesMut::from("text"))))); + // client service + let mut framed = srv.ws_at("/index.html").await.unwrap(); + framed + .send(ws::Message::Text("text".to_string())) + .await + .unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Text(Some(BytesMut::from("text"))) + ); - let framed = srv - .block_on(framed.send(ws::Message::Binary("text".into()))) - .unwrap(); - let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!( - item, - Some(ws::Frame::Binary(Some(Bytes::from_static(b"text").into()))) - ); + framed + .send(ws::Message::Binary("text".into())) + .await + .unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Binary(Some(Bytes::from_static(b"text").into())) + ); - let framed = srv - .block_on(framed.send(ws::Message::Ping("text".into()))) - .unwrap(); - let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!(item, Some(ws::Frame::Pong("text".to_string().into()))); + framed.send(ws::Message::Ping("text".into())).await.unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Pong("text".to_string().into()) + ); - let framed = srv - .block_on(framed.send(ws::Message::Close(Some(ws::CloseCode::Normal.into())))) - .unwrap(); + framed + .send(ws::Message::Close(Some(ws::CloseCode::Normal.into()))) + .await + .unwrap(); - let (item, _framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!( - item, - Some(ws::Frame::Close(Some(ws::CloseCode::Normal.into()))) - ); + let (item, _) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Close(Some(ws::CloseCode::Normal.into())) + ); + }) } From 0de101bc4d05068948a042b35ee18e6f04a61a17 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 21 Nov 2019 13:01:07 +0600 Subject: [PATCH 058/176] update actix-web-codegen tests --- Cargo.toml | 2 +- actix-web-codegen/Cargo.toml | 10 +- actix-web-codegen/src/lib.rs | 4 +- actix-web-codegen/tests/test_macro.rs | 138 ++++++++++++++------------ 4 files changed, 80 insertions(+), 74 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3efc058df..1af2836fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ members = [ #"actix-session", "actix-identity", #"actix-multipart", - #"actix-web-actors", + "actix-web-actors", "actix-web-codegen", "test-server", ] diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 981e00323..f363cfbaa 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web-codegen" -version = "0.1.3" +version = "0.2.0-alpha.1" description = "Actix web proc macros" readme = "README.md" authors = ["Nikolay Kim "] @@ -17,7 +17,7 @@ syn = { version = "1", features = ["full", "parsing"] } proc-macro2 = "1" [dev-dependencies] -actix-web = { version = "1.0.0" } -actix-http = { version = "0.2.4", features=["ssl"] } -actix-http-test = { version = "0.2.0", features=["ssl"] } -futures = { version = "0.1" } +actix-web = { version = "2.0.0-alph.a" } +actix-http = { version = "0.3.0-alpha.1", features=["openssl"] } +actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } +futures = { version = "0.3.1" } diff --git a/actix-web-codegen/src/lib.rs b/actix-web-codegen/src/lib.rs index 88fa4dfda..0a727ed69 100644 --- a/actix-web-codegen/src/lib.rs +++ b/actix-web-codegen/src/lib.rs @@ -35,8 +35,8 @@ //! use futures::{future, Future}; //! //! #[get("/test")] -//! fn async_test() -> impl Future { -//! future::ok(HttpResponse::Ok().finish()) +//! async fn async_test() -> Result { +//! Ok(HttpResponse::Ok().finish()) //! } //! ``` diff --git a/actix-web-codegen/tests/test_macro.rs b/actix-web-codegen/tests/test_macro.rs index 8ecc81dc1..953de9cd5 100644 --- a/actix-web-codegen/tests/test_macro.rs +++ b/actix-web-codegen/tests/test_macro.rs @@ -1,5 +1,5 @@ use actix_http::HttpService; -use actix_http_test::TestServer; +use actix_http_test::{block_on, TestServer}; use actix_web::{http, web::Path, App, HttpResponse, Responder}; use actix_web_codegen::{connect, delete, get, head, options, patch, post, put, trace}; use futures::{future, Future}; @@ -45,12 +45,12 @@ fn trace_test() -> impl Responder { } #[get("/test")] -fn auto_async() -> impl Future { +fn auto_async() -> impl Future> { future::ok(HttpResponse::Ok().finish()) } #[get("/test")] -fn auto_sync() -> impl Future { +fn auto_sync() -> impl Future> { future::ok(HttpResponse::Ok().finish()) } @@ -71,87 +71,93 @@ fn get_param_test(_: Path) -> impl Responder { #[test] fn test_params() { - let mut srv = TestServer::new(|| { - HttpService::new( - App::new() - .service(get_param_test) - .service(put_param_test) - .service(delete_param_test), - ) - }); + block_on(async { + let srv = TestServer::start(|| { + HttpService::new( + App::new() + .service(get_param_test) + .service(put_param_test) + .service(delete_param_test), + ) + }); - let request = srv.request(http::Method::GET, srv.url("/test/it")); - let response = srv.block_on(request.send()).unwrap(); - assert_eq!(response.status(), http::StatusCode::OK); + let request = srv.request(http::Method::GET, srv.url("/test/it")); + let response = request.send().await.unwrap(); + assert_eq!(response.status(), http::StatusCode::OK); - let request = srv.request(http::Method::PUT, srv.url("/test/it")); - let response = srv.block_on(request.send()).unwrap(); - assert_eq!(response.status(), http::StatusCode::CREATED); + let request = srv.request(http::Method::PUT, srv.url("/test/it")); + let response = request.send().await.unwrap(); + assert_eq!(response.status(), http::StatusCode::CREATED); - let request = srv.request(http::Method::DELETE, srv.url("/test/it")); - let response = srv.block_on(request.send()).unwrap(); - assert_eq!(response.status(), http::StatusCode::NO_CONTENT); + let request = srv.request(http::Method::DELETE, srv.url("/test/it")); + let response = request.send().await.unwrap(); + assert_eq!(response.status(), http::StatusCode::NO_CONTENT); + }) } #[test] fn test_body() { - let mut srv = TestServer::new(|| { - HttpService::new( - App::new() - .service(post_test) - .service(put_test) - .service(head_test) - .service(connect_test) - .service(options_test) - .service(trace_test) - .service(patch_test) - .service(test), - ) - }); - let request = srv.request(http::Method::GET, srv.url("/test")); - let response = srv.block_on(request.send()).unwrap(); - assert!(response.status().is_success()); + block_on(async { + let srv = TestServer::start(|| { + HttpService::new( + App::new() + .service(post_test) + .service(put_test) + .service(head_test) + .service(connect_test) + .service(options_test) + .service(trace_test) + .service(patch_test) + .service(test), + ) + }); + let request = srv.request(http::Method::GET, srv.url("/test")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); - let request = srv.request(http::Method::HEAD, srv.url("/test")); - let response = srv.block_on(request.send()).unwrap(); - assert!(response.status().is_success()); + let request = srv.request(http::Method::HEAD, srv.url("/test")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); - let request = srv.request(http::Method::CONNECT, srv.url("/test")); - let response = srv.block_on(request.send()).unwrap(); - assert!(response.status().is_success()); + let request = srv.request(http::Method::CONNECT, srv.url("/test")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); - let request = srv.request(http::Method::OPTIONS, srv.url("/test")); - let response = srv.block_on(request.send()).unwrap(); - assert!(response.status().is_success()); + let request = srv.request(http::Method::OPTIONS, srv.url("/test")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); - let request = srv.request(http::Method::TRACE, srv.url("/test")); - let response = srv.block_on(request.send()).unwrap(); - assert!(response.status().is_success()); + let request = srv.request(http::Method::TRACE, srv.url("/test")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); - let request = srv.request(http::Method::PATCH, srv.url("/test")); - let response = srv.block_on(request.send()).unwrap(); - assert!(response.status().is_success()); + let request = srv.request(http::Method::PATCH, srv.url("/test")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); - let request = srv.request(http::Method::PUT, srv.url("/test")); - let response = srv.block_on(request.send()).unwrap(); - assert!(response.status().is_success()); - assert_eq!(response.status(), http::StatusCode::CREATED); + let request = srv.request(http::Method::PUT, srv.url("/test")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); + assert_eq!(response.status(), http::StatusCode::CREATED); - let request = srv.request(http::Method::POST, srv.url("/test")); - let response = srv.block_on(request.send()).unwrap(); - assert!(response.status().is_success()); - assert_eq!(response.status(), http::StatusCode::NO_CONTENT); + let request = srv.request(http::Method::POST, srv.url("/test")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); + assert_eq!(response.status(), http::StatusCode::NO_CONTENT); - let request = srv.request(http::Method::GET, srv.url("/test")); - let response = srv.block_on(request.send()).unwrap(); - assert!(response.status().is_success()); + let request = srv.request(http::Method::GET, srv.url("/test")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); + }) } #[test] fn test_auto_async() { - let mut srv = TestServer::new(|| HttpService::new(App::new().service(auto_async))); + block_on(async { + let srv = TestServer::start(|| HttpService::new(App::new().service(auto_async))); - let request = srv.request(http::Method::GET, srv.url("/test")); - let response = srv.block_on(request.send()).unwrap(); - assert!(response.status().is_success()); + let request = srv.request(http::Method::GET, srv.url("/test")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); + }) } From 60ada97b3d6959ee040ffb6bf4ca16de7e9d02dc Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 21 Nov 2019 13:08:22 +0600 Subject: [PATCH 059/176] migrate actix-session --- Cargo.toml | 2 +- actix-session/Cargo.toml | 8 +- actix-session/src/cookie.rs | 229 +++++++++++++++++++----------------- actix-session/src/lib.rs | 5 +- 4 files changed, 132 insertions(+), 112 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1af2836fd..6c6face79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ members = [ "actix-cors", "actix-files", "actix-framed", - #"actix-session", + "actix-session", "actix-identity", #"actix-multipart", "actix-web-actors", diff --git a/actix-session/Cargo.toml b/actix-session/Cargo.toml index d2fd5ae50..3ce2a8b40 100644 --- a/actix-session/Cargo.toml +++ b/actix-session/Cargo.toml @@ -24,15 +24,15 @@ default = ["cookie-session"] cookie-session = ["actix-web/secure-cookies"] [dependencies] -actix-web = "1.0.9" -actix-service = "0.4.2" +actix-web = "2.0.0-alpha.1" +actix-service = "1.0.0-alpha.1" bytes = "0.4" derive_more = "0.15.0" -futures = "0.1.24" +futures = "0.3.1" hashbrown = "0.6.3" serde = "1.0" serde_json = "1.0" time = "0.1.42" [dev-dependencies] -actix-rt = "0.2.2" +actix-rt = "1.0.0-alpha.1" diff --git a/actix-session/src/cookie.rs b/actix-session/src/cookie.rs index 192737780..9a486cce2 100644 --- a/actix-session/src/cookie.rs +++ b/actix-session/src/cookie.rs @@ -17,6 +17,7 @@ use std::collections::HashMap; use std::rc::Rc; +use std::task::{Context, Poll}; use actix_service::{Service, Transform}; use actix_web::cookie::{Cookie, CookieJar, Key, SameSite}; @@ -24,8 +25,7 @@ use actix_web::dev::{ServiceRequest, ServiceResponse}; use actix_web::http::{header::SET_COOKIE, HeaderValue}; use actix_web::{Error, HttpMessage, ResponseError}; use derive_more::{Display, From}; -use futures::future::{ok, Future, FutureResult}; -use futures::Poll; +use futures::future::{ok, FutureExt, LocalBoxFuture, Ready}; use serde_json::error::Error as JsonError; use crate::{Session, SessionStatus}; @@ -284,7 +284,7 @@ where type Error = S::Error; type InitError = (); type Transform = CookieSessionMiddleware; - type Future = FutureResult; + type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { ok(CookieSessionMiddleware { @@ -309,10 +309,10 @@ where type Request = ServiceRequest; type Response = ServiceResponse; type Error = S::Error; - type Future = Box>; + type Future = LocalBoxFuture<'static, Result>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.service.poll_ready() + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.service.poll_ready(cx) } /// On first request, a new session cookie is returned in response, regardless @@ -325,29 +325,36 @@ where let (is_new, state) = self.inner.load(&req); Session::set_session(state.into_iter(), &mut req); - Box::new(self.service.call(req).map(move |mut res| { - match Session::get_changes(&mut res) { - (SessionStatus::Changed, Some(state)) - | (SessionStatus::Renewed, Some(state)) => { - res.checked_expr(|res| inner.set_cookie(res, state)) - } - (SessionStatus::Unchanged, _) => - // set a new session cookie upon first request (new client) - { - if is_new { - let state: HashMap = HashMap::new(); - res.checked_expr(|res| inner.set_cookie(res, state.into_iter())) - } else { + let fut = self.service.call(req); + + async move { + fut.await.map(|mut res| { + match Session::get_changes(&mut res) { + (SessionStatus::Changed, Some(state)) + | (SessionStatus::Renewed, Some(state)) => { + res.checked_expr(|res| inner.set_cookie(res, state)) + } + (SessionStatus::Unchanged, _) => + // set a new session cookie upon first request (new client) + { + if is_new { + let state: HashMap = HashMap::new(); + res.checked_expr(|res| { + inner.set_cookie(res, state.into_iter()) + }) + } else { + res + } + } + (SessionStatus::Purged, _) => { + let _ = inner.remove_cookie(&mut res); res } + _ => res, } - (SessionStatus::Purged, _) => { - let _ = inner.remove_cookie(&mut res); - res - } - _ => res, - } - })) + }) + } + .boxed_local() } } @@ -359,101 +366,113 @@ mod tests { #[test] fn cookie_session() { - let mut app = test::init_service( - App::new() - .wrap(CookieSession::signed(&[0; 32]).secure(false)) - .service(web::resource("/").to(|ses: Session| { - let _ = ses.set("counter", 100); - "test" - })), - ); + test::block_on(async { + let mut app = test::init_service( + App::new() + .wrap(CookieSession::signed(&[0; 32]).secure(false)) + .service(web::resource("/").to(|ses: Session| { + let _ = ses.set("counter", 100); + "test" + })), + ) + .await; - let request = test::TestRequest::get().to_request(); - let response = test::block_on(app.call(request)).unwrap(); - assert!(response - .response() - .cookies() - .find(|c| c.name() == "actix-session") - .is_some()); + let request = test::TestRequest::get().to_request(); + let response = app.call(request).await.unwrap(); + assert!(response + .response() + .cookies() + .find(|c| c.name() == "actix-session") + .is_some()); + }) } #[test] fn private_cookie() { - let mut app = test::init_service( - App::new() - .wrap(CookieSession::private(&[0; 32]).secure(false)) - .service(web::resource("/").to(|ses: Session| { - let _ = ses.set("counter", 100); - "test" - })), - ); + test::block_on(async { + let mut app = test::init_service( + App::new() + .wrap(CookieSession::private(&[0; 32]).secure(false)) + .service(web::resource("/").to(|ses: Session| { + let _ = ses.set("counter", 100); + "test" + })), + ) + .await; - let request = test::TestRequest::get().to_request(); - let response = test::block_on(app.call(request)).unwrap(); - assert!(response - .response() - .cookies() - .find(|c| c.name() == "actix-session") - .is_some()); + let request = test::TestRequest::get().to_request(); + let response = app.call(request).await.unwrap(); + assert!(response + .response() + .cookies() + .find(|c| c.name() == "actix-session") + .is_some()); + }) } #[test] fn cookie_session_extractor() { - let mut app = test::init_service( - App::new() - .wrap(CookieSession::signed(&[0; 32]).secure(false)) - .service(web::resource("/").to(|ses: Session| { - let _ = ses.set("counter", 100); - "test" - })), - ); + test::block_on(async { + let mut app = test::init_service( + App::new() + .wrap(CookieSession::signed(&[0; 32]).secure(false)) + .service(web::resource("/").to(|ses: Session| { + let _ = ses.set("counter", 100); + "test" + })), + ) + .await; - let request = test::TestRequest::get().to_request(); - let response = test::block_on(app.call(request)).unwrap(); - assert!(response - .response() - .cookies() - .find(|c| c.name() == "actix-session") - .is_some()); + let request = test::TestRequest::get().to_request(); + let response = app.call(request).await.unwrap(); + assert!(response + .response() + .cookies() + .find(|c| c.name() == "actix-session") + .is_some()); + }) } #[test] fn basics() { - let mut app = test::init_service( - App::new() - .wrap( - CookieSession::signed(&[0; 32]) - .path("/test/") - .name("actix-test") - .domain("localhost") - .http_only(true) - .same_site(SameSite::Lax) - .max_age(100), - ) - .service(web::resource("/").to(|ses: Session| { - let _ = ses.set("counter", 100); - "test" - })) - .service(web::resource("/test/").to(|ses: Session| { - let val: usize = ses.get("counter").unwrap().unwrap(); - format!("counter: {}", val) - })), - ); + test::block_on(async { + let mut app = test::init_service( + App::new() + .wrap( + CookieSession::signed(&[0; 32]) + .path("/test/") + .name("actix-test") + .domain("localhost") + .http_only(true) + .same_site(SameSite::Lax) + .max_age(100), + ) + .service(web::resource("/").to(|ses: Session| { + let _ = ses.set("counter", 100); + "test" + })) + .service(web::resource("/test/").to(|ses: Session| { + let val: usize = ses.get("counter").unwrap().unwrap(); + format!("counter: {}", val) + })), + ) + .await; - let request = test::TestRequest::get().to_request(); - let response = test::block_on(app.call(request)).unwrap(); - let cookie = response - .response() - .cookies() - .find(|c| c.name() == "actix-test") - .unwrap() - .clone(); - assert_eq!(cookie.path().unwrap(), "/test/"); + let request = test::TestRequest::get().to_request(); + let response = app.call(request).await.unwrap(); + let cookie = response + .response() + .cookies() + .find(|c| c.name() == "actix-test") + .unwrap() + .clone(); + assert_eq!(cookie.path().unwrap(), "/test/"); - let request = test::TestRequest::with_uri("/test/") - .cookie(cookie) - .to_request(); - let body = test::read_response(&mut app, request); - assert_eq!(body, Bytes::from_static(b"counter: 100")); + let request = test::TestRequest::with_uri("/test/") + .cookie(cookie) + .to_request(); + let body = test::read_response(&mut app, request).await; + assert_eq!(body, Bytes::from_static(b"counter: 100")); + }) } } diff --git a/actix-session/src/lib.rs b/actix-session/src/lib.rs index 2e9e51714..def35a1e9 100644 --- a/actix-session/src/lib.rs +++ b/actix-session/src/lib.rs @@ -47,6 +47,7 @@ use std::rc::Rc; use actix_web::dev::{Extensions, Payload, ServiceRequest, ServiceResponse}; use actix_web::{Error, FromRequest, HttpMessage, HttpRequest}; +use futures::future::{ok, Ready}; use hashbrown::HashMap; use serde::de::DeserializeOwned; use serde::Serialize; @@ -230,12 +231,12 @@ impl Session { /// ``` impl FromRequest for Session { type Error = Error; - type Future = Result; + type Future = Ready>; type Config = (); #[inline] fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { - Ok(Session::get_session(&mut *req.extensions_mut())) + ok(Session::get_session(&mut *req.extensions_mut())) } } From 471f82f0e0f7ad201708e1bcc3ee6a66b3bb42dd Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 21 Nov 2019 14:25:50 +0600 Subject: [PATCH 060/176] migrate actix-multipart --- Cargo.toml | 6 +- actix-multipart/Cargo.toml | 11 +- actix-multipart/src/extractor.rs | 29 ++- actix-multipart/src/server.rs | 299 ++++++++++++++++--------------- 4 files changed, 177 insertions(+), 168 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6c6face79..6c0f0bc8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ members = [ "actix-framed", "actix-session", "actix-identity", - #"actix-multipart", + "actix-multipart", "actix-web-actors", "actix-web-codegen", "test-server", @@ -125,9 +125,9 @@ actix-http = { path = "actix-http" } actix-http-test = { path = "test-server" } actix-web-codegen = { path = "actix-web-codegen" } # actix-web-actors = { path = "actix-web-actors" } -# actix-session = { path = "actix-session" } +actix-session = { path = "actix-session" } actix-files = { path = "actix-files" } -# actix-multipart = { path = "actix-multipart" } +actix-multipart = { path = "actix-multipart" } awc = { path = "awc" } actix-codec = { git = "https://github.com/actix/actix-net.git" } diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index aa4e9be20..f5cdc8afd 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -18,17 +18,18 @@ name = "actix_multipart" path = "src/lib.rs" [dependencies] -actix-web = { version = "1.0.9", default-features = false } -actix-service = "0.4.2" +actix-web = { version = "2.0.0-alpha.1", default-features = false } +actix-service = "1.0.0-alpha.1" +actix-utils = "0.5.0-alpha.1" bytes = "0.4" derive_more = "0.15.0" httparse = "1.3" -futures = "0.1.24" +futures = "0.3.1" log = "0.4" mime = "0.3" time = "0.1" twoway = "0.2" [dev-dependencies] -actix-rt = "0.2.2" -actix-http = "0.2.11" \ No newline at end of file +actix-rt = "1.0.0-alpha.1" +actix-http = "0.3.0-alpha.1" \ No newline at end of file diff --git a/actix-multipart/src/extractor.rs b/actix-multipart/src/extractor.rs index 7274ed092..71c815227 100644 --- a/actix-multipart/src/extractor.rs +++ b/actix-multipart/src/extractor.rs @@ -1,5 +1,6 @@ //! Multipart payload support use actix_web::{dev::Payload, Error, FromRequest, HttpRequest}; +use futures::future::{ok, Ready}; use crate::server::Multipart; @@ -10,33 +11,31 @@ use crate::server::Multipart; /// ## Server example /// /// ```rust -/// # use futures::{Future, Stream}; -/// # use futures::future::{ok, result, Either}; +/// use futures::{Stream, StreamExt}; /// use actix_web::{web, HttpResponse, Error}; /// use actix_multipart as mp; /// -/// fn index(payload: mp::Multipart) -> impl Future { -/// payload.from_err() // <- get multipart stream for current request -/// .and_then(|field| { // <- iterate over multipart items +/// async fn index(mut payload: mp::Multipart) -> Result { +/// // iterate over multipart stream +/// while let Some(item) = payload.next().await { +/// let mut field = item?; +/// /// // Field in turn is stream of *Bytes* object -/// field.from_err() -/// .fold((), |_, chunk| { -/// println!("-- CHUNK: \n{:?}", std::str::from_utf8(&chunk)); -/// Ok::<_, Error>(()) -/// }) -/// }) -/// .fold((), |_, _| Ok::<_, Error>(())) -/// .map(|_| HttpResponse::Ok().into()) +/// while let Some(chunk) = field.next().await { +/// println!("-- CHUNK: \n{:?}", std::str::from_utf8(&chunk?)); +/// } +/// } +/// Ok(HttpResponse::Ok().into()) /// } /// # fn main() {} /// ``` impl FromRequest for Multipart { type Error = Error; - type Future = Result; + type Future = Ready>; type Config = (); #[inline] fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { - Ok(Multipart::new(req.headers(), payload.take())) + ok(Multipart::new(req.headers(), payload.take())) } } diff --git a/actix-multipart/src/server.rs b/actix-multipart/src/server.rs index a7c787f46..dd7852c8e 100644 --- a/actix-multipart/src/server.rs +++ b/actix-multipart/src/server.rs @@ -1,15 +1,17 @@ //! Multipart payload support use std::cell::{Cell, RefCell, RefMut}; use std::marker::PhantomData; +use std::pin::Pin; use std::rc::Rc; +use std::task::{Context, Poll}; use std::{cmp, fmt}; use bytes::{Bytes, BytesMut}; -use futures::task::{current as current_task, Task}; -use futures::{Async, Poll, Stream}; +use futures::stream::{LocalBoxStream, Stream, StreamExt}; use httparse; use mime; +use actix_utils::task::LocalWaker; use actix_web::error::{ParseError, PayloadError}; use actix_web::http::header::{ self, ContentDisposition, HeaderMap, HeaderName, HeaderValue, @@ -60,7 +62,7 @@ impl Multipart { /// Create multipart instance for boundary. pub fn new(headers: &HeaderMap, stream: S) -> Multipart where - S: Stream + 'static, + S: Stream> + Unpin + 'static, { match Self::boundary(headers) { Ok(boundary) => Multipart { @@ -104,22 +106,25 @@ impl Multipart { } impl Stream for Multipart { - type Item = Field; - type Error = MultipartError; + type Item = Result; - fn poll(&mut self) -> Poll, Self::Error> { + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll> { if let Some(err) = self.error.take() { - Err(err) + Poll::Ready(Some(Err(err))) } else if self.safety.current() { - let mut inner = self.inner.as_mut().unwrap().borrow_mut(); - if let Some(mut payload) = inner.payload.get_mut(&self.safety) { - payload.poll_stream()?; + let this = self.get_mut(); + let mut inner = this.inner.as_mut().unwrap().borrow_mut(); + if let Some(mut payload) = inner.payload.get_mut(&this.safety) { + payload.poll_stream(cx)?; } - inner.poll(&self.safety) + inner.poll(&this.safety, cx) } else if !self.safety.is_clean() { - Err(MultipartError::NotConsumed) + Poll::Ready(Some(Err(MultipartError::NotConsumed))) } else { - Ok(Async::NotReady) + Poll::Pending } } } @@ -238,9 +243,13 @@ impl InnerMultipart { Ok(Some(eof)) } - fn poll(&mut self, safety: &Safety) -> Poll, MultipartError> { + fn poll( + &mut self, + safety: &Safety, + cx: &mut Context, + ) -> Poll>> { if self.state == InnerState::Eof { - Ok(Async::Ready(None)) + Poll::Ready(None) } else { // release field loop { @@ -249,10 +258,13 @@ impl InnerMultipart { if safety.current() { let stop = match self.item { InnerMultipartItem::Field(ref mut field) => { - match field.borrow_mut().poll(safety)? { - Async::NotReady => return Ok(Async::NotReady), - Async::Ready(Some(_)) => continue, - Async::Ready(None) => true, + match field.borrow_mut().poll(safety) { + Poll::Pending => return Poll::Pending, + Poll::Ready(Some(Ok(_))) => continue, + Poll::Ready(Some(Err(e))) => { + return Poll::Ready(Some(Err(e))) + } + Poll::Ready(None) => true, } } InnerMultipartItem::None => false, @@ -277,12 +289,12 @@ impl InnerMultipart { Some(eof) => { if eof { self.state = InnerState::Eof; - return Ok(Async::Ready(None)); + return Poll::Ready(None); } else { self.state = InnerState::Headers; } } - None => return Ok(Async::NotReady), + None => return Poll::Pending, } } // read boundary @@ -291,11 +303,11 @@ impl InnerMultipart { &mut *payload, &self.boundary, )? { - None => return Ok(Async::NotReady), + None => return Poll::Pending, Some(eof) => { if eof { self.state = InnerState::Eof; - return Ok(Async::Ready(None)); + return Poll::Ready(None); } else { self.state = InnerState::Headers; } @@ -311,14 +323,14 @@ impl InnerMultipart { self.state = InnerState::Boundary; headers } else { - return Ok(Async::NotReady); + return Poll::Pending; } } else { unreachable!() } } else { log::debug!("NotReady: field is in flight"); - return Ok(Async::NotReady); + return Poll::Pending; }; // content type @@ -335,7 +347,7 @@ impl InnerMultipart { // nested multipart stream if mt.type_() == mime::MULTIPART { - Err(MultipartError::Nested) + Poll::Ready(Some(Err(MultipartError::Nested))) } else { let field = Rc::new(RefCell::new(InnerField::new( self.payload.clone(), @@ -344,12 +356,7 @@ impl InnerMultipart { )?)); self.item = InnerMultipartItem::Field(Rc::clone(&field)); - Ok(Async::Ready(Some(Field::new( - safety.clone(), - headers, - mt, - field, - )))) + Poll::Ready(Some(Ok(Field::new(safety.clone(cx), headers, mt, field)))) } } } @@ -409,23 +416,21 @@ impl Field { } impl Stream for Field { - type Item = Bytes; - type Error = MultipartError; + type Item = Result; - fn poll(&mut self) -> Poll, Self::Error> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { if self.safety.current() { let mut inner = self.inner.borrow_mut(); if let Some(mut payload) = inner.payload.as_ref().unwrap().get_mut(&self.safety) { - payload.poll_stream()?; + payload.poll_stream(cx)?; } - inner.poll(&self.safety) } else if !self.safety.is_clean() { - Err(MultipartError::NotConsumed) + Poll::Ready(Some(Err(MultipartError::NotConsumed))) } else { - Ok(Async::NotReady) + Poll::Pending } } } @@ -482,9 +487,9 @@ impl InnerField { fn read_len( payload: &mut PayloadBuffer, size: &mut u64, - ) -> Poll, MultipartError> { + ) -> Poll>> { if *size == 0 { - Ok(Async::Ready(None)) + Poll::Ready(None) } else { match payload.read_max(*size)? { Some(mut chunk) => { @@ -494,13 +499,13 @@ impl InnerField { if !chunk.is_empty() { payload.unprocessed(chunk); } - Ok(Async::Ready(Some(ch))) + Poll::Ready(Some(Ok(ch))) } None => { if payload.eof && (*size != 0) { - Err(MultipartError::Incomplete) + Poll::Ready(Some(Err(MultipartError::Incomplete))) } else { - Ok(Async::NotReady) + Poll::Pending } } } @@ -512,15 +517,15 @@ impl InnerField { fn read_stream( payload: &mut PayloadBuffer, boundary: &str, - ) -> Poll, MultipartError> { + ) -> Poll>> { let mut pos = 0; let len = payload.buf.len(); if len == 0 { return if payload.eof { - Err(MultipartError::Incomplete) + Poll::Ready(Some(Err(MultipartError::Incomplete))) } else { - Ok(Async::NotReady) + Poll::Pending }; } @@ -537,10 +542,10 @@ impl InnerField { if let Some(b_len) = b_len { let b_size = boundary.len() + b_len; if len < b_size { - return Ok(Async::NotReady); + return Poll::Pending; } else if &payload.buf[b_len..b_size] == boundary.as_bytes() { // found boundary - return Ok(Async::Ready(None)); + return Poll::Ready(None); } } } @@ -552,9 +557,9 @@ impl InnerField { // check if we have enough data for boundary detection if cur + 4 > len { if cur > 0 { - Ok(Async::Ready(Some(payload.buf.split_to(cur).freeze()))) + Poll::Ready(Some(Ok(payload.buf.split_to(cur).freeze()))) } else { - Ok(Async::NotReady) + Poll::Pending } } else { // check boundary @@ -565,7 +570,7 @@ impl InnerField { { if cur != 0 { // return buffer - Ok(Async::Ready(Some(payload.buf.split_to(cur).freeze()))) + Poll::Ready(Some(Ok(payload.buf.split_to(cur).freeze()))) } else { pos = cur + 1; continue; @@ -577,49 +582,51 @@ impl InnerField { } } } else { - Ok(Async::Ready(Some(payload.buf.take().freeze()))) + Poll::Ready(Some(Ok(payload.buf.take().freeze()))) }; } } - fn poll(&mut self, s: &Safety) -> Poll, MultipartError> { + fn poll(&mut self, s: &Safety) -> Poll>> { if self.payload.is_none() { - return Ok(Async::Ready(None)); + return Poll::Ready(None); } let result = if let Some(mut payload) = self.payload.as_ref().unwrap().get_mut(s) { if !self.eof { let res = if let Some(ref mut len) = self.length { - InnerField::read_len(&mut *payload, len)? + InnerField::read_len(&mut *payload, len) } else { - InnerField::read_stream(&mut *payload, &self.boundary)? + InnerField::read_stream(&mut *payload, &self.boundary) }; match res { - Async::NotReady => return Ok(Async::NotReady), - Async::Ready(Some(bytes)) => return Ok(Async::Ready(Some(bytes))), - Async::Ready(None) => self.eof = true, + Poll::Pending => return Poll::Pending, + Poll::Ready(Some(Ok(bytes))) => return Poll::Ready(Some(Ok(bytes))), + Poll::Ready(Some(Err(e))) => return Poll::Ready(Some(Err(e))), + Poll::Ready(None) => self.eof = true, } } - match payload.readline()? { - None => Async::Ready(None), - Some(line) => { + match payload.readline() { + Ok(None) => Poll::Ready(None), + Ok(Some(line)) => { if line.as_ref() != b"\r\n" { log::warn!("multipart field did not read all the data or it is malformed"); } - Async::Ready(None) + Poll::Ready(None) } + Err(e) => Poll::Ready(Some(Err(e))), } } else { - Async::NotReady + Poll::Pending }; - if Async::Ready(None) == result { + if let Poll::Ready(None) = result { self.payload.take(); } - Ok(result) + result } } @@ -659,7 +666,7 @@ impl Clone for PayloadRef { /// most task. #[derive(Debug)] struct Safety { - task: Option, + task: LocalWaker, level: usize, payload: Rc>, clean: Rc>, @@ -669,7 +676,7 @@ impl Safety { fn new() -> Safety { let payload = Rc::new(PhantomData); Safety { - task: None, + task: LocalWaker::new(), level: Rc::strong_count(&payload), clean: Rc::new(Cell::new(true)), payload, @@ -683,17 +690,17 @@ impl Safety { fn is_clean(&self) -> bool { self.clean.get() } -} -impl Clone for Safety { - fn clone(&self) -> Safety { + fn clone(&self, cx: &mut Context) -> Safety { let payload = Rc::clone(&self.payload); - Safety { - task: Some(current_task()), + let s = Safety { + task: LocalWaker::new(), level: Rc::strong_count(&payload), clean: self.clean.clone(), payload, - } + }; + s.task.register(cx.waker()); + s } } @@ -704,7 +711,7 @@ impl Drop for Safety { self.clean.set(true); } if let Some(task) = self.task.take() { - task.notify() + task.wake() } } } @@ -713,31 +720,32 @@ impl Drop for Safety { struct PayloadBuffer { eof: bool, buf: BytesMut, - stream: Box>, + stream: LocalBoxStream<'static, Result>, } impl PayloadBuffer { /// Create new `PayloadBuffer` instance fn new(stream: S) -> Self where - S: Stream + 'static, + S: Stream> + 'static, { PayloadBuffer { eof: false, buf: BytesMut::new(), - stream: Box::new(stream), + stream: stream.boxed_local(), } } - fn poll_stream(&mut self) -> Result<(), PayloadError> { + fn poll_stream(&mut self, cx: &mut Context) -> Result<(), PayloadError> { loop { - match self.stream.poll()? { - Async::Ready(Some(data)) => self.buf.extend_from_slice(&data), - Async::Ready(None) => { + match Pin::new(&mut self.stream).poll_next(cx) { + Poll::Ready(Some(Ok(data))) => self.buf.extend_from_slice(&data), + Poll::Ready(Some(Err(e))) => return Err(e), + Poll::Ready(None) => { self.eof = true; return Ok(()); } - Async::NotReady => return Ok(()), + Poll::Pending => return Ok(()), } } } @@ -800,13 +808,14 @@ impl PayloadBuffer { #[cfg(test)] mod tests { - use actix_http::h1::Payload; - use bytes::Bytes; - use futures::unsync::mpsc; - use super::*; + + use actix_http::h1::Payload; + use actix_utils::mpsc; use actix_web::http::header::{DispositionParam, DispositionType}; - use actix_web::test::run_on; + use actix_web::test::block_on; + use bytes::Bytes; + use futures::future::lazy; #[test] fn test_boundary() { @@ -852,12 +861,12 @@ mod tests { } fn create_stream() -> ( - mpsc::UnboundedSender>, - impl Stream, + mpsc::Sender>, + impl Stream>, ) { - let (tx, rx) = mpsc::unbounded(); + let (tx, rx) = mpsc::channel(); - (tx, rx.map_err(|_| panic!()).and_then(|res| res)) + (tx, rx.map(|res| res.map_err(|_| panic!()))) } fn create_simple_request_with_header() -> (Bytes, HeaderMap) { @@ -884,28 +893,28 @@ mod tests { #[test] fn test_multipart_no_end_crlf() { - run_on(|| { + block_on(async { let (sender, payload) = create_stream(); let (bytes, headers) = create_simple_request_with_header(); let bytes_stripped = bytes.slice_to(bytes.len()); // strip crlf - sender.unbounded_send(Ok(bytes_stripped)).unwrap(); + sender.send(Ok(bytes_stripped)).unwrap(); drop(sender); // eof let mut multipart = Multipart::new(&headers, payload); - match multipart.poll().unwrap() { - Async::Ready(Some(_)) => (), + match multipart.next().await.unwrap() { + Ok(_) => (), _ => unreachable!(), } - match multipart.poll().unwrap() { - Async::Ready(Some(_)) => (), + match multipart.next().await.unwrap() { + Ok(_) => (), _ => unreachable!(), } - match multipart.poll().unwrap() { - Async::Ready(None) => (), + match multipart.next().await { + None => (), _ => unreachable!(), } }) @@ -913,15 +922,15 @@ mod tests { #[test] fn test_multipart() { - run_on(|| { + block_on(async { let (sender, payload) = create_stream(); let (bytes, headers) = create_simple_request_with_header(); - sender.unbounded_send(Ok(bytes)).unwrap(); + sender.send(Ok(bytes)).unwrap(); let mut multipart = Multipart::new(&headers, payload); - match multipart.poll().unwrap() { - Async::Ready(Some(mut field)) => { + match multipart.next().await { + Some(Ok(mut field)) => { let cd = field.content_disposition().unwrap(); assert_eq!(cd.disposition, DispositionType::FormData); assert_eq!(cd.parameters[0], DispositionParam::Name("file".into())); @@ -929,37 +938,37 @@ mod tests { assert_eq!(field.content_type().type_(), mime::TEXT); assert_eq!(field.content_type().subtype(), mime::PLAIN); - match field.poll().unwrap() { - Async::Ready(Some(chunk)) => assert_eq!(chunk, "test"), + match field.next().await.unwrap() { + Ok(chunk) => assert_eq!(chunk, "test"), _ => unreachable!(), } - match field.poll().unwrap() { - Async::Ready(None) => (), + match field.next().await { + None => (), _ => unreachable!(), } } _ => unreachable!(), } - match multipart.poll().unwrap() { - Async::Ready(Some(mut field)) => { + match multipart.next().await.unwrap() { + Ok(mut field) => { assert_eq!(field.content_type().type_(), mime::TEXT); assert_eq!(field.content_type().subtype(), mime::PLAIN); - match field.poll() { - Ok(Async::Ready(Some(chunk))) => assert_eq!(chunk, "data"), + match field.next().await { + Some(Ok(chunk)) => assert_eq!(chunk, "data"), _ => unreachable!(), } - match field.poll() { - Ok(Async::Ready(None)) => (), + match field.next().await { + None => (), _ => unreachable!(), } } _ => unreachable!(), } - match multipart.poll().unwrap() { - Async::Ready(None) => (), + match multipart.next().await { + None => (), _ => unreachable!(), } }); @@ -967,15 +976,15 @@ mod tests { #[test] fn test_stream() { - run_on(|| { + block_on(async { let (sender, payload) = create_stream(); let (bytes, headers) = create_simple_request_with_header(); - sender.unbounded_send(Ok(bytes)).unwrap(); + sender.send(Ok(bytes)).unwrap(); let mut multipart = Multipart::new(&headers, payload); - match multipart.poll().unwrap() { - Async::Ready(Some(mut field)) => { + match multipart.next().await.unwrap() { + Ok(mut field) => { let cd = field.content_disposition().unwrap(); assert_eq!(cd.disposition, DispositionType::FormData); assert_eq!(cd.parameters[0], DispositionParam::Name("file".into())); @@ -983,37 +992,37 @@ mod tests { assert_eq!(field.content_type().type_(), mime::TEXT); assert_eq!(field.content_type().subtype(), mime::PLAIN); - match field.poll().unwrap() { - Async::Ready(Some(chunk)) => assert_eq!(chunk, "test"), + match field.next().await.unwrap() { + Ok(chunk) => assert_eq!(chunk, "test"), _ => unreachable!(), } - match field.poll().unwrap() { - Async::Ready(None) => (), + match field.next().await { + None => (), _ => unreachable!(), } } _ => unreachable!(), } - match multipart.poll().unwrap() { - Async::Ready(Some(mut field)) => { + match multipart.next().await { + Some(Ok(mut field)) => { assert_eq!(field.content_type().type_(), mime::TEXT); assert_eq!(field.content_type().subtype(), mime::PLAIN); - match field.poll() { - Ok(Async::Ready(Some(chunk))) => assert_eq!(chunk, "data"), + match field.next().await { + Some(Ok(chunk)) => assert_eq!(chunk, "data"), _ => unreachable!(), } - match field.poll() { - Ok(Async::Ready(None)) => (), + match field.next().await { + None => (), _ => unreachable!(), } } _ => unreachable!(), } - match multipart.poll().unwrap() { - Async::Ready(None) => (), + match multipart.next().await { + None => (), _ => unreachable!(), } }); @@ -1021,26 +1030,26 @@ mod tests { #[test] fn test_basic() { - run_on(|| { + block_on(async { let (_, payload) = Payload::create(false); let mut payload = PayloadBuffer::new(payload); assert_eq!(payload.buf.len(), 0); - payload.poll_stream().unwrap(); + lazy(|cx| payload.poll_stream(cx)).await.unwrap(); assert_eq!(None, payload.read_max(1).unwrap()); }) } #[test] fn test_eof() { - run_on(|| { + block_on(async { let (mut sender, payload) = Payload::create(false); let mut payload = PayloadBuffer::new(payload); assert_eq!(None, payload.read_max(4).unwrap()); sender.feed_data(Bytes::from("data")); sender.feed_eof(); - payload.poll_stream().unwrap(); + lazy(|cx| payload.poll_stream(cx)).await.unwrap(); assert_eq!(Some(Bytes::from("data")), payload.read_max(4).unwrap()); assert_eq!(payload.buf.len(), 0); @@ -1051,24 +1060,24 @@ mod tests { #[test] fn test_err() { - run_on(|| { + block_on(async { let (mut sender, payload) = Payload::create(false); let mut payload = PayloadBuffer::new(payload); assert_eq!(None, payload.read_max(1).unwrap()); sender.set_error(PayloadError::Incomplete(None)); - payload.poll_stream().err().unwrap(); + lazy(|cx| payload.poll_stream(cx)).await.err().unwrap(); }) } #[test] fn test_readmax() { - run_on(|| { + block_on(async { let (mut sender, payload) = Payload::create(false); let mut payload = PayloadBuffer::new(payload); sender.feed_data(Bytes::from("line1")); sender.feed_data(Bytes::from("line2")); - payload.poll_stream().unwrap(); + lazy(|cx| payload.poll_stream(cx)).await.unwrap(); assert_eq!(payload.buf.len(), 10); assert_eq!(Some(Bytes::from("line1")), payload.read_max(5).unwrap()); @@ -1081,7 +1090,7 @@ mod tests { #[test] fn test_readexactly() { - run_on(|| { + block_on(async { let (mut sender, payload) = Payload::create(false); let mut payload = PayloadBuffer::new(payload); @@ -1089,7 +1098,7 @@ mod tests { sender.feed_data(Bytes::from("line1")); sender.feed_data(Bytes::from("line2")); - payload.poll_stream().unwrap(); + lazy(|cx| payload.poll_stream(cx)).await.unwrap(); assert_eq!(Some(Bytes::from_static(b"li")), payload.read_exact(2)); assert_eq!(payload.buf.len(), 8); @@ -1101,7 +1110,7 @@ mod tests { #[test] fn test_readuntil() { - run_on(|| { + block_on(async { let (mut sender, payload) = Payload::create(false); let mut payload = PayloadBuffer::new(payload); @@ -1109,7 +1118,7 @@ mod tests { sender.feed_data(Bytes::from("line1")); sender.feed_data(Bytes::from("line2")); - payload.poll_stream().unwrap(); + lazy(|cx| payload.poll_stream(cx)).await.unwrap(); assert_eq!( Some(Bytes::from("line")), From 55698f252425733a7052fdf50835d5e180bf7d97 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 21 Nov 2019 14:52:33 +0600 Subject: [PATCH 061/176] migrade rest of middlewares --- src/middleware/condition.rs | 98 ++++++++++++++++++-------------- src/middleware/errhandlers.rs | 102 +++++++++++++++++++--------------- src/middleware/mod.rs | 10 ++-- src/middleware/normalize.rs | 94 +++++++++++++++++-------------- 4 files changed, 170 insertions(+), 134 deletions(-) diff --git a/src/middleware/condition.rs b/src/middleware/condition.rs index ddc5fdd42..6603fc001 100644 --- a/src/middleware/condition.rs +++ b/src/middleware/condition.rs @@ -1,7 +1,8 @@ //! `Middleware` for conditionally enables another middleware. +use std::task::{Context, Poll}; + use actix_service::{Service, Transform}; -use futures::future::{ok, Either, FutureResult, Map}; -use futures::{Future, Poll}; +use futures::future::{ok, Either, FutureExt, LocalBoxFuture, Ready}; /// `Middleware` for conditionally enables another middleware. /// The controled middleware must not change the `Service` interfaces. @@ -13,11 +14,11 @@ use futures::{Future, Poll}; /// use actix_web::middleware::{Condition, NormalizePath}; /// use actix_web::App; /// -/// fn main() { -/// let enable_normalize = std::env::var("NORMALIZE_PATH") == Ok("true".into()); -/// let app = App::new() -/// .wrap(Condition::new(enable_normalize, NormalizePath)); -/// } +/// # fn main() { +/// let enable_normalize = std::env::var("NORMALIZE_PATH") == Ok("true".into()); +/// let app = App::new() +/// .wrap(Condition::new(enable_normalize, NormalizePath)); +/// # } /// ``` pub struct Condition { trans: T, @@ -32,29 +33,31 @@ impl Condition { impl Transform for Condition where - S: Service, + S: Service + 'static, T: Transform, + T::Future: 'static, + T::InitError: 'static, + T::Transform: 'static, { type Request = S::Request; type Response = S::Response; type Error = S::Error; type InitError = T::InitError; type Transform = ConditionMiddleware; - type Future = Either< - Map Self::Transform>, - FutureResult, - >; + type Future = LocalBoxFuture<'static, Result>; fn new_transform(&self, service: S) -> Self::Future { if self.enable { - let f = self - .trans - .new_transform(service) - .map(ConditionMiddleware::Enable as fn(T::Transform) -> Self::Transform); - Either::A(f) + let f = self.trans.new_transform(service).map(|res| { + res.map( + ConditionMiddleware::Enable as fn(T::Transform) -> Self::Transform, + ) + }); + Either::Left(f) } else { - Either::B(ok(ConditionMiddleware::Disable(service))) + Either::Right(ok(ConditionMiddleware::Disable(service))) } + .boxed_local() } } @@ -73,19 +76,19 @@ where type Error = E::Error; type Future = Either; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { use ConditionMiddleware::*; match self { - Enable(service) => service.poll_ready(), - Disable(service) => service.poll_ready(), + Enable(service) => service.poll_ready(cx), + Disable(service) => service.poll_ready(cx), } } fn call(&mut self, req: E::Request) -> Self::Future { use ConditionMiddleware::*; match self { - Enable(service) => Either::A(service.call(req)), - Disable(service) => Either::B(service.call(req)), + Enable(service) => Either::Left(service.call(req)), + Disable(service) => Either::Right(service.call(req)), } } } @@ -99,7 +102,7 @@ mod tests { use crate::error::Result; use crate::http::{header::CONTENT_TYPE, HeaderValue, StatusCode}; use crate::middleware::errhandlers::*; - use crate::test::{self, TestRequest}; + use crate::test::{self, block_on, TestRequest}; use crate::HttpResponse; fn render_500(mut res: ServiceResponse) -> Result> { @@ -111,33 +114,44 @@ mod tests { #[test] fn test_handler_enabled() { - let srv = |req: ServiceRequest| { - req.into_response(HttpResponse::InternalServerError().finish()) - }; + block_on(async { + let srv = |req: ServiceRequest| { + ok(req.into_response(HttpResponse::InternalServerError().finish())) + }; - let mw = - ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, render_500); + let mw = ErrorHandlers::new() + .handler(StatusCode::INTERNAL_SERVER_ERROR, render_500); - let mut mw = - test::block_on(Condition::new(true, mw).new_transform(srv.into_service())) + let mut mw = Condition::new(true, mw) + .new_transform(srv.into_service()) + .await .unwrap(); - let resp = test::call_service(&mut mw, TestRequest::default().to_srv_request()); - assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); + let resp = + test::call_service(&mut mw, TestRequest::default().to_srv_request()) + .await; + assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); + }) } + #[test] fn test_handler_disabled() { - let srv = |req: ServiceRequest| { - req.into_response(HttpResponse::InternalServerError().finish()) - }; + block_on(async { + let srv = |req: ServiceRequest| { + ok(req.into_response(HttpResponse::InternalServerError().finish())) + }; - let mw = - ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, render_500); + let mw = ErrorHandlers::new() + .handler(StatusCode::INTERNAL_SERVER_ERROR, render_500); - let mut mw = - test::block_on(Condition::new(false, mw).new_transform(srv.into_service())) + let mut mw = Condition::new(false, mw) + .new_transform(srv.into_service()) + .await .unwrap(); - let resp = test::call_service(&mut mw, TestRequest::default().to_srv_request()); - assert_eq!(resp.headers().get(CONTENT_TYPE), None); + let resp = + test::call_service(&mut mw, TestRequest::default().to_srv_request()) + .await; + assert_eq!(resp.headers().get(CONTENT_TYPE), None); + }) } } diff --git a/src/middleware/errhandlers.rs b/src/middleware/errhandlers.rs index 5f73d4d7e..c8a702857 100644 --- a/src/middleware/errhandlers.rs +++ b/src/middleware/errhandlers.rs @@ -1,9 +1,9 @@ //! Custom handlers service for responses. use std::rc::Rc; +use std::task::{Context, Poll}; use actix_service::{Service, Transform}; -use futures::future::{err, ok, Either, Future, FutureResult}; -use futures::Poll; +use futures::future::{err, ok, Either, Future, FutureExt, LocalBoxFuture, Ready}; use hashbrown::HashMap; use crate::dev::{ServiceRequest, ServiceResponse}; @@ -15,7 +15,7 @@ pub enum ErrorHandlerResponse { /// New http response got generated Response(ServiceResponse), /// Result is a future that resolves to a new http response - Future(Box, Error = Error>>), + Future(LocalBoxFuture<'static, Result, Error>>), } type ErrorHandler = dyn Fn(ServiceResponse) -> Result>; @@ -39,17 +39,17 @@ type ErrorHandler = dyn Fn(ServiceResponse) -> Result { handlers: Rc>>>, @@ -92,7 +92,7 @@ where type Error = Error; type InitError = (); type Transform = ErrorHandlersMiddleware; - type Future = FutureResult; + type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { ok(ErrorHandlersMiddleware { @@ -117,26 +117,30 @@ where type Request = ServiceRequest; type Response = ServiceResponse; type Error = Error; - type Future = Box>; + type Future = LocalBoxFuture<'static, Result>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.service.poll_ready() + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.service.poll_ready(cx) } fn call(&mut self, req: ServiceRequest) -> Self::Future { let handlers = self.handlers.clone(); + let fut = self.service.call(req); + + async move { + let res = fut.await?; - Box::new(self.service.call(req).and_then(move |res| { if let Some(handler) = handlers.get(&res.status()) { match handler(res) { - Ok(ErrorHandlerResponse::Response(res)) => Either::A(ok(res)), - Ok(ErrorHandlerResponse::Future(fut)) => Either::B(fut), - Err(e) => Either::A(err(e)), + Ok(ErrorHandlerResponse::Response(res)) => Ok(res), + Ok(ErrorHandlerResponse::Future(fut)) => fut.await, + Err(e) => Err(e), } } else { - Either::A(ok(res)) + Ok(res) } - })) + } + .boxed_local() } } @@ -147,7 +151,7 @@ mod tests { use super::*; use crate::http::{header::CONTENT_TYPE, HeaderValue, StatusCode}; - use crate::test::{self, TestRequest}; + use crate::test::{self, block_on, TestRequest}; use crate::HttpResponse; fn render_500(mut res: ServiceResponse) -> Result> { @@ -159,19 +163,22 @@ mod tests { #[test] fn test_handler() { - let srv = |req: ServiceRequest| { - req.into_response(HttpResponse::InternalServerError().finish()) - }; + block_on(async { + let srv = |req: ServiceRequest| { + ok(req.into_response(HttpResponse::InternalServerError().finish())) + }; - let mut mw = test::block_on( - ErrorHandlers::new() + let mut mw = ErrorHandlers::new() .handler(StatusCode::INTERNAL_SERVER_ERROR, render_500) - .new_transform(srv.into_service()), - ) - .unwrap(); + .new_transform(srv.into_service()) + .await + .unwrap(); - let resp = test::call_service(&mut mw, TestRequest::default().to_srv_request()); - assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); + let resp = + test::call_service(&mut mw, TestRequest::default().to_srv_request()) + .await; + assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); + }) } fn render_500_async( @@ -180,23 +187,26 @@ mod tests { res.response_mut() .headers_mut() .insert(CONTENT_TYPE, HeaderValue::from_static("0001")); - Ok(ErrorHandlerResponse::Future(Box::new(ok(res)))) + Ok(ErrorHandlerResponse::Future(ok(res).boxed_local())) } #[test] fn test_handler_async() { - let srv = |req: ServiceRequest| { - req.into_response(HttpResponse::InternalServerError().finish()) - }; + block_on(async { + let srv = |req: ServiceRequest| { + ok(req.into_response(HttpResponse::InternalServerError().finish())) + }; - let mut mw = test::block_on( - ErrorHandlers::new() + let mut mw = ErrorHandlers::new() .handler(StatusCode::INTERNAL_SERVER_ERROR, render_500_async) - .new_transform(srv.into_service()), - ) - .unwrap(); + .new_transform(srv.into_service()) + .await + .unwrap(); - let resp = test::call_service(&mut mw, TestRequest::default().to_srv_request()); - assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); + let resp = + test::call_service(&mut mw, TestRequest::default().to_srv_request()) + .await; + assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); + }) } } diff --git a/src/middleware/mod.rs b/src/middleware/mod.rs index 30acad15a..84e0758bf 100644 --- a/src/middleware/mod.rs +++ b/src/middleware/mod.rs @@ -2,13 +2,13 @@ mod compress; pub use self::compress::{BodyEncoding, Compress}; -//mod condition; +mod condition; mod defaultheaders; -//pub mod errhandlers; +pub mod errhandlers; mod logger; -//mod normalize; +mod normalize; -//pub use self::condition::Condition; +pub use self::condition::Condition; pub use self::defaultheaders::DefaultHeaders; pub use self::logger::Logger; -//pub use self::normalize::NormalizePath; +pub use self::normalize::NormalizePath; diff --git a/src/middleware/normalize.rs b/src/middleware/normalize.rs index 9cfbefb30..b7eb1384a 100644 --- a/src/middleware/normalize.rs +++ b/src/middleware/normalize.rs @@ -1,9 +1,10 @@ //! `Middleware` to normalize request's URI +use std::task::{Context, Poll}; use actix_http::http::{HttpTryFrom, PathAndQuery, Uri}; use actix_service::{Service, Transform}; use bytes::Bytes; -use futures::future::{self, FutureResult}; +use futures::future::{ok, Ready}; use regex::Regex; use crate::service::{ServiceRequest, ServiceResponse}; @@ -19,15 +20,15 @@ use crate::Error; /// ```rust /// use actix_web::{web, http, middleware, App, HttpResponse}; /// -/// fn main() { -/// let app = App::new() -/// .wrap(middleware::NormalizePath) -/// .service( -/// web::resource("/test") -/// .route(web::get().to(|| HttpResponse::Ok())) -/// .route(web::method(http::Method::HEAD).to(|| HttpResponse::MethodNotAllowed())) -/// ); -/// } +/// # fn main() { +/// let app = App::new() +/// .wrap(middleware::NormalizePath) +/// .service( +/// web::resource("/test") +/// .route(web::get().to(|| HttpResponse::Ok())) +/// .route(web::method(http::Method::HEAD).to(|| HttpResponse::MethodNotAllowed())) +/// ); +/// # } /// ``` pub struct NormalizePath; @@ -42,10 +43,10 @@ where type Error = Error; type InitError = (); type Transform = NormalizePathNormalization; - type Future = FutureResult; + type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { - future::ok(NormalizePathNormalization { + ok(NormalizePathNormalization { service, merge_slash: Regex::new("//+").unwrap(), }) @@ -67,8 +68,8 @@ where type Error = Error; type Future = S::Future; - fn poll_ready(&mut self) -> futures::Poll<(), Self::Error> { - self.service.poll_ready() + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.service.poll_ready(cx) } fn call(&mut self, mut req: ServiceRequest) -> Self::Future { @@ -109,46 +110,57 @@ mod tests { #[test] fn test_wrap() { - let mut app = init_service( - App::new() - .wrap(NormalizePath::default()) - .service(web::resource("/v1/something/").to(|| HttpResponse::Ok())), - ); + block_on(async { + let mut app = init_service( + App::new() + .wrap(NormalizePath::default()) + .service(web::resource("/v1/something/").to(|| HttpResponse::Ok())), + ) + .await; - let req = TestRequest::with_uri("/v1//something////").to_request(); - let res = call_service(&mut app, req); - assert!(res.status().is_success()); + let req = TestRequest::with_uri("/v1//something////").to_request(); + let res = call_service(&mut app, req).await; + assert!(res.status().is_success()); + }) } #[test] fn test_in_place_normalization() { - let srv = |req: ServiceRequest| { - assert_eq!("/v1/something/", req.path()); - req.into_response(HttpResponse::Ok().finish()) - }; + block_on(async { + let srv = |req: ServiceRequest| { + assert_eq!("/v1/something/", req.path()); + ok(req.into_response(HttpResponse::Ok().finish())) + }; - let mut normalize = - block_on(NormalizePath.new_transform(srv.into_service())).unwrap(); + let mut normalize = NormalizePath + .new_transform(srv.into_service()) + .await + .unwrap(); - let req = TestRequest::with_uri("/v1//something////").to_srv_request(); - let res = block_on(normalize.call(req)).unwrap(); - assert!(res.status().is_success()); + let req = TestRequest::with_uri("/v1//something////").to_srv_request(); + let res = normalize.call(req).await.unwrap(); + assert!(res.status().is_success()); + }) } #[test] fn should_normalize_nothing() { - const URI: &str = "/v1/something/"; + block_on(async { + const URI: &str = "/v1/something/"; - let srv = |req: ServiceRequest| { - assert_eq!(URI, req.path()); - req.into_response(HttpResponse::Ok().finish()) - }; + let srv = |req: ServiceRequest| { + assert_eq!(URI, req.path()); + ok(req.into_response(HttpResponse::Ok().finish())) + }; - let mut normalize = - block_on(NormalizePath.new_transform(srv.into_service())).unwrap(); + let mut normalize = NormalizePath + .new_transform(srv.into_service()) + .await + .unwrap(); - let req = TestRequest::with_uri(URI).to_srv_request(); - let res = block_on(normalize.call(req)).unwrap(); - assert!(res.status().is_success()); + let req = TestRequest::with_uri(URI).to_srv_request(); + let res = normalize.call(req).await.unwrap(); + assert!(res.status().is_success()); + }) } } From 53c5151692978ecf21dda31b5c93cb1469e5c36f Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 21 Nov 2019 15:01:34 +0600 Subject: [PATCH 062/176] use response instead of result for asyn c handlers --- examples/basic.rs | 6 ++--- src/handler.rs | 63 +++++++++++++++++++---------------------------- src/resource.rs | 9 +++---- src/route.rs | 15 ++++++----- src/web.rs | 9 +++---- 5 files changed, 43 insertions(+), 59 deletions(-) diff --git a/examples/basic.rs b/examples/basic.rs index 76c977322..d25db7895 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -8,9 +8,9 @@ fn index(req: HttpRequest, name: web::Path) -> String { format!("Hello: {}!\r\n", name) } -async fn index_async(req: HttpRequest) -> Result<&'static str, Error> { +async fn index_async(req: HttpRequest) -> &'static str { println!("REQ: {:?}", req); - Ok("Hello world!\r\n") + "Hello world!\r\n" } #[get("/")] @@ -26,7 +26,7 @@ fn main() -> std::io::Result<()> { App::new() .wrap(middleware::DefaultHeaders::new().header("X-Version", "0.2")) .wrap(middleware::Compress::default()) - // .wrap(middleware::Logger::default()) + .wrap(middleware::Logger::default()) .service(index) .service(no_params) .service( diff --git a/src/handler.rs b/src/handler.rs index 7f5d52945..767f630da 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -119,21 +119,19 @@ impl Future for HandlerServiceResponse { } /// Async handler converter factory -pub trait AsyncFactory: Clone + 'static +pub trait AsyncFactory: Clone + 'static where - R: Future>, + R: Future, O: Responder, - E: Into, { fn call(&self, param: T) -> R; } -impl AsyncFactory<(), R, O, E> for F +impl AsyncFactory<(), R, O> for F where F: Fn() -> R + Clone + 'static, - R: Future>, + R: Future, O: Responder, - E: Into, { fn call(&self, _: ()) -> R { (self)() @@ -141,23 +139,21 @@ where } #[doc(hidden)] -pub struct AsyncHandler +pub struct AsyncHandler where - F: AsyncFactory, - R: Future>, + F: AsyncFactory, + R: Future, O: Responder, - E: Into, { hnd: F, - _t: PhantomData<(T, R, O, E)>, + _t: PhantomData<(T, R, O)>, } -impl AsyncHandler +impl AsyncHandler where - F: AsyncFactory, - R: Future>, + F: AsyncFactory, + R: Future, O: Responder, - E: Into, { pub fn new(hnd: F) -> Self { AsyncHandler { @@ -167,12 +163,11 @@ where } } -impl Clone for AsyncHandler +impl Clone for AsyncHandler where - F: AsyncFactory, - R: Future>, + F: AsyncFactory, + R: Future, O: Responder, - E: Into, { fn clone(&self) -> Self { AsyncHandler { @@ -182,17 +177,16 @@ where } } -impl Service for AsyncHandler +impl Service for AsyncHandler where - F: AsyncFactory, - R: Future>, + F: AsyncFactory, + R: Future, O: Responder, - E: Into, { type Request = (T, HttpRequest); type Response = ServiceResponse; type Error = Infallible; - type Future = AsyncHandlerServiceResponse; + type Future = AsyncHandlerServiceResponse; fn poll_ready(&mut self, _: &mut Context) -> Poll> { Poll::Ready(Ok(())) @@ -209,11 +203,10 @@ where #[doc(hidden)] #[pin_project] -pub struct AsyncHandlerServiceResponse +pub struct AsyncHandlerServiceResponse where - T: Future>, + T: Future, R: Responder, - E: Into, { #[pin] fut: T, @@ -222,11 +215,10 @@ where req: Option, } -impl Future for AsyncHandlerServiceResponse +impl Future for AsyncHandlerServiceResponse where - T: Future>, + T: Future, R: Responder, - E: Into, { type Output = Result; @@ -247,16 +239,12 @@ where } match this.fut.poll(cx) { - Poll::Ready(Ok(res)) => { + Poll::Ready(res) => { let fut = res.respond_to(this.req.as_ref().unwrap()); self.as_mut().project().fut2.set(Some(fut)); self.poll(cx) } Poll::Pending => Poll::Pending, - Poll::Ready(Err(e)) => { - let res: Response = e.into().into(); - Poll::Ready(Ok(ServiceResponse::new(this.req.take().unwrap(), res))) - } } } } @@ -387,11 +375,10 @@ macro_rules! factory_tuple ({ $(($n:tt, $T:ident)),+} => { } } - impl AsyncFactory<($($T,)+), Res, O, E1> for Func + impl AsyncFactory<($($T,)+), Res, O> for Func where Func: Fn($($T,)+) -> Res + Clone + 'static, - Res: Future>, + Res: Future, O: Responder, - E1: Into, { fn call(&self, param: ($($T,)+)) -> Res { (self)($(param.$n,)+) diff --git a/src/resource.rs b/src/resource.rs index 553d41568..904bc124f 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -264,13 +264,12 @@ where /// App::new().service(web::resource("/").route(web::route().to_async(index))); /// ``` #[allow(clippy::wrong_self_convention)] - pub fn to_async(mut self, handler: F) -> Self + pub fn to_async(mut self, handler: F) -> Self where - F: AsyncFactory, + F: AsyncFactory, I: FromRequest + 'static, - R: Future> + 'static, - O: Responder + 'static, - E: Into + 'static, + R: Future + 'static, + U: Responder + 'static, { self.routes.push(Route::new().to_async(handler)); self diff --git a/src/route.rs b/src/route.rs index fb46dbfd2..9b2c4390c 100644 --- a/src/route.rs +++ b/src/route.rs @@ -261,13 +261,12 @@ impl Route { /// } /// ``` #[allow(clippy::wrong_self_convention)] - pub fn to_async(mut self, handler: F) -> Self + pub fn to_async(mut self, handler: F) -> Self where - F: AsyncFactory, + F: AsyncFactory, T: FromRequest + 'static, - R: Future> + 'static, - O: Responder + 'static, - E: Into + 'static, + R: Future + 'static, + U: Responder + 'static, { self.service = Box::new(RouteNewService::new(Extract::new(AsyncHandler::new( handler, @@ -410,7 +409,7 @@ mod tests { .route(web::post().to_async(|| { async { delay_for(Duration::from_millis(100)).await; - Ok::<_, Error>(HttpResponse::Created()) + HttpResponse::Created() } })) .route(web::delete().to_async(|| { @@ -423,9 +422,9 @@ mod tests { .service(web::resource("/json").route(web::get().to_async(|| { async { delay_for(Duration::from_millis(25)).await; - Ok::<_, Error>(web::Json(MyObject { + web::Json(MyObject { name: "test".to_string(), - })) + }) } }))), ) diff --git a/src/web.rs b/src/web.rs index 67cfd51a2..3d716dc23 100644 --- a/src/web.rs +++ b/src/web.rs @@ -265,13 +265,12 @@ where /// web::to_async(index)) /// ); /// ``` -pub fn to_async(handler: F) -> Route +pub fn to_async(handler: F) -> Route where - F: AsyncFactory, + F: AsyncFactory, I: FromRequest + 'static, - R: Future> + 'static, - O: Responder + 'static, - E: Into + 'static, + R: Future + 'static, + U: Responder + 'static, { Route::new().to_async(handler) } From 1f0577f8d504e2a1c1f22c6b39acd3edf13d9f67 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 21 Nov 2019 15:56:49 +0600 Subject: [PATCH 063/176] cleanup api doc examples --- awc/src/lib.rs | 1 - awc/src/request.rs | 2 -- src/app.rs | 2 -- src/lib.rs | 1 - src/resource.rs | 3 --- src/responder.rs | 1 - src/route.rs | 2 -- src/scope.rs | 1 - src/service.rs | 5 ++--- src/web.rs | 7 ++----- 10 files changed, 4 insertions(+), 21 deletions(-) diff --git a/awc/src/lib.rs b/awc/src/lib.rs index 7bbe42195..d6cea6ded 100644 --- a/awc/src/lib.rs +++ b/awc/src/lib.rs @@ -52,7 +52,6 @@ use self::connect::{Connect, ConnectorWrapper}; /// An HTTP Client /// /// ```rust -/// # use futures::future::{Future, lazy}; /// use actix_rt::System; /// use awc::Client; /// diff --git a/awc/src/request.rs b/awc/src/request.rs index 5181f1905..c6b09e95c 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -37,7 +37,6 @@ const HTTPS_ENCODING: &str = "gzip, deflate"; /// builder-like pattern. /// /// ```rust -/// use futures::future::{Future, lazy}; /// use actix_rt::System; /// /// fn main() { @@ -310,7 +309,6 @@ impl ClientRequest { /// /// ```rust /// # use actix_rt::System; - /// # use futures::future::{lazy, Future}; /// fn main() { /// System::new("test").block_on(async { /// awc::Client::new().get("https://www.rust-lang.org") diff --git a/src/app.rs b/src/app.rs index 288256604..4c2b34628 100644 --- a/src/app.rs +++ b/src/app.rs @@ -343,7 +343,6 @@ where /// /// ```rust /// use actix_service::Service; - /// # use futures::Future; /// use actix_web::{middleware, web, App}; /// use actix_web::http::{header::CONTENT_TYPE, HeaderValue}; /// @@ -402,7 +401,6 @@ where /// /// ```rust /// use actix_service::Service; - /// # use futures::Future; /// use actix_web::{web, App}; /// use actix_web::http::{header::CONTENT_TYPE, HeaderValue}; /// diff --git a/src/lib.rs b/src/lib.rs index 1ae81505a..3cd1f78d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -171,7 +171,6 @@ pub mod client { //! An HTTP Client //! //! ```rust - //! # use futures::future::{Future, lazy}; //! use actix_rt::System; //! use actix_web::client::Client; //! diff --git a/src/resource.rs b/src/resource.rs index 904bc124f..a1c0d396b 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -244,7 +244,6 @@ where /// /// ```rust /// use actix_web::*; - /// use futures::future::{ok, Future}; /// /// async fn index(req: HttpRequest) -> Result { /// Ok(HttpResponse::Ok().finish()) @@ -257,7 +256,6 @@ where /// /// ```rust /// # use actix_web::*; - /// # use futures::future::Future; /// # async fn index(req: HttpRequest) -> Result { /// # unimplemented!() /// # } @@ -326,7 +324,6 @@ where /// /// ```rust /// use actix_service::Service; - /// # use futures::Future; /// use actix_web::{web, App}; /// use actix_web::http::{header::CONTENT_TYPE, HeaderValue}; /// diff --git a/src/responder.rs b/src/responder.rs index 2bb422b2e..3f1471721 100644 --- a/src/responder.rs +++ b/src/responder.rs @@ -342,7 +342,6 @@ impl Future for CustomResponderFut { /// Combines two different responder types into a single type /// /// ```rust -/// # use futures::future::{ok, Future}; /// use actix_web::{Either, Error, HttpResponse}; /// /// type RegisterResult = Either>; diff --git a/src/route.rs b/src/route.rs index 9b2c4390c..51305d840 100644 --- a/src/route.rs +++ b/src/route.rs @@ -238,9 +238,7 @@ impl Route { /// This method has to be used if your handler function returns `impl Future<>` /// /// ```rust - /// # use futures::future::ok; /// use actix_web::{web, App, Error}; - /// use futures::Future; /// use serde_derive::Deserialize; /// /// #[derive(Deserialize)] diff --git a/src/scope.rs b/src/scope.rs index 2e59352d6..f5ffe05fa 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -353,7 +353,6 @@ where /// /// ```rust /// use actix_service::Service; - /// # use futures::Future; /// use actix_web::{web, App}; /// use actix_web::http::{header::CONTENT_TYPE, HeaderValue}; /// diff --git a/src/service.rs b/src/service.rs index 9c4e6b4aa..39540b067 100644 --- a/src/service.rs +++ b/src/service.rs @@ -449,11 +449,10 @@ impl WebService { /// Add match guard to a web service. /// /// ```rust - /// use futures::future::{ok, Ready}; /// use actix_web::{web, guard, dev, App, Error, HttpResponse}; /// - /// fn index(req: dev::ServiceRequest) -> Ready> { - /// ok(req.into_response(HttpResponse::Ok().finish())) + /// async fn index(req: dev::ServiceRequest) -> Result { + /// Ok(req.into_response(HttpResponse::Ok().finish())) /// } /// /// fn main() { diff --git a/src/web.rs b/src/web.rs index 3d716dc23..099e26627 100644 --- a/src/web.rs +++ b/src/web.rs @@ -254,7 +254,6 @@ where /// Create a new route and add async handler. /// /// ```rust -/// # use futures::future::{ok, Future}; /// use actix_web::{web, App, HttpResponse, Error}; /// /// async fn index() -> Result { @@ -278,12 +277,10 @@ where /// Create raw service for a specific path. /// /// ```rust -/// # extern crate actix_web; -/// use futures::future::{ok, Ready}; /// use actix_web::{dev, web, guard, App, Error, HttpResponse}; /// -/// fn my_service(req: dev::ServiceRequest) -> Ready> { -/// ok(req.into_response(HttpResponse::Ok().finish())) +/// async fn my_service(req: dev::ServiceRequest) -> Result { +/// Ok(req.into_response(HttpResponse::Ok().finish())) /// } /// /// fn main() { From 0b9e3d381b4e1a95a1e4d77732b1ae0d56903aa6 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 21 Nov 2019 17:36:18 +0600 Subject: [PATCH 064/176] add test with custom connector --- awc/Cargo.toml | 1 + awc/tests/test_client.rs | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/awc/Cargo.toml b/awc/Cargo.toml index e085ea09d..4d5fde549 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -63,6 +63,7 @@ rust-tls = { version = "0.16.0", package="rustls", optional = true, features = [ [dev-dependencies] actix-rt = "1.0.0-alpha.1" +actix-connect = { version = "1.0.0-alpha.1", features=["openssl"] } actix-web = { version = "2.0.0-alpha.1", features=["openssl"] } actix-http = { version = "0.3.0-alpha.1", features=["openssl"] } actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } diff --git a/awc/tests/test_client.rs b/awc/tests/test_client.rs index bcedaf64b..959380306 100644 --- a/awc/tests/test_client.rs +++ b/awc/tests/test_client.rs @@ -125,9 +125,18 @@ fn test_timeout() { ))) }); + let connector = awc::Connector::new() + .connector(actix_connect::new_connector( + actix_connect::start_default_resolver(), + )) + .timeout(Duration::from_secs(15)) + .finish(); + let client = awc::Client::build() + .connector(connector) .timeout(Duration::from_millis(50)) .finish(); + let request = client.get(srv.url("/")).send(); match request.await { Err(SendRequestError::Timeout) => (), From 8683ba8bb03c27d2839a087f9c24e0791929269f Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 21 Nov 2019 21:34:04 +0600 Subject: [PATCH 065/176] rename .to_async() to .to() --- Cargo.toml | 2 +- MIGRATION.md | 7 ++ README.md | 2 +- actix-http/src/response.rs | 40 +++++---- actix-web-codegen/src/route.rs | 2 +- examples/basic.rs | 12 ++- examples/uds.rs | 16 ++-- src/app.rs | 12 +-- src/data.rs | 2 +- src/extract.rs | 4 +- src/handler.rs | 146 ++++----------------------------- src/lib.rs | 4 +- src/request.rs | 2 +- src/resource.rs | 63 ++++---------- src/responder.rs | 5 +- src/route.rs | 65 ++++----------- src/scope.rs | 60 ++++++++------ src/test.rs | 76 +++++++++-------- src/types/form.rs | 2 +- src/types/json.rs | 6 +- src/types/path.rs | 10 +-- src/types/payload.rs | 8 +- src/types/query.rs | 6 +- src/web.rs | 36 ++------ tests/test_server.rs | 40 ++++----- 25 files changed, 232 insertions(+), 396 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6c0f0bc8c..6c9d03487 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,7 +74,7 @@ actix-service = "1.0.0-alpha.1" actix-utils = "0.5.0-alpha.1" actix-router = "0.1.5" actix-rt = "1.0.0-alpha.1" -actix-web-codegen = "0.1.2" +actix-web-codegen = "0.2.0-alpha.1" actix-http = "0.3.0-alpha.1" actix-server = "0.8.0-alpha.1" actix-server-config = "0.3.0-alpha.1" diff --git a/MIGRATION.md b/MIGRATION.md index 2f0f369ad..9709b4f04 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,3 +1,10 @@ +## 2.0.0 + +* Sync handlers has been removed. `.to_async()` methtod has been renamed to `.to()` + + replace `fn` with `async fn` to convert sync handler to async + + ## 1.0.1 * Cors middleware has been moved to `actix-cors` crate diff --git a/README.md b/README.md index 99b7b1760..cee8b73c0 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Actix web is a simple, pragmatic and extremely fast web framework for Rust. ```rust use actix_web::{web, App, HttpServer, Responder}; -fn index(info: web::Path<(u32, String)>) -> impl Responder { +async fn index(info: web::Path<(u32, String)>) -> impl Responder { format!("Hello {}! id:{}", info.1, info.0) } diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index d05505d80..31876813b 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -1,11 +1,14 @@ //! Http response use std::cell::{Ref, RefMut}; +use std::future::Future; use std::io::Write; +use std::pin::Pin; +use std::task::{Context, Poll}; use std::{fmt, str}; use bytes::{BufMut, Bytes, BytesMut}; use futures::future::{ok, Ready}; -use futures::Stream; +use futures::stream::Stream; use serde::Serialize; use serde_json; @@ -280,15 +283,20 @@ impl fmt::Debug for Response { } } -// impl IntoFuture for Response { -// type Item = Response; -// type Error = Error; -// type Future = FutureResult; +impl Future for Response { + type Output = Result; -// fn into_future(self) -> Self::Future { -// ok(self) -// } -// } + fn poll(mut self: Pin<&mut Self>, _: &mut Context) -> Poll { + Poll::Ready(Ok(Response { + head: std::mem::replace( + &mut self.head, + BoxedResponseHead::new(StatusCode::OK), + ), + body: self.body.take_body(), + error: self.error.take(), + })) + } +} pub struct CookieIter<'a> { iter: header::GetAll<'a>, @@ -757,15 +765,13 @@ impl<'a> From<&'a ResponseHead> for ResponseBuilder { } } -// impl IntoFuture for ResponseBuilder { -// type Item = Response; -// type Error = Error; -// type Future = FutureResult; +impl Future for ResponseBuilder { + type Output = Result; -// fn into_future(mut self) -> Self::Future { -// ok(self.finish()) -// } -// } + fn poll(mut self: Pin<&mut Self>, _: &mut Context) -> Poll { + Poll::Ready(Ok(self.finish())) + } +} impl fmt::Debug for ResponseBuilder { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { diff --git a/actix-web-codegen/src/route.rs b/actix-web-codegen/src/route.rs index e792a7f0a..f8e2496c4 100644 --- a/actix-web-codegen/src/route.rs +++ b/actix-web-codegen/src/route.rs @@ -13,7 +13,7 @@ enum ResourceType { impl ToTokens for ResourceType { fn to_tokens(&self, stream: &mut TokenStream2) { let ident = match self { - ResourceType::Async => "to_async", + ResourceType::Async => "to", ResourceType::Sync => "to", }; let ident = Ident::new(ident, Span::call_site()); diff --git a/examples/basic.rs b/examples/basic.rs index d25db7895..6d9a4dcd8 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -1,9 +1,7 @@ -use actix_web::{ - get, middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer, -}; +use actix_web::{get, middleware, web, App, HttpRequest, HttpResponse, HttpServer}; #[get("/resource1/{name}/index.html")] -fn index(req: HttpRequest, name: web::Path) -> String { +async fn index(req: HttpRequest, name: web::Path) -> String { println!("REQ: {:?}", req); format!("Hello: {}!\r\n", name) } @@ -14,7 +12,7 @@ async fn index_async(req: HttpRequest) -> &'static str { } #[get("/")] -fn no_params() -> &'static str { +async fn no_params() -> &'static str { "Hello world!\r\n" } @@ -37,9 +35,9 @@ fn main() -> std::io::Result<()> { .default_service( web::route().to(|| HttpResponse::MethodNotAllowed()), ) - .route(web::get().to_async(index_async)), + .route(web::get().to(index_async)), ) - .service(web::resource("/test1.html").to(|| "Test\r\n")) + .service(web::resource("/test1.html").to(|| async { "Test\r\n" })) }) .bind("127.0.0.1:8080")? .workers(1) diff --git a/examples/uds.rs b/examples/uds.rs index 7da41a2c5..fc6a58de1 100644 --- a/examples/uds.rs +++ b/examples/uds.rs @@ -3,7 +3,7 @@ use actix_web::{ }; #[get("/resource1/{name}/index.html")] -fn index(req: HttpRequest, name: web::Path) -> String { +async fn index(req: HttpRequest, name: web::Path) -> String { println!("REQ: {:?}", req); format!("Hello: {}!\r\n", name) } @@ -14,11 +14,11 @@ async fn index_async(req: HttpRequest) -> Result<&'static str, Error> { } #[get("/")] -fn no_params() -> &'static str { +async fn no_params() -> &'static str { "Hello world!\r\n" } -#[cfg(feature = "uds")] +#[cfg(unix)] fn main() -> std::io::Result<()> { std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info"); env_logger::init(); @@ -27,7 +27,7 @@ fn main() -> std::io::Result<()> { App::new() .wrap(middleware::DefaultHeaders::new().header("X-Version", "0.2")) .wrap(middleware::Compress::default()) - // .wrap(middleware::Logger::default()) + .wrap(middleware::Logger::default()) .service(index) .service(no_params) .service( @@ -36,16 +36,16 @@ fn main() -> std::io::Result<()> { middleware::DefaultHeaders::new().header("X-Version-R2", "0.3"), ) .default_service( - web::route().to(|| ok(HttpResponse::MethodNotAllowed())), + web::route().to(|| HttpResponse::MethodNotAllowed()), ) - .route(web::get().to_async(index_async)), + .route(web::get().to(index_async)), ) - .service(web::resource("/test1.html").to(|| "Test\r\n")) + .service(web::resource("/test1.html").to(|| async { "Test\r\n" })) }) .bind_uds("/Users/fafhrd91/uds-test")? .workers(1) .run() } -#[cfg(not(feature = "uds"))] +#[cfg(not(unix))] fn main() {} diff --git a/src/app.rs b/src/app.rs index 4c2b34628..d9ac8c09d 100644 --- a/src/app.rs +++ b/src/app.rs @@ -90,7 +90,7 @@ where /// counter: Cell, /// } /// - /// fn index(data: web::Data) { + /// async fn index(data: web::Data) { /// data.counter.set(data.counter.get() + 1); /// } /// @@ -192,7 +192,7 @@ where /// ```rust /// use actix_web::{web, App, HttpResponse}; /// - /// fn index(data: web::Path<(String, String)>) -> &'static str { + /// async fn index(data: web::Path<(String, String)>) -> &'static str { /// "Welcome!" /// } /// @@ -247,7 +247,7 @@ where /// ```rust /// use actix_web::{web, App, HttpResponse}; /// - /// fn index() -> &'static str { + /// async fn index() -> &'static str { /// "Welcome!" /// } /// @@ -302,7 +302,7 @@ where /// ```rust /// use actix_web::{web, App, HttpRequest, HttpResponse, Result}; /// - /// fn index(req: HttpRequest) -> Result { + /// async fn index(req: HttpRequest) -> Result { /// let url = req.url_for("youtube", &["asdlkjqme"])?; /// assert_eq!(url.as_str(), "https://youtube.com/watch/asdlkjqme"); /// Ok(HttpResponse::Ok().into()) @@ -346,7 +346,7 @@ where /// use actix_web::{middleware, web, App}; /// use actix_web::http::{header::CONTENT_TYPE, HeaderValue}; /// - /// fn index() -> &'static str { + /// async fn index() -> &'static str { /// "Welcome!" /// } /// @@ -404,7 +404,7 @@ where /// use actix_web::{web, App}; /// use actix_web::http::{header::CONTENT_TYPE, HeaderValue}; /// - /// fn index() -> &'static str { + /// async fn index() -> &'static str { /// "Welcome!" /// } /// diff --git a/src/data.rs b/src/data.rs index a11175c12..a026946aa 100644 --- a/src/data.rs +++ b/src/data.rs @@ -45,7 +45,7 @@ pub(crate) trait DataFactory { /// } /// /// /// Use `Data` extractor to access data in handler. -/// fn index(data: web::Data>) { +/// async fn index(data: web::Data>) { /// let mut data = data.lock().unwrap(); /// data.counter += 1; /// } diff --git a/src/extract.rs b/src/extract.rs index 20a1180ec..9c8633368 100644 --- a/src/extract.rs +++ b/src/extract.rs @@ -75,7 +75,7 @@ pub trait FromRequest: Sized { /// } /// /// /// extract `Thing` from request -/// fn index(supplied_thing: Option) -> String { +/// async fn index(supplied_thing: Option) -> String { /// match supplied_thing { /// // Puns not intended /// Some(thing) => format!("Got something: {:?}", thing), @@ -146,7 +146,7 @@ where /// } /// /// /// extract `Thing` from request -/// fn index(supplied_thing: Result) -> String { +/// async fn index(supplied_thing: Result) -> String { /// match supplied_thing { /// Ok(thing) => format!("Got thing: {:?}", thing), /// Err(e) => format!("Error extracting thing: {}", e) diff --git a/src/handler.rs b/src/handler.rs index 767f630da..a7023422b 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -15,111 +15,8 @@ use crate::request::HttpRequest; use crate::responder::Responder; use crate::service::{ServiceRequest, ServiceResponse}; -/// Handler converter factory -pub trait Factory: Clone -where - R: Responder, -{ - fn call(&self, param: T) -> R; -} - -impl Factory<(), R> for F -where - F: Fn() -> R + Clone, - R: Responder, -{ - fn call(&self, _: ()) -> R { - (self)() - } -} - -#[doc(hidden)] -pub struct Handler -where - F: Factory, - R: Responder, -{ - hnd: F, - _t: PhantomData<(T, R)>, -} - -impl Handler -where - F: Factory, - R: Responder, -{ - pub fn new(hnd: F) -> Self { - Handler { - hnd, - _t: PhantomData, - } - } -} - -impl Clone for Handler -where - F: Factory, - R: Responder, -{ - fn clone(&self) -> Self { - Self { - hnd: self.hnd.clone(), - _t: PhantomData, - } - } -} - -impl Service for Handler -where - F: Factory, - R: Responder, -{ - type Request = (T, HttpRequest); - type Response = ServiceResponse; - type Error = Infallible; - type Future = HandlerServiceResponse; - - fn poll_ready(&mut self, _: &mut Context) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, (param, req): (T, HttpRequest)) -> Self::Future { - let fut = self.hnd.call(param).respond_to(&req); - HandlerServiceResponse { - fut, - req: Some(req), - } - } -} - -#[pin_project] -pub struct HandlerServiceResponse { - #[pin] - fut: T::Future, - req: Option, -} - -impl Future for HandlerServiceResponse { - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let this = self.project(); - - match this.fut.poll(cx) { - Poll::Ready(Ok(res)) => { - Poll::Ready(Ok(ServiceResponse::new(this.req.take().unwrap(), res))) - } - Poll::Pending => Poll::Pending, - Poll::Ready(Err(e)) => { - let res: Response = e.into().into(); - Poll::Ready(Ok(ServiceResponse::new(this.req.take().unwrap(), res))) - } - } - } -} - /// Async handler converter factory -pub trait AsyncFactory: Clone + 'static +pub trait Factory: Clone + 'static where R: Future, O: Responder, @@ -127,7 +24,7 @@ where fn call(&self, param: T) -> R; } -impl AsyncFactory<(), R, O> for F +impl Factory<(), R, O> for F where F: Fn() -> R + Clone + 'static, R: Future, @@ -139,9 +36,9 @@ where } #[doc(hidden)] -pub struct AsyncHandler +pub struct Handler where - F: AsyncFactory, + F: Factory, R: Future, O: Responder, { @@ -149,51 +46,51 @@ where _t: PhantomData<(T, R, O)>, } -impl AsyncHandler +impl Handler where - F: AsyncFactory, + F: Factory, R: Future, O: Responder, { pub fn new(hnd: F) -> Self { - AsyncHandler { + Handler { hnd, _t: PhantomData, } } } -impl Clone for AsyncHandler +impl Clone for Handler where - F: AsyncFactory, + F: Factory, R: Future, O: Responder, { fn clone(&self) -> Self { - AsyncHandler { + Handler { hnd: self.hnd.clone(), _t: PhantomData, } } } -impl Service for AsyncHandler +impl Service for Handler where - F: AsyncFactory, + F: Factory, R: Future, O: Responder, { type Request = (T, HttpRequest); type Response = ServiceResponse; type Error = Infallible; - type Future = AsyncHandlerServiceResponse; + type Future = HandlerServiceResponse; fn poll_ready(&mut self, _: &mut Context) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, (param, req): (T, HttpRequest)) -> Self::Future { - AsyncHandlerServiceResponse { + HandlerServiceResponse { fut: self.hnd.call(param), fut2: None, req: Some(req), @@ -203,7 +100,7 @@ where #[doc(hidden)] #[pin_project] -pub struct AsyncHandlerServiceResponse +pub struct HandlerServiceResponse where T: Future, R: Responder, @@ -215,7 +112,7 @@ where req: Option, } -impl Future for AsyncHandlerServiceResponse +impl Future for HandlerServiceResponse where T: Future, R: Responder, @@ -366,16 +263,7 @@ where /// FromRequest trait impl for tuples macro_rules! factory_tuple ({ $(($n:tt, $T:ident)),+} => { - impl Factory<($($T,)+), Res> for Func - where Func: Fn($($T,)+) -> Res + Clone, - Res: Responder, - { - fn call(&self, param: ($($T,)+)) -> Res { - (self)($(param.$n,)+) - } - } - - impl AsyncFactory<($($T,)+), Res, O> for Func + impl Factory<($($T,)+), Res, O> for Func where Func: Fn($($T,)+) -> Res + Clone + 'static, Res: Future, O: Responder, diff --git a/src/lib.rs b/src/lib.rs index 3cd1f78d6..8063d0d35 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ //! use actix_web::{web, App, Responder, HttpServer}; //! # use std::thread; //! -//! fn index(info: web::Path<(String, u32)>) -> impl Responder { +//! async fn index(info: web::Path<(String, u32)>) -> impl Responder { //! format!("Hello {}! id:{}", info.0, info.1) //! } //! @@ -136,7 +136,7 @@ pub mod dev { pub use crate::config::{AppConfig, AppService}; #[doc(hidden)] - pub use crate::handler::{AsyncFactory, Factory}; + pub use crate::handler::Factory; pub use crate::info::ConnectionInfo; pub use crate::rmap::ResourceMap; pub use crate::service::{ diff --git a/src/request.rs b/src/request.rs index 84744af28..19072fcb1 100644 --- a/src/request.rs +++ b/src/request.rs @@ -276,7 +276,7 @@ impl Drop for HttpRequest { /// use serde_derive::Deserialize; /// /// /// extract `Thing` from request -/// fn index(req: HttpRequest) -> String { +/// async fn index(req: HttpRequest) -> String { /// format!("Got thing: {:?}", req) /// } /// diff --git a/src/resource.rs b/src/resource.rs index a1c0d396b..a06530d48 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -17,7 +17,7 @@ use crate::data::Data; use crate::dev::{insert_slash, AppService, HttpServiceFactory, ResourceDef}; use crate::extract::FromRequest; use crate::guard::Guard; -use crate::handler::{AsyncFactory, Factory}; +use crate::handler::Factory; use crate::responder::Responder; use crate::route::{CreateRouteService, Route, RouteService}; use crate::service::{ServiceRequest, ServiceResponse}; @@ -98,7 +98,7 @@ where /// ```rust /// use actix_web::{web, guard, App, HttpResponse}; /// - /// fn index(data: web::Path<(String, String)>) -> &'static str { + /// async fn index(data: web::Path<(String, String)>) -> &'static str { /// "Welcome!" /// } /// @@ -156,9 +156,9 @@ where /// .route(web::delete().to(delete_handler)) /// ); /// } - /// # fn get_handler() {} - /// # fn post_handler() {} - /// # fn delete_handler() {} + /// # async fn get_handler() -> impl actix_web::Responder { HttpResponse::Ok() } + /// # async fn post_handler() -> impl actix_web::Responder { HttpResponse::Ok() } + /// # async fn delete_handler() -> impl actix_web::Responder { HttpResponse::Ok() } /// ``` pub fn route(mut self, route: Route) -> Self { self.routes.push(route); @@ -174,7 +174,7 @@ where /// use actix_web::{web, App, FromRequest}; /// /// /// extract text data from request - /// fn index(body: String) -> String { + /// async fn index(body: String) -> String { /// format!("Body {}!", body) /// } /// @@ -230,46 +230,14 @@ where /// # fn index(req: HttpRequest) -> HttpResponse { unimplemented!() } /// App::new().service(web::resource("/").route(web::route().to(index))); /// ``` - pub fn to(mut self, handler: F) -> Self + pub fn to(mut self, handler: F) -> Self where - F: Factory + 'static, - I: FromRequest + 'static, - R: Responder + 'static, - { - self.routes.push(Route::new().to(handler)); - self - } - - /// Register a new route and add async handler. - /// - /// ```rust - /// use actix_web::*; - /// - /// async fn index(req: HttpRequest) -> Result { - /// Ok(HttpResponse::Ok().finish()) - /// } - /// - /// App::new().service(web::resource("/").to_async(index)); - /// ``` - /// - /// This is shortcut for: - /// - /// ```rust - /// # use actix_web::*; - /// # async fn index(req: HttpRequest) -> Result { - /// # unimplemented!() - /// # } - /// App::new().service(web::resource("/").route(web::route().to_async(index))); - /// ``` - #[allow(clippy::wrong_self_convention)] - pub fn to_async(mut self, handler: F) -> Self - where - F: AsyncFactory, + F: Factory, I: FromRequest + 'static, R: Future + 'static, U: Responder + 'static, { - self.routes.push(Route::new().to_async(handler)); + self.routes.push(Route::new().to(handler)); self } @@ -327,7 +295,7 @@ where /// use actix_web::{web, App}; /// use actix_web::http::{header::CONTENT_TYPE, HeaderValue}; /// - /// fn index() -> &'static str { + /// async fn index() -> &'static str { /// "Welcome!" /// } /// @@ -705,17 +673,16 @@ mod tests { } #[test] - fn test_to_async() { + fn test_to() { block_on(async { - let mut srv = init_service(App::new().service( - web::resource("/test").to_async(|| { + let mut srv = + init_service(App::new().service(web::resource("/test").to(|| { async { delay_for(Duration::from_millis(100)).await; Ok::<_, Error>(HttpResponse::Ok()) } - }), - )) - .await; + }))) + .await; let req = TestRequest::with_uri("/test").to_request(); let resp = call_service(&mut srv, req).await; assert_eq!(resp.status(), StatusCode::OK); diff --git a/src/responder.rs b/src/responder.rs index 3f1471721..b254567de 100644 --- a/src/responder.rs +++ b/src/responder.rs @@ -475,9 +475,10 @@ pub(crate) mod tests { let mut srv = init_service( App::new() .service( - web::resource("/none").to(|| -> Option<&'static str> { None }), + web::resource("/none") + .to(|| async { Option::<&'static str>::None }), ) - .service(web::resource("/some").to(|| Some("some"))), + .service(web::resource("/some").to(|| async { Some("some") })), ) .await; diff --git a/src/route.rs b/src/route.rs index 51305d840..3ebfc3f52 100644 --- a/src/route.rs +++ b/src/route.rs @@ -5,11 +5,11 @@ use std::task::{Context, Poll}; use actix_http::{http::Method, Error}; use actix_service::{Service, ServiceFactory}; -use futures::future::{ok, Either, FutureExt, LocalBoxFuture, Ready}; +use futures::future::{ok, ready, Either, FutureExt, LocalBoxFuture, Ready}; use crate::extract::FromRequest; use crate::guard::{self, Guard}; -use crate::handler::{AsyncFactory, AsyncHandler, Extract, Factory, Handler}; +use crate::handler::{Extract, Factory, Handler}; use crate::responder::Responder; use crate::service::{ServiceRequest, ServiceResponse}; use crate::HttpResponse; @@ -49,7 +49,7 @@ impl Route { pub fn new() -> Route { Route { service: Box::new(RouteNewService::new(Extract::new(Handler::new(|| { - HttpResponse::NotFound() + ready(HttpResponse::NotFound()) })))), guards: Rc::new(Vec::new()), } @@ -187,7 +187,7 @@ impl Route { /// } /// /// /// extract path info using serde - /// fn index(info: web::Path) -> String { + /// async fn index(info: web::Path) -> String { /// format!("Welcome {}!", info.username) /// } /// @@ -212,7 +212,7 @@ impl Route { /// } /// /// /// extract path info using serde - /// fn index(path: web::Path, query: web::Query>, body: web::Json) -> String { + /// async fn index(path: web::Path, query: web::Query>, body: web::Json) -> String { /// format!("Welcome {}!", path.username) /// } /// @@ -223,52 +223,15 @@ impl Route { /// ); /// } /// ``` - pub fn to(mut self, handler: F) -> Route + pub fn to(mut self, handler: F) -> Self where - F: Factory + 'static, - T: FromRequest + 'static, - R: Responder + 'static, - { - self.service = - Box::new(RouteNewService::new(Extract::new(Handler::new(handler)))); - self - } - - /// Set async handler function, use request extractors for parameters. - /// This method has to be used if your handler function returns `impl Future<>` - /// - /// ```rust - /// use actix_web::{web, App, Error}; - /// use serde_derive::Deserialize; - /// - /// #[derive(Deserialize)] - /// struct Info { - /// username: String, - /// } - /// - /// /// extract path info using serde - /// async fn index(info: web::Path) -> Result<&'static str, Error> { - /// Ok("Hello World!") - /// } - /// - /// fn main() { - /// let app = App::new().service( - /// web::resource("/{username}/index.html") // <- define path parameters - /// .route(web::get().to_async(index)) // <- register async handler - /// ); - /// } - /// ``` - #[allow(clippy::wrong_self_convention)] - pub fn to_async(mut self, handler: F) -> Self - where - F: AsyncFactory, + F: Factory, T: FromRequest + 'static, R: Future + 'static, U: Responder + 'static, { - self.service = Box::new(RouteNewService::new(Extract::new(AsyncHandler::new( - handler, - )))); + self.service = + Box::new(RouteNewService::new(Extract::new(Handler::new(handler)))); self } } @@ -402,22 +365,24 @@ mod tests { web::resource("/test") .route(web::get().to(|| HttpResponse::Ok())) .route(web::put().to(|| { - Err::(error::ErrorBadRequest("err")) + async { + Err::(error::ErrorBadRequest("err")) + } })) - .route(web::post().to_async(|| { + .route(web::post().to(|| { async { delay_for(Duration::from_millis(100)).await; HttpResponse::Created() } })) - .route(web::delete().to_async(|| { + .route(web::delete().to(|| { async { delay_for(Duration::from_millis(100)).await; Err::(error::ErrorBadRequest("err")) } })), ) - .service(web::resource("/json").route(web::get().to_async(|| { + .service(web::resource("/json").route(web::get().to(|| { async { delay_for(Duration::from_millis(25)).await; web::Json(MyObject { diff --git a/src/scope.rs b/src/scope.rs index f5ffe05fa..e5c04d71e 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -46,7 +46,7 @@ type BoxedResponse = LocalBoxFuture<'static, Result>; /// fn main() { /// let app = App::new().service( /// web::scope("/{project_id}/") -/// .service(web::resource("/path1").to(|| HttpResponse::Ok())) +/// .service(web::resource("/path1").to(|| async { HttpResponse::Ok() })) /// .service(web::resource("/path2").route(web::get().to(|| HttpResponse::Ok()))) /// .service(web::resource("/path3").route(web::head().to(|| HttpResponse::MethodNotAllowed()))) /// ); @@ -101,7 +101,7 @@ where /// ```rust /// use actix_web::{web, guard, App, HttpRequest, HttpResponse}; /// - /// fn index(data: web::Path<(String, String)>) -> &'static str { + /// async fn index(data: web::Path<(String, String)>) -> &'static str { /// "Welcome!" /// } /// @@ -132,7 +132,7 @@ where /// counter: Cell, /// } /// - /// fn index(data: web::Data) { + /// async fn index(data: web::Data) { /// data.counter.set(data.counter.get() + 1); /// } /// @@ -228,7 +228,7 @@ where /// /// struct AppState; /// - /// fn index(req: HttpRequest) -> &'static str { + /// async fn index(req: HttpRequest) -> &'static str { /// "Welcome!" /// } /// @@ -258,7 +258,7 @@ where /// ```rust /// use actix_web::{web, App, HttpResponse}; /// - /// fn index(data: web::Path<(String, String)>) -> &'static str { + /// async fn index(data: web::Path<(String, String)>) -> &'static str { /// "Welcome!" /// } /// @@ -356,7 +356,7 @@ where /// use actix_web::{web, App}; /// use actix_web::http::{header::CONTENT_TYPE, HeaderValue}; /// - /// fn index() -> &'static str { + /// async fn index() -> &'static str { /// "Welcome!" /// } /// @@ -846,8 +846,10 @@ mod tests { let mut srv = init_service(App::new().service(web::scope("/ab-{project}").service( web::resource("/path1").to(|r: HttpRequest| { - HttpResponse::Ok() - .body(format!("project: {}", &r.match_info()["project"])) + async move { + HttpResponse::Ok() + .body(format!("project: {}", &r.match_info()["project"])) + } }), ))) .await; @@ -962,8 +964,12 @@ mod tests { let mut srv = init_service(App::new().service(web::scope("/app").service( web::scope("/{project_id}").service(web::resource("/path1").to( |r: HttpRequest| { - HttpResponse::Created() - .body(format!("project: {}", &r.match_info()["project_id"])) + async move { + HttpResponse::Created().body(format!( + "project: {}", + &r.match_info()["project_id"] + )) + } }, )), ))) @@ -989,11 +995,13 @@ mod tests { let mut srv = init_service(App::new().service(web::scope("/app").service( web::scope("/{project}").service(web::scope("/{id}").service( web::resource("/path1").to(|r: HttpRequest| { - HttpResponse::Created().body(format!( - "project: {} - {}", - &r.match_info()["project"], - &r.match_info()["id"], - )) + async move { + HttpResponse::Created().body(format!( + "project: {} - {}", + &r.match_info()["project"], + &r.match_info()["id"], + )) + } }), )), ))) @@ -1241,12 +1249,14 @@ mod tests { s.route( "/", web::get().to(|req: HttpRequest| { - HttpResponse::Ok().body(format!( - "{}", - req.url_for("youtube", &["xxxxxx"]) - .unwrap() - .as_str() - )) + async move { + HttpResponse::Ok().body(format!( + "{}", + req.url_for("youtube", &["xxxxxx"]) + .unwrap() + .as_str() + )) + } }), ); })); @@ -1267,8 +1277,12 @@ mod tests { let mut srv = init_service(App::new().service(web::scope("/a").service( web::scope("/b").service(web::resource("/c/{stuff}").name("c").route( web::get().to(|req: HttpRequest| { - HttpResponse::Ok() - .body(format!("{}", req.url_for("c", &["12345"]).unwrap())) + async move { + HttpResponse::Ok().body(format!( + "{}", + req.url_for("c", &["12345"]).unwrap() + )) + } }), )), ))) diff --git a/src/test.rs b/src/test.rs index 8cee3bc6a..0776b0f15 100644 --- a/src/test.rs +++ b/src/test.rs @@ -55,7 +55,7 @@ pub fn default_service( /// fn test_init_service() { /// let mut app = test::init_service( /// App::new() -/// .service(web::resource("/test").to(|| HttpResponse::Ok())) +/// .service(web::resource("/test").to(|| async { HttpResponse::Ok() })) /// ); /// /// // Create request object @@ -94,14 +94,16 @@ where /// fn test_response() { /// let mut app = test::init_service( /// App::new() -/// .service(web::resource("/test").to(|| HttpResponse::Ok())) -/// ); +/// .service(web::resource("/test").to(|| async { +/// HttpResponse::Ok() +/// })) +/// ).await; /// /// // Create request object /// let req = test::TestRequest::with_uri("/test").to_request(); /// /// // Call application -/// let resp = test::call_service(&mut app, req); +/// let resp = test::call_service(&mut app, req).await; /// assert_eq!(resp.status(), StatusCode::OK); /// } /// ``` @@ -125,15 +127,17 @@ where /// let mut app = test::init_service( /// App::new().service( /// web::resource("/index.html") -/// .route(web::post().to( -/// || HttpResponse::Ok().body("welcome!"))))); +/// .route(web::post().to(|| async { +/// HttpResponse::Ok().body("welcome!") +/// }))) +/// ).await; /// /// let req = test::TestRequest::post() /// .uri("/index.html") /// .header(header::CONTENT_TYPE, "application/json") /// .to_request(); /// -/// let result = test::read_response(&mut app, req); +/// let result = test::read_response(&mut app, req).await; /// assert_eq!(result, Bytes::from_static(b"welcome!")); /// } /// ``` @@ -167,15 +171,17 @@ where /// let mut app = test::init_service( /// App::new().service( /// web::resource("/index.html") -/// .route(web::post().to( -/// || HttpResponse::Ok().body("welcome!"))))); +/// .route(web::post().to(|| async { +/// HttpResponse::Ok().body("welcome!") +/// }))) +/// ).await; /// /// let req = test::TestRequest::post() /// .uri("/index.html") /// .header(header::CONTENT_TYPE, "application/json") /// .to_request(); /// -/// let resp = test::call_service(&mut app, req); +/// let resp = test::call_service(&mut app, req).await; /// let result = test::read_body(resp); /// assert_eq!(result, Bytes::from_static(b"welcome!")); /// } @@ -221,10 +227,11 @@ where /// let mut app = test::init_service( /// App::new().service( /// web::resource("/people") -/// .route(web::post().to(|person: web::Json| { +/// .route(web::post().to(|person: web::Json| async { /// HttpResponse::Ok() /// .json(person.into_inner())}) -/// ))); +/// )) +/// ).await; /// /// let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes(); /// @@ -234,7 +241,7 @@ where /// .set_payload(payload) /// .to_request(); /// -/// let result: Person = test::read_response_json(&mut app, req); +/// let result: Person = test::read_response_json(&mut app, req).await; /// } /// ``` pub async fn read_response_json(app: &mut S, req: Request) -> T @@ -262,7 +269,7 @@ where /// use actix_web::{test, HttpRequest, HttpResponse, HttpMessage}; /// use actix_web::http::{header, StatusCode}; /// -/// fn index(req: HttpRequest) -> HttpResponse { +/// async fn index(req: HttpRequest) -> HttpResponse { /// if let Some(hdr) = req.headers().get(header::CONTENT_TYPE) { /// HttpResponse::Ok().into() /// } else { @@ -275,11 +282,11 @@ where /// let req = test::TestRequest::with_header("content-type", "text/plain") /// .to_http_request(); /// -/// let resp = test::block_on(index(req)).unwrap(); +/// let resp = index(req).await.unwrap(); /// assert_eq!(resp.status(), StatusCode::OK); /// /// let req = test::TestRequest::default().to_http_request(); -/// let resp = test::block_on(index(req)).unwrap(); +/// let resp = index(req).await.unwrap(); /// assert_eq!(resp.status(), StatusCode::BAD_REQUEST); /// } /// ``` @@ -535,9 +542,17 @@ mod tests { let mut app = init_service( App::new().service( web::resource("/index.html") - .route(web::put().to(|| HttpResponse::Ok().body("put!"))) - .route(web::patch().to(|| HttpResponse::Ok().body("patch!"))) - .route(web::delete().to(|| HttpResponse::Ok().body("delete!"))), + .route( + web::put().to(|| async { HttpResponse::Ok().body("put!") }), + ) + .route( + web::patch() + .to(|| async { HttpResponse::Ok().body("patch!") }), + ) + .route( + web::delete() + .to(|| async { HttpResponse::Ok().body("delete!") }), + ), ), ) .await; @@ -567,13 +582,11 @@ mod tests { #[test] fn test_response() { block_on(async { - let mut app = init_service( - App::new().service( - web::resource("/index.html") - .route(web::post().to(|| HttpResponse::Ok().body("welcome!"))), - ), - ) - .await; + let mut app = + init_service(App::new().service(web::resource("/index.html").route( + web::post().to(|| async { HttpResponse::Ok().body("welcome!") }), + ))) + .await; let req = TestRequest::post() .uri("/index.html") @@ -597,7 +610,7 @@ mod tests { let mut app = init_service(App::new().service(web::resource("/people").route( web::post().to(|person: web::Json| { - HttpResponse::Ok().json(person.into_inner()) + async { HttpResponse::Ok().json(person.into_inner()) } }), ))) .await; @@ -621,7 +634,7 @@ mod tests { let mut app = init_service(App::new().service(web::resource("/people").route( web::post().to(|person: web::Form| { - HttpResponse::Ok().json(person.into_inner()) + async { HttpResponse::Ok().json(person.into_inner()) } }), ))) .await; @@ -650,7 +663,7 @@ mod tests { let mut app = init_service(App::new().service(web::resource("/people").route( web::post().to(|person: web::Json| { - HttpResponse::Ok().json(person.into_inner()) + async { HttpResponse::Ok().json(person.into_inner()) } }), ))) .await; @@ -688,8 +701,7 @@ mod tests { } let mut app = init_service( - App::new() - .service(web::resource("/index.html").to_async(async_with_block)), + App::new().service(web::resource("/index.html").to(async_with_block)), ) .await; @@ -721,7 +733,7 @@ mod tests { // let addr = run_on(|| MyActor.start()); // let mut app = init_service(App::new().service( - // web::resource("/index.html").to_async(move || { + // web::resource("/index.html").to(move || { // addr.send(Num(1)).from_err().and_then(|res| { // if res == 1 { // HttpResponse::Ok() diff --git a/src/types/form.rs b/src/types/form.rs index 694fe6dbd..c20dc7a05 100644 --- a/src/types/form.rs +++ b/src/types/form.rs @@ -181,7 +181,7 @@ impl Responder for Form { /// /// /// Extract form data using serde. /// /// Custom configuration is used for this handler, max payload size is 4k -/// fn index(form: web::Form) -> Result { +/// async fn index(form: web::Form) -> Result { /// Ok(format!("Welcome {}!", form.username)) /// } /// diff --git a/src/types/json.rs b/src/types/json.rs index 19f8532bd..206a4e425 100644 --- a/src/types/json.rs +++ b/src/types/json.rs @@ -46,7 +46,7 @@ use crate::responder::Responder; /// } /// /// /// deserialize `Info` from request's body -/// fn index(info: web::Json) -> String { +/// async fn index(info: web::Json) -> String { /// format!("Welcome {}!", info.username) /// } /// @@ -157,7 +157,7 @@ impl Responder for Json { /// } /// /// /// deserialize `Info` from request's body -/// fn index(info: web::Json) -> String { +/// async fn index(info: web::Json) -> String { /// format!("Welcome {}!", info.username) /// } /// @@ -217,7 +217,7 @@ where /// } /// /// /// deserialize `Info` from request's body, max payload size is 4kb -/// fn index(info: web::Json) -> String { +/// async fn index(info: web::Json) -> String { /// format!("Welcome {}!", info.username) /// } /// diff --git a/src/types/path.rs b/src/types/path.rs index 89b9392be..29a574feb 100644 --- a/src/types/path.rs +++ b/src/types/path.rs @@ -24,7 +24,7 @@ use crate::FromRequest; /// /// extract path info from "/{username}/{count}/index.html" url /// /// {username} - deserializes to a String /// /// {count} - - deserializes to a u32 -/// fn index(info: web::Path<(String, u32)>) -> String { +/// async fn index(info: web::Path<(String, u32)>) -> String { /// format!("Welcome {}! {}", info.0, info.1) /// } /// @@ -49,7 +49,7 @@ use crate::FromRequest; /// } /// /// /// extract `Info` from a path using serde -/// fn index(info: web::Path) -> Result { +/// async fn index(info: web::Path) -> Result { /// Ok(format!("Welcome {}!", info.username)) /// } /// @@ -119,7 +119,7 @@ impl fmt::Display for Path { /// /// extract path info from "/{username}/{count}/index.html" url /// /// {username} - deserializes to a String /// /// {count} - - deserializes to a u32 -/// fn index(info: web::Path<(String, u32)>) -> String { +/// async fn index(info: web::Path<(String, u32)>) -> String { /// format!("Welcome {}! {}", info.0, info.1) /// } /// @@ -144,7 +144,7 @@ impl fmt::Display for Path { /// } /// /// /// extract `Info` from a path using serde -/// fn index(info: web::Path) -> Result { +/// async fn index(info: web::Path) -> Result { /// Ok(format!("Welcome {}!", info.username)) /// } /// @@ -206,7 +206,7 @@ where /// } /// /// // deserialize `Info` from request's path -/// fn index(folder: web::Path) -> String { +/// async fn index(folder: web::Path) -> String { /// format!("Selected folder: {:?}!", folder) /// } /// diff --git a/src/types/payload.rs b/src/types/payload.rs index 61f7328b4..ee7e11667 100644 --- a/src/types/payload.rs +++ b/src/types/payload.rs @@ -40,7 +40,7 @@ use crate::request::HttpRequest; /// fn main() { /// let app = App::new().service( /// web::resource("/index.html").route( -/// web::get().to_async(index)) +/// web::get().to(index)) /// ); /// } /// ``` @@ -88,7 +88,7 @@ impl Stream for Payload { /// fn main() { /// let app = App::new().service( /// web::resource("/index.html").route( -/// web::get().to_async(index)) +/// web::get().to(index)) /// ); /// } /// ``` @@ -117,7 +117,7 @@ impl FromRequest for Payload { /// use actix_web::{web, App}; /// /// /// extract binary data from request -/// fn index(body: Bytes) -> String { +/// async fn index(body: Bytes) -> String { /// format!("Body {:?}!", body) /// } /// @@ -169,7 +169,7 @@ impl FromRequest for Bytes { /// use actix_web::{web, App, FromRequest}; /// /// /// extract text data from request -/// fn index(text: String) -> String { +/// async fn index(text: String) -> String { /// format!("Body {}!", text) /// } /// diff --git a/src/types/query.rs b/src/types/query.rs index 8061d7233..e442f1c31 100644 --- a/src/types/query.rs +++ b/src/types/query.rs @@ -40,7 +40,7 @@ use crate::request::HttpRequest; /// // Use `Query` extractor for query information (and destructure it within the signature). /// // This handler gets called only if the request's query string contains a `username` field. /// // The correct request for this handler would be `/index.html?id=64&response_type=Code"`. -/// fn index(web::Query(info): web::Query) -> String { +/// async fn index(web::Query(info): web::Query) -> String { /// format!("Authorization request for client with id={} and type={:?}!", info.id, info.response_type) /// } /// @@ -118,7 +118,7 @@ impl fmt::Display for Query { /// // Use `Query` extractor for query information. /// // This handler get called only if request's query contains `username` field /// // The correct request for this handler would be `/index.html?id=64&response_type=Code"` -/// fn index(info: web::Query) -> String { +/// async fn index(info: web::Query) -> String { /// format!("Authorization request for client with id={} and type={:?}!", info.id, info.response_type) /// } /// @@ -179,7 +179,7 @@ where /// } /// /// /// deserialize `Info` from request's querystring -/// fn index(info: web::Query) -> String { +/// async fn index(info: web::Query) -> String { /// format!("Welcome {}!", info.username) /// } /// diff --git a/src/web.rs b/src/web.rs index 099e26627..22630ae81 100644 --- a/src/web.rs +++ b/src/web.rs @@ -8,7 +8,7 @@ pub use futures::channel::oneshot::Canceled; use crate::error::Error; use crate::extract::FromRequest; -use crate::handler::{AsyncFactory, Factory}; +use crate::handler::Factory; use crate::resource::Resource; use crate::responder::Responder; use crate::route::Route; @@ -231,10 +231,10 @@ pub fn method(method: Method) -> Route { /// Create a new route and add handler. /// /// ```rust -/// use actix_web::{web, App, HttpResponse}; +/// use actix_web::{web, App, HttpResponse, Responder}; /// -/// fn index() -> HttpResponse { -/// unimplemented!() +/// async fn index() -> impl Responder { +/// HttpResponse::Ok() /// } /// /// App::new().service( @@ -242,36 +242,14 @@ pub fn method(method: Method) -> Route { /// web::to(index)) /// ); /// ``` -pub fn to(handler: F) -> Route +pub fn to(handler: F) -> Route where - F: Factory + 'static, - I: FromRequest + 'static, - R: Responder + 'static, -{ - Route::new().to(handler) -} - -/// Create a new route and add async handler. -/// -/// ```rust -/// use actix_web::{web, App, HttpResponse, Error}; -/// -/// async fn index() -> Result { -/// Ok(HttpResponse::Ok().finish()) -/// } -/// -/// App::new().service(web::resource("/").route( -/// web::to_async(index)) -/// ); -/// ``` -pub fn to_async(handler: F) -> Route -where - F: AsyncFactory, + F: Factory, I: FromRequest + 'static, R: Future + 'static, U: Responder + 'static, { - Route::new().to_async(handler) + Route::new().to(handler) } /// Create raw service for a specific path. diff --git a/tests/test_server.rs b/tests/test_server.rs index eeaedec05..0114b21ff 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -11,13 +11,11 @@ use bytes::Bytes; use flate2::read::GzDecoder; use flate2::write::{GzEncoder, ZlibDecoder, ZlibEncoder}; use flate2::Compression; -use futures::future::ok; -use futures::stream::once; +use futures::{future::ok, stream::once}; use rand::{distributions::Alphanumeric, Rng}; -use actix_connect::start_default_resolver; use actix_web::middleware::{BodyEncoding, Compress}; -use actix_web::{dev, http, test, web, App, HttpResponse, HttpServer}; +use actix_web::{dev, http, web, App, HttpResponse, HttpServer}; const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World \ @@ -817,18 +815,19 @@ fn test_brotli_encoding_large() { // } #[cfg(all( - feature = "rust-tls", - feature = "ssl", + feature = "rustls", + feature = "openssl", any(feature = "flate2-zlib", feature = "flate2-rust") ))] #[test] fn test_reading_deflate_encoding_large_random_ssl() { block_on(async { - use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; - use rustls::internal::pemfile::{certs, pkcs8_private_keys}; - use rustls::{NoClientAuth, ServerConfig}; + use open_ssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; + use rust_tls::internal::pemfile::{certs, pkcs8_private_keys}; + use rust_tls::{NoClientAuth, ServerConfig}; use std::fs::File; use std::io::BufReader; + use std::sync::mpsc; let addr = TestServer::unused_addr(); let (tx, rx) = mpsc::channel(); @@ -838,7 +837,7 @@ fn test_reading_deflate_encoding_large_random_ssl() { .take(160_000) .collect::(); - thread::spawn(move || { + std::thread::spawn(move || { let sys = actix_rt::System::new("test"); // load ssl keys @@ -851,11 +850,13 @@ fn test_reading_deflate_encoding_large_random_ssl() { let srv = HttpServer::new(|| { App::new().service(web::resource("/").route(web::to(|bytes: Bytes| { - Ok::<_, Error>( - HttpResponse::Ok() - .encoding(http::ContentEncoding::Identity) - .body(bytes), - ) + async move { + Ok::<_, Error>( + HttpResponse::Ok() + .encoding(http::ContentEncoding::Identity) + .body(bytes), + ) + } }))) }) .bind_rustls(addr, config) @@ -866,8 +867,7 @@ fn test_reading_deflate_encoding_large_random_ssl() { let _ = sys.run(); }); let (srv, _sys) = rx.recv().unwrap(); - test::block_on(futures::lazy(|| Ok::<_, ()>(start_default_resolver()))).unwrap(); - let client = test::run_on(|| { + let client = { let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); builder.set_verify(SslVerifyMode::NONE); let _ = builder.set_alpn_protos(b"\x02h2\x08http/1.1").unwrap(); @@ -880,7 +880,7 @@ fn test_reading_deflate_encoding_large_random_ssl() { .finish(), ) .finish() - }); + }; // encode data let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); @@ -893,11 +893,11 @@ fn test_reading_deflate_encoding_large_random_ssl() { .header(http::header::CONTENT_ENCODING, "deflate") .send_body(enc); - let mut response = test::block_on(req).unwrap(); + let mut response = req.await.unwrap(); assert!(response.status().is_success()); // read response - let bytes = test::block_on(response.body()).unwrap(); + let bytes = response.body().await.unwrap(); assert_eq!(bytes.len(), data.len()); assert_eq!(bytes, Bytes::from(data)); From 512dd2be636ba796ab8e345319e330902449088d Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 22 Nov 2019 07:01:05 +0600 Subject: [PATCH 066/176] disable rustls support --- Cargo.toml | 2 +- README.md | 3 +-- actix-http/Cargo.toml | 4 ++-- awc/Cargo.toml | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6c9d03487..7524bff73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,7 +66,7 @@ fail = ["actix-http/fail"] openssl = ["open-ssl", "actix-server/openssl", "awc/openssl"] # rustls -rustls = ["rust-tls", "actix-server/rustls", "awc/rustls"] +# rustls = ["rust-tls", "actix-server/rustls", "awc/rustls"] [dependencies] actix-codec = "0.2.0-alpha.1" diff --git a/README.md b/README.md index cee8b73c0..00bb3ec4e 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,9 @@ Actix web is a simple, pragmatic and extremely fast web framework for Rust. * [User Guide](https://actix.rs/docs/) * [API Documentation (1.0)](https://docs.rs/actix-web/) -* [API Documentation (0.7)](https://docs.rs/actix-web/0.7.19/actix_web/) * [Chat on gitter](https://gitter.im/actix/actix) * Cargo package: [actix-web](https://crates.io/crates/actix-web) -* Minimum supported Rust version: 1.36 or later +* Minimum supported Rust version: 1.39 or later ## Example diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 32af97ad9..5bffa0ac3 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -29,7 +29,7 @@ default = [] openssl = ["open-ssl", "actix-connect/openssl", "tokio-openssl"] # rustls support -rustls = ["rust-tls", "webpki-roots", "actix-connect/rustls"] +# rustls = ["rust-tls", "webpki-roots", "actix-connect/rustls"] # brotli encoding, requires c compiler brotli = ["brotli2"] @@ -107,7 +107,7 @@ webpki-roots = { version = "0.18", optional = true } [dev-dependencies] actix-rt = "1.0.0-alpha.1" -actix-server = { version = "0.8.0-alpha.1", features=["openssl", "rustls"] } +actix-server = { version = "0.8.0-alpha.1", features=["openssl"] } actix-connect = { version = "1.0.0-alpha.1", features=["openssl"] } actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } env_logger = "0.6" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 4d5fde549..eed7eabe7 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -30,7 +30,7 @@ default = ["brotli", "flate2-zlib"] openssl = ["open-ssl", "actix-http/openssl"] # rustls -rustls = ["rust-tls", "actix-http/rustls"] +# rustls = ["rust-tls", "actix-http/rustls"] # brotli encoding, requires c compiler brotli = ["actix-http/brotli"] From e668acc596334eeda3ebffdcef90467396087d7a Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 22 Nov 2019 10:13:32 +0600 Subject: [PATCH 067/176] update travis config --- .travis.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 82db86a6e..683f77cc5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,9 +10,9 @@ matrix: include: - rust: stable - rust: beta - - rust: nightly-2019-08-10 + - rust: nightly-2019-11-20 allow_failures: - - rust: nightly-2019-08-10 + - rust: nightly-2019-11-20 env: global: @@ -25,7 +25,7 @@ before_install: - sudo apt-get install -y openssl libssl-dev libelf-dev libdw-dev cmake gcc binutils-dev libiberty-dev before_cache: | - if [[ "$TRAVIS_RUST_VERSION" == "nightly-2019-08-10" ]]; then + if [[ "$TRAVIS_RUST_VERSION" == "nightly-2019-11-20" ]]; then RUSTFLAGS="--cfg procmacro2_semver_exempt" cargo install --version 0.6.11 cargo-tarpaulin fi @@ -37,8 +37,8 @@ script: - cargo update - cargo check --all --no-default-features - cargo test --all-features --all -- --nocapture - - cd actix-http; cargo test --no-default-features --features="rust-tls" -- --nocapture; cd .. - - cd awc; cargo test --no-default-features --features="rust-tls" -- --nocapture; cd .. + # - cd actix-http; cargo test --no-default-features --features="rustls" -- --nocapture; cd .. + # - cd awc; cargo test --no-default-features --features="rustls" -- --nocapture; cd .. # Upload docs after_success: @@ -51,7 +51,7 @@ after_success: echo "Uploaded documentation" fi - | - if [[ "$TRAVIS_RUST_VERSION" == "nightly-2019-08-10" ]]; then + if [[ "$TRAVIS_RUST_VERSION" == "nightly-2019-11-20" ]]; then taskset -c 0 cargo tarpaulin --out Xml --all --all-features bash <(curl -s https://codecov.io/bash) echo "Uploaded code coverage" From 57981ca04add91258c3302bdfb4901772ac5f3e8 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 22 Nov 2019 11:49:35 +0600 Subject: [PATCH 068/176] update tests to async handlers --- actix-cors/src/lib.rs | 2 +- actix-files/src/lib.rs | 16 +++-- actix-http/src/message.rs | 10 ++- actix-http/src/response.rs | 5 +- actix-identity/src/lib.rs | 16 ++--- actix-session/src/cookie.rs | 30 ++++++--- actix-web-codegen/tests/test_macro.rs | 22 +++---- awc/tests/test_client.rs | 87 +++++++++++++++------------ test-server/src/lib.rs | 2 +- 9 files changed, 111 insertions(+), 79 deletions(-) diff --git a/actix-cors/src/lib.rs b/actix-cors/src/lib.rs index 40f9fdf99..db7e4cc4f 100644 --- a/actix-cors/src/lib.rs +++ b/actix-cors/src/lib.rs @@ -11,7 +11,7 @@ //! use actix_cors::Cors; //! use actix_web::{http, web, App, HttpRequest, HttpResponse, HttpServer}; //! -//! fn index(req: HttpRequest) -> &'static str { +//! async fn index(req: HttpRequest) -> &'static str { //! "Hello world" //! } //! diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index 72db16957..2f8a5c49c 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -1176,9 +1176,11 @@ mod tests { let mut srv = test::init_service(App::new().wrap(Compress::default()).service( web::resource("/").to(|| { - NamedFile::open("Cargo.toml") - .unwrap() - .set_content_encoding(header::ContentEncoding::Identity) + async { + NamedFile::open("Cargo.toml") + .unwrap() + .set_content_encoding(header::ContentEncoding::Identity) + } }), )) .await; @@ -1199,9 +1201,11 @@ mod tests { let mut srv = test::init_service(App::new().wrap(Compress::default()).service( web::resource("/").to(|| { - NamedFile::open("Cargo.toml") - .unwrap() - .set_content_encoding(header::ContentEncoding::Gzip) + async { + NamedFile::open("Cargo.toml") + .unwrap() + .set_content_encoding(header::ContentEncoding::Gzip) + } }), )) .await; diff --git a/actix-http/src/message.rs b/actix-http/src/message.rs index 316df2611..5994ed39e 100644 --- a/actix-http/src/message.rs +++ b/actix-http/src/message.rs @@ -388,6 +388,12 @@ impl BoxedResponseHead { pub fn new(status: StatusCode) -> Self { RESPONSE_POOL.with(|p| p.get_message(status)) } + + pub(crate) fn take(&mut self) -> Self { + BoxedResponseHead { + head: self.head.take(), + } + } } impl std::ops::Deref for BoxedResponseHead { @@ -406,7 +412,9 @@ impl std::ops::DerefMut for BoxedResponseHead { impl Drop for BoxedResponseHead { fn drop(&mut self) { - RESPONSE_POOL.with(|p| p.release(self.head.take().unwrap())) + if let Some(head) = self.head.take() { + RESPONSE_POOL.with(move |p| p.release(head)) + } } } diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index 31876813b..a5f18cc79 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -288,10 +288,7 @@ impl Future for Response { fn poll(mut self: Pin<&mut Self>, _: &mut Context) -> Poll { Poll::Ready(Ok(Response { - head: std::mem::replace( - &mut self.head, - BoxedResponseHead::new(StatusCode::OK), - ), + head: self.head.take(), body: self.body.take_body(), error: self.error.take(), })) diff --git a/actix-identity/src/lib.rs b/actix-identity/src/lib.rs index 30761d872..2980a7753 100644 --- a/actix-identity/src/lib.rs +++ b/actix-identity/src/lib.rs @@ -16,7 +16,7 @@ //! use actix_web::*; //! use actix_identity::{Identity, CookieIdentityPolicy, IdentityService}; //! -//! fn index(id: Identity) -> String { +//! async fn index(id: Identity) -> String { //! // access request identity //! if let Some(id) = id.identity() { //! format!("Welcome! {}", id) @@ -25,12 +25,12 @@ //! } //! } //! -//! fn login(id: Identity) -> HttpResponse { +//! async fn login(id: Identity) -> HttpResponse { //! id.remember("User1".to_owned()); // <- remember identity //! HttpResponse::Ok().finish() //! } //! -//! fn logout(id: Identity) -> HttpResponse { +//! async fn logout(id: Identity) -> HttpResponse { //! id.forget(); // <- remove identity //! HttpResponse::Ok().finish() //! } @@ -764,11 +764,13 @@ mod tests { .secure(false) .name(COOKIE_NAME)))) .service(web::resource("/").to(|id: Identity| { - let identity = id.identity(); - if identity.is_none() { - id.remember(COOKIE_LOGIN.to_string()) + async move { + let identity = id.identity(); + if identity.is_none() { + id.remember(COOKIE_LOGIN.to_string()) + } + web::Json(identity) } - web::Json(identity) })), ) .await diff --git a/actix-session/src/cookie.rs b/actix-session/src/cookie.rs index 9a486cce2..bb5fba97b 100644 --- a/actix-session/src/cookie.rs +++ b/actix-session/src/cookie.rs @@ -371,8 +371,10 @@ mod tests { App::new() .wrap(CookieSession::signed(&[0; 32]).secure(false)) .service(web::resource("/").to(|ses: Session| { - let _ = ses.set("counter", 100); - "test" + async move { + let _ = ses.set("counter", 100); + "test" + } })), ) .await; @@ -394,8 +396,10 @@ mod tests { App::new() .wrap(CookieSession::private(&[0; 32]).secure(false)) .service(web::resource("/").to(|ses: Session| { - let _ = ses.set("counter", 100); - "test" + async move { + let _ = ses.set("counter", 100); + "test" + } })), ) .await; @@ -417,8 +421,10 @@ mod tests { App::new() .wrap(CookieSession::signed(&[0; 32]).secure(false)) .service(web::resource("/").to(|ses: Session| { - let _ = ses.set("counter", 100); - "test" + async move { + let _ = ses.set("counter", 100); + "test" + } })), ) .await; @@ -448,12 +454,16 @@ mod tests { .max_age(100), ) .service(web::resource("/").to(|ses: Session| { - let _ = ses.set("counter", 100); - "test" + async move { + let _ = ses.set("counter", 100); + "test" + } })) .service(web::resource("/test/").to(|ses: Session| { - let val: usize = ses.get("counter").unwrap().unwrap(); - format!("counter: {}", val) + async move { + let val: usize = ses.get("counter").unwrap().unwrap(); + format!("counter: {}", val) + } })), ) .await; diff --git a/actix-web-codegen/tests/test_macro.rs b/actix-web-codegen/tests/test_macro.rs index 953de9cd5..18c01f374 100644 --- a/actix-web-codegen/tests/test_macro.rs +++ b/actix-web-codegen/tests/test_macro.rs @@ -5,42 +5,42 @@ use actix_web_codegen::{connect, delete, get, head, options, patch, post, put, t use futures::{future, Future}; #[get("/test")] -fn test() -> impl Responder { +async fn test() -> impl Responder { HttpResponse::Ok() } #[put("/test")] -fn put_test() -> impl Responder { +async fn put_test() -> impl Responder { HttpResponse::Created() } #[patch("/test")] -fn patch_test() -> impl Responder { +async fn patch_test() -> impl Responder { HttpResponse::Ok() } #[post("/test")] -fn post_test() -> impl Responder { +async fn post_test() -> impl Responder { HttpResponse::NoContent() } #[head("/test")] -fn head_test() -> impl Responder { +async fn head_test() -> impl Responder { HttpResponse::Ok() } #[connect("/test")] -fn connect_test() -> impl Responder { +async fn connect_test() -> impl Responder { HttpResponse::Ok() } #[options("/test")] -fn options_test() -> impl Responder { +async fn options_test() -> impl Responder { HttpResponse::Ok() } #[trace("/test")] -fn trace_test() -> impl Responder { +async fn trace_test() -> impl Responder { HttpResponse::Ok() } @@ -55,17 +55,17 @@ fn auto_sync() -> impl Future> { } #[put("/test/{param}")] -fn put_param_test(_: Path) -> impl Responder { +async fn put_param_test(_: Path) -> impl Responder { HttpResponse::Created() } #[delete("/test/{param}")] -fn delete_param_test(_: Path) -> impl Responder { +async fn delete_param_test(_: Path) -> impl Responder { HttpResponse::NoContent() } #[get("/test/{param}")] -fn get_param_test(_: Path) -> impl Responder { +async fn get_param_test(_: Path) -> impl Responder { HttpResponse::Ok() } diff --git a/awc/tests/test_client.rs b/awc/tests/test_client.rs index 959380306..9e1948f79 100644 --- a/awc/tests/test_client.rs +++ b/awc/tests/test_client.rs @@ -115,14 +115,14 @@ fn test_form() { fn test_timeout() { block_on(async { let srv = TestServer::start(|| { - HttpService::new(App::new().service(web::resource("/").route( - web::to_async(|| { + HttpService::new(App::new().service(web::resource("/").route(web::to( + || { async { tokio_timer::delay_for(Duration::from_millis(200)).await; Ok::<_, Error>(HttpResponse::Ok().body(STR)) } - }), - ))) + }, + )))) }); let connector = awc::Connector::new() @@ -149,14 +149,14 @@ fn test_timeout() { fn test_timeout_override() { block_on(async { let srv = TestServer::start(|| { - HttpService::new(App::new().service(web::resource("/").route( - web::to_async(|| { + HttpService::new(App::new().service(web::resource("/").route(web::to( + || { async { tokio_timer::delay_for(Duration::from_millis(200)).await; Ok::<_, Error>(HttpResponse::Ok().body(STR)) } - }), - ))) + }, + )))) }); let client = awc::Client::build() @@ -693,12 +693,9 @@ fn test_client_brotli_encoding() { #[test] fn test_client_cookie_handling() { + use std::io::{Error as IoError, ErrorKind}; + block_on(async { - fn err() -> Error { - use std::io::{Error as IoError, ErrorKind}; - // stub some generic error - Error::from(IoError::from(ErrorKind::NotFound)) - } let cookie1 = Cookie::build("cookie1", "value1").finish(); let cookie2 = Cookie::build("cookie2", "value2") .domain("www.example.org") @@ -717,31 +714,45 @@ fn test_client_cookie_handling() { HttpService::new(App::new().route( "/", web::to(move |req: HttpRequest| { - // Check cookies were sent correctly - req.cookie("cookie1") - .ok_or_else(err) - .and_then(|c1| { - if c1.value() == "value1" { - Ok(()) - } else { - Err(err()) - } - }) - .and_then(|()| req.cookie("cookie2").ok_or_else(err)) - .and_then(|c2| { - if c2.value() == "value2" { - Ok(()) - } else { - Err(err()) - } - }) - // Send some cookies back - .map(|_| { - HttpResponse::Ok() - .cookie(cookie1.clone()) - .cookie(cookie2.clone()) - .finish() - }) + let cookie1 = cookie1.clone(); + let cookie2 = cookie2.clone(); + + async move { + // Check cookies were sent correctly + let res: Result<(), Error> = req + .cookie("cookie1") + .ok_or(()) + .and_then(|c1| { + if c1.value() == "value1" { + Ok(()) + } else { + Err(()) + } + }) + .and_then(|()| req.cookie("cookie2").ok_or(())) + .and_then(|c2| { + if c2.value() == "value2" { + Ok(()) + } else { + Err(()) + } + }) + .map_err(|_| { + Error::from(IoError::from(ErrorKind::NotFound)) + }); + + if let Err(e) = res { + Err(e) + } else { + // Send some cookies back + Ok::<_, Error>( + HttpResponse::Ok() + .cookie(cookie1) + .cookie(cookie2) + .finish(), + ) + } + } }), )) }); diff --git a/test-server/src/lib.rs b/test-server/src/lib.rs index 1ec69b100..1911c75d6 100644 --- a/test-server/src/lib.rs +++ b/test-server/src/lib.rs @@ -35,7 +35,7 @@ pub use actix_testing::*; /// let mut srv = TestServer::start( /// || HttpService::new( /// App::new().service( -/// web::resource("/").to_async(my_handler)) +/// web::resource("/").to(my_handler)) /// ) /// ); /// From 525c22de157cc379c9944672897654b27c801b7a Mon Sep 17 00:00:00 2001 From: Martell Malone Date: Thu, 21 Nov 2019 23:13:19 -0800 Subject: [PATCH 069/176] fix typos from updating to futures 0.3 --- MIGRATION.md | 2 +- actix-web-codegen/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 9709b4f04..675dc61ed 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,6 +1,6 @@ ## 2.0.0 -* Sync handlers has been removed. `.to_async()` methtod has been renamed to `.to()` +* Sync handlers has been removed. `.to_async()` method has been renamed to `.to()` replace `fn` with `async fn` to convert sync handler to async diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index f363cfbaa..5336f60b9 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -17,7 +17,7 @@ syn = { version = "1", features = ["full", "parsing"] } proc-macro2 = "1" [dev-dependencies] -actix-web = { version = "2.0.0-alph.a" } +actix-web = { version = "2.0.0-alpha.1" } actix-http = { version = "0.3.0-alpha.1", features=["openssl"] } actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } futures = { version = "0.3.1" } From c5907747ad29d82c89b116a82a0aa3c214315fdf Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Tue, 19 Nov 2019 20:05:16 -0800 Subject: [PATCH 070/176] Remove implementation of Responder for (). Fixes #1108. Rationale: - In Rust, one can omit a semicolon after a function's final expression to make its value the function's return value. It's common for people to include a semicolon after the last expression by mistake - common enough that the Rust compiler suggests removing the semicolon when there's a type mismatch between the function's signature and body. By implementing Responder for (), Actix makes this common mistake a silent error in handler functions. - Functions returning an empty body should return HTTP status 204 ("No Content"), so the current Responder impl for (), which returns status 200 ("OK"), is not really what one wants anyway. - It's not much of a burden to ask handlers to explicitly return `HttpResponse::Ok()` if that is what they want; all the examples in the documentation do this already. --- CHANGES.md | 7 +++++++ src/app.rs | 5 +++-- src/data.rs | 5 +++-- src/resource.rs | 8 ++++---- src/responder.rs | 13 ------------- src/scope.rs | 5 +++-- 6 files changed, 20 insertions(+), 23 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index bb17a7efc..3d4b2d78a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,12 @@ # Changes +## [2.0.0-alpha.2] - 2019-xx-xx + +### Changed + +* Remove implementation of `Responder` for `()`. (#1167) + + ## [1.0.9] - 2019-11-14 ### Added diff --git a/src/app.rs b/src/app.rs index d9ac8c09d..a9dc3f29a 100644 --- a/src/app.rs +++ b/src/app.rs @@ -84,14 +84,15 @@ where /// /// ```rust /// use std::cell::Cell; - /// use actix_web::{web, App}; + /// use actix_web::{web, App, HttpResponse, Responder}; /// /// struct MyData { /// counter: Cell, /// } /// - /// async fn index(data: web::Data) { + /// async fn index(data: web::Data) -> impl Responder { /// data.counter.set(data.counter.get() + 1); + /// HttpResponse::Ok() /// } /// /// fn main() { diff --git a/src/data.rs b/src/data.rs index a026946aa..5ace3a8f3 100644 --- a/src/data.rs +++ b/src/data.rs @@ -38,16 +38,17 @@ pub(crate) trait DataFactory { /// /// ```rust /// use std::sync::Mutex; -/// use actix_web::{web, App}; +/// use actix_web::{web, App, HttpResponse, Responder}; /// /// struct MyData { /// counter: usize, /// } /// /// /// Use `Data` extractor to access data in handler. -/// async fn index(data: web::Data>) { +/// async fn index(data: web::Data>) -> impl Responder { /// let mut data = data.lock().unwrap(); /// data.counter += 1; +/// HttpResponse::Ok() /// } /// /// fn main() { diff --git a/src/resource.rs b/src/resource.rs index a06530d48..758e2f282 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -146,7 +146,7 @@ where /// match guards for route selection. /// /// ```rust - /// use actix_web::{web, guard, App, HttpResponse}; + /// use actix_web::{web, guard, App}; /// /// fn main() { /// let app = App::new().service( @@ -156,9 +156,9 @@ where /// .route(web::delete().to(delete_handler)) /// ); /// } - /// # async fn get_handler() -> impl actix_web::Responder { HttpResponse::Ok() } - /// # async fn post_handler() -> impl actix_web::Responder { HttpResponse::Ok() } - /// # async fn delete_handler() -> impl actix_web::Responder { HttpResponse::Ok() } + /// # async fn get_handler() -> impl actix_web::Responder { actix_web::HttpResponse::Ok() } + /// # async fn post_handler() -> impl actix_web::Responder { actix_web::HttpResponse::Ok() } + /// # async fn delete_handler() -> impl actix_web::Responder { actix_web::HttpResponse::Ok() } /// ``` pub fn route(mut self, route: Route) -> Self { self.routes.push(route); diff --git a/src/responder.rs b/src/responder.rs index b254567de..fd86bb686 100644 --- a/src/responder.rs +++ b/src/responder.rs @@ -131,15 +131,6 @@ impl Responder for ResponseBuilder { } } -impl Responder for () { - type Error = Error; - type Future = Ready>; - - fn respond_to(self, _: &HttpRequest) -> Self::Future { - ok(Response::build(StatusCode::OK).finish()) - } -} - impl Responder for (T, StatusCode) where T: Responder, @@ -530,10 +521,6 @@ pub(crate) mod tests { block_on(async { let req = TestRequest::default().to_http_request(); - let resp: HttpResponse = ().respond_to(&req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!(*resp.body().body(), Body::Empty); - let resp: HttpResponse = "test".respond_to(&req).await.unwrap(); assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.body().bin_ref(), b"test"); diff --git a/src/scope.rs b/src/scope.rs index e5c04d71e..9bec0a1ff 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -126,14 +126,15 @@ where /// /// ```rust /// use std::cell::Cell; - /// use actix_web::{web, App}; + /// use actix_web::{web, App, HttpResponse, Responder}; /// /// struct MyData { /// counter: Cell, /// } /// - /// async fn index(data: web::Data) { + /// async fn index(data: web::Data) -> impl Responder { /// data.counter.set(data.counter.get() + 1); + /// HttpResponse::Ok() /// } /// /// fn main() { From c1c44a7dd69137d99fb05b6aca370eb26fb7ebe0 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Mon, 25 Nov 2019 17:59:14 +0600 Subject: [PATCH 071/176] upgrade derive_more --- CHANGES.md | 4 +++- Cargo.toml | 4 +++- actix-cors/Cargo.toml | 2 +- actix-files/Cargo.toml | 2 +- actix-http/Cargo.toml | 2 +- actix-multipart/Cargo.toml | 2 +- actix-session/Cargo.toml | 2 +- actix-web-codegen/Cargo.toml | 6 +++--- awc/Cargo.toml | 2 +- 9 files changed, 15 insertions(+), 11 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3d4b2d78a..a7569862d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,9 +1,11 @@ # Changes -## [2.0.0-alpha.2] - 2019-xx-xx +## [2.0.0-alpha.1] - 2019-11-22 ### Changed +* Migrated to `std::future` + * Remove implementation of `Responder` for `()`. (#1167) diff --git a/Cargo.toml b/Cargo.toml index 7524bff73..dda01b481 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,7 +83,7 @@ actix-threadpool = "0.2.0-alpha.1" awc = { version = "0.3.0-alpha.1", optional = true } bytes = "0.4" -derive_more = "0.15.0" +derive_more = "0.99.2" encoding_rs = "0.8" futures = "0.3.1" hashbrown = "0.6.3" @@ -125,6 +125,8 @@ actix-http = { path = "actix-http" } actix-http-test = { path = "test-server" } actix-web-codegen = { path = "actix-web-codegen" } # actix-web-actors = { path = "actix-web-actors" } +actix-cors = { path = "actix-cors" } +actix-identity = { path = "actix-identity" } actix-session = { path = "actix-session" } actix-files = { path = "actix-files" } actix-multipart = { path = "actix-multipart" } diff --git a/actix-cors/Cargo.toml b/actix-cors/Cargo.toml index 57aa5833a..11ad10334 100644 --- a/actix-cors/Cargo.toml +++ b/actix-cors/Cargo.toml @@ -19,5 +19,5 @@ path = "src/lib.rs" [dependencies] actix-web = "2.0.0-alpha.1" actix-service = "1.0.0-alpha.1" -derive_more = "0.15.0" +derive_more = "0.99.2" futures = "0.3.1" diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 6e33bb412..f5318b72e 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -24,7 +24,7 @@ actix-service = "1.0.0-alpha.1" bitflags = "1" bytes = "0.4" futures = "0.3.1" -derive_more = "0.15.0" +derive_more = "0.99.2" log = "0.4" mime = "0.3" mime_guess = "2.0.1" diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 5bffa0ac3..cf390e796 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -59,7 +59,7 @@ bitflags = "1.0" bytes = "0.4" copyless = "0.1.4" chrono = "0.4.6" -derive_more = "0.15.0" +derive_more = "0.99.2" either = "1.5.2" encoding_rs = "0.8" futures = "0.3.1" diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index f5cdc8afd..52b33d582 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -22,7 +22,7 @@ actix-web = { version = "2.0.0-alpha.1", default-features = false } actix-service = "1.0.0-alpha.1" actix-utils = "0.5.0-alpha.1" bytes = "0.4" -derive_more = "0.15.0" +derive_more = "0.99.2" httparse = "1.3" futures = "0.3.1" log = "0.4" diff --git a/actix-session/Cargo.toml b/actix-session/Cargo.toml index 3ce2a8b40..a4c53e563 100644 --- a/actix-session/Cargo.toml +++ b/actix-session/Cargo.toml @@ -27,7 +27,7 @@ cookie-session = ["actix-web/secure-cookies"] actix-web = "2.0.0-alpha.1" actix-service = "1.0.0-alpha.1" bytes = "0.4" -derive_more = "0.15.0" +derive_more = "0.99.2" futures = "0.3.1" hashbrown = "0.6.3" serde = "1.0" diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 5336f60b9..0aa81e476 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -12,9 +12,9 @@ workspace = ".." proc-macro = true [dependencies] -quote = "1" -syn = { version = "1", features = ["full", "parsing"] } -proc-macro2 = "1" +quote = "^1" +syn = { version = "^1", features = ["full", "parsing"] } +proc-macro2 = "^1" [dev-dependencies] actix-web = { version = "2.0.0-alpha.1" } diff --git a/awc/Cargo.toml b/awc/Cargo.toml index eed7eabe7..0f338b404 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -48,7 +48,7 @@ actix-http = "0.3.0-alpha.1" base64 = "0.10.1" bytes = "0.4" -derive_more = "0.15.0" +derive_more = "0.99.2" futures = "0.3.1" log =" 0.4" mime = "0.3" From 4dc31aac93977572f062a43e5a38c23160ea2d5d Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Tue, 26 Nov 2019 11:25:50 +0600 Subject: [PATCH 072/176] use actix_rt::test for test setup --- .travis.yml | 11 +- Cargo.toml | 15 +- actix-cors/Cargo.toml | 3 + actix-cors/src/lib.rs | 652 ++++++------ actix-files/Cargo.toml | 1 + actix-files/src/lib.rs | 1180 +++++++++++----------- actix-framed/src/test.rs | 6 +- actix-framed/tests/test_server.rs | 227 +++-- actix-http/Cargo.toml | 10 +- actix-http/src/body.rs | 65 +- actix-http/src/client/connector.rs | 8 +- actix-http/src/client/h1proto.rs | 5 +- actix-http/src/client/h2proto.rs | 5 +- actix-http/src/client/pool.rs | 25 +- actix-http/src/config.rs | 37 +- actix-http/src/cookie/secure/key.rs | 1 - actix-http/src/error.rs | 4 - actix-http/src/h1/decoder.rs | 4 +- actix-http/src/h1/dispatcher.rs | 17 +- actix-http/src/h1/expect.rs | 2 - actix-http/src/h1/payload.rs | 28 +- actix-http/src/h1/service.rs | 2 +- actix-http/src/h1/upgrade.rs | 2 - actix-http/src/h1/utils.rs | 1 - actix-http/src/h2/dispatcher.rs | 4 +- actix-http/src/lib.rs | 3 +- actix-http/src/payload.rs | 1 - actix-http/src/response.rs | 1 - actix-http/src/service.rs | 4 +- actix-http/src/test.rs | 2 +- actix-http/tests/test_client.rs | 95 +- actix-http/tests/test_openssl.rs | 739 +++++++------- actix-http/tests/test_rustls.rs | 641 ++++++------ actix-http/tests/test_server.rs | 1043 +++++++++---------- actix-http/tests/test_ws.rs | 88 +- actix-identity/src/lib.rs | 611 ++++++------ actix-multipart/src/server.rs | 375 ++++--- actix-session/src/cookie.rs | 210 ++-- actix-web-codegen/Cargo.toml | 1 + actix-web-codegen/tests/test_macro.rs | 146 ++- awc/Cargo.toml | 6 +- awc/src/lib.rs | 21 +- awc/src/request.rs | 57 +- awc/src/response.rs | 146 ++- awc/src/sender.rs | 2 +- awc/src/ws.rs | 74 +- awc/tests/test_client.rs | 1057 ++++++++++---------- awc/tests/test_rustls_client.rs | 93 +- awc/tests/test_ssl_client.rs | 91 +- awc/tests/test_ws.rs | 92 +- src/app.rs | 386 ++++--- src/app_service.rs | 36 +- src/config.rs | 128 ++- src/data.rs | 153 ++- src/extract.rs | 30 +- src/lib.rs | 25 +- src/middleware/condition.rs | 66 +- src/middleware/defaultheaders.rs | 83 +- src/middleware/errhandlers.rs | 62 +- src/middleware/logger.rs | 22 +- src/middleware/normalize.rs | 84 +- src/request.rs | 122 ++- src/resource.rs | 357 +++---- src/responder.rs | 308 +++--- src/route.rs | 141 ++- src/scope.rs | 1103 ++++++++++---------- src/service.rs | 54 +- src/test.rs | 332 +++---- src/types/form.rs | 186 ++-- src/types/json.rs | 402 ++++---- src/types/mod.rs | 1 + src/types/path.rs | 177 ++-- src/types/payload.rs | 30 +- src/types/query.rs | 74 +- src/types/readlines.rs | 38 +- src/web.rs | 1 - test-server/Cargo.toml | 2 - test-server/src/lib.rs | 25 +- tests/test_httpserver.rs | 69 +- tests/test_server.rs | 1328 ++++++++++++------------- 80 files changed, 6502 insertions(+), 7237 deletions(-) diff --git a/.travis.yml b/.travis.yml index 683f77cc5..f10f82a48 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,9 +36,12 @@ before_script: script: - cargo update - cargo check --all --no-default-features - - cargo test --all-features --all -- --nocapture - # - cd actix-http; cargo test --no-default-features --features="rustls" -- --nocapture; cd .. - # - cd awc; cargo test --no-default-features --features="rustls" -- --nocapture; cd .. + - | + if [[ "$TRAVIS_RUST_VERSION" == "stable" || "$TRAVIS_RUST_VERSION" == "beta" ]]; then + cargo test --all-features --all -- --nocapture + cd actix-http; cargo test --no-default-features --features="rustls" -- --nocapture; cd .. + cd awc; cargo test --no-default-features --features="rustls" -- --nocapture; cd .. + fi # Upload docs after_success: @@ -51,7 +54,7 @@ after_success: echo "Uploaded documentation" fi - | - if [[ "$TRAVIS_RUST_VERSION" == "nightly-2019-11-20" ]]; then + if [[ "$TRAVIS_RUST_VERSION" == "nightly-2019-11-20" ]]; then taskset -c 0 cargo tarpaulin --out Xml --all --all-features bash <(curl -s https://codecov.io/bash) echo "Uploaded code coverage" diff --git a/Cargo.toml b/Cargo.toml index dda01b481..a1875eb76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,7 +66,7 @@ fail = ["actix-http/fail"] openssl = ["open-ssl", "actix-server/openssl", "awc/openssl"] # rustls -# rustls = ["rust-tls", "actix-server/rustls", "awc/rustls"] +rustls = ["rust-tls", "actix-server/rustls", "awc/rustls"] [dependencies] actix-codec = "0.2.0-alpha.1" @@ -110,7 +110,6 @@ actix-http-test = "0.3.0-alpha.1" rand = "0.7" env_logger = "0.6" serde_derive = "1.0" -tokio-timer = "0.3.0-alpha.6" brotli2 = "0.3.2" flate2 = "1.0.2" @@ -135,19 +134,9 @@ awc = { path = "awc" } actix-codec = { git = "https://github.com/actix/actix-net.git" } actix-connect = { git = "https://github.com/actix/actix-net.git" } actix-rt = { git = "https://github.com/actix/actix-net.git" } +actix-macros = { git = "https://github.com/actix/actix-net.git" } actix-server = { git = "https://github.com/actix/actix-net.git" } actix-server-config = { git = "https://github.com/actix/actix-net.git" } actix-service = { git = "https://github.com/actix/actix-net.git" } actix-testing = { git = "https://github.com/actix/actix-net.git" } -actix-threadpool = { git = "https://github.com/actix/actix-net.git" } actix-utils = { git = "https://github.com/actix/actix-net.git" } - -# actix-codec = { path = "../actix-net/actix-codec" } -# actix-connect = { path = "../actix-net/actix-connect" } -# actix-rt = { path = "../actix-net/actix-rt" } -# actix-server = { path = "../actix-net/actix-server" } -# actix-server-config = { path = "../actix-net/actix-server-config" } -# actix-service = { path = "../actix-net/actix-service" } -# actix-testing = { path = "../actix-net/actix-testing" } -# actix-threadpool = { path = "../actix-net/actix-threadpool" } -# actix-utils = { path = "../actix-net/actix-utils" } diff --git a/actix-cors/Cargo.toml b/actix-cors/Cargo.toml index 11ad10334..ddb5f307e 100644 --- a/actix-cors/Cargo.toml +++ b/actix-cors/Cargo.toml @@ -21,3 +21,6 @@ actix-web = "2.0.0-alpha.1" actix-service = "1.0.0-alpha.1" derive_more = "0.99.2" futures = "0.3.1" + +[dev-dependencies] +actix-rt = "1.0.0-alpha.1" diff --git a/actix-cors/src/lib.rs b/actix-cors/src/lib.rs index db7e4cc4f..551e3bb4d 100644 --- a/actix-cors/src/lib.rs +++ b/actix-cors/src/lib.rs @@ -814,142 +814,136 @@ where #[cfg(test)] mod tests { use actix_service::{service_fn2, Transform}; - use actix_web::test::{self, block_on, TestRequest}; + use actix_web::test::{self, TestRequest}; use super::*; - #[test] + #[actix_rt::test] #[should_panic(expected = "Credentials are allowed, but the Origin is set to")] - fn cors_validates_illegal_allow_credentials() { + async fn cors_validates_illegal_allow_credentials() { let _cors = Cors::new().supports_credentials().send_wildcard().finish(); } - #[test] - fn validate_origin_allows_all_origins() { - block_on(async { - let mut cors = Cors::new() - .finish() - .new_transform(test::ok_service()) - .await - .unwrap(); - let req = TestRequest::with_header("Origin", "https://www.example.com") - .to_srv_request(); + #[actix_rt::test] + async fn validate_origin_allows_all_origins() { + let mut cors = Cors::new() + .finish() + .new_transform(test::ok_service()) + .await + .unwrap(); + let req = TestRequest::with_header("Origin", "https://www.example.com") + .to_srv_request(); - let resp = test::call_service(&mut cors, req).await; - assert_eq!(resp.status(), StatusCode::OK); - }) + let resp = test::call_service(&mut cors, req).await; + assert_eq!(resp.status(), StatusCode::OK); } - #[test] - fn default() { - block_on(async { - let mut cors = Cors::default() - .new_transform(test::ok_service()) - .await - .unwrap(); - let req = TestRequest::with_header("Origin", "https://www.example.com") - .to_srv_request(); + #[actix_rt::test] + async fn default() { + let mut cors = Cors::default() + .new_transform(test::ok_service()) + .await + .unwrap(); + let req = TestRequest::with_header("Origin", "https://www.example.com") + .to_srv_request(); - let resp = test::call_service(&mut cors, req).await; - assert_eq!(resp.status(), StatusCode::OK); - }) + let resp = test::call_service(&mut cors, req).await; + assert_eq!(resp.status(), StatusCode::OK); } - #[test] - fn test_preflight() { - block_on(async { - let mut cors = Cors::new() - .send_wildcard() - .max_age(3600) - .allowed_methods(vec![Method::GET, Method::OPTIONS, Method::POST]) - .allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT]) - .allowed_header(header::CONTENT_TYPE) - .finish() - .new_transform(test::ok_service()) - .await - .unwrap(); + #[actix_rt::test] + async fn test_preflight() { + let mut cors = Cors::new() + .send_wildcard() + .max_age(3600) + .allowed_methods(vec![Method::GET, Method::OPTIONS, Method::POST]) + .allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT]) + .allowed_header(header::CONTENT_TYPE) + .finish() + .new_transform(test::ok_service()) + .await + .unwrap(); - let req = TestRequest::with_header("Origin", "https://www.example.com") - .method(Method::OPTIONS) - .header(header::ACCESS_CONTROL_REQUEST_HEADERS, "X-Not-Allowed") - .to_srv_request(); + let req = TestRequest::with_header("Origin", "https://www.example.com") + .method(Method::OPTIONS) + .header(header::ACCESS_CONTROL_REQUEST_HEADERS, "X-Not-Allowed") + .to_srv_request(); - assert!(cors.inner.validate_allowed_method(req.head()).is_err()); - assert!(cors.inner.validate_allowed_headers(req.head()).is_err()); - let resp = test::call_service(&mut cors, req).await; - assert_eq!(resp.status(), StatusCode::BAD_REQUEST); + assert!(cors.inner.validate_allowed_method(req.head()).is_err()); + assert!(cors.inner.validate_allowed_headers(req.head()).is_err()); + let resp = test::call_service(&mut cors, req).await; + assert_eq!(resp.status(), StatusCode::BAD_REQUEST); - let req = TestRequest::with_header("Origin", "https://www.example.com") - .header(header::ACCESS_CONTROL_REQUEST_METHOD, "put") - .method(Method::OPTIONS) - .to_srv_request(); + let req = TestRequest::with_header("Origin", "https://www.example.com") + .header(header::ACCESS_CONTROL_REQUEST_METHOD, "put") + .method(Method::OPTIONS) + .to_srv_request(); - assert!(cors.inner.validate_allowed_method(req.head()).is_err()); - assert!(cors.inner.validate_allowed_headers(req.head()).is_ok()); + assert!(cors.inner.validate_allowed_method(req.head()).is_err()); + assert!(cors.inner.validate_allowed_headers(req.head()).is_ok()); - let req = TestRequest::with_header("Origin", "https://www.example.com") - .header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST") - .header( - header::ACCESS_CONTROL_REQUEST_HEADERS, - "AUTHORIZATION,ACCEPT", - ) - .method(Method::OPTIONS) - .to_srv_request(); + let req = TestRequest::with_header("Origin", "https://www.example.com") + .header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST") + .header( + header::ACCESS_CONTROL_REQUEST_HEADERS, + "AUTHORIZATION,ACCEPT", + ) + .method(Method::OPTIONS) + .to_srv_request(); - let resp = test::call_service(&mut cors, req).await; - assert_eq!( - &b"*"[..], - resp.headers() - .get(&header::ACCESS_CONTROL_ALLOW_ORIGIN) - .unwrap() - .as_bytes() - ); - assert_eq!( - &b"3600"[..], - resp.headers() - .get(&header::ACCESS_CONTROL_MAX_AGE) - .unwrap() - .as_bytes() - ); - let hdr = resp - .headers() - .get(&header::ACCESS_CONTROL_ALLOW_HEADERS) + let resp = test::call_service(&mut cors, req).await; + assert_eq!( + &b"*"[..], + resp.headers() + .get(&header::ACCESS_CONTROL_ALLOW_ORIGIN) .unwrap() - .to_str() - .unwrap(); - assert!(hdr.contains("authorization")); - assert!(hdr.contains("accept")); - assert!(hdr.contains("content-type")); - - let methods = resp - .headers() - .get(header::ACCESS_CONTROL_ALLOW_METHODS) + .as_bytes() + ); + assert_eq!( + &b"3600"[..], + resp.headers() + .get(&header::ACCESS_CONTROL_MAX_AGE) .unwrap() - .to_str() - .unwrap(); - assert!(methods.contains("POST")); - assert!(methods.contains("GET")); - assert!(methods.contains("OPTIONS")); + .as_bytes() + ); + let hdr = resp + .headers() + .get(&header::ACCESS_CONTROL_ALLOW_HEADERS) + .unwrap() + .to_str() + .unwrap(); + assert!(hdr.contains("authorization")); + assert!(hdr.contains("accept")); + assert!(hdr.contains("content-type")); - Rc::get_mut(&mut cors.inner).unwrap().preflight = false; + let methods = resp + .headers() + .get(header::ACCESS_CONTROL_ALLOW_METHODS) + .unwrap() + .to_str() + .unwrap(); + assert!(methods.contains("POST")); + assert!(methods.contains("GET")); + assert!(methods.contains("OPTIONS")); - let req = TestRequest::with_header("Origin", "https://www.example.com") - .header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST") - .header( - header::ACCESS_CONTROL_REQUEST_HEADERS, - "AUTHORIZATION,ACCEPT", - ) - .method(Method::OPTIONS) - .to_srv_request(); + Rc::get_mut(&mut cors.inner).unwrap().preflight = false; - let resp = test::call_service(&mut cors, req).await; - assert_eq!(resp.status(), StatusCode::OK); - }) + let req = TestRequest::with_header("Origin", "https://www.example.com") + .header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST") + .header( + header::ACCESS_CONTROL_REQUEST_HEADERS, + "AUTHORIZATION,ACCEPT", + ) + .method(Method::OPTIONS) + .to_srv_request(); + + let resp = test::call_service(&mut cors, req).await; + assert_eq!(resp.status(), StatusCode::OK); } - // #[test] + // #[actix_rt::test] // #[should_panic(expected = "MissingOrigin")] - // fn test_validate_missing_origin() { + // async fn test_validate_missing_origin() { // let cors = Cors::build() // .allowed_origin("https://www.example.com") // .finish(); @@ -957,257 +951,245 @@ mod tests { // cors.start(&req).unwrap(); // } - #[test] + #[actix_rt::test] #[should_panic(expected = "OriginNotAllowed")] - fn test_validate_not_allowed_origin() { - block_on(async { - let cors = Cors::new() - .allowed_origin("https://www.example.com") - .finish() - .new_transform(test::ok_service()) - .await - .unwrap(); + async fn test_validate_not_allowed_origin() { + let cors = Cors::new() + .allowed_origin("https://www.example.com") + .finish() + .new_transform(test::ok_service()) + .await + .unwrap(); - let req = TestRequest::with_header("Origin", "https://www.unknown.com") - .method(Method::GET) - .to_srv_request(); - cors.inner.validate_origin(req.head()).unwrap(); - cors.inner.validate_allowed_method(req.head()).unwrap(); - cors.inner.validate_allowed_headers(req.head()).unwrap(); - }) + let req = TestRequest::with_header("Origin", "https://www.unknown.com") + .method(Method::GET) + .to_srv_request(); + cors.inner.validate_origin(req.head()).unwrap(); + cors.inner.validate_allowed_method(req.head()).unwrap(); + cors.inner.validate_allowed_headers(req.head()).unwrap(); } - #[test] - fn test_validate_origin() { - block_on(async { - let mut cors = Cors::new() - .allowed_origin("https://www.example.com") - .finish() - .new_transform(test::ok_service()) - .await - .unwrap(); + #[actix_rt::test] + async fn test_validate_origin() { + let mut cors = Cors::new() + .allowed_origin("https://www.example.com") + .finish() + .new_transform(test::ok_service()) + .await + .unwrap(); - let req = TestRequest::with_header("Origin", "https://www.example.com") - .method(Method::GET) - .to_srv_request(); + let req = TestRequest::with_header("Origin", "https://www.example.com") + .method(Method::GET) + .to_srv_request(); - let resp = test::call_service(&mut cors, req).await; - assert_eq!(resp.status(), StatusCode::OK); - }) + let resp = test::call_service(&mut cors, req).await; + assert_eq!(resp.status(), StatusCode::OK); } - #[test] - fn test_no_origin_response() { - block_on(async { - let mut cors = Cors::new() - .disable_preflight() - .finish() - .new_transform(test::ok_service()) - .await - .unwrap(); + #[actix_rt::test] + async fn test_no_origin_response() { + let mut cors = Cors::new() + .disable_preflight() + .finish() + .new_transform(test::ok_service()) + .await + .unwrap(); - let req = TestRequest::default().method(Method::GET).to_srv_request(); - let resp = test::call_service(&mut cors, req).await; - assert!(resp - .headers() - .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) - .is_none()); + let req = TestRequest::default().method(Method::GET).to_srv_request(); + let resp = test::call_service(&mut cors, req).await; + assert!(resp + .headers() + .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) + .is_none()); - let req = TestRequest::with_header("Origin", "https://www.example.com") - .method(Method::OPTIONS) - .to_srv_request(); - let resp = test::call_service(&mut cors, req).await; - assert_eq!( - &b"https://www.example.com"[..], - resp.headers() - .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) - .unwrap() - .as_bytes() - ); - }) - } - - #[test] - fn test_response() { - block_on(async { - let exposed_headers = vec![header::AUTHORIZATION, header::ACCEPT]; - let mut cors = Cors::new() - .send_wildcard() - .disable_preflight() - .max_age(3600) - .allowed_methods(vec![Method::GET, Method::OPTIONS, Method::POST]) - .allowed_headers(exposed_headers.clone()) - .expose_headers(exposed_headers.clone()) - .allowed_header(header::CONTENT_TYPE) - .finish() - .new_transform(test::ok_service()) - .await - .unwrap(); - - let req = TestRequest::with_header("Origin", "https://www.example.com") - .method(Method::OPTIONS) - .to_srv_request(); - - let resp = test::call_service(&mut cors, req).await; - assert_eq!( - &b"*"[..], - resp.headers() - .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) - .unwrap() - .as_bytes() - ); - assert_eq!( - &b"Origin"[..], - resp.headers().get(header::VARY).unwrap().as_bytes() - ); - - { - let headers = resp - .headers() - .get(header::ACCESS_CONTROL_EXPOSE_HEADERS) - .unwrap() - .to_str() - .unwrap() - .split(',') - .map(|s| s.trim()) - .collect::>(); - - for h in exposed_headers { - assert!(headers.contains(&h.as_str())); - } - } - - let exposed_headers = vec![header::AUTHORIZATION, header::ACCEPT]; - let mut cors = Cors::new() - .send_wildcard() - .disable_preflight() - .max_age(3600) - .allowed_methods(vec![Method::GET, Method::OPTIONS, Method::POST]) - .allowed_headers(exposed_headers.clone()) - .expose_headers(exposed_headers.clone()) - .allowed_header(header::CONTENT_TYPE) - .finish() - .new_transform(service_fn2(|req: ServiceRequest| { - ok(req.into_response( - HttpResponse::Ok().header(header::VARY, "Accept").finish(), - )) - })) - .await - .unwrap(); - let req = TestRequest::with_header("Origin", "https://www.example.com") - .method(Method::OPTIONS) - .to_srv_request(); - let resp = test::call_service(&mut cors, req).await; - assert_eq!( - &b"Accept, Origin"[..], - resp.headers().get(header::VARY).unwrap().as_bytes() - ); - - let mut cors = Cors::new() - .disable_vary_header() - .allowed_origin("https://www.example.com") - .allowed_origin("https://www.google.com") - .finish() - .new_transform(test::ok_service()) - .await - .unwrap(); - - let req = TestRequest::with_header("Origin", "https://www.example.com") - .method(Method::OPTIONS) - .header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST") - .to_srv_request(); - let resp = test::call_service(&mut cors, req).await; - - let origins_str = resp - .headers() + let req = TestRequest::with_header("Origin", "https://www.example.com") + .method(Method::OPTIONS) + .to_srv_request(); + let resp = test::call_service(&mut cors, req).await; + assert_eq!( + &b"https://www.example.com"[..], + resp.headers() .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) .unwrap() + .as_bytes() + ); + } + + #[actix_rt::test] + async fn test_response() { + let exposed_headers = vec![header::AUTHORIZATION, header::ACCEPT]; + let mut cors = Cors::new() + .send_wildcard() + .disable_preflight() + .max_age(3600) + .allowed_methods(vec![Method::GET, Method::OPTIONS, Method::POST]) + .allowed_headers(exposed_headers.clone()) + .expose_headers(exposed_headers.clone()) + .allowed_header(header::CONTENT_TYPE) + .finish() + .new_transform(test::ok_service()) + .await + .unwrap(); + + let req = TestRequest::with_header("Origin", "https://www.example.com") + .method(Method::OPTIONS) + .to_srv_request(); + + let resp = test::call_service(&mut cors, req).await; + assert_eq!( + &b"*"[..], + resp.headers() + .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) + .unwrap() + .as_bytes() + ); + assert_eq!( + &b"Origin"[..], + resp.headers().get(header::VARY).unwrap().as_bytes() + ); + + { + let headers = resp + .headers() + .get(header::ACCESS_CONTROL_EXPOSE_HEADERS) + .unwrap() .to_str() - .unwrap(); + .unwrap() + .split(',') + .map(|s| s.trim()) + .collect::>(); - assert_eq!("https://www.example.com", origins_str); - }) + for h in exposed_headers { + assert!(headers.contains(&h.as_str())); + } + } + + let exposed_headers = vec![header::AUTHORIZATION, header::ACCEPT]; + let mut cors = Cors::new() + .send_wildcard() + .disable_preflight() + .max_age(3600) + .allowed_methods(vec![Method::GET, Method::OPTIONS, Method::POST]) + .allowed_headers(exposed_headers.clone()) + .expose_headers(exposed_headers.clone()) + .allowed_header(header::CONTENT_TYPE) + .finish() + .new_transform(service_fn2(|req: ServiceRequest| { + ok(req.into_response( + HttpResponse::Ok().header(header::VARY, "Accept").finish(), + )) + })) + .await + .unwrap(); + let req = TestRequest::with_header("Origin", "https://www.example.com") + .method(Method::OPTIONS) + .to_srv_request(); + let resp = test::call_service(&mut cors, req).await; + assert_eq!( + &b"Accept, Origin"[..], + resp.headers().get(header::VARY).unwrap().as_bytes() + ); + + let mut cors = Cors::new() + .disable_vary_header() + .allowed_origin("https://www.example.com") + .allowed_origin("https://www.google.com") + .finish() + .new_transform(test::ok_service()) + .await + .unwrap(); + + let req = TestRequest::with_header("Origin", "https://www.example.com") + .method(Method::OPTIONS) + .header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST") + .to_srv_request(); + let resp = test::call_service(&mut cors, req).await; + + let origins_str = resp + .headers() + .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) + .unwrap() + .to_str() + .unwrap(); + + assert_eq!("https://www.example.com", origins_str); } - #[test] - fn test_multiple_origins() { - block_on(async { - let mut cors = Cors::new() - .allowed_origin("https://example.com") - .allowed_origin("https://example.org") - .allowed_methods(vec![Method::GET]) - .finish() - .new_transform(test::ok_service()) - .await - .unwrap(); + #[actix_rt::test] + async fn test_multiple_origins() { + let mut cors = Cors::new() + .allowed_origin("https://example.com") + .allowed_origin("https://example.org") + .allowed_methods(vec![Method::GET]) + .finish() + .new_transform(test::ok_service()) + .await + .unwrap(); - let req = TestRequest::with_header("Origin", "https://example.com") - .method(Method::GET) - .to_srv_request(); + let req = TestRequest::with_header("Origin", "https://example.com") + .method(Method::GET) + .to_srv_request(); - let resp = test::call_service(&mut cors, req).await; - assert_eq!( - &b"https://example.com"[..], - resp.headers() - .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) - .unwrap() - .as_bytes() - ); + let resp = test::call_service(&mut cors, req).await; + assert_eq!( + &b"https://example.com"[..], + resp.headers() + .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) + .unwrap() + .as_bytes() + ); - let req = TestRequest::with_header("Origin", "https://example.org") - .method(Method::GET) - .to_srv_request(); + let req = TestRequest::with_header("Origin", "https://example.org") + .method(Method::GET) + .to_srv_request(); - let resp = test::call_service(&mut cors, req).await; - assert_eq!( - &b"https://example.org"[..], - resp.headers() - .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) - .unwrap() - .as_bytes() - ); - }) + let resp = test::call_service(&mut cors, req).await; + assert_eq!( + &b"https://example.org"[..], + resp.headers() + .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) + .unwrap() + .as_bytes() + ); } - #[test] - fn test_multiple_origins_preflight() { - block_on(async { - let mut cors = Cors::new() - .allowed_origin("https://example.com") - .allowed_origin("https://example.org") - .allowed_methods(vec![Method::GET]) - .finish() - .new_transform(test::ok_service()) - .await - .unwrap(); + #[actix_rt::test] + async fn test_multiple_origins_preflight() { + let mut cors = Cors::new() + .allowed_origin("https://example.com") + .allowed_origin("https://example.org") + .allowed_methods(vec![Method::GET]) + .finish() + .new_transform(test::ok_service()) + .await + .unwrap(); - let req = TestRequest::with_header("Origin", "https://example.com") - .header(header::ACCESS_CONTROL_REQUEST_METHOD, "GET") - .method(Method::OPTIONS) - .to_srv_request(); + let req = TestRequest::with_header("Origin", "https://example.com") + .header(header::ACCESS_CONTROL_REQUEST_METHOD, "GET") + .method(Method::OPTIONS) + .to_srv_request(); - let resp = test::call_service(&mut cors, req).await; - assert_eq!( - &b"https://example.com"[..], - resp.headers() - .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) - .unwrap() - .as_bytes() - ); + let resp = test::call_service(&mut cors, req).await; + assert_eq!( + &b"https://example.com"[..], + resp.headers() + .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) + .unwrap() + .as_bytes() + ); - let req = TestRequest::with_header("Origin", "https://example.org") - .header(header::ACCESS_CONTROL_REQUEST_METHOD, "GET") - .method(Method::OPTIONS) - .to_srv_request(); + let req = TestRequest::with_header("Origin", "https://example.org") + .header(header::ACCESS_CONTROL_REQUEST_METHOD, "GET") + .method(Method::OPTIONS) + .to_srv_request(); - let resp = test::call_service(&mut cors, req).await; - assert_eq!( - &b"https://example.org"[..], - resp.headers() - .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) - .unwrap() - .as_bytes() - ); - }) + let resp = test::call_service(&mut cors, req).await; + assert_eq!( + &b"https://example.org"[..], + resp.headers() + .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) + .unwrap() + .as_bytes() + ); } } diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index f5318b72e..19366b902 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -32,4 +32,5 @@ percent-encoding = "2.1" v_htmlescape = "0.4" [dev-dependencies] +actix-rt = "1.0.0-alpha.1" actix-web = { version = "2.0.0-alpha.1", features=["openssl"] } diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index 2f8a5c49c..ed8b6c3b9 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -12,7 +12,7 @@ use std::rc::Rc; use std::task::{Context, Poll}; use std::{cmp, io}; -use actix_service::boxed::{self, BoxedNewService, BoxedService}; +use actix_service::boxed::{self, BoxService, BoxServiceFactory}; use actix_service::{IntoServiceFactory, Service, ServiceFactory}; use actix_web::dev::{ AppService, HttpServiceFactory, Payload, ResourceDef, ServiceRequest, @@ -39,8 +39,8 @@ use self::error::{FilesError, UriSegmentError}; pub use crate::named::NamedFile; pub use crate::range::HttpRange; -type HttpService = BoxedService; -type HttpNewService = BoxedNewService<(), ServiceRequest, ServiceResponse, Error, ()>; +type HttpService = BoxService; +type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>; /// Return the MIME type associated with a filename extension (case-insensitive). /// If `ext` is empty or no associated type for the extension was found, returns @@ -644,11 +644,11 @@ mod tests { }; use actix_web::http::{Method, StatusCode}; use actix_web::middleware::Compress; - use actix_web::test::{self, block_on, TestRequest}; + use actix_web::test::{self, TestRequest}; use actix_web::{App, Responder}; - #[test] - fn test_file_extension_to_mime() { + #[actix_rt::test] + async fn test_file_extension_to_mime() { let m = file_extension_to_mime("jpg"); assert_eq!(m, mime::IMAGE_JPEG); @@ -659,678 +659,622 @@ mod tests { assert_eq!(m, mime::APPLICATION_OCTET_STREAM); } - #[test] - fn test_if_modified_since_without_if_none_match() { - block_on(async { - let file = NamedFile::open("Cargo.toml").unwrap(); - let since = - header::HttpDate::from(SystemTime::now().add(Duration::from_secs(60))); + #[actix_rt::test] + async fn test_if_modified_since_without_if_none_match() { + let file = NamedFile::open("Cargo.toml").unwrap(); + let since = + header::HttpDate::from(SystemTime::now().add(Duration::from_secs(60))); - let req = TestRequest::default() - .header(header::IF_MODIFIED_SINCE, since) - .to_http_request(); - let resp = file.respond_to(&req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_MODIFIED); - }) + let req = TestRequest::default() + .header(header::IF_MODIFIED_SINCE, since) + .to_http_request(); + let resp = file.respond_to(&req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_MODIFIED); } - #[test] - fn test_if_modified_since_with_if_none_match() { - block_on(async { - let file = NamedFile::open("Cargo.toml").unwrap(); - let since = - header::HttpDate::from(SystemTime::now().add(Duration::from_secs(60))); + #[actix_rt::test] + async fn test_if_modified_since_with_if_none_match() { + let file = NamedFile::open("Cargo.toml").unwrap(); + let since = + header::HttpDate::from(SystemTime::now().add(Duration::from_secs(60))); - let req = TestRequest::default() - .header(header::IF_NONE_MATCH, "miss_etag") - .header(header::IF_MODIFIED_SINCE, since) - .to_http_request(); - let resp = file.respond_to(&req).await.unwrap(); - assert_ne!(resp.status(), StatusCode::NOT_MODIFIED); - }) + let req = TestRequest::default() + .header(header::IF_NONE_MATCH, "miss_etag") + .header(header::IF_MODIFIED_SINCE, since) + .to_http_request(); + let resp = file.respond_to(&req).await.unwrap(); + assert_ne!(resp.status(), StatusCode::NOT_MODIFIED); } - #[test] - fn test_named_file_text() { - block_on(async { - assert!(NamedFile::open("test--").is_err()); - let mut file = NamedFile::open("Cargo.toml").unwrap(); - { - file.file(); - let _f: &File = &file; - } - { - let _f: &mut File = &mut file; - } + #[actix_rt::test] + async fn test_named_file_text() { + assert!(NamedFile::open("test--").is_err()); + let mut file = NamedFile::open("Cargo.toml").unwrap(); + { + file.file(); + let _f: &File = &file; + } + { + let _f: &mut File = &mut file; + } - let req = TestRequest::default().to_http_request(); - let resp = file.respond_to(&req).await.unwrap(); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - "text/x-toml" - ); - assert_eq!( - resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), - "inline; filename=\"Cargo.toml\"" - ); - }) + let req = TestRequest::default().to_http_request(); + let resp = file.respond_to(&req).await.unwrap(); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + "text/x-toml" + ); + assert_eq!( + resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), + "inline; filename=\"Cargo.toml\"" + ); } - #[test] - fn test_named_file_content_disposition() { - block_on(async { - assert!(NamedFile::open("test--").is_err()); - let mut file = NamedFile::open("Cargo.toml").unwrap(); - { - file.file(); - let _f: &File = &file; - } - { - let _f: &mut File = &mut file; - } + #[actix_rt::test] + async fn test_named_file_content_disposition() { + assert!(NamedFile::open("test--").is_err()); + let mut file = NamedFile::open("Cargo.toml").unwrap(); + { + file.file(); + let _f: &File = &file; + } + { + let _f: &mut File = &mut file; + } - let req = TestRequest::default().to_http_request(); - let resp = file.respond_to(&req).await.unwrap(); - assert_eq!( - resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), - "inline; filename=\"Cargo.toml\"" - ); + let req = TestRequest::default().to_http_request(); + let resp = file.respond_to(&req).await.unwrap(); + assert_eq!( + resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), + "inline; filename=\"Cargo.toml\"" + ); - let file = NamedFile::open("Cargo.toml") - .unwrap() - .disable_content_disposition(); - let req = TestRequest::default().to_http_request(); - let resp = file.respond_to(&req).await.unwrap(); - assert!(resp.headers().get(header::CONTENT_DISPOSITION).is_none()); - }) + let file = NamedFile::open("Cargo.toml") + .unwrap() + .disable_content_disposition(); + let req = TestRequest::default().to_http_request(); + let resp = file.respond_to(&req).await.unwrap(); + assert!(resp.headers().get(header::CONTENT_DISPOSITION).is_none()); } - #[test] - fn test_named_file_non_ascii_file_name() { - block_on(async { - let mut file = - NamedFile::from_file(File::open("Cargo.toml").unwrap(), "貨物.toml") - .unwrap(); - { - file.file(); - let _f: &File = &file; - } - { - let _f: &mut File = &mut file; - } + #[actix_rt::test] + async fn test_named_file_non_ascii_file_name() { + let mut file = + NamedFile::from_file(File::open("Cargo.toml").unwrap(), "貨物.toml") + .unwrap(); + { + file.file(); + let _f: &File = &file; + } + { + let _f: &mut File = &mut file; + } - let req = TestRequest::default().to_http_request(); - let resp = file.respond_to(&req).await.unwrap(); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - "text/x-toml" - ); - assert_eq!( + let req = TestRequest::default().to_http_request(); + let resp = file.respond_to(&req).await.unwrap(); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + "text/x-toml" + ); + assert_eq!( resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), "inline; filename=\"貨物.toml\"; filename*=UTF-8''%E8%B2%A8%E7%89%A9.toml" ); - }) } - #[test] - fn test_named_file_set_content_type() { - block_on(async { - let mut file = NamedFile::open("Cargo.toml") - .unwrap() - .set_content_type(mime::TEXT_XML); - { - file.file(); - let _f: &File = &file; - } - { - let _f: &mut File = &mut file; - } + #[actix_rt::test] + async fn test_named_file_set_content_type() { + let mut file = NamedFile::open("Cargo.toml") + .unwrap() + .set_content_type(mime::TEXT_XML); + { + file.file(); + let _f: &File = &file; + } + { + let _f: &mut File = &mut file; + } - let req = TestRequest::default().to_http_request(); - let resp = file.respond_to(&req).await.unwrap(); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - "text/xml" - ); - assert_eq!( - resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), - "inline; filename=\"Cargo.toml\"" - ); - }) + let req = TestRequest::default().to_http_request(); + let resp = file.respond_to(&req).await.unwrap(); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + "text/xml" + ); + assert_eq!( + resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), + "inline; filename=\"Cargo.toml\"" + ); } - #[test] - fn test_named_file_image() { - block_on(async { - let mut file = NamedFile::open("tests/test.png").unwrap(); - { - file.file(); - let _f: &File = &file; - } - { - let _f: &mut File = &mut file; - } + #[actix_rt::test] + async fn test_named_file_image() { + let mut file = NamedFile::open("tests/test.png").unwrap(); + { + file.file(); + let _f: &File = &file; + } + { + let _f: &mut File = &mut file; + } - let req = TestRequest::default().to_http_request(); - let resp = file.respond_to(&req).await.unwrap(); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - "image/png" - ); - assert_eq!( - resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), - "inline; filename=\"test.png\"" - ); - }) + let req = TestRequest::default().to_http_request(); + let resp = file.respond_to(&req).await.unwrap(); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + "image/png" + ); + assert_eq!( + resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), + "inline; filename=\"test.png\"" + ); } - #[test] - fn test_named_file_image_attachment() { - block_on(async { - let cd = ContentDisposition { - disposition: DispositionType::Attachment, - parameters: vec![DispositionParam::Filename(String::from("test.png"))], - }; - let mut file = NamedFile::open("tests/test.png") - .unwrap() - .set_content_disposition(cd); - { - file.file(); - let _f: &File = &file; - } - { - let _f: &mut File = &mut file; - } + #[actix_rt::test] + async fn test_named_file_image_attachment() { + let cd = ContentDisposition { + disposition: DispositionType::Attachment, + parameters: vec![DispositionParam::Filename(String::from("test.png"))], + }; + let mut file = NamedFile::open("tests/test.png") + .unwrap() + .set_content_disposition(cd); + { + file.file(); + let _f: &File = &file; + } + { + let _f: &mut File = &mut file; + } - let req = TestRequest::default().to_http_request(); - let resp = file.respond_to(&req).await.unwrap(); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - "image/png" - ); - assert_eq!( - resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), - "attachment; filename=\"test.png\"" - ); - }) + let req = TestRequest::default().to_http_request(); + let resp = file.respond_to(&req).await.unwrap(); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + "image/png" + ); + assert_eq!( + resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), + "attachment; filename=\"test.png\"" + ); } - #[test] - fn test_named_file_binary() { - block_on(async { - let mut file = NamedFile::open("tests/test.binary").unwrap(); - { - file.file(); - let _f: &File = &file; - } - { - let _f: &mut File = &mut file; - } + #[actix_rt::test] + async fn test_named_file_binary() { + let mut file = NamedFile::open("tests/test.binary").unwrap(); + { + file.file(); + let _f: &File = &file; + } + { + let _f: &mut File = &mut file; + } - let req = TestRequest::default().to_http_request(); - let resp = file.respond_to(&req).await.unwrap(); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - "application/octet-stream" - ); - assert_eq!( - resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), - "attachment; filename=\"test.binary\"" - ); - }) + let req = TestRequest::default().to_http_request(); + let resp = file.respond_to(&req).await.unwrap(); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + "application/octet-stream" + ); + assert_eq!( + resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), + "attachment; filename=\"test.binary\"" + ); } - #[test] - fn test_named_file_status_code_text() { - block_on(async { - let mut file = NamedFile::open("Cargo.toml") - .unwrap() - .set_status_code(StatusCode::NOT_FOUND); - { - file.file(); - let _f: &File = &file; - } - { - let _f: &mut File = &mut file; - } + #[actix_rt::test] + async fn test_named_file_status_code_text() { + let mut file = NamedFile::open("Cargo.toml") + .unwrap() + .set_status_code(StatusCode::NOT_FOUND); + { + file.file(); + let _f: &File = &file; + } + { + let _f: &mut File = &mut file; + } - let req = TestRequest::default().to_http_request(); - let resp = file.respond_to(&req).await.unwrap(); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - "text/x-toml" - ); - assert_eq!( - resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), - "inline; filename=\"Cargo.toml\"" - ); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); - }) + let req = TestRequest::default().to_http_request(); + let resp = file.respond_to(&req).await.unwrap(); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + "text/x-toml" + ); + assert_eq!( + resp.headers().get(header::CONTENT_DISPOSITION).unwrap(), + "inline; filename=\"Cargo.toml\"" + ); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); } - #[test] - fn test_mime_override() { - block_on(async { - fn all_attachment(_: &mime::Name) -> DispositionType { - DispositionType::Attachment - } + #[actix_rt::test] + async fn test_mime_override() { + fn all_attachment(_: &mime::Name) -> DispositionType { + DispositionType::Attachment + } - let mut srv = test::init_service( - App::new().service( - Files::new("/", ".") - .mime_override(all_attachment) - .index_file("Cargo.toml"), - ), - ) - .await; + let mut srv = test::init_service( + App::new().service( + Files::new("/", ".") + .mime_override(all_attachment) + .index_file("Cargo.toml"), + ), + ) + .await; - let request = TestRequest::get().uri("/").to_request(); - let response = test::call_service(&mut srv, request).await; - assert_eq!(response.status(), StatusCode::OK); + let request = TestRequest::get().uri("/").to_request(); + let response = test::call_service(&mut srv, request).await; + assert_eq!(response.status(), StatusCode::OK); - let content_disposition = response - .headers() - .get(header::CONTENT_DISPOSITION) - .expect("To have CONTENT_DISPOSITION"); - let content_disposition = content_disposition - .to_str() - .expect("Convert CONTENT_DISPOSITION to str"); - assert_eq!(content_disposition, "attachment; filename=\"Cargo.toml\""); - }) + let content_disposition = response + .headers() + .get(header::CONTENT_DISPOSITION) + .expect("To have CONTENT_DISPOSITION"); + let content_disposition = content_disposition + .to_str() + .expect("Convert CONTENT_DISPOSITION to str"); + assert_eq!(content_disposition, "attachment; filename=\"Cargo.toml\""); } - #[test] - fn test_named_file_ranges_status_code() { - block_on(async { - let mut srv = test::init_service( - App::new().service(Files::new("/test", ".").index_file("Cargo.toml")), - ) - .await; + #[actix_rt::test] + async fn test_named_file_ranges_status_code() { + let mut srv = test::init_service( + App::new().service(Files::new("/test", ".").index_file("Cargo.toml")), + ) + .await; - // Valid range header - let request = TestRequest::get() - .uri("/t%65st/Cargo.toml") - .header(header::RANGE, "bytes=10-20") - .to_request(); - let response = test::call_service(&mut srv, request).await; - assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT); + // Valid range header + let request = TestRequest::get() + .uri("/t%65st/Cargo.toml") + .header(header::RANGE, "bytes=10-20") + .to_request(); + let response = test::call_service(&mut srv, request).await; + assert_eq!(response.status(), StatusCode::PARTIAL_CONTENT); - // Invalid range header - let request = TestRequest::get() - .uri("/t%65st/Cargo.toml") - .header(header::RANGE, "bytes=1-0") - .to_request(); - let response = test::call_service(&mut srv, request).await; + // Invalid range header + let request = TestRequest::get() + .uri("/t%65st/Cargo.toml") + .header(header::RANGE, "bytes=1-0") + .to_request(); + let response = test::call_service(&mut srv, request).await; - assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE); - }) + assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE); } - #[test] - fn test_named_file_content_range_headers() { - block_on(async { - let mut srv = test::init_service( - App::new() - .service(Files::new("/test", ".").index_file("tests/test.binary")), - ) - .await; + #[actix_rt::test] + async fn test_named_file_content_range_headers() { + let mut srv = test::init_service( + App::new().service(Files::new("/test", ".").index_file("tests/test.binary")), + ) + .await; - // Valid range header - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") - .header(header::RANGE, "bytes=10-20") - .to_request(); + // Valid range header + let request = TestRequest::get() + .uri("/t%65st/tests/test.binary") + .header(header::RANGE, "bytes=10-20") + .to_request(); - let response = test::call_service(&mut srv, request).await; - let contentrange = response - .headers() - .get(header::CONTENT_RANGE) + let response = test::call_service(&mut srv, request).await; + let contentrange = response + .headers() + .get(header::CONTENT_RANGE) + .unwrap() + .to_str() + .unwrap(); + + assert_eq!(contentrange, "bytes 10-20/100"); + + // Invalid range header + let request = TestRequest::get() + .uri("/t%65st/tests/test.binary") + .header(header::RANGE, "bytes=10-5") + .to_request(); + let response = test::call_service(&mut srv, request).await; + + let contentrange = response + .headers() + .get(header::CONTENT_RANGE) + .unwrap() + .to_str() + .unwrap(); + + assert_eq!(contentrange, "bytes */100"); + } + + #[actix_rt::test] + async fn test_named_file_content_length_headers() { + // use actix_web::body::{MessageBody, ResponseBody}; + + let mut srv = test::init_service( + App::new().service(Files::new("test", ".").index_file("tests/test.binary")), + ) + .await; + + // Valid range header + let request = TestRequest::get() + .uri("/t%65st/tests/test.binary") + .header(header::RANGE, "bytes=10-20") + .to_request(); + let _response = test::call_service(&mut srv, request).await; + + // let contentlength = response + // .headers() + // .get(header::CONTENT_LENGTH) + // .unwrap() + // .to_str() + // .unwrap(); + // assert_eq!(contentlength, "11"); + + // Invalid range header + let request = TestRequest::get() + .uri("/t%65st/tests/test.binary") + .header(header::RANGE, "bytes=10-8") + .to_request(); + let response = test::call_service(&mut srv, request).await; + assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE); + + // Without range header + let request = TestRequest::get() + .uri("/t%65st/tests/test.binary") + // .no_default_headers() + .to_request(); + let _response = test::call_service(&mut srv, request).await; + + // let contentlength = response + // .headers() + // .get(header::CONTENT_LENGTH) + // .unwrap() + // .to_str() + // .unwrap(); + // assert_eq!(contentlength, "100"); + + // chunked + let request = TestRequest::get() + .uri("/t%65st/tests/test.binary") + .to_request(); + let response = test::call_service(&mut srv, request).await; + + // with enabled compression + // { + // let te = response + // .headers() + // .get(header::TRANSFER_ENCODING) + // .unwrap() + // .to_str() + // .unwrap(); + // assert_eq!(te, "chunked"); + // } + + let bytes = test::read_body(response).await; + let data = Bytes::from(fs::read("tests/test.binary").unwrap()); + assert_eq!(bytes, data); + } + + #[actix_rt::test] + async fn test_head_content_length_headers() { + let mut srv = test::init_service( + App::new().service(Files::new("test", ".").index_file("tests/test.binary")), + ) + .await; + + // Valid range header + let request = TestRequest::default() + .method(Method::HEAD) + .uri("/t%65st/tests/test.binary") + .to_request(); + let _response = test::call_service(&mut srv, request).await; + + // TODO: fix check + // let contentlength = response + // .headers() + // .get(header::CONTENT_LENGTH) + // .unwrap() + // .to_str() + // .unwrap(); + // assert_eq!(contentlength, "100"); + } + + #[actix_rt::test] + async fn test_static_files_with_spaces() { + let mut srv = test::init_service( + App::new().service(Files::new("/", ".").index_file("Cargo.toml")), + ) + .await; + let request = TestRequest::get() + .uri("/tests/test%20space.binary") + .to_request(); + let response = test::call_service(&mut srv, request).await; + assert_eq!(response.status(), StatusCode::OK); + + let bytes = test::read_body(response).await; + let data = Bytes::from(fs::read("tests/test space.binary").unwrap()); + assert_eq!(bytes, data); + } + + #[actix_rt::test] + async fn test_files_not_allowed() { + let mut srv = test::init_service(App::new().service(Files::new("/", "."))).await; + + let req = TestRequest::default() + .uri("/Cargo.toml") + .method(Method::POST) + .to_request(); + + let resp = test::call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); + + let mut srv = test::init_service(App::new().service(Files::new("/", "."))).await; + let req = TestRequest::default() + .method(Method::PUT) + .uri("/Cargo.toml") + .to_request(); + let resp = test::call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); + } + + #[actix_rt::test] + async fn test_files_guards() { + let mut srv = test::init_service( + App::new().service(Files::new("/", ".").use_guards(guard::Post())), + ) + .await; + + let req = TestRequest::default() + .uri("/Cargo.toml") + .method(Method::POST) + .to_request(); + + let resp = test::call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + } + + #[actix_rt::test] + async fn test_named_file_content_encoding() { + let mut srv = test::init_service(App::new().wrap(Compress::default()).service( + web::resource("/").to(|| { + async { + NamedFile::open("Cargo.toml") + .unwrap() + .set_content_encoding(header::ContentEncoding::Identity) + } + }), + )) + .await; + + let request = TestRequest::get() + .uri("/") + .header(header::ACCEPT_ENCODING, "gzip") + .to_request(); + let res = test::call_service(&mut srv, request).await; + assert_eq!(res.status(), StatusCode::OK); + assert!(!res.headers().contains_key(header::CONTENT_ENCODING)); + } + + #[actix_rt::test] + async fn test_named_file_content_encoding_gzip() { + let mut srv = test::init_service(App::new().wrap(Compress::default()).service( + web::resource("/").to(|| { + async { + NamedFile::open("Cargo.toml") + .unwrap() + .set_content_encoding(header::ContentEncoding::Gzip) + } + }), + )) + .await; + + let request = TestRequest::get() + .uri("/") + .header(header::ACCEPT_ENCODING, "gzip") + .to_request(); + let res = test::call_service(&mut srv, request).await; + assert_eq!(res.status(), StatusCode::OK); + assert_eq!( + res.headers() + .get(header::CONTENT_ENCODING) .unwrap() .to_str() - .unwrap(); - - assert_eq!(contentrange, "bytes 10-20/100"); - - // Invalid range header - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") - .header(header::RANGE, "bytes=10-5") - .to_request(); - let response = test::call_service(&mut srv, request).await; - - let contentrange = response - .headers() - .get(header::CONTENT_RANGE) - .unwrap() - .to_str() - .unwrap(); - - assert_eq!(contentrange, "bytes */100"); - }) + .unwrap(), + "gzip" + ); } - #[test] - fn test_named_file_content_length_headers() { - block_on(async { - // use actix_web::body::{MessageBody, ResponseBody}; - - let mut srv = test::init_service( - App::new() - .service(Files::new("test", ".").index_file("tests/test.binary")), - ) - .await; - - // Valid range header - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") - .header(header::RANGE, "bytes=10-20") - .to_request(); - let _response = test::call_service(&mut srv, request).await; - - // let contentlength = response - // .headers() - // .get(header::CONTENT_LENGTH) - // .unwrap() - // .to_str() - // .unwrap(); - // assert_eq!(contentlength, "11"); - - // Invalid range header - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") - .header(header::RANGE, "bytes=10-8") - .to_request(); - let response = test::call_service(&mut srv, request).await; - assert_eq!(response.status(), StatusCode::RANGE_NOT_SATISFIABLE); - - // Without range header - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") - // .no_default_headers() - .to_request(); - let _response = test::call_service(&mut srv, request).await; - - // let contentlength = response - // .headers() - // .get(header::CONTENT_LENGTH) - // .unwrap() - // .to_str() - // .unwrap(); - // assert_eq!(contentlength, "100"); - - // chunked - let request = TestRequest::get() - .uri("/t%65st/tests/test.binary") - .to_request(); - let response = test::call_service(&mut srv, request).await; - - // with enabled compression - // { - // let te = response - // .headers() - // .get(header::TRANSFER_ENCODING) - // .unwrap() - // .to_str() - // .unwrap(); - // assert_eq!(te, "chunked"); - // } - - let bytes = test::read_body(response).await; - let data = Bytes::from(fs::read("tests/test.binary").unwrap()); - assert_eq!(bytes, data); - }) + #[actix_rt::test] + async fn test_named_file_allowed_method() { + let req = TestRequest::default().method(Method::GET).to_http_request(); + let file = NamedFile::open("Cargo.toml").unwrap(); + let resp = file.respond_to(&req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); } - #[test] - fn test_head_content_length_headers() { - block_on(async { - let mut srv = test::init_service( - App::new() - .service(Files::new("test", ".").index_file("tests/test.binary")), - ) - .await; + #[actix_rt::test] + async fn test_static_files() { + let mut srv = test::init_service( + App::new().service(Files::new("/", ".").show_files_listing()), + ) + .await; + let req = TestRequest::with_uri("/missing").to_request(); - // Valid range header - let request = TestRequest::default() - .method(Method::HEAD) - .uri("/t%65st/tests/test.binary") - .to_request(); - let _response = test::call_service(&mut srv, request).await; + let resp = test::call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::NOT_FOUND); - // TODO: fix check - // let contentlength = response - // .headers() - // .get(header::CONTENT_LENGTH) - // .unwrap() - // .to_str() - // .unwrap(); - // assert_eq!(contentlength, "100"); - }) + let mut srv = test::init_service(App::new().service(Files::new("/", "."))).await; + + let req = TestRequest::default().to_request(); + let resp = test::call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + + let mut srv = test::init_service( + App::new().service(Files::new("/", ".").show_files_listing()), + ) + .await; + let req = TestRequest::with_uri("/tests").to_request(); + let resp = test::call_service(&mut srv, req).await; + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + "text/html; charset=utf-8" + ); + + let bytes = test::read_body(resp).await; + assert!(format!("{:?}", bytes).contains("/tests/test.png")); } - #[test] - fn test_static_files_with_spaces() { - block_on(async { - let mut srv = test::init_service( - App::new().service(Files::new("/", ".").index_file("Cargo.toml")), - ) - .await; - let request = TestRequest::get() - .uri("/tests/test%20space.binary") - .to_request(); - let response = test::call_service(&mut srv, request).await; - assert_eq!(response.status(), StatusCode::OK); + #[actix_rt::test] + async fn test_redirect_to_slash_directory() { + // should not redirect if no index + let mut srv = test::init_service( + App::new().service(Files::new("/", ".").redirect_to_slash_directory()), + ) + .await; + let req = TestRequest::with_uri("/tests").to_request(); + let resp = test::call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::NOT_FOUND); - let bytes = test::read_body(response).await; - let data = Bytes::from(fs::read("tests/test space.binary").unwrap()); - assert_eq!(bytes, data); - }) + // should redirect if index present + let mut srv = test::init_service( + App::new().service( + Files::new("/", ".") + .index_file("test.png") + .redirect_to_slash_directory(), + ), + ) + .await; + let req = TestRequest::with_uri("/tests").to_request(); + let resp = test::call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::FOUND); + + // should not redirect if the path is wrong + let req = TestRequest::with_uri("/not_existing").to_request(); + let resp = test::call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::NOT_FOUND); } - #[test] - fn test_files_not_allowed() { - block_on(async { - let mut srv = - test::init_service(App::new().service(Files::new("/", "."))).await; - - let req = TestRequest::default() - .uri("/Cargo.toml") - .method(Method::POST) - .to_request(); - - let resp = test::call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); - - let mut srv = - test::init_service(App::new().service(Files::new("/", "."))).await; - let req = TestRequest::default() - .method(Method::PUT) - .uri("/Cargo.toml") - .to_request(); - let resp = test::call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); - }) - } - - #[test] - fn test_files_guards() { - block_on(async { - let mut srv = test::init_service( - App::new().service(Files::new("/", ".").use_guards(guard::Post())), - ) - .await; - - let req = TestRequest::default() - .uri("/Cargo.toml") - .method(Method::POST) - .to_request(); - - let resp = test::call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); - }) - } - - #[test] - fn test_named_file_content_encoding() { - block_on(async { - let mut srv = - test::init_service(App::new().wrap(Compress::default()).service( - web::resource("/").to(|| { - async { - NamedFile::open("Cargo.toml") - .unwrap() - .set_content_encoding(header::ContentEncoding::Identity) - } - }), - )) - .await; - - let request = TestRequest::get() - .uri("/") - .header(header::ACCEPT_ENCODING, "gzip") - .to_request(); - let res = test::call_service(&mut srv, request).await; - assert_eq!(res.status(), StatusCode::OK); - assert!(!res.headers().contains_key(header::CONTENT_ENCODING)); - }) - } - - #[test] - fn test_named_file_content_encoding_gzip() { - block_on(async { - let mut srv = - test::init_service(App::new().wrap(Compress::default()).service( - web::resource("/").to(|| { - async { - NamedFile::open("Cargo.toml") - .unwrap() - .set_content_encoding(header::ContentEncoding::Gzip) - } - }), - )) - .await; - - let request = TestRequest::get() - .uri("/") - .header(header::ACCEPT_ENCODING, "gzip") - .to_request(); - let res = test::call_service(&mut srv, request).await; - assert_eq!(res.status(), StatusCode::OK); - assert_eq!( - res.headers() - .get(header::CONTENT_ENCODING) - .unwrap() - .to_str() - .unwrap(), - "gzip" - ); - }) - } - - #[test] - fn test_named_file_allowed_method() { - block_on(async { - let req = TestRequest::default().method(Method::GET).to_http_request(); - let file = NamedFile::open("Cargo.toml").unwrap(); - let resp = file.respond_to(&req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - }) - } - - #[test] - fn test_static_files() { - block_on(async { - let mut srv = test::init_service( - App::new().service(Files::new("/", ".").show_files_listing()), - ) - .await; - let req = TestRequest::with_uri("/missing").to_request(); - - let resp = test::call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::NOT_FOUND); - - let mut srv = - test::init_service(App::new().service(Files::new("/", "."))).await; - - let req = TestRequest::default().to_request(); - let resp = test::call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::NOT_FOUND); - - let mut srv = test::init_service( - App::new().service(Files::new("/", ".").show_files_listing()), - ) - .await; - let req = TestRequest::with_uri("/tests").to_request(); - let resp = test::call_service(&mut srv, req).await; - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - "text/html; charset=utf-8" - ); - - let bytes = test::read_body(resp).await; - assert!(format!("{:?}", bytes).contains("/tests/test.png")); - }) - } - - #[test] - fn test_redirect_to_slash_directory() { - block_on(async { - // should not redirect if no index - let mut srv = test::init_service( - App::new().service(Files::new("/", ".").redirect_to_slash_directory()), - ) - .await; - let req = TestRequest::with_uri("/tests").to_request(); - let resp = test::call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::NOT_FOUND); - - // should redirect if index present - let mut srv = test::init_service( - App::new().service( - Files::new("/", ".") - .index_file("test.png") - .redirect_to_slash_directory(), - ), - ) - .await; - let req = TestRequest::with_uri("/tests").to_request(); - let resp = test::call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::FOUND); - - // should not redirect if the path is wrong - let req = TestRequest::with_uri("/not_existing").to_request(); - let resp = test::call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::NOT_FOUND); - }) - } - - #[test] - fn test_static_files_bad_directory() { + #[actix_rt::test] + async fn test_static_files_bad_directory() { let _st: Files = Files::new("/", "missing"); let _st: Files = Files::new("/", "Cargo.toml"); } - #[test] - fn test_default_handler_file_missing() { - block_on(async { - let mut st = Files::new("/", ".") - .default_handler(|req: ServiceRequest| { - ok(req.into_response(HttpResponse::Ok().body("default content"))) - }) - .new_service(&()) - .await - .unwrap(); - let req = TestRequest::with_uri("/missing").to_srv_request(); + #[actix_rt::test] + async fn test_default_handler_file_missing() { + let mut st = Files::new("/", ".") + .default_handler(|req: ServiceRequest| { + ok(req.into_response(HttpResponse::Ok().body("default content"))) + }) + .new_service(&()) + .await + .unwrap(); + let req = TestRequest::with_uri("/missing").to_srv_request(); - let resp = test::call_service(&mut st, req).await; - assert_eq!(resp.status(), StatusCode::OK); - let bytes = test::read_body(resp).await; - assert_eq!(bytes, Bytes::from_static(b"default content")); - }) + let resp = test::call_service(&mut st, req).await; + assert_eq!(resp.status(), StatusCode::OK); + let bytes = test::read_body(resp).await; + assert_eq!(bytes, Bytes::from_static(b"default content")); } - // #[test] - // fn test_serve_index() { + // #[actix_rt::test] + // async fn test_serve_index() { // let st = Files::new(".").index_file("test.binary"); // let req = TestRequest::default().uri("/tests").finish(); @@ -1375,8 +1319,8 @@ mod tests { // assert_eq!(resp.status(), StatusCode::NOT_FOUND); // } - // #[test] - // fn test_serve_index_nested() { + // #[actix_rt::test] + // async fn test_serve_index_nested() { // let st = Files::new(".").index_file("mod.rs"); // let req = TestRequest::default().uri("/src/client").finish(); // let resp = st.handle(&req).respond_to(&req).unwrap(); @@ -1392,7 +1336,7 @@ mod tests { // ); // } - // #[test] + // #[actix_rt::test] // fn integration_serve_index() { // let mut srv = test::TestServer::with_factory(|| { // App::new().handler( @@ -1425,7 +1369,7 @@ mod tests { // assert_eq!(response.status(), StatusCode::NOT_FOUND); // } - // #[test] + // #[actix_rt::test] // fn integration_percent_encoded() { // let mut srv = test::TestServer::with_factory(|| { // App::new().handler( @@ -1443,8 +1387,8 @@ mod tests { // assert_eq!(response.status(), StatusCode::OK); // } - #[test] - fn test_path_buf() { + #[actix_rt::test] + async fn test_path_buf() { assert_eq!( PathBufWrp::get_pathbuf("/test/.tt").map(|t| t.0), Err(UriSegmentError::BadStart('.')) diff --git a/actix-framed/src/test.rs b/actix-framed/src/test.rs index b90a493dc..7969d51ff 100644 --- a/actix-framed/src/test.rs +++ b/actix-framed/src/test.rs @@ -7,7 +7,6 @@ use actix_http::http::header::{Header, HeaderName, IntoHeaderValue}; use actix_http::http::{HttpTryFrom, Method, Uri, Version}; use actix_http::test::{TestBuffer, TestRequest as HttpTestRequest}; use actix_router::{Path, Url}; -use actix_rt::Runtime; use crate::{FramedRequest, State}; @@ -119,13 +118,12 @@ impl TestRequest { } /// This method generates `FramedRequest` instance and executes async handler - pub fn run(self, f: F) -> Result + pub async fn run(self, f: F) -> Result where F: FnOnce(FramedRequest) -> R, R: Future>, { - let mut rt = Runtime::new().unwrap(); - rt.block_on(f(self.finish())) + f(self.finish()).await } } diff --git a/actix-framed/tests/test_server.rs b/actix-framed/tests/test_server.rs index 6e4bb6ada..4d1028d31 100644 --- a/actix-framed/tests/test_server.rs +++ b/actix-framed/tests/test_server.rs @@ -1,6 +1,6 @@ use actix_codec::{AsyncRead, AsyncWrite}; use actix_http::{body, http::StatusCode, ws, Error, HttpService, Response}; -use actix_http_test::{block_on, TestServer}; +use actix_http_test::TestServer; use actix_service::{pipeline_factory, IntoServiceFactory, ServiceFactory}; use actix_utils::framed::FramedTransport; use bytes::{Bytes, BytesMut}; @@ -38,126 +38,121 @@ async fn service(msg: ws::Frame) -> Result { Ok(msg) } -#[test] -fn test_simple() { - block_on(async { - let mut srv = TestServer::start(|| { - HttpService::build() - .upgrade( - FramedApp::new() - .service(FramedRoute::get("/index.html").to(ws_service)), - ) - .finish(|_| future::ok::<_, Error>(Response::NotFound())) - }); - - assert!(srv.ws_at("/test").await.is_err()); - - // client service - let mut framed = srv.ws_at("/index.html").await.unwrap(); - framed - .send(ws::Message::Text("text".to_string())) - .await - .unwrap(); - let (item, mut framed) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Text(Some(BytesMut::from("text"))) - ); - - framed - .send(ws::Message::Binary("text".into())) - .await - .unwrap(); - let (item, mut framed) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Binary(Some(Bytes::from_static(b"text").into())) - ); - - framed.send(ws::Message::Ping("text".into())).await.unwrap(); - let (item, mut framed) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Pong("text".to_string().into()) - ); - - framed - .send(ws::Message::Close(Some(ws::CloseCode::Normal.into()))) - .await - .unwrap(); - - let (item, _) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Close(Some(ws::CloseCode::Normal.into())) - ); - }) -} - -#[test] -fn test_service() { - block_on(async { - let mut srv = TestServer::start(|| { - pipeline_factory(actix_http::h1::OneRequest::new().map_err(|_| ())).and_then( - pipeline_factory( - pipeline_factory(VerifyWebSockets::default()) - .then(SendError::default()) - .map_err(|_| ()), - ) - .and_then( - FramedApp::new() - .service(FramedRoute::get("/index.html").to(ws_service)) - .into_factory() - .map_err(|_| ()), - ), +#[actix_rt::test] +async fn test_simple() { + let mut srv = TestServer::start(|| { + HttpService::build() + .upgrade( + FramedApp::new().service(FramedRoute::get("/index.html").to(ws_service)), ) - }); + .finish(|_| future::ok::<_, Error>(Response::NotFound())) + }); - // non ws request - let res = srv.get("/index.html").send().await.unwrap(); - assert_eq!(res.status(), StatusCode::BAD_REQUEST); + assert!(srv.ws_at("/test").await.is_err()); - // not found - assert!(srv.ws_at("/test").await.is_err()); + // client service + let mut framed = srv.ws_at("/index.html").await.unwrap(); + framed + .send(ws::Message::Text("text".to_string())) + .await + .unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Text(Some(BytesMut::from("text"))) + ); - // client service - let mut framed = srv.ws_at("/index.html").await.unwrap(); - framed - .send(ws::Message::Text("text".to_string())) - .await - .unwrap(); - let (item, mut framed) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Text(Some(BytesMut::from("text"))) - ); + framed + .send(ws::Message::Binary("text".into())) + .await + .unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Binary(Some(Bytes::from_static(b"text").into())) + ); - framed - .send(ws::Message::Binary("text".into())) - .await - .unwrap(); - let (item, mut framed) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Binary(Some(Bytes::from_static(b"text").into())) - ); + framed.send(ws::Message::Ping("text".into())).await.unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Pong("text".to_string().into()) + ); - framed.send(ws::Message::Ping("text".into())).await.unwrap(); - let (item, mut framed) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Pong("text".to_string().into()) - ); + framed + .send(ws::Message::Close(Some(ws::CloseCode::Normal.into()))) + .await + .unwrap(); - framed - .send(ws::Message::Close(Some(ws::CloseCode::Normal.into()))) - .await - .unwrap(); - - let (item, _) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Close(Some(ws::CloseCode::Normal.into())) - ); - }) + let (item, _) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Close(Some(ws::CloseCode::Normal.into())) + ); +} + +#[actix_rt::test] +async fn test_service() { + let mut srv = TestServer::start(|| { + pipeline_factory(actix_http::h1::OneRequest::new().map_err(|_| ())).and_then( + pipeline_factory( + pipeline_factory(VerifyWebSockets::default()) + .then(SendError::default()) + .map_err(|_| ()), + ) + .and_then( + FramedApp::new() + .service(FramedRoute::get("/index.html").to(ws_service)) + .into_factory() + .map_err(|_| ()), + ), + ) + }); + + // non ws request + let res = srv.get("/index.html").send().await.unwrap(); + assert_eq!(res.status(), StatusCode::BAD_REQUEST); + + // not found + assert!(srv.ws_at("/test").await.is_err()); + + // client service + let mut framed = srv.ws_at("/index.html").await.unwrap(); + framed + .send(ws::Message::Text("text".to_string())) + .await + .unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Text(Some(BytesMut::from("text"))) + ); + + framed + .send(ws::Message::Binary("text".into())) + .await + .unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Binary(Some(Bytes::from_static(b"text").into())) + ); + + framed.send(ws::Message::Ping("text".into())).await.unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Pong("text".to_string().into()) + ); + + framed + .send(ws::Message::Close(Some(ws::CloseCode::Normal.into()))) + .await + .unwrap(); + + let (item, _) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Close(Some(ws::CloseCode::Normal.into())) + ); } diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index cf390e796..cfed0bf14 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -29,7 +29,7 @@ default = [] openssl = ["open-ssl", "actix-connect/openssl", "tokio-openssl"] # rustls support -# rustls = ["rust-tls", "webpki-roots", "actix-connect/rustls"] +rustls = ["rust-tls", "webpki-roots", "actix-connect/rustls"] # brotli encoding, requires c compiler brotli = ["brotli2"] @@ -52,6 +52,7 @@ actix-codec = "0.2.0-alpha.1" actix-connect = "1.0.0-alpha.1" actix-utils = "0.5.0-alpha.1" actix-server-config = "0.3.0-alpha.1" +actix-rt = "1.0.0-alpha.1" actix-threadpool = "0.2.0-alpha.1" base64 = "0.10" @@ -83,11 +84,7 @@ slab = "0.4" serde_urlencoded = "0.6.1" time = "0.1.42" -tokio = "=0.2.0-alpha.6" -tokio-io = "=0.2.0-alpha.6" tokio-net = "=0.2.0-alpha.6" -tokio-timer = "0.3.0-alpha.6" -tokio-executor = "=0.2.0-alpha.6" trust-dns-resolver = { version="0.18.0-alpha.1", default-features = false } # for secure cookie @@ -106,8 +103,7 @@ rust-tls = { version = "0.16.0", package="rustls", optional = true } webpki-roots = { version = "0.18", optional = true } [dev-dependencies] -actix-rt = "1.0.0-alpha.1" -actix-server = { version = "0.8.0-alpha.1", features=["openssl"] } +actix-server = { version = "0.8.0-alpha.1", features=["openssl", "rustls"] } actix-connect = { version = "1.0.0-alpha.1", features=["openssl"] } actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } env_logger = "0.6" diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs index 1d3a43fe5..b69c21eaa 100644 --- a/actix-http/src/body.rs +++ b/actix-http/src/body.rs @@ -432,8 +432,7 @@ where #[cfg(test)] mod tests { use super::*; - use actix_http_test::block_on; - use futures::future::{lazy, poll_fn}; + use futures::future::poll_fn; impl Body { pub(crate) fn get_ref(&self) -> &[u8] { @@ -453,21 +452,21 @@ mod tests { } } - #[test] - fn test_static_str() { + #[actix_rt::test] + async fn test_static_str() { assert_eq!(Body::from("").size(), BodySize::Sized(0)); assert_eq!(Body::from("test").size(), BodySize::Sized(4)); assert_eq!(Body::from("test").get_ref(), b"test"); assert_eq!("test".size(), BodySize::Sized(4)); assert_eq!( - block_on(poll_fn(|cx| "test".poll_next(cx))).unwrap().ok(), + poll_fn(|cx| "test".poll_next(cx)).await.unwrap().ok(), Some(Bytes::from("test")) ); } - #[test] - fn test_static_bytes() { + #[actix_rt::test] + async fn test_static_bytes() { assert_eq!(Body::from(b"test".as_ref()).size(), BodySize::Sized(4)); assert_eq!(Body::from(b"test".as_ref()).get_ref(), b"test"); assert_eq!( @@ -478,55 +477,57 @@ mod tests { assert_eq!((&b"test"[..]).size(), BodySize::Sized(4)); assert_eq!( - block_on(poll_fn(|cx| (&b"test"[..]).poll_next(cx))) + poll_fn(|cx| (&b"test"[..]).poll_next(cx)) + .await .unwrap() .ok(), Some(Bytes::from("test")) ); } - #[test] - fn test_vec() { + #[actix_rt::test] + async fn test_vec() { assert_eq!(Body::from(Vec::from("test")).size(), BodySize::Sized(4)); assert_eq!(Body::from(Vec::from("test")).get_ref(), b"test"); assert_eq!(Vec::from("test").size(), BodySize::Sized(4)); assert_eq!( - block_on(poll_fn(|cx| Vec::from("test").poll_next(cx))) + poll_fn(|cx| Vec::from("test").poll_next(cx)) + .await .unwrap() .ok(), Some(Bytes::from("test")) ); } - #[test] - fn test_bytes() { + #[actix_rt::test] + async fn test_bytes() { let mut b = Bytes::from("test"); assert_eq!(Body::from(b.clone()).size(), BodySize::Sized(4)); assert_eq!(Body::from(b.clone()).get_ref(), b"test"); assert_eq!(b.size(), BodySize::Sized(4)); assert_eq!( - block_on(poll_fn(|cx| b.poll_next(cx))).unwrap().ok(), + poll_fn(|cx| b.poll_next(cx)).await.unwrap().ok(), Some(Bytes::from("test")) ); } - #[test] - fn test_bytes_mut() { + #[actix_rt::test] + async fn test_bytes_mut() { let mut b = BytesMut::from("test"); assert_eq!(Body::from(b.clone()).size(), BodySize::Sized(4)); assert_eq!(Body::from(b.clone()).get_ref(), b"test"); assert_eq!(b.size(), BodySize::Sized(4)); assert_eq!( - block_on(poll_fn(|cx| b.poll_next(cx))).unwrap().ok(), + poll_fn(|cx| b.poll_next(cx)).await.unwrap().ok(), Some(Bytes::from("test")) ); } - #[test] - fn test_string() { + #[actix_rt::test] + async fn test_string() { let mut b = "test".to_owned(); assert_eq!(Body::from(b.clone()).size(), BodySize::Sized(4)); assert_eq!(Body::from(b.clone()).get_ref(), b"test"); @@ -535,26 +536,26 @@ mod tests { assert_eq!(b.size(), BodySize::Sized(4)); assert_eq!( - block_on(poll_fn(|cx| b.poll_next(cx))).unwrap().ok(), + poll_fn(|cx| b.poll_next(cx)).await.unwrap().ok(), Some(Bytes::from("test")) ); } - #[test] - fn test_unit() { + #[actix_rt::test] + async fn test_unit() { assert_eq!(().size(), BodySize::Empty); - assert!(block_on(poll_fn(|cx| ().poll_next(cx))).is_none()); + assert!(poll_fn(|cx| ().poll_next(cx)).await.is_none()); } - #[test] - fn test_box() { + #[actix_rt::test] + async fn test_box() { let mut val = Box::new(()); assert_eq!(val.size(), BodySize::Empty); - assert!(block_on(poll_fn(|cx| val.poll_next(cx))).is_none()); + assert!(poll_fn(|cx| val.poll_next(cx)).await.is_none()); } - #[test] - fn test_body_eq() { + #[actix_rt::test] + async fn test_body_eq() { assert!(Body::None == Body::None); assert!(Body::None != Body::Empty); assert!(Body::Empty == Body::Empty); @@ -566,15 +567,15 @@ mod tests { assert!(Body::Bytes(Bytes::from_static(b"1")) != Body::None); } - #[test] - fn test_body_debug() { + #[actix_rt::test] + async fn test_body_debug() { assert!(format!("{:?}", Body::None).contains("Body::None")); assert!(format!("{:?}", Body::Empty).contains("Body::Empty")); assert!(format!("{:?}", Body::Bytes(Bytes::from_static(b"1"))).contains("1")); } - #[test] - fn test_serde_json() { + #[actix_rt::test] + async fn test_serde_json() { use serde_json::json; assert_eq!( Body::from(serde_json::Value::String("test".into())).size(), diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index 1895f5306..eaa3d97e4 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -1,8 +1,5 @@ use std::fmt; -use std::future::Future; use std::marker::PhantomData; -use std::pin::Pin; -use std::task::{Context, Poll}; use std::time::Duration; use actix_codec::{AsyncRead, AsyncWrite}; @@ -11,7 +8,6 @@ use actix_connect::{ }; use actix_service::{apply_fn, Service}; use actix_utils::timeout::{TimeoutError, TimeoutService}; -use futures::future::Ready; use http::Uri; use tokio_net::tcp::TcpStream; @@ -344,7 +340,6 @@ mod connect_impl { use std::task::{Context, Poll}; use futures::future::{err, Either, Ready}; - use futures::ready; use super::*; use crate::client::connection::IoConnection; @@ -402,7 +397,10 @@ mod connect_impl { #[cfg(any(feature = "openssl", feature = "rustls"))] mod connect_impl { + use std::future::Future; use std::marker::PhantomData; + use std::pin::Pin; + use std::task::{Context, Poll}; use futures::future::Either; use futures::ready; diff --git a/actix-http/src/client/h1proto.rs b/actix-http/src/client/h1proto.rs index 041a36856..ddfc7a314 100644 --- a/actix-http/src/client/h1proto.rs +++ b/actix-http/src/client/h1proto.rs @@ -1,4 +1,3 @@ -use std::future::Future; use std::io::Write; use std::pin::Pin; use std::task::{Context, Poll}; @@ -6,8 +5,8 @@ use std::{io, time}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; use bytes::{BufMut, Bytes, BytesMut}; -use futures::future::{ok, poll_fn, Either}; -use futures::{Sink, SinkExt, Stream, StreamExt}; +use futures::future::poll_fn; +use futures::{SinkExt, Stream, StreamExt}; use crate::error::PayloadError; use crate::h1; diff --git a/actix-http/src/client/h2proto.rs b/actix-http/src/client/h2proto.rs index 1647abf81..a94562f2d 100644 --- a/actix-http/src/client/h2proto.rs +++ b/actix-http/src/client/h2proto.rs @@ -1,11 +1,8 @@ -use std::future::Future; -use std::pin::Pin; -use std::task::{Context, Poll}; use std::time; use actix_codec::{AsyncRead, AsyncWrite}; use bytes::Bytes; -use futures::future::{err, poll_fn, Either}; +use futures::future::poll_fn; use h2::{client::SendRequest, SendStream}; use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, TRANSFER_ENCODING}; use http::{request::Request, HttpTryFrom, Method, Version}; diff --git a/actix-http/src/client/pool.rs b/actix-http/src/client/pool.rs index 1952dca59..c61039866 100644 --- a/actix-http/src/client/pool.rs +++ b/actix-http/src/client/pool.rs @@ -1,23 +1,22 @@ use std::cell::RefCell; use std::collections::VecDeque; use std::future::Future; -use std::io; use std::pin::Pin; use std::rc::Rc; use std::task::{Context, Poll}; use std::time::{Duration, Instant}; use actix_codec::{AsyncRead, AsyncWrite}; +use actix_rt::time::{delay_for, Delay}; use actix_service::Service; use actix_utils::{oneshot, task::LocalWaker}; use bytes::Bytes; -use futures::future::{err, ok, poll_fn, Either, FutureExt, LocalBoxFuture, Ready}; +use futures::future::{poll_fn, FutureExt, LocalBoxFuture}; use h2::client::{handshake, Connection, SendRequest}; use hashbrown::HashMap; use http::uri::Authority; use indexmap::IndexSet; use slab::Slab; -use tokio_timer::{delay_for, Delay}; use super::connection::{ConnectionType, IoConnection}; use super::error::ConnectError; @@ -100,7 +99,7 @@ where fn call(&mut self, req: Connect) -> Self::Future { // start support future - tokio_executor::current_thread::spawn(ConnectorPoolSupport { + actix_rt::spawn(ConnectorPoolSupport { connector: self.0.clone(), inner: self.1.clone(), }); @@ -139,7 +138,7 @@ where )) } else { let (snd, connection) = handshake(io).await?; - tokio_executor::current_thread::spawn(connection.map(|_| ())); + actix_rt::spawn(connection.map(|_| ())); Ok(IoConnection::new( ConnectionType::H2(snd), Instant::now(), @@ -328,9 +327,7 @@ where { if let Some(timeout) = self.disconnect_timeout { if let ConnectionType::H1(io) = conn.io { - tokio_executor::current_thread::spawn(CloseConnection::new( - io, timeout, - )) + actix_rt::spawn(CloseConnection::new(io, timeout)) } } } else { @@ -342,9 +339,9 @@ where Poll::Ready(Ok(n)) if n > 0 => { if let Some(timeout) = self.disconnect_timeout { if let ConnectionType::H1(io) = io { - tokio_executor::current_thread::spawn( - CloseConnection::new(io, timeout), - ) + actix_rt::spawn(CloseConnection::new( + io, timeout, + )) } } continue; @@ -376,7 +373,7 @@ where self.acquired -= 1; if let Some(timeout) = self.disconnect_timeout { if let ConnectionType::H1(io) = io { - tokio_executor::current_thread::spawn(CloseConnection::new(io, timeout)) + actix_rt::spawn(CloseConnection::new(io, timeout)) } } self.check_availibility(); @@ -518,7 +515,7 @@ where inner: Rc>>, fut: F, ) { - tokio_executor::current_thread::spawn(OpenWaitingConnection { + actix_rt::spawn(OpenWaitingConnection { key, fut, h2: None, @@ -554,7 +551,7 @@ where if let Some(ref mut h2) = this.h2 { return match Pin::new(h2).poll(cx) { Poll::Ready(Ok((snd, connection))) => { - tokio_executor::current_thread::spawn(connection.map(|_| ())); + actix_rt::spawn(connection.map(|_| ())); let rx = this.rx.take().unwrap(); let _ = rx.send(Ok(IoConnection::new( ConnectionType::H2(snd), diff --git a/actix-http/src/config.rs b/actix-http/src/config.rs index 488e4d98a..bab3cdc6d 100644 --- a/actix-http/src/config.rs +++ b/actix-http/src/config.rs @@ -4,10 +4,10 @@ use std::fmt::Write; use std::rc::Rc; use std::time::{Duration, Instant}; +use actix_rt::time::{delay, delay_for, Delay}; use bytes::BytesMut; -use futures::{future, Future, FutureExt}; +use futures::{future, FutureExt}; use time; -use tokio_timer::{delay, delay_for, Delay}; // "Sun, 06 Nov 1994 08:49:37 GMT".len() const DATE_VALUE_LENGTH: usize = 29; @@ -242,12 +242,10 @@ impl DateService { // periodic date update let s = self.clone(); - tokio_executor::current_thread::spawn( - delay_for(Duration::from_millis(500)).then(move |_| { - s.0.reset(); - future::ready(()) - }), - ); + actix_rt::spawn(delay_for(Duration::from_millis(500)).then(move |_| { + s.0.reset(); + future::ready(()) + })); } } @@ -265,26 +263,19 @@ impl DateService { #[cfg(test)] mod tests { use super::*; - use actix_rt::System; - use futures::future; #[test] fn test_date_len() { assert_eq!(DATE_VALUE_LENGTH, "Sun, 06 Nov 1994 08:49:37 GMT".len()); } - #[test] - fn test_date() { - let mut rt = System::new("test"); - - let _ = rt.block_on(future::lazy(|_| { - let settings = ServiceConfig::new(KeepAlive::Os, 0, 0); - let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10); - settings.set_date(&mut buf1); - let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10); - settings.set_date(&mut buf2); - assert_eq!(buf1, buf2); - future::ok::<_, ()>(()) - })); + #[actix_rt::test] + async fn test_date() { + let settings = ServiceConfig::new(KeepAlive::Os, 0, 0); + let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10); + settings.set_date(&mut buf1); + let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10); + settings.set_date(&mut buf2); + assert_eq!(buf1, buf2); } } diff --git a/actix-http/src/cookie/secure/key.rs b/actix-http/src/cookie/secure/key.rs index 95058ed81..779c16b75 100644 --- a/actix-http/src/cookie/secure/key.rs +++ b/actix-http/src/cookie/secure/key.rs @@ -1,5 +1,4 @@ use ring::hkdf::{Algorithm, KeyType, Prk, HKDF_SHA256}; -use ring::hmac; use ring::rand::{SecureRandom, SystemRandom}; use super::private::KEY_LEN as PRIVATE_KEY_LEN; diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index a725789a2..f1767cf19 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -16,7 +16,6 @@ use httparse; use serde::de::value::Error as DeError; use serde_json::error::Error as JsonError; use serde_urlencoded::ser::Error as FormError; -use tokio_timer::Error as TimerError; // re-export for convinience use crate::body::Body; @@ -178,9 +177,6 @@ impl ResponseError for JsonError {} /// `InternalServerError` for `FormError` impl ResponseError for FormError {} -/// `InternalServerError` for `TimerError` -impl ResponseError for TimerError {} - #[cfg(feature = "openssl")] /// `InternalServerError` for `openssl::ssl::Error` impl ResponseError for open_ssl::ssl::Error {} diff --git a/actix-http/src/h1/decoder.rs b/actix-http/src/h1/decoder.rs index 272270ca1..ffa00288f 100644 --- a/actix-http/src/h1/decoder.rs +++ b/actix-http/src/h1/decoder.rs @@ -1,9 +1,7 @@ -use std::future::Future; use std::io; use std::marker::PhantomData; use std::mem::MaybeUninit; -use std::pin::Pin; -use std::task::{Context, Poll}; +use std::task::Poll; use actix_codec::Decoder; use bytes::{Bytes, BytesMut}; diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 8c0896029..154b3ed40 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -3,15 +3,15 @@ use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use std::time::Instant; -use std::{fmt, io, io::Write, net}; +use std::{fmt, io, net}; -use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed, FramedParts}; +use actix_codec::{AsyncRead, Decoder, Encoder, Framed, FramedParts}; +use actix_rt::time::{delay, Delay}; use actix_server_config::IoStream; use actix_service::Service; use bitflags::bitflags; use bytes::{BufMut, BytesMut}; use log::{error, trace}; -use tokio_timer::{delay, Delay}; use crate::body::{Body, BodySize, MessageBody, ResponseBody}; use crate::cloneable::CloneableService; @@ -893,10 +893,9 @@ mod tests { use crate::h1::{ExpectHandler, UpgradeHandler}; use crate::test::TestBuffer; - #[test] - fn test_req_parse_err() { - let mut sys = actix_rt::System::new("test"); - let _ = sys.block_on(lazy(|cx| { + #[actix_rt::test] + async fn test_req_parse_err() { + lazy(|cx| { let buf = TestBuffer::new("GET /test HTTP/1\r\n\r\n"); let mut h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new( @@ -918,7 +917,7 @@ mod tests { assert!(inner.flags.contains(Flags::READ_DISCONNECT)); assert_eq!(&inner.io.write_buf[..26], b"HTTP/1.1 400 Bad Request\r\n"); } - ok::<_, ()>(()) - })); + }) + .await; } } diff --git a/actix-http/src/h1/expect.rs b/actix-http/src/h1/expect.rs index 79831eae1..d6b4a9f1e 100644 --- a/actix-http/src/h1/expect.rs +++ b/actix-http/src/h1/expect.rs @@ -1,5 +1,3 @@ -use std::future::Future; -use std::pin::Pin; use std::task::{Context, Poll}; use actix_server_config::ServerConfig; diff --git a/actix-http/src/h1/payload.rs b/actix-http/src/h1/payload.rs index 2b52cfd86..46f2f9728 100644 --- a/actix-http/src/h1/payload.rs +++ b/actix-http/src/h1/payload.rs @@ -1,7 +1,6 @@ //! Payload stream use std::cell::RefCell; use std::collections::VecDeque; -use std::future::Future; use std::pin::Pin; use std::rc::{Rc, Weak}; use std::task::{Context, Poll}; @@ -227,24 +226,19 @@ impl Inner { #[cfg(test)] mod tests { use super::*; - use actix_rt::Runtime; - use futures::future::{poll_fn, ready}; + use futures::future::poll_fn; - #[test] - fn test_unread_data() { - Runtime::new().unwrap().block_on(async { - let (_, mut payload) = Payload::create(false); + #[actix_rt::test] + async fn test_unread_data() { + let (_, mut payload) = Payload::create(false); - payload.unread_data(Bytes::from("data")); - assert!(!payload.is_empty()); - assert_eq!(payload.len(), 4); + payload.unread_data(Bytes::from("data")); + assert!(!payload.is_empty()); + assert_eq!(payload.len(), 4); - assert_eq!( - Bytes::from("data"), - poll_fn(|cx| payload.readany(cx)).await.unwrap().unwrap() - ); - - ready(()) - }); + assert_eq!( + Bytes::from("data"), + poll_fn(|cx| payload.readany(cx)).await.unwrap().unwrap() + ); } } diff --git a/actix-http/src/h1/service.rs b/actix-http/src/h1/service.rs index ce8ff6626..197c92887 100644 --- a/actix-http/src/h1/service.rs +++ b/actix-http/src/h1/service.rs @@ -9,7 +9,7 @@ use actix_codec::Framed; use actix_server_config::{Io, IoStream, ServerConfig as SrvConfig}; use actix_service::{IntoServiceFactory, Service, ServiceFactory}; use futures::future::{ok, Ready}; -use futures::{ready, Stream}; +use futures::ready; use crate::body::MessageBody; use crate::cloneable::CloneableService; diff --git a/actix-http/src/h1/upgrade.rs b/actix-http/src/h1/upgrade.rs index 43ab53d01..ce46fbe93 100644 --- a/actix-http/src/h1/upgrade.rs +++ b/actix-http/src/h1/upgrade.rs @@ -1,6 +1,4 @@ -use std::future::Future; use std::marker::PhantomData; -use std::pin::Pin; use std::task::{Context, Poll}; use actix_codec::Framed; diff --git a/actix-http/src/h1/utils.rs b/actix-http/src/h1/utils.rs index 7057bf1ca..7af0b124e 100644 --- a/actix-http/src/h1/utils.rs +++ b/actix-http/src/h1/utils.rs @@ -3,7 +3,6 @@ use std::pin::Pin; use std::task::{Context, Poll}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; -use futures::Sink; use crate::body::{BodySize, MessageBody, ResponseBody}; use crate::error::Error; diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index 1a52a60f2..188553806 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -7,6 +7,7 @@ use std::time::Instant; use std::{fmt, mem, net}; use actix_codec::{AsyncRead, AsyncWrite}; +use actix_rt::time::Delay; use actix_server_config::IoStream; use actix_service::Service; use bitflags::bitflags; @@ -19,7 +20,6 @@ use http::header::{ }; use http::HttpTryFrom; use log::{debug, error, trace}; -use tokio_timer::Delay; use crate::body::{Body, BodySize, MessageBody, ResponseBody}; use crate::cloneable::CloneableService; @@ -139,7 +139,7 @@ where on_connect.set(&mut req.extensions_mut()); } - tokio_executor::current_thread::spawn(ServiceResponse::< + actix_rt::spawn(ServiceResponse::< S::Future, S::Response, S::Error, diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs index 4d17347db..b57fdddce 100644 --- a/actix-http/src/lib.rs +++ b/actix-http/src/lib.rs @@ -4,8 +4,7 @@ clippy::too_many_arguments, clippy::new_without_default, clippy::borrow_interior_mutable_const, - clippy::write_with_newline, - unused_imports + clippy::write_with_newline )] #[macro_use] diff --git a/actix-http/src/payload.rs b/actix-http/src/payload.rs index f2cc6414f..b3ec04d11 100644 --- a/actix-http/src/payload.rs +++ b/actix-http/src/payload.rs @@ -1,4 +1,3 @@ -use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index a5f18cc79..5eb0228dc 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -7,7 +7,6 @@ use std::task::{Context, Poll}; use std::{fmt, str}; use bytes::{BufMut, Bytes, BytesMut}; -use futures::future::{ok, Ready}; use futures::stream::Stream; use serde::Serialize; use serde_json; diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index e18b10130..7340c15fd 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -8,7 +8,7 @@ use actix_server_config::{ Io as ServerIo, IoStream, Protocol, ServerConfig as SrvConfig, }; use actix_service::{IntoServiceFactory, Service, ServiceFactory}; -use bytes::{Buf, BufMut, Bytes, BytesMut}; +use bytes::{BufMut, Bytes, BytesMut}; use futures::{ready, Future}; use h2::server::{self, Handshake}; use pin_project::{pin_project, project}; @@ -659,7 +659,7 @@ impl AsyncRead for Io { // } } -impl tokio_io::AsyncWrite for Io { +impl actix_codec::AsyncWrite for Io { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/actix-http/src/test.rs b/actix-http/src/test.rs index 26f2c223f..744f057dc 100644 --- a/actix-http/src/test.rs +++ b/actix-http/src/test.rs @@ -7,7 +7,7 @@ use std::task::{Context, Poll}; use actix_codec::{AsyncRead, AsyncWrite}; use actix_server_config::IoStream; -use bytes::{Buf, Bytes, BytesMut}; +use bytes::{Bytes, BytesMut}; use http::header::{self, HeaderName, HeaderValue}; use http::{HttpTryFrom, Method, Uri, Version}; use percent_encoding::percent_encode; diff --git a/actix-http/tests/test_client.rs b/actix-http/tests/test_client.rs index 05248966a..cdcaea028 100644 --- a/actix-http/tests/test_client.rs +++ b/actix-http/tests/test_client.rs @@ -3,7 +3,7 @@ use bytes::Bytes; use futures::future::{self, ok}; use actix_http::{http, HttpService, Request, Response}; -use actix_http_test::{block_on, TestServer}; +use actix_http_test::TestServer; const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World \ @@ -27,65 +27,58 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World"; -#[test] -fn test_h1_v2() { - block_on(async { - let srv = TestServer::start(move || { - HttpService::build() - .finish(|_| future::ok::<_, ()>(Response::Ok().body(STR))) - }); +#[actix_rt::test] +async fn test_h1_v2() { + let srv = TestServer::start(move || { + HttpService::build().finish(|_| future::ok::<_, ()>(Response::Ok().body(STR))) + }); - let response = srv.get("/").send().await.unwrap(); - assert!(response.status().is_success()); + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); - let request = srv.get("/").header("x-test", "111").send(); - let mut response = request.await.unwrap(); - assert!(response.status().is_success()); + let request = srv.get("/").header("x-test", "111").send(); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = response.body().await.unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - let mut response = srv.post("/").send().await.unwrap(); - assert!(response.status().is_success()); + let mut response = srv.post("/").send().await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = response.body().await.unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - }) + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); } -#[test] -fn test_connection_close() { - block_on(async { - let srv = TestServer::start(move || { - HttpService::build() - .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map(|_| ()) - }); +#[actix_rt::test] +async fn test_connection_close() { + let srv = TestServer::start(move || { + HttpService::build() + .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) + .map(|_| ()) + }); - let response = srv.get("/").force_close().send().await.unwrap(); - assert!(response.status().is_success()); - }) + let response = srv.get("/").force_close().send().await.unwrap(); + assert!(response.status().is_success()); } -#[test] -fn test_with_query_parameter() { - block_on(async { - let srv = TestServer::start(move || { - HttpService::build() - .finish(|req: Request| { - if req.uri().query().unwrap().contains("qp=") { - ok::<_, ()>(Response::Ok().finish()) - } else { - ok::<_, ()>(Response::BadRequest().finish()) - } - }) - .map(|_| ()) - }); +#[actix_rt::test] +async fn test_with_query_parameter() { + let srv = TestServer::start(move || { + HttpService::build() + .finish(|req: Request| { + if req.uri().query().unwrap().contains("qp=") { + ok::<_, ()>(Response::Ok().finish()) + } else { + ok::<_, ()>(Response::BadRequest().finish()) + } + }) + .map(|_| ()) + }); - let request = srv.request(http::Method::GET, srv.url("/?qp=5")); - let response = request.send().await.unwrap(); - assert!(response.status().is_success()); - }) + let request = srv.request(http::Method::GET, srv.url("/?qp=5")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); } diff --git a/actix-http/tests/test_openssl.rs b/actix-http/tests/test_openssl.rs index 7eaa8e2a2..0fdddaa1c 100644 --- a/actix-http/tests/test_openssl.rs +++ b/actix-http/tests/test_openssl.rs @@ -2,7 +2,7 @@ use std::io; use actix_codec::{AsyncRead, AsyncWrite}; -use actix_http_test::{block_on, TestServer}; +use actix_http_test::TestServer; use actix_server::ssl::OpensslAcceptor; use actix_server_config::ServerConfig; use actix_service::{factory_fn_cfg, pipeline_factory, service_fn2, ServiceFactory}; @@ -57,156 +57,147 @@ fn ssl_acceptor() -> io::Result io::Result<()> { - block_on(async { - let openssl = ssl_acceptor()?; - let srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|_| ok::<_, Error>(Response::Ok().finish())) - .map_err(|_| ()), - ) - }); +#[actix_rt::test] +async fn test_h2() -> io::Result<()> { + let openssl = ssl_acceptor()?; + let srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|_| ok::<_, Error>(Response::Ok().finish())) + .map_err(|_| ()), + ) + }); - let response = srv.sget("/").send().await.unwrap(); - assert!(response.status().is_success()); - Ok(()) - }) + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + Ok(()) } -#[test] -fn test_h2_1() -> io::Result<()> { - block_on(async { - let openssl = ssl_acceptor()?; - let srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .finish(|req: Request| { - assert!(req.peer_addr().is_some()); - assert_eq!(req.version(), Version::HTTP_2); - ok::<_, Error>(Response::Ok().finish()) - }) - .map_err(|_| ()), - ) - }); +#[actix_rt::test] +async fn test_h2_1() -> io::Result<()> { + let openssl = ssl_acceptor()?; + let srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .finish(|req: Request| { + assert!(req.peer_addr().is_some()); + assert_eq!(req.version(), Version::HTTP_2); + ok::<_, Error>(Response::Ok().finish()) + }) + .map_err(|_| ()), + ) + }); - let response = srv.sget("/").send().await.unwrap(); - assert!(response.status().is_success()); - Ok(()) - }) + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + Ok(()) } -#[test] -fn test_h2_body() -> io::Result<()> { - block_on(async { - let data = "HELLOWORLD".to_owned().repeat(64 * 1024); - let openssl = ssl_acceptor()?; - let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|mut req: Request<_>| { - async move { - let body = load_body(req.take_payload()).await?; - Ok::<_, Error>(Response::Ok().body(body)) - } - }) - .map_err(|_| ()), - ) - }); +#[actix_rt::test] +async fn test_h2_body() -> io::Result<()> { + let data = "HELLOWORLD".to_owned().repeat(64 * 1024); + let openssl = ssl_acceptor()?; + let mut srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|mut req: Request<_>| { + async move { + let body = load_body(req.take_payload()).await?; + Ok::<_, Error>(Response::Ok().body(body)) + } + }) + .map_err(|_| ()), + ) + }); - let response = srv.sget("/").send_body(data.clone()).await.unwrap(); - assert!(response.status().is_success()); + let response = srv.sget("/").send_body(data.clone()).await.unwrap(); + assert!(response.status().is_success()); - let body = srv.load_body(response).await.unwrap(); - assert_eq!(&body, data.as_bytes()); - Ok(()) - }) + let body = srv.load_body(response).await.unwrap(); + assert_eq!(&body, data.as_bytes()); + Ok(()) } -#[test] -fn test_h2_content_length() { - block_on(async { - let openssl = ssl_acceptor().unwrap(); +#[actix_rt::test] +async fn test_h2_content_length() { + let openssl = ssl_acceptor().unwrap(); - let srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|req: Request| { - let indx: usize = req.uri().path()[1..].parse().unwrap(); - let statuses = [ - StatusCode::NO_CONTENT, - StatusCode::CONTINUE, - StatusCode::SWITCHING_PROTOCOLS, - StatusCode::PROCESSING, - StatusCode::OK, - StatusCode::NOT_FOUND, - ]; - ok::<_, ()>(Response::new(statuses[indx])) - }) - .map_err(|_| ()), - ) - }); + let srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|req: Request| { + let indx: usize = req.uri().path()[1..].parse().unwrap(); + let statuses = [ + StatusCode::NO_CONTENT, + StatusCode::CONTINUE, + StatusCode::SWITCHING_PROTOCOLS, + StatusCode::PROCESSING, + StatusCode::OK, + StatusCode::NOT_FOUND, + ]; + ok::<_, ()>(Response::new(statuses[indx])) + }) + .map_err(|_| ()), + ) + }); - let header = HeaderName::from_static("content-length"); - let value = HeaderValue::from_static("0"); + let header = HeaderName::from_static("content-length"); + let value = HeaderValue::from_static("0"); - { - for i in 0..4 { - let req = srv - .request(Method::GET, srv.surl(&format!("/{}", i))) - .send(); - let response = req.await.unwrap(); - assert_eq!(response.headers().get(&header), None); + { + for i in 0..4 { + let req = srv + .request(Method::GET, srv.surl(&format!("/{}", i))) + .send(); + let response = req.await.unwrap(); + assert_eq!(response.headers().get(&header), None); - let req = srv - .request(Method::HEAD, srv.surl(&format!("/{}", i))) - .send(); - let response = req.await.unwrap(); - assert_eq!(response.headers().get(&header), None); - } - - for i in 4..6 { - let req = srv - .request(Method::GET, srv.surl(&format!("/{}", i))) - .send(); - let response = req.await.unwrap(); - assert_eq!(response.headers().get(&header), Some(&value)); - } + let req = srv + .request(Method::HEAD, srv.surl(&format!("/{}", i))) + .send(); + let response = req.await.unwrap(); + assert_eq!(response.headers().get(&header), None); } - }) + + for i in 4..6 { + let req = srv + .request(Method::GET, srv.surl(&format!("/{}", i))) + .send(); + let response = req.await.unwrap(); + assert_eq!(response.headers().get(&header), Some(&value)); + } + } } -#[test] -fn test_h2_headers() { - block_on(async { - let data = STR.repeat(10); - let data2 = data.clone(); - let openssl = ssl_acceptor().unwrap(); +#[actix_rt::test] +async fn test_h2_headers() { + let data = STR.repeat(10); + let data2 = data.clone(); + let openssl = ssl_acceptor().unwrap(); - let mut srv = TestServer::start(move || { - let data = data.clone(); - pipeline_factory(openssl + let mut srv = TestServer::start(move || { + let data = data.clone(); + pipeline_factory(openssl .clone() .map_err(|e| println!("Openssl error: {}", e))) .and_then( @@ -232,15 +223,14 @@ fn test_h2_headers() { } ok::<_, ()>(builder.body(data.clone())) }).map_err(|_| ())) - }); + }); - let response = srv.sget("/").send().await.unwrap(); - assert!(response.status().is_success()); + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.load_body(response).await.unwrap(); - assert_eq!(bytes, Bytes::from(data2)); - }) + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from(data2)); } const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ @@ -265,281 +255,262 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World"; -#[test] -fn test_h2_body2() { - block_on(async { - let openssl = ssl_acceptor().unwrap(); - let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) - }); +#[actix_rt::test] +async fn test_h2_body2() { + let openssl = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) + .map_err(|_| ()), + ) + }); - let response = srv.sget("/").send().await.unwrap(); - assert!(response.status().is_success()); + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.load_body(response).await.unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - }) + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); } -#[test] -fn test_h2_head_empty() { - block_on(async { - let openssl = ssl_acceptor().unwrap(); - let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) - }); +#[actix_rt::test] +async fn test_h2_head_empty() { + let openssl = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) + .map_err(|_| ()), + ) + }); - let response = srv.shead("/").send().await.unwrap(); - assert!(response.status().is_success()); - assert_eq!(response.version(), Version::HTTP_2); + let response = srv.shead("/").send().await.unwrap(); + assert!(response.status().is_success()); + assert_eq!(response.version(), Version::HTTP_2); - { - let len = response.headers().get(header::CONTENT_LENGTH).unwrap(); - assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); - } + { + let len = response.headers().get(header::CONTENT_LENGTH).unwrap(); + assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); + } - // read response - let bytes = srv.load_body(response).await.unwrap(); - assert!(bytes.is_empty()); - }) + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert!(bytes.is_empty()); } -#[test] -fn test_h2_head_binary() { - block_on(async { - let openssl = ssl_acceptor().unwrap(); - let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|_| { - ok::<_, ()>( - Response::Ok().content_length(STR.len() as u64).body(STR), - ) - }) - .map_err(|_| ()), - ) - }); +#[actix_rt::test] +async fn test_h2_head_binary() { + let openssl = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|_| { + ok::<_, ()>( + Response::Ok().content_length(STR.len() as u64).body(STR), + ) + }) + .map_err(|_| ()), + ) + }); - let response = srv.shead("/").send().await.unwrap(); - assert!(response.status().is_success()); + let response = srv.shead("/").send().await.unwrap(); + assert!(response.status().is_success()); - { - let len = response.headers().get(header::CONTENT_LENGTH).unwrap(); - assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); - } + { + let len = response.headers().get(header::CONTENT_LENGTH).unwrap(); + assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); + } - // read response - let bytes = srv.load_body(response).await.unwrap(); - assert!(bytes.is_empty()); - }) + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert!(bytes.is_empty()); } -#[test] -fn test_h2_head_binary2() { - block_on(async { - let openssl = ssl_acceptor().unwrap(); - let srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) - }); +#[actix_rt::test] +async fn test_h2_head_binary2() { + let openssl = ssl_acceptor().unwrap(); + let srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) + .map_err(|_| ()), + ) + }); - let response = srv.shead("/").send().await.unwrap(); - assert!(response.status().is_success()); + let response = srv.shead("/").send().await.unwrap(); + assert!(response.status().is_success()); - { - let len = response.headers().get(header::CONTENT_LENGTH).unwrap(); - assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); - } - }) + { + let len = response.headers().get(header::CONTENT_LENGTH).unwrap(); + assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); + } } -#[test] -fn test_h2_body_length() { - block_on(async { - let openssl = ssl_acceptor().unwrap(); - let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|_| { - let body = once(ok(Bytes::from_static(STR.as_ref()))); +#[actix_rt::test] +async fn test_h2_body_length() { + let openssl = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|_| { + let body = once(ok(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok() + .body(body::SizedStream::new(STR.len() as u64, body)), + ) + }) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); +} + +#[actix_rt::test] +async fn test_h2_body_chunked_explicit() { + let openssl = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|_| { + let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok() + .header(header::TRANSFER_ENCODING, "chunked") + .streaming(body), + ) + }) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + assert!(!response.headers().contains_key(header::TRANSFER_ENCODING)); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + + // decode + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); +} + +#[actix_rt::test] +async fn test_h2_response_http_error_handling() { + let openssl = ssl_acceptor().unwrap(); + + let mut srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(factory_fn_cfg(|_: &ServerConfig| { + ok::<_, ()>(service_fn2(|_| { + let broken_header = Bytes::from_static(b"\0\0\0"); ok::<_, ()>( Response::Ok() - .body(body::SizedStream::new(STR.len() as u64, body)), + .header(header::CONTENT_TYPE, broken_header) + .body(STR), ) - }) - .map_err(|_| ()), - ) - }); - - let response = srv.sget("/").send().await.unwrap(); - assert!(response.status().is_success()); - - // read response - let bytes = srv.load_body(response).await.unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - }) -} - -#[test] -fn test_h2_body_chunked_explicit() { - block_on(async { - let openssl = ssl_acceptor().unwrap(); - let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|_| { - let body = - once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok() - .header(header::TRANSFER_ENCODING, "chunked") - .streaming(body), - ) - }) - .map_err(|_| ()), - ) - }); - - let response = srv.sget("/").send().await.unwrap(); - assert!(response.status().is_success()); - assert!(!response.headers().contains_key(header::TRANSFER_ENCODING)); - - // read response - let bytes = srv.load_body(response).await.unwrap(); - - // decode - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - }) -} - -#[test] -fn test_h2_response_http_error_handling() { - block_on(async { - let openssl = ssl_acceptor().unwrap(); - - let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(factory_fn_cfg(|_: &ServerConfig| { - ok::<_, ()>(service_fn2(|_| { - let broken_header = Bytes::from_static(b"\0\0\0"); - ok::<_, ()>( - Response::Ok() - .header(header::CONTENT_TYPE, broken_header) - .body(STR), - ) - })) })) - .map_err(|_| ()), - ) - }); + })) + .map_err(|_| ()), + ) + }); - let response = srv.sget("/").send().await.unwrap(); - assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR); + let response = srv.sget("/").send().await.unwrap(); + assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR); - // read response - let bytes = srv.load_body(response).await.unwrap(); - assert_eq!(bytes, Bytes::from_static(b"failed to parse header value")); - }) + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(b"failed to parse header value")); } -#[test] -fn test_h2_service_error() { - block_on(async { - let openssl = ssl_acceptor().unwrap(); +#[actix_rt::test] +async fn test_h2_service_error() { + let openssl = ssl_acceptor().unwrap(); - let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|_| err::(ErrorBadRequest("error"))) - .map_err(|_| ()), - ) - }); + let mut srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(|_| err::(ErrorBadRequest("error"))) + .map_err(|_| ()), + ) + }); - let response = srv.sget("/").send().await.unwrap(); - assert_eq!(response.status(), StatusCode::BAD_REQUEST); + let response = srv.sget("/").send().await.unwrap(); + assert_eq!(response.status(), StatusCode::BAD_REQUEST); - // read response - let bytes = srv.load_body(response).await.unwrap(); - assert_eq!(bytes, Bytes::from_static(b"error")); - }) + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(b"error")); } -#[test] -fn test_h2_on_connect() { - block_on(async { - let openssl = ssl_acceptor().unwrap(); +#[actix_rt::test] +async fn test_h2_on_connect() { + let openssl = ssl_acceptor().unwrap(); - let srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .on_connect(|_| 10usize) - .h2(|req: Request| { - assert!(req.extensions().contains::()); - ok::<_, ()>(Response::Ok().finish()) - }) - .map_err(|_| ()), - ) - }); + let srv = TestServer::start(move || { + pipeline_factory( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .on_connect(|_| 10usize) + .h2(|req: Request| { + assert!(req.extensions().contains::()); + ok::<_, ()>(Response::Ok().finish()) + }) + .map_err(|_| ()), + ) + }); - let response = srv.sget("/").send().await.unwrap(); - assert!(response.status().is_success()); - }) + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); } diff --git a/actix-http/tests/test_rustls.rs b/actix-http/tests/test_rustls.rs index c36d05794..4a649ca37 100644 --- a/actix-http/tests/test_rustls.rs +++ b/actix-http/tests/test_rustls.rs @@ -4,7 +4,7 @@ use actix_http::error::PayloadError; use actix_http::http::header::{self, HeaderName, HeaderValue}; use actix_http::http::{Method, StatusCode, Version}; use actix_http::{body, error, Error, HttpService, Request, Response}; -use actix_http_test::{block_on, TestServer}; +use actix_http_test::TestServer; use actix_server::ssl::RustlsAcceptor; use actix_server_config::ServerConfig; use actix_service::{factory_fn_cfg, pipeline_factory, service_fn2, ServiceFactory}; @@ -45,140 +45,131 @@ fn ssl_acceptor() -> io::Result Ok(RustlsAcceptor::new(config)) } -#[test] -fn test_h2() -> io::Result<()> { - block_on(async { - let rustls = ssl_acceptor()?; - let srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|_| future::ok::<_, Error>(Response::Ok().finish())) - .map_err(|_| ()), - ) - }); +#[actix_rt::test] +async fn test_h2() -> io::Result<()> { + let rustls = ssl_acceptor()?; + let srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(|_| future::ok::<_, Error>(Response::Ok().finish())) + .map_err(|_| ()), + ) + }); - let response = srv.sget("/").send().await.unwrap(); - assert!(response.status().is_success()); - Ok(()) - }) + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + Ok(()) } -#[test] -fn test_h2_1() -> io::Result<()> { - block_on(async { - let rustls = ssl_acceptor()?; - let srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .finish(|req: Request| { - assert!(req.peer_addr().is_some()); - assert_eq!(req.version(), Version::HTTP_2); - future::ok::<_, Error>(Response::Ok().finish()) - }) - .map_err(|_| ()), - ) - }); +#[actix_rt::test] +async fn test_h2_1() -> io::Result<()> { + let rustls = ssl_acceptor()?; + let srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .finish(|req: Request| { + assert!(req.peer_addr().is_some()); + assert_eq!(req.version(), Version::HTTP_2); + future::ok::<_, Error>(Response::Ok().finish()) + }) + .map_err(|_| ()), + ) + }); - let response = srv.sget("/").send().await.unwrap(); - assert!(response.status().is_success()); - Ok(()) - }) + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + Ok(()) } -#[test] -fn test_h2_body1() -> io::Result<()> { - block_on(async { - let data = "HELLOWORLD".to_owned().repeat(64 * 1024); - let rustls = ssl_acceptor()?; - let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|mut req: Request<_>| { - async move { - let body = load_body(req.take_payload()).await?; - Ok::<_, Error>(Response::Ok().body(body)) - } - }) - .map_err(|_| ()), - ) - }); +#[actix_rt::test] +async fn test_h2_body1() -> io::Result<()> { + let data = "HELLOWORLD".to_owned().repeat(64 * 1024); + let rustls = ssl_acceptor()?; + let mut srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(|mut req: Request<_>| { + async move { + let body = load_body(req.take_payload()).await?; + Ok::<_, Error>(Response::Ok().body(body)) + } + }) + .map_err(|_| ()), + ) + }); - let response = srv.sget("/").send_body(data.clone()).await.unwrap(); - assert!(response.status().is_success()); + let response = srv.sget("/").send_body(data.clone()).await.unwrap(); + assert!(response.status().is_success()); - let body = srv.load_body(response).await.unwrap(); - assert_eq!(&body, data.as_bytes()); - Ok(()) - }) + let body = srv.load_body(response).await.unwrap(); + assert_eq!(&body, data.as_bytes()); + Ok(()) } -#[test] -fn test_h2_content_length() { - block_on(async { - let rustls = ssl_acceptor().unwrap(); +#[actix_rt::test] +async fn test_h2_content_length() { + let rustls = ssl_acceptor().unwrap(); - let srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|req: Request| { - let indx: usize = req.uri().path()[1..].parse().unwrap(); - let statuses = [ - StatusCode::NO_CONTENT, - StatusCode::CONTINUE, - StatusCode::SWITCHING_PROTOCOLS, - StatusCode::PROCESSING, - StatusCode::OK, - StatusCode::NOT_FOUND, - ]; - future::ok::<_, ()>(Response::new(statuses[indx])) - }) - .map_err(|_| ()), - ) - }); + let srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(|req: Request| { + let indx: usize = req.uri().path()[1..].parse().unwrap(); + let statuses = [ + StatusCode::NO_CONTENT, + StatusCode::CONTINUE, + StatusCode::SWITCHING_PROTOCOLS, + StatusCode::PROCESSING, + StatusCode::OK, + StatusCode::NOT_FOUND, + ]; + future::ok::<_, ()>(Response::new(statuses[indx])) + }) + .map_err(|_| ()), + ) + }); - let header = HeaderName::from_static("content-length"); - let value = HeaderValue::from_static("0"); + let header = HeaderName::from_static("content-length"); + let value = HeaderValue::from_static("0"); - { - for i in 0..4 { - let req = srv - .request(Method::GET, srv.surl(&format!("/{}", i))) - .send(); - let response = req.await.unwrap(); - assert_eq!(response.headers().get(&header), None); + { + for i in 0..4 { + let req = srv + .request(Method::GET, srv.surl(&format!("/{}", i))) + .send(); + let response = req.await.unwrap(); + assert_eq!(response.headers().get(&header), None); - let req = srv - .request(Method::HEAD, srv.surl(&format!("/{}", i))) - .send(); - let response = req.await.unwrap(); - assert_eq!(response.headers().get(&header), None); - } - - for i in 4..6 { - let req = srv - .request(Method::GET, srv.surl(&format!("/{}", i))) - .send(); - let response = req.await.unwrap(); - assert_eq!(response.headers().get(&header), Some(&value)); - } + let req = srv + .request(Method::HEAD, srv.surl(&format!("/{}", i))) + .send(); + let response = req.await.unwrap(); + assert_eq!(response.headers().get(&header), None); } - }) + + for i in 4..6 { + let req = srv + .request(Method::GET, srv.surl(&format!("/{}", i))) + .send(); + let response = req.await.unwrap(); + assert_eq!(response.headers().get(&header), Some(&value)); + } + } } -#[test] -fn test_h2_headers() { - block_on(async { - let data = STR.repeat(10); - let data2 = data.clone(); - let rustls = ssl_acceptor().unwrap(); +#[actix_rt::test] +async fn test_h2_headers() { + let data = STR.repeat(10); + let data2 = data.clone(); + let rustls = ssl_acceptor().unwrap(); - let mut srv = TestServer::start(move || { - let data = data.clone(); - pipeline_factory(rustls + let mut srv = TestServer::start(move || { + let data = data.clone(); + pipeline_factory(rustls .clone() .map_err(|e| println!("Rustls error: {}", e))) .and_then( @@ -204,15 +195,14 @@ fn test_h2_headers() { } future::ok::<_, ()>(config.body(data.clone())) }).map_err(|_| ())) - }); + }); - let response = srv.sget("/").send().await.unwrap(); - assert!(response.status().is_success()); + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.load_body(response).await.unwrap(); - assert_eq!(bytes, Bytes::from(data2)); - }) + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from(data2)); } const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ @@ -237,238 +227,215 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World"; -#[test] -fn test_h2_body2() { - block_on(async { - let rustls = ssl_acceptor().unwrap(); - let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|_| future::ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) - }); +#[actix_rt::test] +async fn test_h2_body2() { + let rustls = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(|_| future::ok::<_, ()>(Response::Ok().body(STR))) + .map_err(|_| ()), + ) + }); - let response = srv.sget("/").send().await.unwrap(); - assert!(response.status().is_success()); + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.load_body(response).await.unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - }) + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); } -#[test] -fn test_h2_head_empty() { - block_on(async { - let rustls = ssl_acceptor().unwrap(); - let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) - }); +#[actix_rt::test] +async fn test_h2_head_empty() { + let rustls = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) + .map_err(|_| ()), + ) + }); - let response = srv.shead("/").send().await.unwrap(); - assert!(response.status().is_success()); - assert_eq!(response.version(), Version::HTTP_2); + let response = srv.shead("/").send().await.unwrap(); + assert!(response.status().is_success()); + assert_eq!(response.version(), Version::HTTP_2); - { - let len = response - .headers() - .get(http::header::CONTENT_LENGTH) - .unwrap(); - assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); - } + { + let len = response + .headers() + .get(http::header::CONTENT_LENGTH) + .unwrap(); + assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); + } - // read response - let bytes = srv.load_body(response).await.unwrap(); - assert!(bytes.is_empty()); - }) + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert!(bytes.is_empty()); } -#[test] -fn test_h2_head_binary() { - block_on(async { - let rustls = ssl_acceptor().unwrap(); - let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|_| { +#[actix_rt::test] +async fn test_h2_head_binary() { + let rustls = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(|_| { + ok::<_, ()>( + Response::Ok().content_length(STR.len() as u64).body(STR), + ) + }) + .map_err(|_| ()), + ) + }); + + let response = srv.shead("/").send().await.unwrap(); + assert!(response.status().is_success()); + + { + let len = response + .headers() + .get(http::header::CONTENT_LENGTH) + .unwrap(); + assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); + } + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert!(bytes.is_empty()); +} + +#[actix_rt::test] +async fn test_h2_head_binary2() { + let rustls = ssl_acceptor().unwrap(); + let srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) + .map_err(|_| ()), + ) + }); + + let response = srv.shead("/").send().await.unwrap(); + assert!(response.status().is_success()); + + { + let len = response + .headers() + .get(http::header::CONTENT_LENGTH) + .unwrap(); + assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); + } +} + +#[actix_rt::test] +async fn test_h2_body_length() { + let rustls = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(|_| { + let body = once(ok(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok() + .body(body::SizedStream::new(STR.len() as u64, body)), + ) + }) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); +} + +#[actix_rt::test] +async fn test_h2_body_chunked_explicit() { + let rustls = ssl_acceptor().unwrap(); + let mut srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(|_| { + let body = + once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok() + .header(header::TRANSFER_ENCODING, "chunked") + .streaming(body), + ) + }) + .map_err(|_| ()), + ) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + assert!(!response.headers().contains_key(header::TRANSFER_ENCODING)); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + + // decode + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); +} + +#[actix_rt::test] +async fn test_h2_response_http_error_handling() { + let rustls = ssl_acceptor().unwrap(); + + let mut srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(factory_fn_cfg(|_: &ServerConfig| { + ok::<_, ()>(service_fn2(|_| { + let broken_header = Bytes::from_static(b"\0\0\0"); ok::<_, ()>( Response::Ok() - .content_length(STR.len() as u64) + .header(http::header::CONTENT_TYPE, broken_header) .body(STR), ) - }) - .map_err(|_| ()), - ) - }); - - let response = srv.shead("/").send().await.unwrap(); - assert!(response.status().is_success()); - - { - let len = response - .headers() - .get(http::header::CONTENT_LENGTH) - .unwrap(); - assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); - } - - // read response - let bytes = srv.load_body(response).await.unwrap(); - assert!(bytes.is_empty()); - }) -} - -#[test] -fn test_h2_head_binary2() { - block_on(async { - let rustls = ssl_acceptor().unwrap(); - let srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) - }); - - let response = srv.shead("/").send().await.unwrap(); - assert!(response.status().is_success()); - - { - let len = response - .headers() - .get(http::header::CONTENT_LENGTH) - .unwrap(); - assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); - } - }) -} - -#[test] -fn test_h2_body_length() { - block_on(async { - let rustls = ssl_acceptor().unwrap(); - let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|_| { - let body = once(ok(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok().body(body::SizedStream::new( - STR.len() as u64, - body, - )), - ) - }) - .map_err(|_| ()), - ) - }); - - let response = srv.sget("/").send().await.unwrap(); - assert!(response.status().is_success()); - - // read response - let bytes = srv.load_body(response).await.unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - }) -} - -#[test] -fn test_h2_body_chunked_explicit() { - block_on(async { - let rustls = ssl_acceptor().unwrap(); - let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|_| { - let body = - once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok() - .header(header::TRANSFER_ENCODING, "chunked") - .streaming(body), - ) - }) - .map_err(|_| ()), - ) - }); - - let response = srv.sget("/").send().await.unwrap(); - assert!(response.status().is_success()); - assert!(!response.headers().contains_key(header::TRANSFER_ENCODING)); - - // read response - let bytes = srv.load_body(response).await.unwrap(); - - // decode - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - }) -} - -#[test] -fn test_h2_response_http_error_handling() { - block_on(async { - let rustls = ssl_acceptor().unwrap(); - - let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(factory_fn_cfg(|_: &ServerConfig| { - ok::<_, ()>(service_fn2(|_| { - let broken_header = Bytes::from_static(b"\0\0\0"); - ok::<_, ()>( - Response::Ok() - .header( - http::header::CONTENT_TYPE, - broken_header, - ) - .body(STR), - ) - })) })) - .map_err(|_| ()), - ) - }); + })) + .map_err(|_| ()), + ) + }); - let response = srv.sget("/").send().await.unwrap(); - assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR); + let response = srv.sget("/").send().await.unwrap(); + assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR); - // read response - let bytes = srv.load_body(response).await.unwrap(); - assert_eq!(bytes, Bytes::from_static(b"failed to parse header value")); - }) + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(b"failed to parse header value")); } -#[test] -fn test_h2_service_error() { - block_on(async { - let rustls = ssl_acceptor().unwrap(); +#[actix_rt::test] +async fn test_h2_service_error() { + let rustls = ssl_acceptor().unwrap(); - let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|_| err::(error::ErrorBadRequest("error"))) - .map_err(|_| ()), - ) - }); + let mut srv = TestServer::start(move || { + pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) + .and_then( + HttpService::build() + .h2(|_| err::(error::ErrorBadRequest("error"))) + .map_err(|_| ()), + ) + }); - let response = srv.sget("/").send().await.unwrap(); - assert_eq!(response.status(), http::StatusCode::BAD_REQUEST); + let response = srv.sget("/").send().await.unwrap(); + assert_eq!(response.status(), http::StatusCode::BAD_REQUEST); - // read response - let bytes = srv.load_body(response).await.unwrap(); - assert_eq!(bytes, Bytes::from_static(b"error")); - }) + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(b"error")); } diff --git a/actix-http/tests/test_server.rs b/actix-http/tests/test_server.rs index c37e8fad1..a3ce3f9cb 100644 --- a/actix-http/tests/test_server.rs +++ b/actix-http/tests/test_server.rs @@ -2,395 +2,361 @@ use std::io::{Read, Write}; use std::time::Duration; use std::{net, thread}; -use actix_http_test::{block_on, TestServer}; +use actix_http_test::TestServer; +use actix_rt::time::delay_for; use actix_server_config::ServerConfig; use actix_service::{factory_fn_cfg, pipeline, service_fn, ServiceFactory}; use bytes::Bytes; use futures::future::{self, err, ok, ready, FutureExt}; use futures::stream::{once, StreamExt}; use regex::Regex; -use tokio_timer::delay_for; use actix_http::httpmessage::HttpMessage; use actix_http::{ body, error, http, http::header, Error, HttpService, KeepAlive, Request, Response, }; -#[test] -fn test_h1() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::build() - .keep_alive(KeepAlive::Disabled) - .client_timeout(1000) - .client_disconnect(1000) - .h1(|req: Request| { - assert!(req.peer_addr().is_some()); - future::ok::<_, ()>(Response::Ok().finish()) - }) - }); +#[actix_rt::test] +async fn test_h1() { + let srv = TestServer::start(|| { + HttpService::build() + .keep_alive(KeepAlive::Disabled) + .client_timeout(1000) + .client_disconnect(1000) + .h1(|req: Request| { + assert!(req.peer_addr().is_some()); + future::ok::<_, ()>(Response::Ok().finish()) + }) + }); - let response = srv.get("/").send().await.unwrap(); - assert!(response.status().is_success()); - }) + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); } -#[test] -fn test_h1_2() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::build() - .keep_alive(KeepAlive::Disabled) - .client_timeout(1000) - .client_disconnect(1000) - .finish(|req: Request| { - assert!(req.peer_addr().is_some()); - assert_eq!(req.version(), http::Version::HTTP_11); - future::ok::<_, ()>(Response::Ok().finish()) - }) - .map(|_| ()) - }); +#[actix_rt::test] +async fn test_h1_2() { + let srv = TestServer::start(|| { + HttpService::build() + .keep_alive(KeepAlive::Disabled) + .client_timeout(1000) + .client_disconnect(1000) + .finish(|req: Request| { + assert!(req.peer_addr().is_some()); + assert_eq!(req.version(), http::Version::HTTP_11); + future::ok::<_, ()>(Response::Ok().finish()) + }) + .map(|_| ()) + }); - let response = srv.get("/").send().await.unwrap(); - assert!(response.status().is_success()); - }) + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); } -#[test] -fn test_expect_continue() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::build() - .expect(service_fn(|req: Request| { +#[actix_rt::test] +async fn test_expect_continue() { + let srv = TestServer::start(|| { + HttpService::build() + .expect(service_fn(|req: Request| { + if req.head().uri.query() == Some("yes=") { + ok(req) + } else { + err(error::ErrorPreconditionFailed("error")) + } + })) + .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); + + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); + assert!(data.starts_with("HTTP/1.1 412 Precondition Failed\r\ncontent-length")); + + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test?yes= HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); + assert!(data.starts_with("HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\n")); +} + +#[actix_rt::test] +async fn test_expect_continue_h1() { + let srv = TestServer::start(|| { + HttpService::build() + .expect(service_fn(|req: Request| { + delay_for(Duration::from_millis(20)).then(move |_| { if req.head().uri.query() == Some("yes=") { ok(req) } else { err(error::ErrorPreconditionFailed("error")) } - })) - .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); - - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); - let mut data = String::new(); - let _ = stream.read_to_string(&mut data); - assert!(data.starts_with("HTTP/1.1 412 Precondition Failed\r\ncontent-length")); - - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = - stream.write_all(b"GET /test?yes= HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); - let mut data = String::new(); - let _ = stream.read_to_string(&mut data); - assert!(data.starts_with("HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\n")); - }) -} - -#[test] -fn test_expect_continue_h1() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::build() - .expect(service_fn(|req: Request| { - delay_for(Duration::from_millis(20)).then(move |_| { - if req.head().uri.query() == Some("yes=") { - ok(req) - } else { - err(error::ErrorPreconditionFailed("error")) - } - }) - })) - .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); - - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); - let mut data = String::new(); - let _ = stream.read_to_string(&mut data); - assert!(data.starts_with("HTTP/1.1 412 Precondition Failed\r\ncontent-length")); - - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = - stream.write_all(b"GET /test?yes= HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); - let mut data = String::new(); - let _ = stream.read_to_string(&mut data); - assert!(data.starts_with("HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\n")); - }) -} - -#[test] -fn test_chunked_payload() { - block_on(async { - let chunk_sizes = vec![32768, 32, 32768]; - let total_size: usize = chunk_sizes.iter().sum(); - - let srv = TestServer::start(|| { - HttpService::build().h1(service_fn(|mut request: Request| { - request - .take_payload() - .map(|res| match res { - Ok(pl) => pl, - Err(e) => panic!(format!("Error reading payload: {}", e)), - }) - .fold(0usize, |acc, chunk| ready(acc + chunk.len())) - .map(|req_size| { - Ok::<_, Error>(Response::Ok().body(format!("size={}", req_size))) - }) + }) })) - }); + .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); - let returned_size = { - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream - .write_all(b"POST /test HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"); + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); + assert!(data.starts_with("HTTP/1.1 412 Precondition Failed\r\ncontent-length")); - for chunk_size in chunk_sizes.iter() { - let mut bytes = Vec::new(); - let random_bytes: Vec = - (0..*chunk_size).map(|_| rand::random::()).collect(); - - bytes.extend(format!("{:X}\r\n", chunk_size).as_bytes()); - bytes.extend(&random_bytes[..]); - bytes.extend(b"\r\n"); - let _ = stream.write_all(&bytes); - } - - let _ = stream.write_all(b"0\r\n\r\n"); - stream.shutdown(net::Shutdown::Write).unwrap(); - - let mut data = String::new(); - let _ = stream.read_to_string(&mut data); - - let re = Regex::new(r"size=(\d+)").unwrap(); - let size: usize = match re.captures(&data) { - Some(caps) => caps.get(1).unwrap().as_str().parse().unwrap(), - None => { - panic!(format!("Failed to find size in HTTP Response: {}", data)) - } - }; - size - }; - - assert_eq!(returned_size, total_size); - }) + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test?yes= HTTP/1.1\r\nexpect: 100-continue\r\n\r\n"); + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); + assert!(data.starts_with("HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\n")); } -#[test] -fn test_slow_request() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::build() - .client_timeout(100) - .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); +#[actix_rt::test] +async fn test_chunked_payload() { + let chunk_sizes = vec![32768, 32, 32768]; + let total_size: usize = chunk_sizes.iter().sum(); - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n"); - let mut data = String::new(); - let _ = stream.read_to_string(&mut data); - assert!(data.starts_with("HTTP/1.1 408 Request Timeout")); - }) -} - -#[test] -fn test_http1_malformed_request() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); - - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test/tests/test HTTP1.1\r\n"); - let mut data = String::new(); - let _ = stream.read_to_string(&mut data); - assert!(data.starts_with("HTTP/1.1 400 Bad Request")); - }) -} - -#[test] -fn test_http1_keepalive() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); - - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); - - let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); - }) -} - -#[test] -fn test_http1_keepalive_timeout() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::build() - .keep_alive(1) - .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); - - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); - thread::sleep(Duration::from_millis(1100)); - - let mut data = vec![0; 1024]; - let res = stream.read(&mut data).unwrap(); - assert_eq!(res, 0); - }) -} - -#[test] -fn test_http1_keepalive_close() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); + let srv = TestServer::start(|| { + HttpService::build().h1(service_fn(|mut request: Request| { + request + .take_payload() + .map(|res| match res { + Ok(pl) => pl, + Err(e) => panic!(format!("Error reading payload: {}", e)), + }) + .fold(0usize, |acc, chunk| ready(acc + chunk.len())) + .map(|req_size| { + Ok::<_, Error>(Response::Ok().body(format!("size={}", req_size))) + }) + })) + }); + let returned_size = { let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); let _ = stream - .write_all(b"GET /test/tests/test HTTP/1.1\r\nconnection: close\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); + .write_all(b"POST /test HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"); - let mut data = vec![0; 1024]; - let res = stream.read(&mut data).unwrap(); - assert_eq!(res, 0); - }) -} + for chunk_size in chunk_sizes.iter() { + let mut bytes = Vec::new(); + let random_bytes: Vec = + (0..*chunk_size).map(|_| rand::random::()).collect(); -#[test] -fn test_http10_keepalive_default_close() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); - - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test/tests/test HTTP/1.0\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.0 200 OK\r\n"); - - let mut data = vec![0; 1024]; - let res = stream.read(&mut data).unwrap(); - assert_eq!(res, 0); - }) -} - -#[test] -fn test_http10_keepalive() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); - - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all( - b"GET /test/tests/test HTTP/1.0\r\nconnection: keep-alive\r\n\r\n", - ); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.0 200 OK\r\n"); - - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test/tests/test HTTP/1.0\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.0 200 OK\r\n"); - - let mut data = vec![0; 1024]; - let res = stream.read(&mut data).unwrap(); - assert_eq!(res, 0); - }) -} - -#[test] -fn test_http1_keepalive_disabled() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::build() - .keep_alive(KeepAlive::Disabled) - .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) - }); - - let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); - let mut data = vec![0; 1024]; - let _ = stream.read(&mut data); - assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); - - let mut data = vec![0; 1024]; - let res = stream.read(&mut data).unwrap(); - assert_eq!(res, 0); - }) -} - -#[test] -fn test_content_length() { - block_on(async { - use actix_http::http::{ - header::{HeaderName, HeaderValue}, - StatusCode, - }; - - let srv = TestServer::start(|| { - HttpService::build().h1(|req: Request| { - let indx: usize = req.uri().path()[1..].parse().unwrap(); - let statuses = [ - StatusCode::NO_CONTENT, - StatusCode::CONTINUE, - StatusCode::SWITCHING_PROTOCOLS, - StatusCode::PROCESSING, - StatusCode::OK, - StatusCode::NOT_FOUND, - ]; - future::ok::<_, ()>(Response::new(statuses[indx])) - }) - }); - - let header = HeaderName::from_static("content-length"); - let value = HeaderValue::from_static("0"); - - { - for i in 0..4 { - let req = srv.request(http::Method::GET, srv.url(&format!("/{}", i))); - let response = req.send().await.unwrap(); - assert_eq!(response.headers().get(&header), None); - - let req = srv.request(http::Method::HEAD, srv.url(&format!("/{}", i))); - let response = req.send().await.unwrap(); - assert_eq!(response.headers().get(&header), None); - } - - for i in 4..6 { - let req = srv.request(http::Method::GET, srv.url(&format!("/{}", i))); - let response = req.send().await.unwrap(); - assert_eq!(response.headers().get(&header), Some(&value)); - } + bytes.extend(format!("{:X}\r\n", chunk_size).as_bytes()); + bytes.extend(&random_bytes[..]); + bytes.extend(b"\r\n"); + let _ = stream.write_all(&bytes); } - }) + + let _ = stream.write_all(b"0\r\n\r\n"); + stream.shutdown(net::Shutdown::Write).unwrap(); + + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); + + let re = Regex::new(r"size=(\d+)").unwrap(); + let size: usize = match re.captures(&data) { + Some(caps) => caps.get(1).unwrap().as_str().parse().unwrap(), + None => panic!(format!("Failed to find size in HTTP Response: {}", data)), + }; + size + }; + + assert_eq!(returned_size, total_size); } -#[test] -fn test_h1_headers() { - block_on(async { - let data = STR.repeat(10); - let data2 = data.clone(); +#[actix_rt::test] +async fn test_slow_request() { + let srv = TestServer::start(|| { + HttpService::build() + .client_timeout(100) + .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); - let mut srv = TestServer::start(move || { - let data = data.clone(); - HttpService::build().h1(move |_| { + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n"); + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); + assert!(data.starts_with("HTTP/1.1 408 Request Timeout")); +} + +#[actix_rt::test] +async fn test_http1_malformed_request() { + let srv = TestServer::start(|| { + HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); + + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test/tests/test HTTP1.1\r\n"); + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); + assert!(data.starts_with("HTTP/1.1 400 Bad Request")); +} + +#[actix_rt::test] +async fn test_http1_keepalive() { + let srv = TestServer::start(|| { + HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); + + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); + + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); +} + +#[actix_rt::test] +async fn test_http1_keepalive_timeout() { + let srv = TestServer::start(|| { + HttpService::build() + .keep_alive(1) + .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); + + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); + thread::sleep(Duration::from_millis(1100)); + + let mut data = vec![0; 1024]; + let res = stream.read(&mut data).unwrap(); + assert_eq!(res, 0); +} + +#[actix_rt::test] +async fn test_http1_keepalive_close() { + let srv = TestServer::start(|| { + HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); + + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = + stream.write_all(b"GET /test/tests/test HTTP/1.1\r\nconnection: close\r\n\r\n"); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); + + let mut data = vec![0; 1024]; + let res = stream.read(&mut data).unwrap(); + assert_eq!(res, 0); +} + +#[actix_rt::test] +async fn test_http10_keepalive_default_close() { + let srv = TestServer::start(|| { + HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); + + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.0\r\n\r\n"); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.0 200 OK\r\n"); + + let mut data = vec![0; 1024]; + let res = stream.read(&mut data).unwrap(); + assert_eq!(res, 0); +} + +#[actix_rt::test] +async fn test_http10_keepalive() { + let srv = TestServer::start(|| { + HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); + + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream + .write_all(b"GET /test/tests/test HTTP/1.0\r\nconnection: keep-alive\r\n\r\n"); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.0 200 OK\r\n"); + + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.0\r\n\r\n"); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.0 200 OK\r\n"); + + let mut data = vec![0; 1024]; + let res = stream.read(&mut data).unwrap(); + assert_eq!(res, 0); +} + +#[actix_rt::test] +async fn test_http1_keepalive_disabled() { + let srv = TestServer::start(|| { + HttpService::build() + .keep_alive(KeepAlive::Disabled) + .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + }); + + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n\r\n"); + let mut data = vec![0; 1024]; + let _ = stream.read(&mut data); + assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); + + let mut data = vec![0; 1024]; + let res = stream.read(&mut data).unwrap(); + assert_eq!(res, 0); +} + +#[actix_rt::test] +async fn test_content_length() { + use actix_http::http::{ + header::{HeaderName, HeaderValue}, + StatusCode, + }; + + let srv = TestServer::start(|| { + HttpService::build().h1(|req: Request| { + let indx: usize = req.uri().path()[1..].parse().unwrap(); + let statuses = [ + StatusCode::NO_CONTENT, + StatusCode::CONTINUE, + StatusCode::SWITCHING_PROTOCOLS, + StatusCode::PROCESSING, + StatusCode::OK, + StatusCode::NOT_FOUND, + ]; + future::ok::<_, ()>(Response::new(statuses[indx])) + }) + }); + + let header = HeaderName::from_static("content-length"); + let value = HeaderValue::from_static("0"); + + { + for i in 0..4 { + let req = srv.request(http::Method::GET, srv.url(&format!("/{}", i))); + let response = req.send().await.unwrap(); + assert_eq!(response.headers().get(&header), None); + + let req = srv.request(http::Method::HEAD, srv.url(&format!("/{}", i))); + let response = req.send().await.unwrap(); + assert_eq!(response.headers().get(&header), None); + } + + for i in 4..6 { + let req = srv.request(http::Method::GET, srv.url(&format!("/{}", i))); + let response = req.send().await.unwrap(); + assert_eq!(response.headers().get(&header), Some(&value)); + } + } +} + +#[actix_rt::test] +async fn test_h1_headers() { + let data = STR.repeat(10); + let data2 = data.clone(); + + let mut srv = TestServer::start(move || { + let data = data.clone(); + HttpService::build().h1(move |_| { let mut builder = Response::Ok(); for idx in 0..90 { builder.header( @@ -412,15 +378,14 @@ fn test_h1_headers() { } future::ok::<_, ()>(builder.body(data.clone())) }) - }); + }); - let response = srv.get("/").send().await.unwrap(); - assert!(response.status().is_success()); + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.load_body(response).await.unwrap(); - assert_eq!(bytes, Bytes::from(data2)); - }) + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from(data2)); } const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ @@ -445,230 +410,210 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World"; -#[test] -fn test_h1_body() { - block_on(async { - let mut srv = TestServer::start(|| { - HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) - }); +#[actix_rt::test] +async fn test_h1_body() { + let mut srv = TestServer::start(|| { + HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) + }); - let response = srv.get("/").send().await.unwrap(); - assert!(response.status().is_success()); + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.load_body(response).await.unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - }) + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); } -#[test] -fn test_h1_head_empty() { - block_on(async { - let mut srv = TestServer::start(|| { - HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) - }); +#[actix_rt::test] +async fn test_h1_head_empty() { + let mut srv = TestServer::start(|| { + HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) + }); - let response = srv.head("/").send().await.unwrap(); - assert!(response.status().is_success()); + let response = srv.head("/").send().await.unwrap(); + assert!(response.status().is_success()); - { - let len = response - .headers() - .get(http::header::CONTENT_LENGTH) - .unwrap(); - assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); - } + { + let len = response + .headers() + .get(http::header::CONTENT_LENGTH) + .unwrap(); + assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); + } - // read response - let bytes = srv.load_body(response).await.unwrap(); - assert!(bytes.is_empty()); - }) + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert!(bytes.is_empty()); } -#[test] -fn test_h1_head_binary() { - block_on(async { - let mut srv = TestServer::start(|| { - HttpService::build().h1(|_| { - ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) - }) - }); +#[actix_rt::test] +async fn test_h1_head_binary() { + let mut srv = TestServer::start(|| { + HttpService::build().h1(|_| { + ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) + }) + }); - let response = srv.head("/").send().await.unwrap(); - assert!(response.status().is_success()); + let response = srv.head("/").send().await.unwrap(); + assert!(response.status().is_success()); - { - let len = response - .headers() - .get(http::header::CONTENT_LENGTH) - .unwrap(); - assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); - } + { + let len = response + .headers() + .get(http::header::CONTENT_LENGTH) + .unwrap(); + assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); + } - // read response - let bytes = srv.load_body(response).await.unwrap(); - assert!(bytes.is_empty()); - }) + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert!(bytes.is_empty()); } -#[test] -fn test_h1_head_binary2() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) - }); +#[actix_rt::test] +async fn test_h1_head_binary2() { + let srv = TestServer::start(|| { + HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) + }); - let response = srv.head("/").send().await.unwrap(); - assert!(response.status().is_success()); + let response = srv.head("/").send().await.unwrap(); + assert!(response.status().is_success()); - { - let len = response - .headers() - .get(http::header::CONTENT_LENGTH) - .unwrap(); - assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); - } - }) + { + let len = response + .headers() + .get(http::header::CONTENT_LENGTH) + .unwrap(); + assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); + } } -#[test] -fn test_h1_body_length() { - block_on(async { - let mut srv = TestServer::start(|| { - HttpService::build().h1(|_| { - let body = once(ok(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok().body(body::SizedStream::new(STR.len() as u64, body)), - ) - }) - }); +#[actix_rt::test] +async fn test_h1_body_length() { + let mut srv = TestServer::start(|| { + HttpService::build().h1(|_| { + let body = once(ok(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok().body(body::SizedStream::new(STR.len() as u64, body)), + ) + }) + }); - let response = srv.get("/").send().await.unwrap(); - assert!(response.status().is_success()); + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = srv.load_body(response).await.unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - }) + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); } -#[test] -fn test_h1_body_chunked_explicit() { - block_on(async { - let mut srv = TestServer::start(|| { - HttpService::build().h1(|_| { - let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); +#[actix_rt::test] +async fn test_h1_body_chunked_explicit() { + let mut srv = TestServer::start(|| { + HttpService::build().h1(|_| { + let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok() + .header(header::TRANSFER_ENCODING, "chunked") + .streaming(body), + ) + }) + }); + + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); + assert_eq!( + response + .headers() + .get(header::TRANSFER_ENCODING) + .unwrap() + .to_str() + .unwrap(), + "chunked" + ); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + + // decode + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); +} + +#[actix_rt::test] +async fn test_h1_body_chunked_implicit() { + let mut srv = TestServer::start(|| { + HttpService::build().h1(|_| { + let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>(Response::Ok().streaming(body)) + }) + }); + + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); + assert_eq!( + response + .headers() + .get(header::TRANSFER_ENCODING) + .unwrap() + .to_str() + .unwrap(), + "chunked" + ); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); +} + +#[actix_rt::test] +async fn test_h1_response_http_error_handling() { + let mut srv = TestServer::start(|| { + HttpService::build().h1(factory_fn_cfg(|_: &ServerConfig| { + ok::<_, ()>(pipeline(|_| { + let broken_header = Bytes::from_static(b"\0\0\0"); ok::<_, ()>( Response::Ok() - .header(header::TRANSFER_ENCODING, "chunked") - .streaming(body), + .header(http::header::CONTENT_TYPE, broken_header) + .body(STR), ) - }) - }); - - let response = srv.get("/").send().await.unwrap(); - assert!(response.status().is_success()); - assert_eq!( - response - .headers() - .get(header::TRANSFER_ENCODING) - .unwrap() - .to_str() - .unwrap(), - "chunked" - ); - - // read response - let bytes = srv.load_body(response).await.unwrap(); - - // decode - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - }) -} - -#[test] -fn test_h1_body_chunked_implicit() { - block_on(async { - let mut srv = TestServer::start(|| { - HttpService::build().h1(|_| { - let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>(Response::Ok().streaming(body)) - }) - }); - - let response = srv.get("/").send().await.unwrap(); - assert!(response.status().is_success()); - assert_eq!( - response - .headers() - .get(header::TRANSFER_ENCODING) - .unwrap() - .to_str() - .unwrap(), - "chunked" - ); - - // read response - let bytes = srv.load_body(response).await.unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - }) -} - -#[test] -fn test_h1_response_http_error_handling() { - block_on(async { - let mut srv = TestServer::start(|| { - HttpService::build().h1(factory_fn_cfg(|_: &ServerConfig| { - ok::<_, ()>(pipeline(|_| { - let broken_header = Bytes::from_static(b"\0\0\0"); - ok::<_, ()>( - Response::Ok() - .header(http::header::CONTENT_TYPE, broken_header) - .body(STR), - ) - })) })) - }); + })) + }); - let response = srv.get("/").send().await.unwrap(); - assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR); + let response = srv.get("/").send().await.unwrap(); + assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR); - // read response - let bytes = srv.load_body(response).await.unwrap(); - assert_eq!(bytes, Bytes::from_static(b"failed to parse header value")); - }) + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(b"failed to parse header value")); } -#[test] -fn test_h1_service_error() { - block_on(async { - let mut srv = TestServer::start(|| { - HttpService::build() - .h1(|_| future::err::(error::ErrorBadRequest("error"))) - }); +#[actix_rt::test] +async fn test_h1_service_error() { + let mut srv = TestServer::start(|| { + HttpService::build() + .h1(|_| future::err::(error::ErrorBadRequest("error"))) + }); - let response = srv.get("/").send().await.unwrap(); - assert_eq!(response.status(), http::StatusCode::BAD_REQUEST); + let response = srv.get("/").send().await.unwrap(); + assert_eq!(response.status(), http::StatusCode::BAD_REQUEST); - // read response - let bytes = srv.load_body(response).await.unwrap(); - assert_eq!(bytes, Bytes::from_static(b"error")); - }) + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(b"error")); } -#[test] -fn test_h1_on_connect() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::build() - .on_connect(|_| 10usize) - .h1(|req: Request| { - assert!(req.extensions().contains::()); - future::ok::<_, ()>(Response::Ok().finish()) - }) - }); +#[actix_rt::test] +async fn test_h1_on_connect() { + let srv = TestServer::start(|| { + HttpService::build() + .on_connect(|_| 10usize) + .h1(|req: Request| { + assert!(req.extensions().contains::()); + future::ok::<_, ()>(Response::Ok().finish()) + }) + }); - let response = srv.get("/").send().await.unwrap(); - assert!(response.status().is_success()); - }) + let response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); } diff --git a/actix-http/tests/test_ws.rs b/actix-http/tests/test_ws.rs index 74c1cb405..aa81bc41b 100644 --- a/actix-http/tests/test_ws.rs +++ b/actix-http/tests/test_ws.rs @@ -1,6 +1,6 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_http::{body, h1, ws, Error, HttpService, Request, Response}; -use actix_http_test::{block_on, TestServer}; +use actix_http_test::TestServer; use actix_utils::framed::FramedTransport; use bytes::{Bytes, BytesMut}; use futures::future; @@ -34,53 +34,51 @@ async fn service(msg: ws::Frame) -> Result { Ok(msg) } -#[test] -fn test_simple() { - block_on(async { - let mut srv = TestServer::start(|| { - HttpService::build() - .upgrade(actix_service::service_fn(ws_service)) - .finish(|_| future::ok::<_, ()>(Response::NotFound())) - }); +#[actix_rt::test] +async fn test_simple() { + let mut srv = TestServer::start(|| { + HttpService::build() + .upgrade(actix_service::service_fn(ws_service)) + .finish(|_| future::ok::<_, ()>(Response::NotFound())) + }); - // client service - let mut framed = srv.ws().await.unwrap(); - framed - .send(ws::Message::Text("text".to_string())) - .await - .unwrap(); - let (item, mut framed) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Text(Some(BytesMut::from("text"))) - ); + // client service + let mut framed = srv.ws().await.unwrap(); + framed + .send(ws::Message::Text("text".to_string())) + .await + .unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Text(Some(BytesMut::from("text"))) + ); - framed - .send(ws::Message::Binary("text".into())) - .await - .unwrap(); - let (item, mut framed) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Binary(Some(Bytes::from_static(b"text").into())) - ); + framed + .send(ws::Message::Binary("text".into())) + .await + .unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Binary(Some(Bytes::from_static(b"text").into())) + ); - framed.send(ws::Message::Ping("text".into())).await.unwrap(); - let (item, mut framed) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Pong("text".to_string().into()) - ); + framed.send(ws::Message::Ping("text".into())).await.unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Pong("text".to_string().into()) + ); - framed - .send(ws::Message::Close(Some(ws::CloseCode::Normal.into()))) - .await - .unwrap(); + framed + .send(ws::Message::Close(Some(ws::CloseCode::Normal.into()))) + .await + .unwrap(); - let (item, _framed) = framed.into_future().await; - assert_eq!( - item.unwrap().unwrap(), - ws::Frame::Close(Some(ws::CloseCode::Normal.into())) - ); - }) + let (item, _framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Close(Some(ws::CloseCode::Normal.into())) + ); } diff --git a/actix-identity/src/lib.rs b/actix-identity/src/lib.rs index 2980a7753..5dfd2ae65 100644 --- a/actix-identity/src/lib.rs +++ b/actix-identity/src/lib.rs @@ -607,144 +607,130 @@ mod tests { use super::*; use actix_web::http::StatusCode; - use actix_web::test::{self, block_on, TestRequest}; + use actix_web::test::{self, TestRequest}; use actix_web::{web, App, Error, HttpResponse}; const COOKIE_KEY_MASTER: [u8; 32] = [0; 32]; const COOKIE_NAME: &'static str = "actix_auth"; const COOKIE_LOGIN: &'static str = "test"; - #[test] - fn test_identity() { - block_on(async { - let mut srv = test::init_service( - App::new() - .wrap(IdentityService::new( - CookieIdentityPolicy::new(&COOKIE_KEY_MASTER) - .domain("www.rust-lang.org") - .name(COOKIE_NAME) - .path("/") - .secure(true), - )) - .service(web::resource("/index").to(|id: Identity| { - if id.identity().is_some() { - HttpResponse::Created() - } else { - HttpResponse::Ok() - } - })) - .service(web::resource("/login").to(|id: Identity| { - id.remember(COOKIE_LOGIN.to_string()); + #[actix_rt::test] + async fn test_identity() { + let mut srv = test::init_service( + App::new() + .wrap(IdentityService::new( + CookieIdentityPolicy::new(&COOKIE_KEY_MASTER) + .domain("www.rust-lang.org") + .name(COOKIE_NAME) + .path("/") + .secure(true), + )) + .service(web::resource("/index").to(|id: Identity| { + if id.identity().is_some() { + HttpResponse::Created() + } else { HttpResponse::Ok() - })) - .service(web::resource("/logout").to(|id: Identity| { - if id.identity().is_some() { - id.forget(); - HttpResponse::Ok() - } else { - HttpResponse::BadRequest() - } - })), - ) - .await; - let resp = test::call_service( - &mut srv, - TestRequest::with_uri("/index").to_request(), - ) - .await; - assert_eq!(resp.status(), StatusCode::OK); + } + })) + .service(web::resource("/login").to(|id: Identity| { + id.remember(COOKIE_LOGIN.to_string()); + HttpResponse::Ok() + })) + .service(web::resource("/logout").to(|id: Identity| { + if id.identity().is_some() { + id.forget(); + HttpResponse::Ok() + } else { + HttpResponse::BadRequest() + } + })), + ) + .await; + let resp = + test::call_service(&mut srv, TestRequest::with_uri("/index").to_request()) + .await; + assert_eq!(resp.status(), StatusCode::OK); - let resp = test::call_service( - &mut srv, - TestRequest::with_uri("/login").to_request(), - ) - .await; - assert_eq!(resp.status(), StatusCode::OK); - let c = resp.response().cookies().next().unwrap().to_owned(); + let resp = + test::call_service(&mut srv, TestRequest::with_uri("/login").to_request()) + .await; + assert_eq!(resp.status(), StatusCode::OK); + let c = resp.response().cookies().next().unwrap().to_owned(); - let resp = test::call_service( - &mut srv, - TestRequest::with_uri("/index") - .cookie(c.clone()) - .to_request(), - ) - .await; - assert_eq!(resp.status(), StatusCode::CREATED); + let resp = test::call_service( + &mut srv, + TestRequest::with_uri("/index") + .cookie(c.clone()) + .to_request(), + ) + .await; + assert_eq!(resp.status(), StatusCode::CREATED); - let resp = test::call_service( - &mut srv, - TestRequest::with_uri("/logout") - .cookie(c.clone()) - .to_request(), - ) - .await; - assert_eq!(resp.status(), StatusCode::OK); - assert!(resp.headers().contains_key(header::SET_COOKIE)) - }) + let resp = test::call_service( + &mut srv, + TestRequest::with_uri("/logout") + .cookie(c.clone()) + .to_request(), + ) + .await; + assert_eq!(resp.status(), StatusCode::OK); + assert!(resp.headers().contains_key(header::SET_COOKIE)) } - #[test] - fn test_identity_max_age_time() { - block_on(async { - let duration = Duration::days(1); - let mut srv = test::init_service( - App::new() - .wrap(IdentityService::new( - CookieIdentityPolicy::new(&COOKIE_KEY_MASTER) - .domain("www.rust-lang.org") - .name(COOKIE_NAME) - .path("/") - .max_age_time(duration) - .secure(true), - )) - .service(web::resource("/login").to(|id: Identity| { - id.remember("test".to_string()); - HttpResponse::Ok() - })), - ) - .await; - let resp = test::call_service( - &mut srv, - TestRequest::with_uri("/login").to_request(), - ) - .await; - assert_eq!(resp.status(), StatusCode::OK); - assert!(resp.headers().contains_key(header::SET_COOKIE)); - let c = resp.response().cookies().next().unwrap().to_owned(); - assert_eq!(duration, c.max_age().unwrap()); - }) + #[actix_rt::test] + async fn test_identity_max_age_time() { + let duration = Duration::days(1); + let mut srv = test::init_service( + App::new() + .wrap(IdentityService::new( + CookieIdentityPolicy::new(&COOKIE_KEY_MASTER) + .domain("www.rust-lang.org") + .name(COOKIE_NAME) + .path("/") + .max_age_time(duration) + .secure(true), + )) + .service(web::resource("/login").to(|id: Identity| { + id.remember("test".to_string()); + HttpResponse::Ok() + })), + ) + .await; + let resp = + test::call_service(&mut srv, TestRequest::with_uri("/login").to_request()) + .await; + assert_eq!(resp.status(), StatusCode::OK); + assert!(resp.headers().contains_key(header::SET_COOKIE)); + let c = resp.response().cookies().next().unwrap().to_owned(); + assert_eq!(duration, c.max_age().unwrap()); } - #[test] - fn test_identity_max_age() { - block_on(async { - let seconds = 60; - let mut srv = test::init_service( - App::new() - .wrap(IdentityService::new( - CookieIdentityPolicy::new(&COOKIE_KEY_MASTER) - .domain("www.rust-lang.org") - .name(COOKIE_NAME) - .path("/") - .max_age(seconds) - .secure(true), - )) - .service(web::resource("/login").to(|id: Identity| { - id.remember("test".to_string()); - HttpResponse::Ok() - })), - ) - .await; - let resp = test::call_service( - &mut srv, - TestRequest::with_uri("/login").to_request(), - ) - .await; - assert_eq!(resp.status(), StatusCode::OK); - assert!(resp.headers().contains_key(header::SET_COOKIE)); - let c = resp.response().cookies().next().unwrap().to_owned(); - assert_eq!(Duration::seconds(seconds as i64), c.max_age().unwrap()); - }) + #[actix_rt::test] + async fn test_identity_max_age() { + let seconds = 60; + let mut srv = test::init_service( + App::new() + .wrap(IdentityService::new( + CookieIdentityPolicy::new(&COOKIE_KEY_MASTER) + .domain("www.rust-lang.org") + .name(COOKIE_NAME) + .path("/") + .max_age(seconds) + .secure(true), + )) + .service(web::resource("/login").to(|id: Identity| { + id.remember("test".to_string()); + HttpResponse::Ok() + })), + ) + .await; + let resp = + test::call_service(&mut srv, TestRequest::with_uri("/login").to_request()) + .await; + assert_eq!(resp.status(), StatusCode::OK); + assert!(resp.headers().contains_key(header::SET_COOKIE)); + let c = resp.response().cookies().next().unwrap().to_owned(); + assert_eq!(Duration::seconds(seconds as i64), c.max_age().unwrap()); } async fn create_identity_server< @@ -885,223 +871,202 @@ mod tests { assert!(cookies.get(COOKIE_NAME).is_none()); } - #[test] - fn test_identity_legacy_cookie_is_set() { - block_on(async { - let mut srv = create_identity_server(|c| c).await; - let mut resp = - test::call_service(&mut srv, TestRequest::with_uri("/").to_request()) - .await; - assert_legacy_login_cookie(&mut resp, COOKIE_LOGIN); - assert_logged_in(resp, None).await; - }) + #[actix_rt::test] + async fn test_identity_legacy_cookie_is_set() { + let mut srv = create_identity_server(|c| c).await; + let mut resp = + test::call_service(&mut srv, TestRequest::with_uri("/").to_request()).await; + assert_legacy_login_cookie(&mut resp, COOKIE_LOGIN); + assert_logged_in(resp, None).await; } - #[test] - fn test_identity_legacy_cookie_works() { - block_on(async { - let mut srv = create_identity_server(|c| c).await; - let cookie = legacy_login_cookie(COOKIE_LOGIN); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ) - .await; - assert_no_login_cookie(&mut resp); - assert_logged_in(resp, Some(COOKIE_LOGIN)).await; - }) + #[actix_rt::test] + async fn test_identity_legacy_cookie_works() { + let mut srv = create_identity_server(|c| c).await; + let cookie = legacy_login_cookie(COOKIE_LOGIN); + let mut resp = test::call_service( + &mut srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_no_login_cookie(&mut resp); + assert_logged_in(resp, Some(COOKIE_LOGIN)).await; } - #[test] - fn test_identity_legacy_cookie_rejected_if_visit_timestamp_needed() { - block_on(async { - let mut srv = - create_identity_server(|c| c.visit_deadline(Duration::days(90))).await; - let cookie = legacy_login_cookie(COOKIE_LOGIN); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ) - .await; - assert_login_cookie( - &mut resp, - COOKIE_LOGIN, - LoginTimestampCheck::NoTimestamp, - VisitTimeStampCheck::NewTimestamp, - ); - assert_logged_in(resp, None).await; - }) + #[actix_rt::test] + async fn test_identity_legacy_cookie_rejected_if_visit_timestamp_needed() { + let mut srv = + create_identity_server(|c| c.visit_deadline(Duration::days(90))).await; + let cookie = legacy_login_cookie(COOKIE_LOGIN); + let mut resp = test::call_service( + &mut srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_login_cookie( + &mut resp, + COOKIE_LOGIN, + LoginTimestampCheck::NoTimestamp, + VisitTimeStampCheck::NewTimestamp, + ); + assert_logged_in(resp, None).await; } - #[test] - fn test_identity_legacy_cookie_rejected_if_login_timestamp_needed() { - block_on(async { - let mut srv = - create_identity_server(|c| c.login_deadline(Duration::days(90))).await; - let cookie = legacy_login_cookie(COOKIE_LOGIN); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ) - .await; - assert_login_cookie( - &mut resp, - COOKIE_LOGIN, - LoginTimestampCheck::NewTimestamp, - VisitTimeStampCheck::NoTimestamp, - ); - assert_logged_in(resp, None).await; - }) + #[actix_rt::test] + async fn test_identity_legacy_cookie_rejected_if_login_timestamp_needed() { + let mut srv = + create_identity_server(|c| c.login_deadline(Duration::days(90))).await; + let cookie = legacy_login_cookie(COOKIE_LOGIN); + let mut resp = test::call_service( + &mut srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_login_cookie( + &mut resp, + COOKIE_LOGIN, + LoginTimestampCheck::NewTimestamp, + VisitTimeStampCheck::NoTimestamp, + ); + assert_logged_in(resp, None).await; } - #[test] - fn test_identity_cookie_rejected_if_login_timestamp_needed() { - block_on(async { - let mut srv = - create_identity_server(|c| c.login_deadline(Duration::days(90))).await; - let cookie = login_cookie(COOKIE_LOGIN, None, Some(SystemTime::now())); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ) - .await; - assert_login_cookie( - &mut resp, - COOKIE_LOGIN, - LoginTimestampCheck::NewTimestamp, - VisitTimeStampCheck::NoTimestamp, - ); - assert_logged_in(resp, None).await; - }) + #[actix_rt::test] + async fn test_identity_cookie_rejected_if_login_timestamp_needed() { + let mut srv = + create_identity_server(|c| c.login_deadline(Duration::days(90))).await; + let cookie = login_cookie(COOKIE_LOGIN, None, Some(SystemTime::now())); + let mut resp = test::call_service( + &mut srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_login_cookie( + &mut resp, + COOKIE_LOGIN, + LoginTimestampCheck::NewTimestamp, + VisitTimeStampCheck::NoTimestamp, + ); + assert_logged_in(resp, None).await; } - #[test] - fn test_identity_cookie_rejected_if_visit_timestamp_needed() { - block_on(async { - let mut srv = - create_identity_server(|c| c.visit_deadline(Duration::days(90))).await; - let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now()), None); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ) - .await; - assert_login_cookie( - &mut resp, - COOKIE_LOGIN, - LoginTimestampCheck::NoTimestamp, - VisitTimeStampCheck::NewTimestamp, - ); - assert_logged_in(resp, None).await; - }) + #[actix_rt::test] + async fn test_identity_cookie_rejected_if_visit_timestamp_needed() { + let mut srv = + create_identity_server(|c| c.visit_deadline(Duration::days(90))).await; + let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now()), None); + let mut resp = test::call_service( + &mut srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_login_cookie( + &mut resp, + COOKIE_LOGIN, + LoginTimestampCheck::NoTimestamp, + VisitTimeStampCheck::NewTimestamp, + ); + assert_logged_in(resp, None).await; } - #[test] - fn test_identity_cookie_rejected_if_login_timestamp_too_old() { - block_on(async { - let mut srv = - create_identity_server(|c| c.login_deadline(Duration::days(90))).await; - let cookie = login_cookie( - COOKIE_LOGIN, - Some(SystemTime::now() - Duration::days(180).to_std().unwrap()), - None, - ); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ) - .await; - assert_login_cookie( - &mut resp, - COOKIE_LOGIN, - LoginTimestampCheck::NewTimestamp, - VisitTimeStampCheck::NoTimestamp, - ); - assert_logged_in(resp, None).await; - }) + #[actix_rt::test] + async fn test_identity_cookie_rejected_if_login_timestamp_too_old() { + let mut srv = + create_identity_server(|c| c.login_deadline(Duration::days(90))).await; + let cookie = login_cookie( + COOKIE_LOGIN, + Some(SystemTime::now() - Duration::days(180).to_std().unwrap()), + None, + ); + let mut resp = test::call_service( + &mut srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_login_cookie( + &mut resp, + COOKIE_LOGIN, + LoginTimestampCheck::NewTimestamp, + VisitTimeStampCheck::NoTimestamp, + ); + assert_logged_in(resp, None).await; } - #[test] - fn test_identity_cookie_rejected_if_visit_timestamp_too_old() { - block_on(async { - let mut srv = - create_identity_server(|c| c.visit_deadline(Duration::days(90))).await; - let cookie = login_cookie( - COOKIE_LOGIN, - None, - Some(SystemTime::now() - Duration::days(180).to_std().unwrap()), - ); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ) - .await; - assert_login_cookie( - &mut resp, - COOKIE_LOGIN, - LoginTimestampCheck::NoTimestamp, - VisitTimeStampCheck::NewTimestamp, - ); - assert_logged_in(resp, None).await; - }) + #[actix_rt::test] + async fn test_identity_cookie_rejected_if_visit_timestamp_too_old() { + let mut srv = + create_identity_server(|c| c.visit_deadline(Duration::days(90))).await; + let cookie = login_cookie( + COOKIE_LOGIN, + None, + Some(SystemTime::now() - Duration::days(180).to_std().unwrap()), + ); + let mut resp = test::call_service( + &mut srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_login_cookie( + &mut resp, + COOKIE_LOGIN, + LoginTimestampCheck::NoTimestamp, + VisitTimeStampCheck::NewTimestamp, + ); + assert_logged_in(resp, None).await; } - #[test] - fn test_identity_cookie_not_updated_on_login_deadline() { - block_on(async { - let mut srv = - create_identity_server(|c| c.login_deadline(Duration::days(90))).await; - let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now()), None); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ) - .await; - assert_no_login_cookie(&mut resp); - assert_logged_in(resp, Some(COOKIE_LOGIN)).await; - }) + #[actix_rt::test] + async fn test_identity_cookie_not_updated_on_login_deadline() { + let mut srv = + create_identity_server(|c| c.login_deadline(Duration::days(90))).await; + let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now()), None); + let mut resp = test::call_service( + &mut srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_no_login_cookie(&mut resp); + assert_logged_in(resp, Some(COOKIE_LOGIN)).await; } - #[test] - fn test_identity_cookie_updated_on_visit_deadline() { - block_on(async { - let mut srv = create_identity_server(|c| { - c.visit_deadline(Duration::days(90)) - .login_deadline(Duration::days(90)) - }) - .await; - let timestamp = SystemTime::now() - Duration::days(1).to_std().unwrap(); - let cookie = login_cookie(COOKIE_LOGIN, Some(timestamp), Some(timestamp)); - let mut resp = test::call_service( - &mut srv, - TestRequest::with_uri("/") - .cookie(cookie.clone()) - .to_request(), - ) - .await; - assert_login_cookie( - &mut resp, - COOKIE_LOGIN, - LoginTimestampCheck::OldTimestamp(timestamp), - VisitTimeStampCheck::NewTimestamp, - ); - assert_logged_in(resp, Some(COOKIE_LOGIN)).await; + #[actix_rt::test] + async fn test_identity_cookie_updated_on_visit_deadline() { + let mut srv = create_identity_server(|c| { + c.visit_deadline(Duration::days(90)) + .login_deadline(Duration::days(90)) }) + .await; + let timestamp = SystemTime::now() - Duration::days(1).to_std().unwrap(); + let cookie = login_cookie(COOKIE_LOGIN, Some(timestamp), Some(timestamp)); + let mut resp = test::call_service( + &mut srv, + TestRequest::with_uri("/") + .cookie(cookie.clone()) + .to_request(), + ) + .await; + assert_login_cookie( + &mut resp, + COOKIE_LOGIN, + LoginTimestampCheck::OldTimestamp(timestamp), + VisitTimeStampCheck::NewTimestamp, + ); + assert_logged_in(resp, Some(COOKIE_LOGIN)).await; } } diff --git a/actix-multipart/src/server.rs b/actix-multipart/src/server.rs index dd7852c8e..c49896761 100644 --- a/actix-multipart/src/server.rs +++ b/actix-multipart/src/server.rs @@ -813,12 +813,11 @@ mod tests { use actix_http::h1::Payload; use actix_utils::mpsc; use actix_web::http::header::{DispositionParam, DispositionType}; - use actix_web::test::block_on; use bytes::Bytes; use futures::future::lazy; - #[test] - fn test_boundary() { + #[actix_rt::test] + async fn test_boundary() { let headers = HeaderMap::new(); match Multipart::boundary(&headers) { Err(MultipartError::NoContentType) => (), @@ -891,246 +890,228 @@ mod tests { (bytes, headers) } - #[test] - fn test_multipart_no_end_crlf() { - block_on(async { - let (sender, payload) = create_stream(); - let (bytes, headers) = create_simple_request_with_header(); - let bytes_stripped = bytes.slice_to(bytes.len()); // strip crlf + #[actix_rt::test] + async fn test_multipart_no_end_crlf() { + let (sender, payload) = create_stream(); + let (bytes, headers) = create_simple_request_with_header(); + let bytes_stripped = bytes.slice_to(bytes.len()); // strip crlf - sender.send(Ok(bytes_stripped)).unwrap(); - drop(sender); // eof + sender.send(Ok(bytes_stripped)).unwrap(); + drop(sender); // eof - let mut multipart = Multipart::new(&headers, payload); + let mut multipart = Multipart::new(&headers, payload); - match multipart.next().await.unwrap() { - Ok(_) => (), - _ => unreachable!(), - } + match multipart.next().await.unwrap() { + Ok(_) => (), + _ => unreachable!(), + } - match multipart.next().await.unwrap() { - Ok(_) => (), - _ => unreachable!(), - } + match multipart.next().await.unwrap() { + Ok(_) => (), + _ => unreachable!(), + } - match multipart.next().await { - None => (), - _ => unreachable!(), - } - }) + match multipart.next().await { + None => (), + _ => unreachable!(), + } } - #[test] - fn test_multipart() { - block_on(async { - let (sender, payload) = create_stream(); - let (bytes, headers) = create_simple_request_with_header(); + #[actix_rt::test] + async fn test_multipart() { + let (sender, payload) = create_stream(); + let (bytes, headers) = create_simple_request_with_header(); - sender.send(Ok(bytes)).unwrap(); + sender.send(Ok(bytes)).unwrap(); - let mut multipart = Multipart::new(&headers, payload); - match multipart.next().await { - Some(Ok(mut field)) => { - let cd = field.content_disposition().unwrap(); - assert_eq!(cd.disposition, DispositionType::FormData); - assert_eq!(cd.parameters[0], DispositionParam::Name("file".into())); + let mut multipart = Multipart::new(&headers, payload); + match multipart.next().await { + Some(Ok(mut field)) => { + let cd = field.content_disposition().unwrap(); + assert_eq!(cd.disposition, DispositionType::FormData); + assert_eq!(cd.parameters[0], DispositionParam::Name("file".into())); - assert_eq!(field.content_type().type_(), mime::TEXT); - assert_eq!(field.content_type().subtype(), mime::PLAIN); + assert_eq!(field.content_type().type_(), mime::TEXT); + assert_eq!(field.content_type().subtype(), mime::PLAIN); - match field.next().await.unwrap() { - Ok(chunk) => assert_eq!(chunk, "test"), - _ => unreachable!(), - } - match field.next().await { - None => (), - _ => unreachable!(), - } + match field.next().await.unwrap() { + Ok(chunk) => assert_eq!(chunk, "test"), + _ => unreachable!(), } - _ => unreachable!(), - } - - match multipart.next().await.unwrap() { - Ok(mut field) => { - assert_eq!(field.content_type().type_(), mime::TEXT); - assert_eq!(field.content_type().subtype(), mime::PLAIN); - - match field.next().await { - Some(Ok(chunk)) => assert_eq!(chunk, "data"), - _ => unreachable!(), - } - match field.next().await { - None => (), - _ => unreachable!(), - } + match field.next().await { + None => (), + _ => unreachable!(), } - _ => unreachable!(), } + _ => unreachable!(), + } - match multipart.next().await { - None => (), - _ => unreachable!(), - } - }); - } + match multipart.next().await.unwrap() { + Ok(mut field) => { + assert_eq!(field.content_type().type_(), mime::TEXT); + assert_eq!(field.content_type().subtype(), mime::PLAIN); - #[test] - fn test_stream() { - block_on(async { - let (sender, payload) = create_stream(); - let (bytes, headers) = create_simple_request_with_header(); - - sender.send(Ok(bytes)).unwrap(); - - let mut multipart = Multipart::new(&headers, payload); - match multipart.next().await.unwrap() { - Ok(mut field) => { - let cd = field.content_disposition().unwrap(); - assert_eq!(cd.disposition, DispositionType::FormData); - assert_eq!(cd.parameters[0], DispositionParam::Name("file".into())); - - assert_eq!(field.content_type().type_(), mime::TEXT); - assert_eq!(field.content_type().subtype(), mime::PLAIN); - - match field.next().await.unwrap() { - Ok(chunk) => assert_eq!(chunk, "test"), - _ => unreachable!(), - } - match field.next().await { - None => (), - _ => unreachable!(), - } + match field.next().await { + Some(Ok(chunk)) => assert_eq!(chunk, "data"), + _ => unreachable!(), } - _ => unreachable!(), - } - - match multipart.next().await { - Some(Ok(mut field)) => { - assert_eq!(field.content_type().type_(), mime::TEXT); - assert_eq!(field.content_type().subtype(), mime::PLAIN); - - match field.next().await { - Some(Ok(chunk)) => assert_eq!(chunk, "data"), - _ => unreachable!(), - } - match field.next().await { - None => (), - _ => unreachable!(), - } + match field.next().await { + None => (), + _ => unreachable!(), } - _ => unreachable!(), } + _ => unreachable!(), + } - match multipart.next().await { - None => (), - _ => unreachable!(), + match multipart.next().await { + None => (), + _ => unreachable!(), + } + } + + #[actix_rt::test] + async fn test_stream() { + let (sender, payload) = create_stream(); + let (bytes, headers) = create_simple_request_with_header(); + + sender.send(Ok(bytes)).unwrap(); + + let mut multipart = Multipart::new(&headers, payload); + match multipart.next().await.unwrap() { + Ok(mut field) => { + let cd = field.content_disposition().unwrap(); + assert_eq!(cd.disposition, DispositionType::FormData); + assert_eq!(cd.parameters[0], DispositionParam::Name("file".into())); + + assert_eq!(field.content_type().type_(), mime::TEXT); + assert_eq!(field.content_type().subtype(), mime::PLAIN); + + match field.next().await.unwrap() { + Ok(chunk) => assert_eq!(chunk, "test"), + _ => unreachable!(), + } + match field.next().await { + None => (), + _ => unreachable!(), + } } - }); + _ => unreachable!(), + } + + match multipart.next().await { + Some(Ok(mut field)) => { + assert_eq!(field.content_type().type_(), mime::TEXT); + assert_eq!(field.content_type().subtype(), mime::PLAIN); + + match field.next().await { + Some(Ok(chunk)) => assert_eq!(chunk, "data"), + _ => unreachable!(), + } + match field.next().await { + None => (), + _ => unreachable!(), + } + } + _ => unreachable!(), + } + + match multipart.next().await { + None => (), + _ => unreachable!(), + } } - #[test] - fn test_basic() { - block_on(async { - let (_, payload) = Payload::create(false); - let mut payload = PayloadBuffer::new(payload); + #[actix_rt::test] + async fn test_basic() { + let (_, payload) = Payload::create(false); + let mut payload = PayloadBuffer::new(payload); - assert_eq!(payload.buf.len(), 0); - lazy(|cx| payload.poll_stream(cx)).await.unwrap(); - assert_eq!(None, payload.read_max(1).unwrap()); - }) + assert_eq!(payload.buf.len(), 0); + lazy(|cx| payload.poll_stream(cx)).await.unwrap(); + assert_eq!(None, payload.read_max(1).unwrap()); } - #[test] - fn test_eof() { - block_on(async { - let (mut sender, payload) = Payload::create(false); - let mut payload = PayloadBuffer::new(payload); + #[actix_rt::test] + async fn test_eof() { + let (mut sender, payload) = Payload::create(false); + let mut payload = PayloadBuffer::new(payload); - assert_eq!(None, payload.read_max(4).unwrap()); - sender.feed_data(Bytes::from("data")); - sender.feed_eof(); - lazy(|cx| payload.poll_stream(cx)).await.unwrap(); + assert_eq!(None, payload.read_max(4).unwrap()); + sender.feed_data(Bytes::from("data")); + sender.feed_eof(); + lazy(|cx| payload.poll_stream(cx)).await.unwrap(); - assert_eq!(Some(Bytes::from("data")), payload.read_max(4).unwrap()); - assert_eq!(payload.buf.len(), 0); - assert!(payload.read_max(1).is_err()); - assert!(payload.eof); - }) + assert_eq!(Some(Bytes::from("data")), payload.read_max(4).unwrap()); + assert_eq!(payload.buf.len(), 0); + assert!(payload.read_max(1).is_err()); + assert!(payload.eof); } - #[test] - fn test_err() { - block_on(async { - let (mut sender, payload) = Payload::create(false); - let mut payload = PayloadBuffer::new(payload); - assert_eq!(None, payload.read_max(1).unwrap()); - sender.set_error(PayloadError::Incomplete(None)); - lazy(|cx| payload.poll_stream(cx)).await.err().unwrap(); - }) + #[actix_rt::test] + async fn test_err() { + let (mut sender, payload) = Payload::create(false); + let mut payload = PayloadBuffer::new(payload); + assert_eq!(None, payload.read_max(1).unwrap()); + sender.set_error(PayloadError::Incomplete(None)); + lazy(|cx| payload.poll_stream(cx)).await.err().unwrap(); } - #[test] - fn test_readmax() { - block_on(async { - let (mut sender, payload) = Payload::create(false); - let mut payload = PayloadBuffer::new(payload); + #[actix_rt::test] + async fn test_readmax() { + let (mut sender, payload) = Payload::create(false); + let mut payload = PayloadBuffer::new(payload); - sender.feed_data(Bytes::from("line1")); - sender.feed_data(Bytes::from("line2")); - lazy(|cx| payload.poll_stream(cx)).await.unwrap(); - assert_eq!(payload.buf.len(), 10); + sender.feed_data(Bytes::from("line1")); + sender.feed_data(Bytes::from("line2")); + lazy(|cx| payload.poll_stream(cx)).await.unwrap(); + assert_eq!(payload.buf.len(), 10); - assert_eq!(Some(Bytes::from("line1")), payload.read_max(5).unwrap()); - assert_eq!(payload.buf.len(), 5); + assert_eq!(Some(Bytes::from("line1")), payload.read_max(5).unwrap()); + assert_eq!(payload.buf.len(), 5); - assert_eq!(Some(Bytes::from("line2")), payload.read_max(5).unwrap()); - assert_eq!(payload.buf.len(), 0); - }) + assert_eq!(Some(Bytes::from("line2")), payload.read_max(5).unwrap()); + assert_eq!(payload.buf.len(), 0); } - #[test] - fn test_readexactly() { - block_on(async { - let (mut sender, payload) = Payload::create(false); - let mut payload = PayloadBuffer::new(payload); + #[actix_rt::test] + async fn test_readexactly() { + let (mut sender, payload) = Payload::create(false); + let mut payload = PayloadBuffer::new(payload); - assert_eq!(None, payload.read_exact(2)); + assert_eq!(None, payload.read_exact(2)); - sender.feed_data(Bytes::from("line1")); - sender.feed_data(Bytes::from("line2")); - lazy(|cx| payload.poll_stream(cx)).await.unwrap(); + sender.feed_data(Bytes::from("line1")); + sender.feed_data(Bytes::from("line2")); + lazy(|cx| payload.poll_stream(cx)).await.unwrap(); - assert_eq!(Some(Bytes::from_static(b"li")), payload.read_exact(2)); - assert_eq!(payload.buf.len(), 8); + assert_eq!(Some(Bytes::from_static(b"li")), payload.read_exact(2)); + assert_eq!(payload.buf.len(), 8); - assert_eq!(Some(Bytes::from_static(b"ne1l")), payload.read_exact(4)); - assert_eq!(payload.buf.len(), 4); - }) + assert_eq!(Some(Bytes::from_static(b"ne1l")), payload.read_exact(4)); + assert_eq!(payload.buf.len(), 4); } - #[test] - fn test_readuntil() { - block_on(async { - let (mut sender, payload) = Payload::create(false); - let mut payload = PayloadBuffer::new(payload); + #[actix_rt::test] + async fn test_readuntil() { + let (mut sender, payload) = Payload::create(false); + let mut payload = PayloadBuffer::new(payload); - assert_eq!(None, payload.read_until(b"ne").unwrap()); + assert_eq!(None, payload.read_until(b"ne").unwrap()); - sender.feed_data(Bytes::from("line1")); - sender.feed_data(Bytes::from("line2")); - lazy(|cx| payload.poll_stream(cx)).await.unwrap(); + sender.feed_data(Bytes::from("line1")); + sender.feed_data(Bytes::from("line2")); + lazy(|cx| payload.poll_stream(cx)).await.unwrap(); - assert_eq!( - Some(Bytes::from("line")), - payload.read_until(b"ne").unwrap() - ); - assert_eq!(payload.buf.len(), 6); + assert_eq!( + Some(Bytes::from("line")), + payload.read_until(b"ne").unwrap() + ); + assert_eq!(payload.buf.len(), 6); - assert_eq!( - Some(Bytes::from("1line2")), - payload.read_until(b"2").unwrap() - ); - assert_eq!(payload.buf.len(), 0); - }) + assert_eq!( + Some(Bytes::from("1line2")), + payload.read_until(b"2").unwrap() + ); + assert_eq!(payload.buf.len(), 0); } } diff --git a/actix-session/src/cookie.rs b/actix-session/src/cookie.rs index bb5fba97b..5d66d6537 100644 --- a/actix-session/src/cookie.rs +++ b/actix-session/src/cookie.rs @@ -364,125 +364,117 @@ mod tests { use actix_web::{test, web, App}; use bytes::Bytes; - #[test] - fn cookie_session() { - test::block_on(async { - let mut app = test::init_service( - App::new() - .wrap(CookieSession::signed(&[0; 32]).secure(false)) - .service(web::resource("/").to(|ses: Session| { - async move { - let _ = ses.set("counter", 100); - "test" - } - })), - ) - .await; + #[actix_rt::test] + async fn cookie_session() { + let mut app = test::init_service( + App::new() + .wrap(CookieSession::signed(&[0; 32]).secure(false)) + .service(web::resource("/").to(|ses: Session| { + async move { + let _ = ses.set("counter", 100); + "test" + } + })), + ) + .await; - let request = test::TestRequest::get().to_request(); - let response = app.call(request).await.unwrap(); - assert!(response - .response() - .cookies() - .find(|c| c.name() == "actix-session") - .is_some()); - }) + let request = test::TestRequest::get().to_request(); + let response = app.call(request).await.unwrap(); + assert!(response + .response() + .cookies() + .find(|c| c.name() == "actix-session") + .is_some()); } - #[test] - fn private_cookie() { - test::block_on(async { - let mut app = test::init_service( - App::new() - .wrap(CookieSession::private(&[0; 32]).secure(false)) - .service(web::resource("/").to(|ses: Session| { - async move { - let _ = ses.set("counter", 100); - "test" - } - })), - ) - .await; + #[actix_rt::test] + async fn private_cookie() { + let mut app = test::init_service( + App::new() + .wrap(CookieSession::private(&[0; 32]).secure(false)) + .service(web::resource("/").to(|ses: Session| { + async move { + let _ = ses.set("counter", 100); + "test" + } + })), + ) + .await; - let request = test::TestRequest::get().to_request(); - let response = app.call(request).await.unwrap(); - assert!(response - .response() - .cookies() - .find(|c| c.name() == "actix-session") - .is_some()); - }) + let request = test::TestRequest::get().to_request(); + let response = app.call(request).await.unwrap(); + assert!(response + .response() + .cookies() + .find(|c| c.name() == "actix-session") + .is_some()); } - #[test] - fn cookie_session_extractor() { - test::block_on(async { - let mut app = test::init_service( - App::new() - .wrap(CookieSession::signed(&[0; 32]).secure(false)) - .service(web::resource("/").to(|ses: Session| { - async move { - let _ = ses.set("counter", 100); - "test" - } - })), - ) - .await; + #[actix_rt::test] + async fn cookie_session_extractor() { + let mut app = test::init_service( + App::new() + .wrap(CookieSession::signed(&[0; 32]).secure(false)) + .service(web::resource("/").to(|ses: Session| { + async move { + let _ = ses.set("counter", 100); + "test" + } + })), + ) + .await; - let request = test::TestRequest::get().to_request(); - let response = app.call(request).await.unwrap(); - assert!(response - .response() - .cookies() - .find(|c| c.name() == "actix-session") - .is_some()); - }) + let request = test::TestRequest::get().to_request(); + let response = app.call(request).await.unwrap(); + assert!(response + .response() + .cookies() + .find(|c| c.name() == "actix-session") + .is_some()); } - #[test] - fn basics() { - test::block_on(async { - let mut app = test::init_service( - App::new() - .wrap( - CookieSession::signed(&[0; 32]) - .path("/test/") - .name("actix-test") - .domain("localhost") - .http_only(true) - .same_site(SameSite::Lax) - .max_age(100), - ) - .service(web::resource("/").to(|ses: Session| { - async move { - let _ = ses.set("counter", 100); - "test" - } - })) - .service(web::resource("/test/").to(|ses: Session| { - async move { - let val: usize = ses.get("counter").unwrap().unwrap(); - format!("counter: {}", val) - } - })), - ) - .await; + #[actix_rt::test] + async fn basics() { + let mut app = test::init_service( + App::new() + .wrap( + CookieSession::signed(&[0; 32]) + .path("/test/") + .name("actix-test") + .domain("localhost") + .http_only(true) + .same_site(SameSite::Lax) + .max_age(100), + ) + .service(web::resource("/").to(|ses: Session| { + async move { + let _ = ses.set("counter", 100); + "test" + } + })) + .service(web::resource("/test/").to(|ses: Session| { + async move { + let val: usize = ses.get("counter").unwrap().unwrap(); + format!("counter: {}", val) + } + })), + ) + .await; - let request = test::TestRequest::get().to_request(); - let response = app.call(request).await.unwrap(); - let cookie = response - .response() - .cookies() - .find(|c| c.name() == "actix-test") - .unwrap() - .clone(); - assert_eq!(cookie.path().unwrap(), "/test/"); + let request = test::TestRequest::get().to_request(); + let response = app.call(request).await.unwrap(); + let cookie = response + .response() + .cookies() + .find(|c| c.name() == "actix-test") + .unwrap() + .clone(); + assert_eq!(cookie.path().unwrap(), "/test/"); - let request = test::TestRequest::with_uri("/test/") - .cookie(cookie) - .to_request(); - let body = test::read_response(&mut app, request).await; - assert_eq!(body, Bytes::from_static(b"counter: 100")); - }) + let request = test::TestRequest::with_uri("/test/") + .cookie(cookie) + .to_request(); + let body = test::read_response(&mut app, request).await; + assert_eq!(body, Bytes::from_static(b"counter: 100")); } } diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 0aa81e476..95883363a 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -17,6 +17,7 @@ syn = { version = "^1", features = ["full", "parsing"] } proc-macro2 = "^1" [dev-dependencies] +actix-rt = { version = "1.0.0-alpha.1" } actix-web = { version = "2.0.0-alpha.1" } actix-http = { version = "0.3.0-alpha.1", features=["openssl"] } actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } diff --git a/actix-web-codegen/tests/test_macro.rs b/actix-web-codegen/tests/test_macro.rs index 18c01f374..b6ac6dd18 100644 --- a/actix-web-codegen/tests/test_macro.rs +++ b/actix-web-codegen/tests/test_macro.rs @@ -1,5 +1,5 @@ use actix_http::HttpService; -use actix_http_test::{block_on, TestServer}; +use actix_http_test::TestServer; use actix_web::{http, web::Path, App, HttpResponse, Responder}; use actix_web_codegen::{connect, delete, get, head, options, patch, post, put, trace}; use futures::{future, Future}; @@ -69,95 +69,89 @@ async fn get_param_test(_: Path) -> impl Responder { HttpResponse::Ok() } -#[test] -fn test_params() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::new( - App::new() - .service(get_param_test) - .service(put_param_test) - .service(delete_param_test), - ) - }); +#[actix_rt::test] +async fn test_params() { + let srv = TestServer::start(|| { + HttpService::new( + App::new() + .service(get_param_test) + .service(put_param_test) + .service(delete_param_test), + ) + }); - let request = srv.request(http::Method::GET, srv.url("/test/it")); - let response = request.send().await.unwrap(); - assert_eq!(response.status(), http::StatusCode::OK); + let request = srv.request(http::Method::GET, srv.url("/test/it")); + let response = request.send().await.unwrap(); + assert_eq!(response.status(), http::StatusCode::OK); - let request = srv.request(http::Method::PUT, srv.url("/test/it")); - let response = request.send().await.unwrap(); - assert_eq!(response.status(), http::StatusCode::CREATED); + let request = srv.request(http::Method::PUT, srv.url("/test/it")); + let response = request.send().await.unwrap(); + assert_eq!(response.status(), http::StatusCode::CREATED); - let request = srv.request(http::Method::DELETE, srv.url("/test/it")); - let response = request.send().await.unwrap(); - assert_eq!(response.status(), http::StatusCode::NO_CONTENT); - }) + let request = srv.request(http::Method::DELETE, srv.url("/test/it")); + let response = request.send().await.unwrap(); + assert_eq!(response.status(), http::StatusCode::NO_CONTENT); } -#[test] -fn test_body() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::new( - App::new() - .service(post_test) - .service(put_test) - .service(head_test) - .service(connect_test) - .service(options_test) - .service(trace_test) - .service(patch_test) - .service(test), - ) - }); - let request = srv.request(http::Method::GET, srv.url("/test")); - let response = request.send().await.unwrap(); - assert!(response.status().is_success()); +#[actix_rt::test] +async fn test_body() { + let srv = TestServer::start(|| { + HttpService::new( + App::new() + .service(post_test) + .service(put_test) + .service(head_test) + .service(connect_test) + .service(options_test) + .service(trace_test) + .service(patch_test) + .service(test), + ) + }); + let request = srv.request(http::Method::GET, srv.url("/test")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); - let request = srv.request(http::Method::HEAD, srv.url("/test")); - let response = request.send().await.unwrap(); - assert!(response.status().is_success()); + let request = srv.request(http::Method::HEAD, srv.url("/test")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); - let request = srv.request(http::Method::CONNECT, srv.url("/test")); - let response = request.send().await.unwrap(); - assert!(response.status().is_success()); + let request = srv.request(http::Method::CONNECT, srv.url("/test")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); - let request = srv.request(http::Method::OPTIONS, srv.url("/test")); - let response = request.send().await.unwrap(); - assert!(response.status().is_success()); + let request = srv.request(http::Method::OPTIONS, srv.url("/test")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); - let request = srv.request(http::Method::TRACE, srv.url("/test")); - let response = request.send().await.unwrap(); - assert!(response.status().is_success()); + let request = srv.request(http::Method::TRACE, srv.url("/test")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); - let request = srv.request(http::Method::PATCH, srv.url("/test")); - let response = request.send().await.unwrap(); - assert!(response.status().is_success()); + let request = srv.request(http::Method::PATCH, srv.url("/test")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); - let request = srv.request(http::Method::PUT, srv.url("/test")); - let response = request.send().await.unwrap(); - assert!(response.status().is_success()); - assert_eq!(response.status(), http::StatusCode::CREATED); + let request = srv.request(http::Method::PUT, srv.url("/test")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); + assert_eq!(response.status(), http::StatusCode::CREATED); - let request = srv.request(http::Method::POST, srv.url("/test")); - let response = request.send().await.unwrap(); - assert!(response.status().is_success()); - assert_eq!(response.status(), http::StatusCode::NO_CONTENT); + let request = srv.request(http::Method::POST, srv.url("/test")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); + assert_eq!(response.status(), http::StatusCode::NO_CONTENT); - let request = srv.request(http::Method::GET, srv.url("/test")); - let response = request.send().await.unwrap(); - assert!(response.status().is_success()); - }) + let request = srv.request(http::Method::GET, srv.url("/test")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); } -#[test] -fn test_auto_async() { - block_on(async { - let srv = TestServer::start(|| HttpService::new(App::new().service(auto_async))); +#[actix_rt::test] +async fn test_auto_async() { + let srv = TestServer::start(|| HttpService::new(App::new().service(auto_async))); - let request = srv.request(http::Method::GET, srv.url("/test")); - let response = request.send().await.unwrap(); - assert!(response.status().is_success()); - }) + let request = srv.request(http::Method::GET, srv.url("/test")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); } diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 0f338b404..1b35c279b 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -30,7 +30,7 @@ default = ["brotli", "flate2-zlib"] openssl = ["open-ssl", "actix-http/openssl"] # rustls -# rustls = ["rust-tls", "actix-http/rustls"] +rustls = ["rust-tls", "actix-http/rustls"] # brotli encoding, requires c compiler brotli = ["actix-http/brotli"] @@ -45,6 +45,7 @@ flate2-rust = ["actix-http/flate2-rust"] actix-codec = "0.2.0-alpha.1" actix-service = "1.0.0-alpha.1" actix-http = "0.3.0-alpha.1" +actix-rt = "1.0.0-alpha.1" base64 = "0.10.1" bytes = "0.4" @@ -57,12 +58,10 @@ rand = "0.7" serde = "1.0" serde_json = "1.0" serde_urlencoded = "0.6.1" -tokio-timer = "0.3.0-alpha.6" open-ssl = { version="0.10", package="openssl", optional = true } rust-tls = { version = "0.16.0", package="rustls", optional = true, features = ["dangerous_configuration"] } [dev-dependencies] -actix-rt = "1.0.0-alpha.1" actix-connect = { version = "1.0.0-alpha.1", features=["openssl"] } actix-web = { version = "2.0.0-alpha.1", features=["openssl"] } actix-http = { version = "0.3.0-alpha.1", features=["openssl"] } @@ -73,5 +72,4 @@ brotli2 = { version="0.3.2" } flate2 = { version="1.0.2" } env_logger = "0.6" rand = "0.7" -tokio-tcp = "0.1" webpki = { version = "0.21" } diff --git a/awc/src/lib.rs b/awc/src/lib.rs index d6cea6ded..e995519ea 100644 --- a/awc/src/lib.rs +++ b/awc/src/lib.rs @@ -6,19 +6,16 @@ //! use actix_rt::System; //! use awc::Client; //! -//! fn main() { -//! System::new("test").block_on(async { -//! let mut client = Client::default(); +//! #[actix_rt::main] +//! async fn main() { +//! let mut client = Client::default(); //! -//! client.get("http://www.rust-lang.org") // <- Create request builder -//! .header("User-Agent", "Actix-web") -//! .send() // <- Send http request -//! .await -//! .and_then(|response| { // <- server http response -//! println!("Response: {:?}", response); -//! Ok(()) -//! }) -//! }); +//! let response = client.get("http://www.rust-lang.org") // <- Create request builder +//! .header("User-Agent", "Actix-web") +//! .send() // <- Send http request +//! .await; +//! +//! println!("Response: {:?}", response); //! } //! ``` use std::cell::RefCell; diff --git a/awc/src/request.rs b/awc/src/request.rs index c6b09e95c..3660f8086 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -39,19 +39,18 @@ const HTTPS_ENCODING: &str = "gzip, deflate"; /// ```rust /// use actix_rt::System; /// -/// fn main() { -/// System::new("test").block_on(async { -/// let response = awc::Client::new() -/// .get("http://www.rust-lang.org") // <- Create request builder -/// .header("User-Agent", "Actix-web") -/// .send() // <- Send http request -/// .await; +/// #[actix_rt::main] +/// async fn main() { +/// let response = awc::Client::new() +/// .get("http://www.rust-lang.org") // <- Create request builder +/// .header("User-Agent", "Actix-web") +/// .send() // <- Send http request +/// .await; /// -/// response.and_then(|response| { // <- server http response -/// println!("Response: {:?}", response); -/// Ok(()) -/// }) -/// }); +/// response.and_then(|response| { // <- server http response +/// println!("Response: {:?}", response); +/// Ok(()) +/// }); /// } /// ``` pub struct ClientRequest { @@ -308,25 +307,21 @@ impl ClientRequest { /// Set a cookie /// /// ```rust - /// # use actix_rt::System; - /// fn main() { - /// System::new("test").block_on(async { - /// awc::Client::new().get("https://www.rust-lang.org") - /// .cookie( - /// awc::http::Cookie::build("name", "value") - /// .domain("www.rust-lang.org") - /// .path("/") - /// .secure(true) - /// .http_only(true) - /// .finish(), - /// ) - /// .send() - /// .await - /// .and_then(|response| { - /// println!("Response: {:?}", response); - /// Ok(()) - /// }) - /// }); + /// #[actix_rt::main] + /// async fn main() { + /// let resp = awc::Client::new().get("https://www.rust-lang.org") + /// .cookie( + /// awc::http::Cookie::build("name", "value") + /// .domain("www.rust-lang.org") + /// .path("/") + /// .secure(true) + /// .http_only(true) + /// .finish(), + /// ) + /// .send() + /// .await; + /// + /// println!("Response: {:?}", resp); /// } /// ``` pub fn cookie(mut self, cookie: Cookie<'_>) -> Self { diff --git a/awc/src/response.rs b/awc/src/response.rs index 5ef8e18b5..00ab4cee1 100644 --- a/awc/src/response.rs +++ b/awc/src/response.rs @@ -358,41 +358,37 @@ where #[cfg(test)] mod tests { use super::*; - use actix_http_test::block_on; use serde::{Deserialize, Serialize}; use crate::{http::header, test::TestResponse}; - #[test] - fn test_body() { - block_on(async { - let mut req = - TestResponse::with_header(header::CONTENT_LENGTH, "xxxx").finish(); - match req.body().await.err().unwrap() { - PayloadError::UnknownLength => (), - _ => unreachable!("error"), - } + #[actix_rt::test] + async fn test_body() { + let mut req = TestResponse::with_header(header::CONTENT_LENGTH, "xxxx").finish(); + match req.body().await.err().unwrap() { + PayloadError::UnknownLength => (), + _ => unreachable!("error"), + } - let mut req = - TestResponse::with_header(header::CONTENT_LENGTH, "1000000").finish(); - match req.body().await.err().unwrap() { - PayloadError::Overflow => (), - _ => unreachable!("error"), - } + let mut req = + TestResponse::with_header(header::CONTENT_LENGTH, "1000000").finish(); + match req.body().await.err().unwrap() { + PayloadError::Overflow => (), + _ => unreachable!("error"), + } - let mut req = TestResponse::default() - .set_payload(Bytes::from_static(b"test")) - .finish(); - assert_eq!(req.body().await.ok().unwrap(), Bytes::from_static(b"test")); + let mut req = TestResponse::default() + .set_payload(Bytes::from_static(b"test")) + .finish(); + assert_eq!(req.body().await.ok().unwrap(), Bytes::from_static(b"test")); - let mut req = TestResponse::default() - .set_payload(Bytes::from_static(b"11111111111111")) - .finish(); - match req.body().limit(5).await.err().unwrap() { - PayloadError::Overflow => (), - _ => unreachable!("error"), - } - }) + let mut req = TestResponse::default() + .set_payload(Bytes::from_static(b"11111111111111")) + .finish(); + match req.body().limit(5).await.err().unwrap() { + PayloadError::Overflow => (), + _ => unreachable!("error"), + } } #[derive(Serialize, Deserialize, PartialEq, Debug)] @@ -414,58 +410,56 @@ mod tests { } } - #[test] - fn test_json_body() { - block_on(async { - let mut req = TestResponse::default().finish(); - let json = JsonBody::<_, MyObject>::new(&mut req).await; - assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); + #[actix_rt::test] + async fn test_json_body() { + let mut req = TestResponse::default().finish(); + let json = JsonBody::<_, MyObject>::new(&mut req).await; + assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); - let mut req = TestResponse::default() - .header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/text"), - ) - .finish(); - let json = JsonBody::<_, MyObject>::new(&mut req).await; - assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); + let mut req = TestResponse::default() + .header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/text"), + ) + .finish(); + let json = JsonBody::<_, MyObject>::new(&mut req).await; + assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); - let mut req = TestResponse::default() - .header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/json"), - ) - .header( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("10000"), - ) - .finish(); + let mut req = TestResponse::default() + .header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/json"), + ) + .header( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("10000"), + ) + .finish(); - let json = JsonBody::<_, MyObject>::new(&mut req).limit(100).await; - assert!(json_eq( - json.err().unwrap(), - JsonPayloadError::Payload(PayloadError::Overflow) - )); + let json = JsonBody::<_, MyObject>::new(&mut req).limit(100).await; + assert!(json_eq( + json.err().unwrap(), + JsonPayloadError::Payload(PayloadError::Overflow) + )); - let mut req = TestResponse::default() - .header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/json"), - ) - .header( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("16"), - ) - .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .finish(); + let mut req = TestResponse::default() + .header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/json"), + ) + .header( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("16"), + ) + .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) + .finish(); - let json = JsonBody::<_, MyObject>::new(&mut req).await; - assert_eq!( - json.ok().unwrap(), - MyObject { - name: "test".to_owned() - } - ); - }) + let json = JsonBody::<_, MyObject>::new(&mut req).await; + assert_eq!( + json.ok().unwrap(), + MyObject { + name: "test".to_owned() + } + ); } } diff --git a/awc/src/sender.rs b/awc/src/sender.rs index f7461113a..9cf158c0d 100644 --- a/awc/src/sender.rs +++ b/awc/src/sender.rs @@ -4,12 +4,12 @@ use std::rc::Rc; use std::task::{Context, Poll}; use std::time::Duration; +use actix_rt::time::{delay_for, Delay}; use bytes::Bytes; use derive_more::From; use futures::{future::LocalBoxFuture, ready, Future, Stream}; use serde::Serialize; use serde_json; -use tokio_timer::{delay_for, Delay}; use actix_http::body::{Body, BodyStream}; use actix_http::encoding::Decoder; diff --git a/awc/src/ws.rs b/awc/src/ws.rs index 8819b4990..075c83562 100644 --- a/awc/src/ws.rs +++ b/awc/src/ws.rs @@ -7,8 +7,8 @@ use std::{fmt, str}; use actix_codec::Framed; use actix_http::cookie::{Cookie, CookieJar}; use actix_http::{ws, Payload, RequestHead}; +use actix_rt::time::Timeout; use percent_encoding::percent_encode; -use tokio_timer::Timeout; use actix_http::cookie::USERINFO; pub use actix_http::ws::{CloseCode, CloseReason, Codec, Frame, Message}; @@ -389,21 +389,19 @@ impl fmt::Debug for WebsocketsRequest { #[cfg(test)] mod tests { - use actix_web::test::block_on; - use super::*; use crate::Client; - #[test] - fn test_debug() { + #[actix_rt::test] + async fn test_debug() { let request = Client::new().ws("/").header("x-test", "111"); let repr = format!("{:?}", request); assert!(repr.contains("WebsocketsRequest")); assert!(repr.contains("x-test")); } - #[test] - fn test_header_override() { + #[actix_rt::test] + async fn test_header_override() { let req = Client::build() .header(header::CONTENT_TYPE, "111") .finish() @@ -421,8 +419,8 @@ mod tests { ); } - #[test] - fn basic_auth() { + #[actix_rt::test] + async fn basic_auth() { let req = Client::new() .ws("/") .basic_auth("username", Some("password")); @@ -448,8 +446,8 @@ mod tests { ); } - #[test] - fn bearer_auth() { + #[actix_rt::test] + async fn bearer_auth() { let req = Client::new().ws("/").bearer_auth("someS3cr3tAutht0k3n"); assert_eq!( req.head @@ -463,35 +461,33 @@ mod tests { let _ = req.connect(); } - #[test] - fn basics() { - block_on(async { - let req = Client::new() - .ws("http://localhost/") - .origin("test-origin") - .max_frame_size(100) - .server_mode() - .protocols(&["v1", "v2"]) - .set_header_if_none(header::CONTENT_TYPE, "json") - .set_header_if_none(header::CONTENT_TYPE, "text") - .cookie(Cookie::build("cookie1", "value1").finish()); - assert_eq!( - req.origin.as_ref().unwrap().to_str().unwrap(), - "test-origin" - ); - assert_eq!(req.max_size, 100); - assert_eq!(req.server_mode, true); - assert_eq!(req.protocols, Some("v1,v2".to_string())); - assert_eq!( - req.head.headers.get(header::CONTENT_TYPE).unwrap(), - header::HeaderValue::from_static("json") - ); + #[actix_rt::test] + async fn basics() { + let req = Client::new() + .ws("http://localhost/") + .origin("test-origin") + .max_frame_size(100) + .server_mode() + .protocols(&["v1", "v2"]) + .set_header_if_none(header::CONTENT_TYPE, "json") + .set_header_if_none(header::CONTENT_TYPE, "text") + .cookie(Cookie::build("cookie1", "value1").finish()); + assert_eq!( + req.origin.as_ref().unwrap().to_str().unwrap(), + "test-origin" + ); + assert_eq!(req.max_size, 100); + assert_eq!(req.server_mode, true); + assert_eq!(req.protocols, Some("v1,v2".to_string())); + assert_eq!( + req.head.headers.get(header::CONTENT_TYPE).unwrap(), + header::HeaderValue::from_static("json") + ); - let _ = req.connect().await; + let _ = req.connect().await; - assert!(Client::new().ws("/").connect().await.is_err()); - assert!(Client::new().ws("http:///test").connect().await.is_err()); - assert!(Client::new().ws("hmm://test.com/").connect().await.is_err()); - }) + assert!(Client::new().ws("/").connect().await.is_err()); + assert!(Client::new().ws("http:///test").connect().await.is_err()); + assert!(Client::new().ws("hmm://test.com/").connect().await.is_err()); } } diff --git a/awc/tests/test_client.rs b/awc/tests/test_client.rs index 9e1948f79..15e9a07ac 100644 --- a/awc/tests/test_client.rs +++ b/awc/tests/test_client.rs @@ -13,7 +13,7 @@ use futures::future::ok; use rand::Rng; use actix_http::HttpService; -use actix_http_test::{block_on, TestServer}; +use actix_http_test::TestServer; use actix_service::pipeline_factory; use actix_web::http::Cookie; use actix_web::middleware::{BodyEncoding, Compress}; @@ -42,514 +42,472 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World"; -#[test] -fn test_simple() { - block_on(async { - let srv = TestServer::start(|| { +#[actix_rt::test] +async fn test_simple() { + let srv = + TestServer::start(|| { HttpService::new(App::new().service( web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))), )) }); - let request = srv.get("/").header("x-test", "111").send(); - let mut response = request.await.unwrap(); - assert!(response.status().is_success()); + let request = srv.get("/").header("x-test", "111").send(); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = response.body().await.unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - let mut response = srv.post("/").send().await.unwrap(); - assert!(response.status().is_success()); + let mut response = srv.post("/").send().await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = response.body().await.unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - // camel case - let response = srv.post("/").camel_case().send().await.unwrap(); - assert!(response.status().is_success()); - }) + // camel case + let response = srv.post("/").camel_case().send().await.unwrap(); + assert!(response.status().is_success()); } -#[test] -fn test_json() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::new( - App::new().service( - web::resource("/") - .route(web::to(|_: web::Json| HttpResponse::Ok())), - ), - ) - }); +#[actix_rt::test] +async fn test_json() { + let srv = TestServer::start(|| { + HttpService::new(App::new().service( + web::resource("/").route(web::to(|_: web::Json| HttpResponse::Ok())), + )) + }); - let request = srv - .get("/") - .header("x-test", "111") - .send_json(&"TEST".to_string()); - let response = request.await.unwrap(); - assert!(response.status().is_success()); - }) + let request = srv + .get("/") + .header("x-test", "111") + .send_json(&"TEST".to_string()); + let response = request.await.unwrap(); + assert!(response.status().is_success()); } -#[test] -fn test_form() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::new(App::new().service(web::resource("/").route(web::to( - |_: web::Form>| HttpResponse::Ok(), - )))) - }); +#[actix_rt::test] +async fn test_form() { + let srv = TestServer::start(|| { + HttpService::new(App::new().service(web::resource("/").route(web::to( + |_: web::Form>| HttpResponse::Ok(), + )))) + }); - let mut data = HashMap::new(); - let _ = data.insert("key".to_string(), "TEST".to_string()); + let mut data = HashMap::new(); + let _ = data.insert("key".to_string(), "TEST".to_string()); - let request = srv.get("/").header("x-test", "111").send_form(&data); - let response = request.await.unwrap(); - assert!(response.status().is_success()); - }) + let request = srv.get("/").header("x-test", "111").send_form(&data); + let response = request.await.unwrap(); + assert!(response.status().is_success()); } -#[test] -fn test_timeout() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::new(App::new().service(web::resource("/").route(web::to( - || { - async { - tokio_timer::delay_for(Duration::from_millis(200)).await; - Ok::<_, Error>(HttpResponse::Ok().body(STR)) - } - }, - )))) - }); +#[actix_rt::test] +async fn test_timeout() { + let srv = TestServer::start(|| { + HttpService::new(App::new().service(web::resource("/").route(web::to(|| { + async { + actix_rt::time::delay_for(Duration::from_millis(200)).await; + Ok::<_, Error>(HttpResponse::Ok().body(STR)) + } + })))) + }); - let connector = awc::Connector::new() - .connector(actix_connect::new_connector( - actix_connect::start_default_resolver(), - )) - .timeout(Duration::from_secs(15)) - .finish(); + let connector = awc::Connector::new() + .connector(actix_connect::new_connector( + actix_connect::start_default_resolver(), + )) + .timeout(Duration::from_secs(15)) + .finish(); - let client = awc::Client::build() - .connector(connector) - .timeout(Duration::from_millis(50)) - .finish(); + let client = awc::Client::build() + .connector(connector) + .timeout(Duration::from_millis(50)) + .finish(); - let request = client.get(srv.url("/")).send(); - match request.await { - Err(SendRequestError::Timeout) => (), - _ => panic!(), - } - }) + let request = client.get(srv.url("/")).send(); + match request.await { + Err(SendRequestError::Timeout) => (), + _ => panic!(), + } } -#[test] -fn test_timeout_override() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::new(App::new().service(web::resource("/").route(web::to( - || { - async { - tokio_timer::delay_for(Duration::from_millis(200)).await; - Ok::<_, Error>(HttpResponse::Ok().body(STR)) - } - }, - )))) - }); +#[actix_rt::test] +async fn test_timeout_override() { + let srv = TestServer::start(|| { + HttpService::new(App::new().service(web::resource("/").route(web::to(|| { + async { + actix_rt::time::delay_for(Duration::from_millis(200)).await; + Ok::<_, Error>(HttpResponse::Ok().body(STR)) + } + })))) + }); - let client = awc::Client::build() - .timeout(Duration::from_millis(50000)) - .finish(); - let request = client - .get(srv.url("/")) - .timeout(Duration::from_millis(50)) - .send(); - match request.await { - Err(SendRequestError::Timeout) => (), - _ => panic!(), - } - }) + let client = awc::Client::build() + .timeout(Duration::from_millis(50000)) + .finish(); + let request = client + .get(srv.url("/")) + .timeout(Duration::from_millis(50)) + .send(); + match request.await { + Err(SendRequestError::Timeout) => (), + _ => panic!(), + } } -#[test] -fn test_connection_reuse() { - block_on(async { - let num = Arc::new(AtomicUsize::new(0)); - let num2 = num.clone(); +#[actix_rt::test] +async fn test_connection_reuse() { + let num = Arc::new(AtomicUsize::new(0)); + let num2 = num.clone(); - let srv = TestServer::start(move || { - let num2 = num2.clone(); - pipeline_factory(move |io| { - num2.fetch_add(1, Ordering::Relaxed); - ok(io) - }) - .and_then(HttpService::new(App::new().service( - web::resource("/").route(web::to(|| HttpResponse::Ok())), - ))) - }); + let srv = TestServer::start(move || { + let num2 = num2.clone(); + pipeline_factory(move |io| { + num2.fetch_add(1, Ordering::Relaxed); + ok(io) + }) + .and_then(HttpService::new( + App::new().service(web::resource("/").route(web::to(|| HttpResponse::Ok()))), + )) + }); - let client = awc::Client::default(); + let client = awc::Client::default(); - // req 1 - let request = client.get(srv.url("/")).send(); - let response = request.await.unwrap(); - assert!(response.status().is_success()); + // req 1 + let request = client.get(srv.url("/")).send(); + let response = request.await.unwrap(); + assert!(response.status().is_success()); - // req 2 - let req = client.post(srv.url("/")); - let response = req.send().await.unwrap(); - assert!(response.status().is_success()); + // req 2 + let req = client.post(srv.url("/")); + let response = req.send().await.unwrap(); + assert!(response.status().is_success()); - // one connection - assert_eq!(num.load(Ordering::Relaxed), 1); - }) + // one connection + assert_eq!(num.load(Ordering::Relaxed), 1); } -#[test] -fn test_connection_force_close() { - block_on(async { - let num = Arc::new(AtomicUsize::new(0)); - let num2 = num.clone(); +#[actix_rt::test] +async fn test_connection_force_close() { + let num = Arc::new(AtomicUsize::new(0)); + let num2 = num.clone(); - let srv = TestServer::start(move || { - let num2 = num2.clone(); - pipeline_factory(move |io| { - num2.fetch_add(1, Ordering::Relaxed); - ok(io) - }) - .and_then(HttpService::new(App::new().service( - web::resource("/").route(web::to(|| HttpResponse::Ok())), - ))) - }); + let srv = TestServer::start(move || { + let num2 = num2.clone(); + pipeline_factory(move |io| { + num2.fetch_add(1, Ordering::Relaxed); + ok(io) + }) + .and_then(HttpService::new( + App::new().service(web::resource("/").route(web::to(|| HttpResponse::Ok()))), + )) + }); - let client = awc::Client::default(); + let client = awc::Client::default(); - // req 1 - let request = client.get(srv.url("/")).force_close().send(); - let response = request.await.unwrap(); - assert!(response.status().is_success()); + // req 1 + let request = client.get(srv.url("/")).force_close().send(); + let response = request.await.unwrap(); + assert!(response.status().is_success()); - // req 2 - let req = client.post(srv.url("/")).force_close(); - let response = req.send().await.unwrap(); - assert!(response.status().is_success()); + // req 2 + let req = client.post(srv.url("/")).force_close(); + let response = req.send().await.unwrap(); + assert!(response.status().is_success()); - // two connection - assert_eq!(num.load(Ordering::Relaxed), 2); - }) + // two connection + assert_eq!(num.load(Ordering::Relaxed), 2); } -#[test] -fn test_connection_server_close() { - block_on(async { - let num = Arc::new(AtomicUsize::new(0)); - let num2 = num.clone(); +#[actix_rt::test] +async fn test_connection_server_close() { + let num = Arc::new(AtomicUsize::new(0)); + let num2 = num.clone(); - let srv = TestServer::start(move || { - let num2 = num2.clone(); - pipeline_factory(move |io| { - num2.fetch_add(1, Ordering::Relaxed); - ok(io) - }) - .and_then(HttpService::new( - App::new().service( - web::resource("/") - .route(web::to(|| HttpResponse::Ok().force_close().finish())), - ), - )) - }); + let srv = TestServer::start(move || { + let num2 = num2.clone(); + pipeline_factory(move |io| { + num2.fetch_add(1, Ordering::Relaxed); + ok(io) + }) + .and_then(HttpService::new( + App::new().service( + web::resource("/") + .route(web::to(|| HttpResponse::Ok().force_close().finish())), + ), + )) + }); - let client = awc::Client::default(); + let client = awc::Client::default(); - // req 1 - let request = client.get(srv.url("/")).send(); - let response = request.await.unwrap(); - assert!(response.status().is_success()); + // req 1 + let request = client.get(srv.url("/")).send(); + let response = request.await.unwrap(); + assert!(response.status().is_success()); - // req 2 - let req = client.post(srv.url("/")); - let response = req.send().await.unwrap(); - assert!(response.status().is_success()); + // req 2 + let req = client.post(srv.url("/")); + let response = req.send().await.unwrap(); + assert!(response.status().is_success()); - // two connection - assert_eq!(num.load(Ordering::Relaxed), 2); - }) + // two connection + assert_eq!(num.load(Ordering::Relaxed), 2); } -#[test] -fn test_connection_wait_queue() { - block_on(async { - let num = Arc::new(AtomicUsize::new(0)); - let num2 = num.clone(); +#[actix_rt::test] +async fn test_connection_wait_queue() { + let num = Arc::new(AtomicUsize::new(0)); + let num2 = num.clone(); - let srv = TestServer::start(move || { - let num2 = num2.clone(); - pipeline_factory(move |io| { - num2.fetch_add(1, Ordering::Relaxed); - ok(io) - }) - .and_then(HttpService::new(App::new().service( - web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))), - ))) - }); + let srv = TestServer::start(move || { + let num2 = num2.clone(); + pipeline_factory(move |io| { + num2.fetch_add(1, Ordering::Relaxed); + ok(io) + }) + .and_then(HttpService::new(App::new().service( + web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))), + ))) + }); - let client = awc::Client::build() - .connector(awc::Connector::new().limit(1).finish()) - .finish(); + let client = awc::Client::build() + .connector(awc::Connector::new().limit(1).finish()) + .finish(); - // req 1 - let request = client.get(srv.url("/")).send(); - let mut response = request.await.unwrap(); - assert!(response.status().is_success()); + // req 1 + let request = client.get(srv.url("/")).send(); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - // req 2 - let req2 = client.post(srv.url("/")); - let req2_fut = req2.send(); + // req 2 + let req2 = client.post(srv.url("/")); + let req2_fut = req2.send(); - // read response 1 - let bytes = response.body().await.unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response 1 + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - // req 2 - let response = req2_fut.await.unwrap(); - assert!(response.status().is_success()); + // req 2 + let response = req2_fut.await.unwrap(); + assert!(response.status().is_success()); - // two connection - assert_eq!(num.load(Ordering::Relaxed), 1); - }) + // two connection + assert_eq!(num.load(Ordering::Relaxed), 1); } -#[test] -fn test_connection_wait_queue_force_close() { - block_on(async { - let num = Arc::new(AtomicUsize::new(0)); - let num2 = num.clone(); +#[actix_rt::test] +async fn test_connection_wait_queue_force_close() { + let num = Arc::new(AtomicUsize::new(0)); + let num2 = num.clone(); - let srv = TestServer::start(move || { - let num2 = num2.clone(); - pipeline_factory(move |io| { - num2.fetch_add(1, Ordering::Relaxed); - ok(io) - }) - .and_then(HttpService::new( - App::new().service( - web::resource("/") - .route(web::to(|| HttpResponse::Ok().force_close().body(STR))), - ), - )) - }); + let srv = TestServer::start(move || { + let num2 = num2.clone(); + pipeline_factory(move |io| { + num2.fetch_add(1, Ordering::Relaxed); + ok(io) + }) + .and_then(HttpService::new( + App::new().service( + web::resource("/") + .route(web::to(|| HttpResponse::Ok().force_close().body(STR))), + ), + )) + }); - let client = awc::Client::build() - .connector(awc::Connector::new().limit(1).finish()) - .finish(); + let client = awc::Client::build() + .connector(awc::Connector::new().limit(1).finish()) + .finish(); - // req 1 - let request = client.get(srv.url("/")).send(); - let mut response = request.await.unwrap(); - assert!(response.status().is_success()); + // req 1 + let request = client.get(srv.url("/")).send(); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - // req 2 - let req2 = client.post(srv.url("/")); - let req2_fut = req2.send(); + // req 2 + let req2 = client.post(srv.url("/")); + let req2_fut = req2.send(); - // read response 1 - let bytes = response.body().await.unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); + // read response 1 + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - // req 2 - let response = req2_fut.await.unwrap(); - assert!(response.status().is_success()); + // req 2 + let response = req2_fut.await.unwrap(); + assert!(response.status().is_success()); - // two connection - assert_eq!(num.load(Ordering::Relaxed), 2); - }) + // two connection + assert_eq!(num.load(Ordering::Relaxed), 2); } -#[test] -fn test_with_query_parameter() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::new(App::new().service(web::resource("/").to( - |req: HttpRequest| { - if req.query_string().contains("qp") { - HttpResponse::Ok() - } else { - HttpResponse::BadRequest() - } - }, - ))) - }); - - let res = awc::Client::new() - .get(srv.url("/?qp=5")) - .send() - .await - .unwrap(); - assert!(res.status().is_success()); - }) -} - -#[test] -fn test_no_decompress() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::new(App::new().wrap(Compress::default()).service( - web::resource("/").route(web::to(|| { - let mut res = HttpResponse::Ok().body(STR); - res.encoding(header::ContentEncoding::Gzip); - res - })), - )) - }); - - let mut res = awc::Client::new() - .get(srv.url("/")) - .no_decompress() - .send() - .await - .unwrap(); - assert!(res.status().is_success()); - - // read response - let bytes = res.body().await.unwrap(); - - let mut e = GzDecoder::new(&bytes[..]); - let mut dec = Vec::new(); - e.read_to_end(&mut dec).unwrap(); - assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); - - // POST - let mut res = awc::Client::new() - .post(srv.url("/")) - .no_decompress() - .send() - .await - .unwrap(); - assert!(res.status().is_success()); - - let bytes = res.body().await.unwrap(); - let mut e = GzDecoder::new(&bytes[..]); - let mut dec = Vec::new(); - e.read_to_end(&mut dec).unwrap(); - assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); - }) -} - -#[test] -fn test_client_gzip_encoding() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::new(App::new().service(web::resource("/").route(web::to( - || { - let mut e = GzEncoder::new(Vec::new(), Compression::default()); - e.write_all(STR.as_ref()).unwrap(); - let data = e.finish().unwrap(); - +#[actix_rt::test] +async fn test_with_query_parameter() { + let srv = TestServer::start(|| { + HttpService::new(App::new().service(web::resource("/").to( + |req: HttpRequest| { + if req.query_string().contains("qp") { HttpResponse::Ok() - .header("content-encoding", "gzip") - .body(data) - }, - )))) - }); + } else { + HttpResponse::BadRequest() + } + }, + ))) + }); - // client request - let mut response = srv.post("/").send().await.unwrap(); - assert!(response.status().is_success()); - - // read response - let bytes = response.body().await.unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - }) + let res = awc::Client::new() + .get(srv.url("/?qp=5")) + .send() + .await + .unwrap(); + assert!(res.status().is_success()); } -#[test] -fn test_client_gzip_encoding_large() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::new(App::new().service(web::resource("/").route(web::to( - || { - let mut e = GzEncoder::new(Vec::new(), Compression::default()); - e.write_all(STR.repeat(10).as_ref()).unwrap(); - let data = e.finish().unwrap(); +#[actix_rt::test] +async fn test_no_decompress() { + let srv = TestServer::start(|| { + HttpService::new(App::new().wrap(Compress::default()).service( + web::resource("/").route(web::to(|| { + let mut res = HttpResponse::Ok().body(STR); + res.encoding(header::ContentEncoding::Gzip); + res + })), + )) + }); - HttpResponse::Ok() - .header("content-encoding", "gzip") - .body(data) - }, - )))) - }); + let mut res = awc::Client::new() + .get(srv.url("/")) + .no_decompress() + .send() + .await + .unwrap(); + assert!(res.status().is_success()); - // client request - let mut response = srv.post("/").send().await.unwrap(); - assert!(response.status().is_success()); + // read response + let bytes = res.body().await.unwrap(); - // read response - let bytes = response.body().await.unwrap(); - assert_eq!(bytes, Bytes::from(STR.repeat(10))); - }) + let mut e = GzDecoder::new(&bytes[..]); + let mut dec = Vec::new(); + e.read_to_end(&mut dec).unwrap(); + assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); + + // POST + let mut res = awc::Client::new() + .post(srv.url("/")) + .no_decompress() + .send() + .await + .unwrap(); + assert!(res.status().is_success()); + + let bytes = res.body().await.unwrap(); + let mut e = GzDecoder::new(&bytes[..]); + let mut dec = Vec::new(); + e.read_to_end(&mut dec).unwrap(); + assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); } -#[test] -fn test_client_gzip_encoding_large_random() { - block_on(async { - let data = rand::thread_rng() - .sample_iter(&rand::distributions::Alphanumeric) - .take(100_000) - .collect::(); +#[actix_rt::test] +async fn test_client_gzip_encoding() { + let srv = TestServer::start(|| { + HttpService::new(App::new().service(web::resource("/").route(web::to(|| { + let mut e = GzEncoder::new(Vec::new(), Compression::default()); + e.write_all(STR.as_ref()).unwrap(); + let data = e.finish().unwrap(); - let srv = TestServer::start(|| { - HttpService::new(App::new().service(web::resource("/").route(web::to( - |data: Bytes| { - let mut e = GzEncoder::new(Vec::new(), Compression::default()); - e.write_all(&data).unwrap(); - let data = e.finish().unwrap(); - HttpResponse::Ok() - .header("content-encoding", "gzip") - .body(data) - }, - )))) - }); + HttpResponse::Ok() + .header("content-encoding", "gzip") + .body(data) + })))) + }); - // client request - let mut response = srv.post("/").send_body(data.clone()).await.unwrap(); - assert!(response.status().is_success()); + // client request + let mut response = srv.post("/").send().await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = response.body().await.unwrap(); - assert_eq!(bytes, Bytes::from(data)); - }) + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); } -#[test] -fn test_client_brotli_encoding() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::new(App::new().service(web::resource("/").route(web::to( - |data: Bytes| { - let mut e = BrotliEncoder::new(Vec::new(), 5); - e.write_all(&data).unwrap(); - let data = e.finish().unwrap(); - HttpResponse::Ok() - .header("content-encoding", "br") - .body(data) - }, - )))) - }); +#[actix_rt::test] +async fn test_client_gzip_encoding_large() { + let srv = TestServer::start(|| { + HttpService::new(App::new().service(web::resource("/").route(web::to(|| { + let mut e = GzEncoder::new(Vec::new(), Compression::default()); + e.write_all(STR.repeat(10).as_ref()).unwrap(); + let data = e.finish().unwrap(); - // client request - let mut response = srv.post("/").send_body(STR).await.unwrap(); - assert!(response.status().is_success()); + HttpResponse::Ok() + .header("content-encoding", "gzip") + .body(data) + })))) + }); - // read response - let bytes = response.body().await.unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - }) + // client request + let mut response = srv.post("/").send().await.unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from(STR.repeat(10))); } -// #[test] -// fn test_client_brotli_encoding_large_random() { +#[actix_rt::test] +async fn test_client_gzip_encoding_large_random() { + let data = rand::thread_rng() + .sample_iter(&rand::distributions::Alphanumeric) + .take(100_000) + .collect::(); + + let srv = TestServer::start(|| { + HttpService::new(App::new().service(web::resource("/").route(web::to( + |data: Bytes| { + let mut e = GzEncoder::new(Vec::new(), Compression::default()); + e.write_all(&data).unwrap(); + let data = e.finish().unwrap(); + HttpResponse::Ok() + .header("content-encoding", "gzip") + .body(data) + }, + )))) + }); + + // client request + let mut response = srv.post("/").send_body(data.clone()).await.unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from(data)); +} + +#[actix_rt::test] +async fn test_client_brotli_encoding() { + let srv = TestServer::start(|| { + HttpService::new(App::new().service(web::resource("/").route(web::to( + |data: Bytes| { + let mut e = BrotliEncoder::new(Vec::new(), 5); + e.write_all(&data).unwrap(); + let data = e.finish().unwrap(); + HttpResponse::Ok() + .header("content-encoding", "br") + .body(data) + }, + )))) + }); + + // client request + let mut response = srv.post("/").send_body(STR).await.unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); +} + +// #[actix_rt::test] +// async fn test_client_brotli_encoding_large_random() { // let data = rand::thread_rng() // .sample_iter(&rand::distributions::Alphanumeric) // .take(70_000) @@ -583,8 +541,8 @@ fn test_client_brotli_encoding() { // } // #[cfg(feature = "brotli")] -// #[test] -// fn test_client_deflate_encoding() { +// #[actix_rt::test] +// async fn test_client_deflate_encoding() { // let srv = test::TestServer::start(|app| { // app.handler(|req: &HttpRequest| { // req.body() @@ -611,8 +569,8 @@ fn test_client_brotli_encoding() { // assert_eq!(bytes, Bytes::from_static(STR.as_ref())); // } -// #[test] -// fn test_client_deflate_encoding_large_random() { +// #[actix_rt::test] +// async fn test_client_deflate_encoding_large_random() { // let data = rand::thread_rng() // .sample_iter(&rand::distributions::Alphanumeric) // .take(70_000) @@ -644,8 +602,8 @@ fn test_client_brotli_encoding() { // assert_eq!(bytes, Bytes::from(data)); // } -// #[test] -// fn test_client_streaming_explicit() { +// #[actix_rt::test] +// async fn test_client_streaming_explicit() { // let srv = test::TestServer::start(|app| { // app.handler(|req: &HttpRequest| { // req.body() @@ -671,8 +629,8 @@ fn test_client_brotli_encoding() { // assert_eq!(bytes, Bytes::from_static(STR.as_ref())); // } -// #[test] -// fn test_body_streaming_implicit() { +// #[actix_rt::test] +// async fn test_body_streaming_implicit() { // let srv = test::TestServer::start(|app| { // app.handler(|_| { // let body = once(Ok(Bytes::from_static(STR.as_ref()))); @@ -691,83 +649,76 @@ fn test_client_brotli_encoding() { // assert_eq!(bytes, Bytes::from_static(STR.as_ref())); // } -#[test] -fn test_client_cookie_handling() { +#[actix_rt::test] +async fn test_client_cookie_handling() { use std::io::{Error as IoError, ErrorKind}; - block_on(async { - let cookie1 = Cookie::build("cookie1", "value1").finish(); - let cookie2 = Cookie::build("cookie2", "value2") - .domain("www.example.org") - .path("/") - .secure(true) - .http_only(true) - .finish(); - // Q: are all these clones really necessary? A: Yes, possibly - let cookie1b = cookie1.clone(); - let cookie2b = cookie2.clone(); + let cookie1 = Cookie::build("cookie1", "value1").finish(); + let cookie2 = Cookie::build("cookie2", "value2") + .domain("www.example.org") + .path("/") + .secure(true) + .http_only(true) + .finish(); + // Q: are all these clones really necessary? A: Yes, possibly + let cookie1b = cookie1.clone(); + let cookie2b = cookie2.clone(); - let srv = TestServer::start(move || { - let cookie1 = cookie1b.clone(); - let cookie2 = cookie2b.clone(); + let srv = TestServer::start(move || { + let cookie1 = cookie1b.clone(); + let cookie2 = cookie2b.clone(); - HttpService::new(App::new().route( - "/", - web::to(move |req: HttpRequest| { - let cookie1 = cookie1.clone(); - let cookie2 = cookie2.clone(); + HttpService::new(App::new().route( + "/", + web::to(move |req: HttpRequest| { + let cookie1 = cookie1.clone(); + let cookie2 = cookie2.clone(); - async move { - // Check cookies were sent correctly - let res: Result<(), Error> = req - .cookie("cookie1") - .ok_or(()) - .and_then(|c1| { - if c1.value() == "value1" { - Ok(()) - } else { - Err(()) - } - }) - .and_then(|()| req.cookie("cookie2").ok_or(())) - .and_then(|c2| { - if c2.value() == "value2" { - Ok(()) - } else { - Err(()) - } - }) - .map_err(|_| { - Error::from(IoError::from(ErrorKind::NotFound)) - }); + async move { + // Check cookies were sent correctly + let res: Result<(), Error> = req + .cookie("cookie1") + .ok_or(()) + .and_then(|c1| { + if c1.value() == "value1" { + Ok(()) + } else { + Err(()) + } + }) + .and_then(|()| req.cookie("cookie2").ok_or(())) + .and_then(|c2| { + if c2.value() == "value2" { + Ok(()) + } else { + Err(()) + } + }) + .map_err(|_| Error::from(IoError::from(ErrorKind::NotFound))); - if let Err(e) = res { - Err(e) - } else { - // Send some cookies back - Ok::<_, Error>( - HttpResponse::Ok() - .cookie(cookie1) - .cookie(cookie2) - .finish(), - ) - } + if let Err(e) = res { + Err(e) + } else { + // Send some cookies back + Ok::<_, Error>( + HttpResponse::Ok().cookie(cookie1).cookie(cookie2).finish(), + ) } - }), - )) - }); + } + }), + )) + }); - let request = srv.get("/").cookie(cookie1.clone()).cookie(cookie2.clone()); - let response = request.send().await.unwrap(); - assert!(response.status().is_success()); - let c1 = response.cookie("cookie1").expect("Missing cookie1"); - assert_eq!(c1, cookie1); - let c2 = response.cookie("cookie2").expect("Missing cookie2"); - assert_eq!(c2, cookie2); - }) + let request = srv.get("/").cookie(cookie1.clone()).cookie(cookie2.clone()); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); + let c1 = response.cookie("cookie1").expect("Missing cookie1"); + assert_eq!(c1, cookie1); + let c2 = response.cookie("cookie2").expect("Missing cookie2"); + assert_eq!(c2, cookie2); } -// #[test] +// #[actix_rt::test] // fn client_read_until_eof() { // let addr = test::TestServer::unused_addr(); @@ -797,62 +748,58 @@ fn test_client_cookie_handling() { // assert_eq!(bytes, Bytes::from_static(b"welcome!")); // } -#[test] -fn client_basic_auth() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::new(App::new().route( - "/", - web::to(|req: HttpRequest| { - if req - .headers() - .get(header::AUTHORIZATION) - .unwrap() - .to_str() - .unwrap() - == "Basic dXNlcm5hbWU6cGFzc3dvcmQ=" - { - HttpResponse::Ok() - } else { - HttpResponse::BadRequest() - } - }), - )) - }); +#[actix_rt::test] +async fn client_basic_auth() { + let srv = TestServer::start(|| { + HttpService::new(App::new().route( + "/", + web::to(|req: HttpRequest| { + if req + .headers() + .get(header::AUTHORIZATION) + .unwrap() + .to_str() + .unwrap() + == "Basic dXNlcm5hbWU6cGFzc3dvcmQ=" + { + HttpResponse::Ok() + } else { + HttpResponse::BadRequest() + } + }), + )) + }); - // set authorization header to Basic - let request = srv.get("/").basic_auth("username", Some("password")); - let response = request.send().await.unwrap(); - assert!(response.status().is_success()); - }) + // set authorization header to Basic + let request = srv.get("/").basic_auth("username", Some("password")); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); } -#[test] -fn client_bearer_auth() { - block_on(async { - let srv = TestServer::start(|| { - HttpService::new(App::new().route( - "/", - web::to(|req: HttpRequest| { - if req - .headers() - .get(header::AUTHORIZATION) - .unwrap() - .to_str() - .unwrap() - == "Bearer someS3cr3tAutht0k3n" - { - HttpResponse::Ok() - } else { - HttpResponse::BadRequest() - } - }), - )) - }); +#[actix_rt::test] +async fn client_bearer_auth() { + let srv = TestServer::start(|| { + HttpService::new(App::new().route( + "/", + web::to(|req: HttpRequest| { + if req + .headers() + .get(header::AUTHORIZATION) + .unwrap() + .to_str() + .unwrap() + == "Bearer someS3cr3tAutht0k3n" + { + HttpResponse::Ok() + } else { + HttpResponse::BadRequest() + } + }), + )) + }); - // set authorization header to Bearer - let request = srv.get("/").bearer_auth("someS3cr3tAutht0k3n"); - let response = request.send().await.unwrap(); - assert!(response.status().is_success()); - }) + // set authorization header to Bearer + let request = srv.get("/").bearer_auth("someS3cr3tAutht0k3n"); + let response = request.send().await.unwrap(); + assert!(response.status().is_success()); } diff --git a/awc/tests/test_rustls_client.rs b/awc/tests/test_rustls_client.rs index bdfd21031..ac60d8e83 100644 --- a/awc/tests/test_rustls_client.rs +++ b/awc/tests/test_rustls_client.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use actix_codec::{AsyncRead, AsyncWrite}; use actix_http::HttpService; -use actix_http_test::{block_on, TestServer}; +use actix_http_test::TestServer; use actix_server::ssl::OpensslAcceptor; use actix_service::{pipeline_factory, ServiceFactory}; use actix_web::http::Version; @@ -53,57 +53,54 @@ mod danger { } } -// #[test] -fn _test_connection_reuse_h2() { - block_on(async { - let openssl = ssl_acceptor().unwrap(); - let num = Arc::new(AtomicUsize::new(0)); - let num2 = num.clone(); +// #[actix_rt::test] +async fn _test_connection_reuse_h2() { + let openssl = ssl_acceptor().unwrap(); + let num = Arc::new(AtomicUsize::new(0)); + let num2 = num.clone(); - let srv = TestServer::start(move || { - let num2 = num2.clone(); - pipeline_factory(move |io| { - num2.fetch_add(1, Ordering::Relaxed); - ok(io) - }) - .and_then( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(App::new().service( - web::resource("/").route(web::to(|| HttpResponse::Ok())), - )) - .map_err(|_| ()), - ) - }); + let srv = TestServer::start(move || { + let num2 = num2.clone(); + pipeline_factory(move |io| { + num2.fetch_add(1, Ordering::Relaxed); + ok(io) + }) + .and_then( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(App::new() + .service(web::resource("/").route(web::to(|| HttpResponse::Ok())))) + .map_err(|_| ()), + ) + }); - // disable ssl verification - let mut config = ClientConfig::new(); - let protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; - config.set_protocols(&protos); - config - .dangerous() - .set_certificate_verifier(Arc::new(danger::NoCertificateVerification {})); + // disable ssl verification + let mut config = ClientConfig::new(); + let protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; + config.set_protocols(&protos); + config + .dangerous() + .set_certificate_verifier(Arc::new(danger::NoCertificateVerification {})); - let client = awc::Client::build() - .connector(awc::Connector::new().rustls(Arc::new(config)).finish()) - .finish(); + let client = awc::Client::build() + .connector(awc::Connector::new().rustls(Arc::new(config)).finish()) + .finish(); - // req 1 - let request = client.get(srv.surl("/")).send(); - let response = request.await.unwrap(); - assert!(response.status().is_success()); + // req 1 + let request = client.get(srv.surl("/")).send(); + let response = request.await.unwrap(); + assert!(response.status().is_success()); - // req 2 - let req = client.post(srv.surl("/")); - let response = req.send().await.unwrap(); - assert!(response.status().is_success()); - assert_eq!(response.version(), Version::HTTP_2); + // req 2 + let req = client.post(srv.surl("/")); + let response = req.send().await.unwrap(); + assert!(response.status().is_success()); + assert_eq!(response.version(), Version::HTTP_2); - // one connection - assert_eq!(num.load(Ordering::Relaxed), 1); - }) + // one connection + assert_eq!(num.load(Ordering::Relaxed), 1); } diff --git a/awc/tests/test_ssl_client.rs b/awc/tests/test_ssl_client.rs index d37dba291..1abb071a4 100644 --- a/awc/tests/test_ssl_client.rs +++ b/awc/tests/test_ssl_client.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use actix_codec::{AsyncRead, AsyncWrite}; use actix_http::HttpService; -use actix_http_test::{block_on, TestServer}; +use actix_http_test::TestServer; use actix_server::ssl::OpensslAcceptor; use actix_service::{pipeline_factory, ServiceFactory}; use actix_web::http::Version; @@ -35,56 +35,53 @@ fn ssl_acceptor() -> Result> { Ok(actix_server::ssl::OpensslAcceptor::new(builder.build())) } -#[test] -fn test_connection_reuse_h2() { - block_on(async { - let openssl = ssl_acceptor().unwrap(); - let num = Arc::new(AtomicUsize::new(0)); - let num2 = num.clone(); +#[actix_rt::test] +async fn test_connection_reuse_h2() { + let openssl = ssl_acceptor().unwrap(); + let num = Arc::new(AtomicUsize::new(0)); + let num2 = num.clone(); - let srv = TestServer::start(move || { - let num2 = num2.clone(); - pipeline_factory(move |io| { - num2.fetch_add(1, Ordering::Relaxed); - ok(io) - }) - .and_then( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(App::new().service( - web::resource("/").route(web::to(|| HttpResponse::Ok())), - )) - .map_err(|_| ()), - ) - }); + let srv = TestServer::start(move || { + let num2 = num2.clone(); + pipeline_factory(move |io| { + num2.fetch_add(1, Ordering::Relaxed); + ok(io) + }) + .and_then( + openssl + .clone() + .map_err(|e| println!("Openssl error: {}", e)), + ) + .and_then( + HttpService::build() + .h2(App::new() + .service(web::resource("/").route(web::to(|| HttpResponse::Ok())))) + .map_err(|_| ()), + ) + }); - // disable ssl verification - let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); - builder.set_verify(SslVerifyMode::NONE); - let _ = builder - .set_alpn_protos(b"\x02h2\x08http/1.1") - .map_err(|e| log::error!("Can not set alpn protocol: {:?}", e)); + // disable ssl verification + let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); + builder.set_verify(SslVerifyMode::NONE); + let _ = builder + .set_alpn_protos(b"\x02h2\x08http/1.1") + .map_err(|e| log::error!("Can not set alpn protocol: {:?}", e)); - let client = awc::Client::build() - .connector(awc::Connector::new().ssl(builder.build()).finish()) - .finish(); + let client = awc::Client::build() + .connector(awc::Connector::new().ssl(builder.build()).finish()) + .finish(); - // req 1 - let request = client.get(srv.surl("/")).send(); - let response = request.await.unwrap(); - assert!(response.status().is_success()); + // req 1 + let request = client.get(srv.surl("/")).send(); + let response = request.await.unwrap(); + assert!(response.status().is_success()); - // req 2 - let req = client.post(srv.surl("/")); - let response = req.send().await.unwrap(); - assert!(response.status().is_success()); - assert_eq!(response.version(), Version::HTTP_2); + // req 2 + let req = client.post(srv.surl("/")); + let response = req.send().await.unwrap(); + assert!(response.status().is_success()); + assert_eq!(response.version(), Version::HTTP_2); - // one connection - assert_eq!(num.load(Ordering::Relaxed), 1); - }) + // one connection + assert_eq!(num.load(Ordering::Relaxed), 1); } diff --git a/awc/tests/test_ws.rs b/awc/tests/test_ws.rs index 633e8db51..2e1d3981e 100644 --- a/awc/tests/test_ws.rs +++ b/awc/tests/test_ws.rs @@ -2,7 +2,7 @@ use std::io; use actix_codec::Framed; use actix_http::{body::BodySize, h1, ws, Error, HttpService, Request, Response}; -use actix_http_test::{block_on, TestServer}; +use actix_http_test::TestServer; use bytes::{Bytes, BytesMut}; use futures::future::ok; use futures::{SinkExt, StreamExt}; @@ -28,56 +28,54 @@ async fn ws_service(req: ws::Frame) -> Result { } } -#[test] -fn test_simple() { - block_on(async { - let mut srv = TestServer::start(|| { - HttpService::build() - .upgrade(|(req, mut framed): (Request, Framed<_, _>)| { - async move { - let res = ws::handshake_response(req.head()).finish(); - // send handshake response - framed - .send(h1::Message::Item((res.drop_body(), BodySize::None))) - .await?; +#[actix_rt::test] +async fn test_simple() { + let mut srv = TestServer::start(|| { + HttpService::build() + .upgrade(|(req, mut framed): (Request, Framed<_, _>)| { + async move { + let res = ws::handshake_response(req.head()).finish(); + // send handshake response + framed + .send(h1::Message::Item((res.drop_body(), BodySize::None))) + .await?; - // start websocket service - let framed = framed.into_framed(ws::Codec::new()); - ws::Transport::with(framed, ws_service).await - } - }) - .finish(|_| ok::<_, Error>(Response::NotFound())) - }); + // start websocket service + let framed = framed.into_framed(ws::Codec::new()); + ws::Transport::with(framed, ws_service).await + } + }) + .finish(|_| ok::<_, Error>(Response::NotFound())) + }); - // client service - let mut framed = srv.ws().await.unwrap(); - framed - .send(ws::Message::Text("text".to_string())) - .await - .unwrap(); - let item = framed.next().await.unwrap().unwrap(); - assert_eq!(item, ws::Frame::Text(Some(BytesMut::from("text")))); + // client service + let mut framed = srv.ws().await.unwrap(); + framed + .send(ws::Message::Text("text".to_string())) + .await + .unwrap(); + let item = framed.next().await.unwrap().unwrap(); + assert_eq!(item, ws::Frame::Text(Some(BytesMut::from("text")))); - framed - .send(ws::Message::Binary("text".into())) - .await - .unwrap(); - let item = framed.next().await.unwrap().unwrap(); - assert_eq!( - item, - ws::Frame::Binary(Some(Bytes::from_static(b"text").into())) - ); + framed + .send(ws::Message::Binary("text".into())) + .await + .unwrap(); + let item = framed.next().await.unwrap().unwrap(); + assert_eq!( + item, + ws::Frame::Binary(Some(Bytes::from_static(b"text").into())) + ); - framed.send(ws::Message::Ping("text".into())).await.unwrap(); - let item = framed.next().await.unwrap().unwrap(); - assert_eq!(item, ws::Frame::Pong("text".to_string().into())); + framed.send(ws::Message::Ping("text".into())).await.unwrap(); + let item = framed.next().await.unwrap().unwrap(); + assert_eq!(item, ws::Frame::Pong("text".to_string().into())); - framed - .send(ws::Message::Close(Some(ws::CloseCode::Normal.into()))) - .await - .unwrap(); + framed + .send(ws::Message::Close(Some(ws::CloseCode::Normal.into()))) + .await + .unwrap(); - let item = framed.next().await.unwrap().unwrap(); - assert_eq!(item, ws::Frame::Close(Some(ws::CloseCode::Normal.into()))); - }) + let item = framed.next().await.unwrap().unwrap(); + assert_eq!(item, ws::Frame::Close(Some(ws::CloseCode::Normal.into()))); } diff --git a/src/app.rs b/src/app.rs index a9dc3f29a..d67817d21 100644 --- a/src/app.rs +++ b/src/app.rs @@ -2,12 +2,10 @@ use std::cell::RefCell; use std::fmt; use std::future::Future; use std::marker::PhantomData; -use std::pin::Pin; use std::rc::Rc; -use std::task::{Context, Poll}; use actix_http::body::{Body, MessageBody}; -use actix_service::boxed::{self, BoxedNewService}; +use actix_service::boxed::{self, BoxServiceFactory}; use actix_service::{ apply, apply_fn_factory, IntoServiceFactory, ServiceFactory, Transform, }; @@ -25,7 +23,7 @@ use crate::service::{ ServiceResponse, }; -type HttpNewService = BoxedNewService<(), ServiceRequest, ServiceResponse, Error, ()>; +type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>; type FnDataFactory = Box LocalBoxFuture<'static, Result, ()>>>; @@ -485,231 +483,195 @@ where mod tests { use actix_service::Service; use bytes::Bytes; - use futures::future::{ok, Future}; + use futures::future::ok; use super::*; use crate::http::{header, HeaderValue, Method, StatusCode}; use crate::middleware::DefaultHeaders; - use crate::service::{ServiceRequest, ServiceResponse}; - use crate::test::{block_on, call_service, init_service, read_body, TestRequest}; - use crate::{web, Error, HttpRequest, HttpResponse}; + use crate::service::ServiceRequest; + use crate::test::{call_service, init_service, read_body, TestRequest}; + use crate::{web, HttpRequest, HttpResponse}; - #[test] - fn test_default_resource() { - block_on(async { - let mut srv = init_service( - App::new().service(web::resource("/test").to(|| HttpResponse::Ok())), - ) + #[actix_rt::test] + async fn test_default_resource() { + let mut srv = init_service( + App::new().service(web::resource("/test").to(|| HttpResponse::Ok())), + ) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + + let req = TestRequest::with_uri("/blah").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + + let mut srv = init_service( + App::new() + .service(web::resource("/test").to(|| HttpResponse::Ok())) + .service( + web::resource("/test2") + .default_service(|r: ServiceRequest| { + ok(r.into_response(HttpResponse::Created())) + }) + .route(web::get().to(|| HttpResponse::Ok())), + ) + .default_service(|r: ServiceRequest| { + ok(r.into_response(HttpResponse::MethodNotAllowed())) + }), + ) + .await; + + let req = TestRequest::with_uri("/blah").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); + + let req = TestRequest::with_uri("/test2").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + + let req = TestRequest::with_uri("/test2") + .method(Method::POST) + .to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::CREATED); + } + + #[actix_rt::test] + async fn test_data_factory() { + let mut srv = + init_service(App::new().data_factory(|| ok::<_, ()>(10usize)).service( + web::resource("/").to(|_: web::Data| HttpResponse::Ok()), + )) .await; - let req = TestRequest::with_uri("/test").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::default().to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); - let req = TestRequest::with_uri("/blah").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); - - let mut srv = init_service( - App::new() - .service(web::resource("/test").to(|| HttpResponse::Ok())) - .service( - web::resource("/test2") - .default_service(|r: ServiceRequest| { - ok(r.into_response(HttpResponse::Created())) - }) - .route(web::get().to(|| HttpResponse::Ok())), - ) - .default_service(|r: ServiceRequest| { - ok(r.into_response(HttpResponse::MethodNotAllowed())) - }), - ) + let mut srv = + init_service(App::new().data_factory(|| ok::<_, ()>(10u32)).service( + web::resource("/").to(|_: web::Data| HttpResponse::Ok()), + )) .await; - - let req = TestRequest::with_uri("/blah").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); - - let req = TestRequest::with_uri("/test2").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - - let req = TestRequest::with_uri("/test2") - .method(Method::POST) - .to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::CREATED); - }) + let req = TestRequest::default().to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); } - #[test] - fn test_data_factory() { - block_on(async { - let mut srv = - init_service(App::new().data_factory(|| ok::<_, ()>(10usize)).service( - web::resource("/").to(|_: web::Data| HttpResponse::Ok()), - )) - .await; - let req = TestRequest::default().to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - - let mut srv = - init_service(App::new().data_factory(|| ok::<_, ()>(10u32)).service( - web::resource("/").to(|_: web::Data| HttpResponse::Ok()), - )) - .await; - let req = TestRequest::default().to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); - }) + #[actix_rt::test] + async fn test_wrap() { + let mut srv = init_service( + App::new() + .wrap( + DefaultHeaders::new() + .header(header::CONTENT_TYPE, HeaderValue::from_static("0001")), + ) + .route("/test", web::get().to(|| HttpResponse::Ok())), + ) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + HeaderValue::from_static("0001") + ); } - fn md( - req: ServiceRequest, - srv: &mut S, - ) -> impl Future, Error>> - where - S: Service< - Request = ServiceRequest, - Response = ServiceResponse, - Error = Error, - >, - { - let fut = srv.call(req); - async move { - let mut res = fut.await?; - res.headers_mut() - .insert(header::CONTENT_TYPE, HeaderValue::from_static("0001")); - Ok(res) - } + #[actix_rt::test] + async fn test_router_wrap() { + let mut srv = init_service( + App::new() + .route("/test", web::get().to(|| HttpResponse::Ok())) + .wrap( + DefaultHeaders::new() + .header(header::CONTENT_TYPE, HeaderValue::from_static("0001")), + ), + ) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + HeaderValue::from_static("0001") + ); } - #[test] - fn test_wrap() { - block_on(async { - let mut srv = - init_service( - App::new() - .wrap(DefaultHeaders::new().header( + #[actix_rt::test] + async fn test_wrap_fn() { + let mut srv = init_service( + App::new() + .wrap_fn(|req, srv| { + let fut = srv.call(req); + async move { + let mut res = fut.await?; + res.headers_mut().insert( header::CONTENT_TYPE, HeaderValue::from_static("0001"), + ); + Ok(res) + } + }) + .service(web::resource("/test").to(|| HttpResponse::Ok())), + ) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + HeaderValue::from_static("0001") + ); + } + + #[actix_rt::test] + async fn test_router_wrap_fn() { + let mut srv = init_service( + App::new() + .route("/test", web::get().to(|| HttpResponse::Ok())) + .wrap_fn(|req, srv| { + let fut = srv.call(req); + async { + let mut res = fut.await?; + res.headers_mut().insert( + header::CONTENT_TYPE, + HeaderValue::from_static("0001"), + ); + Ok(res) + } + }), + ) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + HeaderValue::from_static("0001") + ); + } + + #[actix_rt::test] + async fn test_external_resource() { + let mut srv = init_service( + App::new() + .external_resource("youtube", "https://youtube.com/watch/{video_id}") + .route( + "/test", + web::get().to(|req: HttpRequest| { + HttpResponse::Ok().body(format!( + "{}", + req.url_for("youtube", &["12345"]).unwrap() )) - .route("/test", web::get().to(|| HttpResponse::Ok())), - ) - .await; - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - HeaderValue::from_static("0001") - ); - }) - } - - #[test] - fn test_router_wrap() { - block_on(async { - let mut srv = - init_service( - App::new() - .route("/test", web::get().to(|| HttpResponse::Ok())) - .wrap(DefaultHeaders::new().header( - header::CONTENT_TYPE, - HeaderValue::from_static("0001"), - )), - ) - .await; - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - HeaderValue::from_static("0001") - ); - }) - } - - #[test] - fn test_wrap_fn() { - block_on(async { - let mut srv = init_service( - App::new() - .wrap_fn(|req, srv| { - let fut = srv.call(req); - async move { - let mut res = fut.await?; - res.headers_mut().insert( - header::CONTENT_TYPE, - HeaderValue::from_static("0001"), - ); - Ok(res) - } - }) - .service(web::resource("/test").to(|| HttpResponse::Ok())), - ) - .await; - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - HeaderValue::from_static("0001") - ); - }) - } - - #[test] - fn test_router_wrap_fn() { - block_on(async { - let mut srv = init_service( - App::new() - .route("/test", web::get().to(|| HttpResponse::Ok())) - .wrap_fn(|req, srv| { - let fut = srv.call(req); - async { - let mut res = fut.await?; - res.headers_mut().insert( - header::CONTENT_TYPE, - HeaderValue::from_static("0001"), - ); - Ok(res) - } }), - ) - .await; - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - HeaderValue::from_static("0001") - ); - }) - } - - #[test] - fn test_external_resource() { - block_on(async { - let mut srv = init_service( - App::new() - .external_resource("youtube", "https://youtube.com/watch/{video_id}") - .route( - "/test", - web::get().to(|req: HttpRequest| { - HttpResponse::Ok().body(format!( - "{}", - req.url_for("youtube", &["12345"]).unwrap() - )) - }), - ), - ) - .await; - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); - let body = read_body(resp).await; - assert_eq!(body, Bytes::from_static(b"https://youtube.com/watch/12345")); - }) + ), + ) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + let body = read_body(resp).await; + assert_eq!(body, Bytes::from_static(b"https://youtube.com/watch/12345")); } } diff --git a/src/app_service.rs b/src/app_service.rs index 7407ee2fb..3fa5a6eed 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -8,9 +8,9 @@ use std::task::{Context, Poll}; use actix_http::{Extensions, Request, Response}; use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url}; use actix_server_config::ServerConfig; -use actix_service::boxed::{self, BoxedNewService, BoxedService}; +use actix_service::boxed::{self, BoxService, BoxServiceFactory}; use actix_service::{service_fn, Service, ServiceFactory}; -use futures::future::{ok, Either, FutureExt, LocalBoxFuture, Ready}; +use futures::future::{ok, FutureExt, LocalBoxFuture}; use crate::config::{AppConfig, AppService}; use crate::data::DataFactory; @@ -21,9 +21,9 @@ use crate::rmap::ResourceMap; use crate::service::{AppServiceFactory, ServiceRequest, ServiceResponse}; type Guards = Vec>; -type HttpService = BoxedService; -type HttpNewService = BoxedNewService<(), ServiceRequest, ServiceResponse, Error, ()>; -type BoxedResponse = LocalBoxFuture<'static, Result>; +type HttpService = BoxService; +type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>; +type BoxResponse = LocalBoxFuture<'static, Result>; type FnDataFactory = Box LocalBoxFuture<'static, Result, ()>>>; @@ -387,7 +387,7 @@ impl Service for AppRouting { type Request = ServiceRequest; type Response = ServiceResponse; type Error = Error; - type Future = BoxedResponse; + type Future = BoxResponse; fn poll_ready(&mut self, _: &mut Context) -> Poll> { if self.ready.is_none() { @@ -447,13 +447,12 @@ impl ServiceFactory for AppEntry { #[cfg(test)] mod tests { - use actix_service::Service; - use std::sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }; + use std::sync::atomic::{AtomicBool, Ordering}; + use std::sync::Arc; - use crate::{test, web, App, HttpResponse}; + use crate::test::{init_service, TestRequest}; + use crate::{web, App, HttpResponse}; + use actix_service::Service; struct DropData(Arc); @@ -463,19 +462,20 @@ mod tests { } } - #[test] - fn drop_data() { + #[actix_rt::test] + async fn test_drop_data() { let data = Arc::new(AtomicBool::new(false)); - test::block_on(async { - let mut app = test::init_service( + + { + let mut app = init_service( App::new() .data(DropData(data.clone())) .service(web::resource("/test").to(|| HttpResponse::Ok())), ) .await; - let req = test::TestRequest::with_uri("/test").to_request(); + let req = TestRequest::with_uri("/test").to_request(); let _ = app.call(req).await.unwrap(); - }); + } assert!(data.load(Ordering::Relaxed)); } } diff --git a/src/config.rs b/src/config.rs index 3ce18f98b..57ba10079 100644 --- a/src/config.rs +++ b/src/config.rs @@ -18,7 +18,7 @@ use crate::service::{ type Guards = Vec>; type HttpNewService = - boxed::BoxedNewService<(), ServiceRequest, ServiceResponse, Error, ()>; + boxed::BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>; /// Application configuration pub struct AppService { @@ -246,28 +246,27 @@ mod tests { use super::*; use crate::http::{Method, StatusCode}; - use crate::test::{block_on, call_service, init_service, read_body, TestRequest}; + use crate::test::{call_service, init_service, read_body, TestRequest}; use crate::{web, App, HttpRequest, HttpResponse}; - #[test] - fn test_data() { - block_on(async { - let cfg = |cfg: &mut ServiceConfig| { - cfg.data(10usize); - }; + #[actix_rt::test] + async fn test_data() { + let cfg = |cfg: &mut ServiceConfig| { + cfg.data(10usize); + }; - let mut srv = init_service(App::new().configure(cfg).service( + let mut srv = + init_service(App::new().configure(cfg).service( web::resource("/").to(|_: web::Data| HttpResponse::Ok()), )) .await; - let req = TestRequest::default().to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - }) + let req = TestRequest::default().to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); } - // #[test] - // fn test_data_factory() { + // #[actix_rt::test] + // async fn test_data_factory() { // let cfg = |cfg: &mut ServiceConfig| { // cfg.data_factory(|| { // sleep(std::time::Duration::from_millis(50)).then(|_| { @@ -282,7 +281,7 @@ mod tests { // web::resource("/").to(|_: web::Data| HttpResponse::Ok()), // )); // let req = TestRequest::default().to_request(); - // let resp = block_on(srv.call(req)).unwrap(); + // let resp = srv.call(req).await.unwrap(); // assert_eq!(resp.status(), StatusCode::OK); // let cfg2 = |cfg: &mut ServiceConfig| { @@ -294,63 +293,58 @@ mod tests { // .configure(cfg2), // ); // let req = TestRequest::default().to_request(); - // let resp = block_on(srv.call(req)).unwrap(); + // let resp = srv.call(req).await.unwrap(); // assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); // } - #[test] - fn test_external_resource() { - block_on(async { - let mut srv = init_service( - App::new() - .configure(|cfg| { - cfg.external_resource( - "youtube", - "https://youtube.com/watch/{video_id}", - ); - }) - .route( - "/test", - web::get().to(|req: HttpRequest| { - HttpResponse::Ok().body(format!( - "{}", - req.url_for("youtube", &["12345"]).unwrap() - )) - }), - ), - ) - .await; - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); - let body = read_body(resp).await; - assert_eq!(body, Bytes::from_static(b"https://youtube.com/watch/12345")); - }) + #[actix_rt::test] + async fn test_external_resource() { + let mut srv = init_service( + App::new() + .configure(|cfg| { + cfg.external_resource( + "youtube", + "https://youtube.com/watch/{video_id}", + ); + }) + .route( + "/test", + web::get().to(|req: HttpRequest| { + HttpResponse::Ok().body(format!( + "{}", + req.url_for("youtube", &["12345"]).unwrap() + )) + }), + ), + ) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + let body = read_body(resp).await; + assert_eq!(body, Bytes::from_static(b"https://youtube.com/watch/12345")); } - #[test] - fn test_service() { - block_on(async { - let mut srv = init_service(App::new().configure(|cfg| { - cfg.service( - web::resource("/test") - .route(web::get().to(|| HttpResponse::Created())), - ) - .route("/index.html", web::get().to(|| HttpResponse::Ok())); - })) - .await; + #[actix_rt::test] + async fn test_service() { + let mut srv = init_service(App::new().configure(|cfg| { + cfg.service( + web::resource("/test").route(web::get().to(|| HttpResponse::Created())), + ) + .route("/index.html", web::get().to(|| HttpResponse::Ok())); + })) + .await; - let req = TestRequest::with_uri("/test") - .method(Method::GET) - .to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::CREATED); + let req = TestRequest::with_uri("/test") + .method(Method::GET) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::CREATED); - let req = TestRequest::with_uri("/index.html") - .method(Method::GET) - .to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); - }) + let req = TestRequest::with_uri("/index.html") + .method(Method::GET) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); } } diff --git a/src/data.rs b/src/data.rs index 5ace3a8f3..e8928188f 100644 --- a/src/data.rs +++ b/src/data.rs @@ -139,104 +139,97 @@ mod tests { use super::*; use crate::http::StatusCode; - use crate::test::{block_on, init_service, TestRequest}; + use crate::test::{init_service, TestRequest}; use crate::{web, App, HttpResponse}; - #[test] - fn test_data_extractor() { - block_on(async { - let mut srv = init_service(App::new().data(10usize).service( + #[actix_rt::test] + async fn test_data_extractor() { + let mut srv = + init_service(App::new().data(10usize).service( web::resource("/").to(|_: web::Data| HttpResponse::Ok()), )) .await; - let req = TestRequest::default().to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::default().to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); - let mut srv = init_service(App::new().data(10u32).service( + let mut srv = + init_service(App::new().data(10u32).service( web::resource("/").to(|_: web::Data| HttpResponse::Ok()), )) .await; - let req = TestRequest::default().to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); - }) + let req = TestRequest::default().to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); } - #[test] - fn test_register_data_extractor() { - block_on(async { - let mut srv = - init_service(App::new().register_data(Data::new(10usize)).service( - web::resource("/").to(|_: web::Data| HttpResponse::Ok()), - )) - .await; - - let req = TestRequest::default().to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - - let mut srv = - init_service(App::new().register_data(Data::new(10u32)).service( - web::resource("/").to(|_: web::Data| HttpResponse::Ok()), - )) - .await; - let req = TestRequest::default().to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); - }) - } - - #[test] - fn test_route_data_extractor() { - block_on(async { - let mut srv = init_service(App::new().service( - web::resource("/").data(10usize).route(web::get().to( - |data: web::Data| { - let _ = data.clone(); - HttpResponse::Ok() - }, - )), + #[actix_rt::test] + async fn test_register_data_extractor() { + let mut srv = + init_service(App::new().register_data(Data::new(10usize)).service( + web::resource("/").to(|_: web::Data| HttpResponse::Ok()), )) .await; - let req = TestRequest::default().to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::default().to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); - // different type - let mut srv = init_service( - App::new().service( - web::resource("/") - .data(10u32) - .route(web::get().to(|_: web::Data| HttpResponse::Ok())), - ), - ) - .await; - let req = TestRequest::default().to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); - }) - } - - #[test] - fn test_override_data() { - block_on(async { - let mut srv = init_service(App::new().data(1usize).service( - web::resource("/").data(10usize).route(web::get().to( - |data: web::Data| { - assert_eq!(*data, 10); - let _ = data.clone(); - HttpResponse::Ok() - }, - )), + let mut srv = + init_service(App::new().register_data(Data::new(10u32)).service( + web::resource("/").to(|_: web::Data| HttpResponse::Ok()), )) .await; + let req = TestRequest::default().to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); + } - let req = TestRequest::default().to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - }) + #[actix_rt::test] + async fn test_route_data_extractor() { + let mut srv = + init_service(App::new().service(web::resource("/").data(10usize).route( + web::get().to(|data: web::Data| { + let _ = data.clone(); + HttpResponse::Ok() + }), + ))) + .await; + + let req = TestRequest::default().to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + + // different type + let mut srv = init_service( + App::new().service( + web::resource("/") + .data(10u32) + .route(web::get().to(|_: web::Data| HttpResponse::Ok())), + ), + ) + .await; + let req = TestRequest::default().to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); + } + + #[actix_rt::test] + async fn test_override_data() { + let mut srv = init_service(App::new().data(1usize).service( + web::resource("/").data(10usize).route(web::get().to( + |data: web::Data| { + assert_eq!(*data, 10); + let _ = data.clone(); + HttpResponse::Ok() + }, + )), + )) + .await; + + let req = TestRequest::default().to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); } } diff --git a/src/extract.rs b/src/extract.rs index 9c8633368..d43402c73 100644 --- a/src/extract.rs +++ b/src/extract.rs @@ -270,7 +270,7 @@ mod tests { use serde_derive::Deserialize; use super::*; - use crate::test::{block_on, TestRequest}; + use crate::test::TestRequest; use crate::types::{Form, FormConfig}; #[derive(Deserialize, Debug, PartialEq)] @@ -278,8 +278,8 @@ mod tests { hello: String, } - #[test] - fn test_option() { + #[actix_rt::test] + async fn test_option() { let (req, mut pl) = TestRequest::with_header( header::CONTENT_TYPE, "application/x-www-form-urlencoded", @@ -287,7 +287,9 @@ mod tests { .data(FormConfig::default().limit(4096)) .to_http_parts(); - let r = block_on(Option::>::from_request(&req, &mut pl)).unwrap(); + let r = Option::>::from_request(&req, &mut pl) + .await + .unwrap(); assert_eq!(r, None); let (req, mut pl) = TestRequest::with_header( @@ -298,7 +300,9 @@ mod tests { .set_payload(Bytes::from_static(b"hello=world")) .to_http_parts(); - let r = block_on(Option::>::from_request(&req, &mut pl)).unwrap(); + let r = Option::>::from_request(&req, &mut pl) + .await + .unwrap(); assert_eq!( r, Some(Form(Info { @@ -314,12 +318,14 @@ mod tests { .set_payload(Bytes::from_static(b"bye=world")) .to_http_parts(); - let r = block_on(Option::>::from_request(&req, &mut pl)).unwrap(); + let r = Option::>::from_request(&req, &mut pl) + .await + .unwrap(); assert_eq!(r, None); } - #[test] - fn test_result() { + #[actix_rt::test] + async fn test_result() { let (req, mut pl) = TestRequest::with_header( header::CONTENT_TYPE, "application/x-www-form-urlencoded", @@ -328,7 +334,8 @@ mod tests { .set_payload(Bytes::from_static(b"hello=world")) .to_http_parts(); - let r = block_on(Result::, Error>::from_request(&req, &mut pl)) + let r = Result::, Error>::from_request(&req, &mut pl) + .await .unwrap() .unwrap(); assert_eq!( @@ -346,8 +353,9 @@ mod tests { .set_payload(Bytes::from_static(b"bye=world")) .to_http_parts(); - let r = - block_on(Result::, Error>::from_request(&req, &mut pl)).unwrap(); + let r = Result::, Error>::from_request(&req, &mut pl) + .await + .unwrap(); assert!(r.is_err()); } } diff --git a/src/lib.rs b/src/lib.rs index 8063d0d35..4d1facd8d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(clippy::borrow_interior_mutable_const, unused_imports, dead_code)] +#![allow(clippy::borrow_interior_mutable_const)] //! Actix web is a small, pragmatic, and extremely fast web framework //! for Rust. //! @@ -143,9 +143,9 @@ pub mod dev { HttpServiceFactory, ServiceRequest, ServiceResponse, WebService, }; - //pub use crate::types::form::UrlEncoded; - //pub use crate::types::json::JsonBody; - //pub use crate::types::readlines::Readlines; + pub use crate::types::form::UrlEncoded; + pub use crate::types::json::JsonBody; + pub use crate::types::readlines::Readlines; pub use actix_http::body::{Body, BodySize, MessageBody, ResponseBody, SizedStream}; pub use actix_http::encoding::Decoder as Decompress; @@ -174,17 +174,16 @@ pub mod client { //! use actix_rt::System; //! use actix_web::client::Client; //! - //! fn main() { - //! System::new("test").block_on(async { - //! let mut client = Client::default(); + //! #[actix_rt::main] + //! async fn main() { + //! let mut client = Client::default(); //! - //! // Create request builder and send request - //! let response = client.get("http://www.rust-lang.org") - //! .header("User-Agent", "Actix-web") - //! .send().await; // <- Send http request + //! // Create request builder and send request + //! let response = client.get("http://www.rust-lang.org") + //! .header("User-Agent", "Actix-web") + //! .send().await; // <- Send http request //! - //! println!("Response: {:?}", response); - //! }); + //! println!("Response: {:?}", response); //! } //! ``` pub use awc::error::{ diff --git a/src/middleware/condition.rs b/src/middleware/condition.rs index 6603fc001..2ede81783 100644 --- a/src/middleware/condition.rs +++ b/src/middleware/condition.rs @@ -2,7 +2,7 @@ use std::task::{Context, Poll}; use actix_service::{Service, Transform}; -use futures::future::{ok, Either, FutureExt, LocalBoxFuture, Ready}; +use futures::future::{ok, Either, FutureExt, LocalBoxFuture}; /// `Middleware` for conditionally enables another middleware. /// The controled middleware must not change the `Service` interfaces. @@ -102,7 +102,7 @@ mod tests { use crate::error::Result; use crate::http::{header::CONTENT_TYPE, HeaderValue, StatusCode}; use crate::middleware::errhandlers::*; - use crate::test::{self, block_on, TestRequest}; + use crate::test::{self, TestRequest}; use crate::HttpResponse; fn render_500(mut res: ServiceResponse) -> Result> { @@ -112,46 +112,40 @@ mod tests { Ok(ErrorHandlerResponse::Response(res)) } - #[test] - fn test_handler_enabled() { - block_on(async { - let srv = |req: ServiceRequest| { - ok(req.into_response(HttpResponse::InternalServerError().finish())) - }; + #[actix_rt::test] + async fn test_handler_enabled() { + let srv = |req: ServiceRequest| { + ok(req.into_response(HttpResponse::InternalServerError().finish())) + }; - let mw = ErrorHandlers::new() - .handler(StatusCode::INTERNAL_SERVER_ERROR, render_500); + let mw = + ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, render_500); - let mut mw = Condition::new(true, mw) - .new_transform(srv.into_service()) - .await - .unwrap(); - let resp = - test::call_service(&mut mw, TestRequest::default().to_srv_request()) - .await; - assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); - }) + let mut mw = Condition::new(true, mw) + .new_transform(srv.into_service()) + .await + .unwrap(); + let resp = + test::call_service(&mut mw, TestRequest::default().to_srv_request()).await; + assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); } - #[test] - fn test_handler_disabled() { - block_on(async { - let srv = |req: ServiceRequest| { - ok(req.into_response(HttpResponse::InternalServerError().finish())) - }; + #[actix_rt::test] + async fn test_handler_disabled() { + let srv = |req: ServiceRequest| { + ok(req.into_response(HttpResponse::InternalServerError().finish())) + }; - let mw = ErrorHandlers::new() - .handler(StatusCode::INTERNAL_SERVER_ERROR, render_500); + let mw = + ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, render_500); - let mut mw = Condition::new(false, mw) - .new_transform(srv.into_service()) - .await - .unwrap(); + let mut mw = Condition::new(false, mw) + .new_transform(srv.into_service()) + .await + .unwrap(); - let resp = - test::call_service(&mut mw, TestRequest::default().to_srv_request()) - .await; - assert_eq!(resp.headers().get(CONTENT_TYPE), None); - }) + let resp = + test::call_service(&mut mw, TestRequest::default().to_srv_request()).await; + assert_eq!(resp.headers().get(CONTENT_TYPE), None); } } diff --git a/src/middleware/defaultheaders.rs b/src/middleware/defaultheaders.rs index 5c995503a..05a031065 100644 --- a/src/middleware/defaultheaders.rs +++ b/src/middleware/defaultheaders.rs @@ -1,6 +1,4 @@ //! Middleware for setting default response headers -use std::future::Future; -use std::pin::Pin; use std::rc::Rc; use std::task::{Context, Poll}; @@ -161,55 +159,50 @@ mod tests { use super::*; use crate::dev::ServiceRequest; use crate::http::header::CONTENT_TYPE; - use crate::test::{block_on, ok_service, TestRequest}; + use crate::test::{ok_service, TestRequest}; use crate::HttpResponse; - #[test] - fn test_default_headers() { - block_on(async { - let mut mw = DefaultHeaders::new() - .header(CONTENT_TYPE, "0001") - .new_transform(ok_service()) - .await - .unwrap(); + #[actix_rt::test] + async fn test_default_headers() { + let mut mw = DefaultHeaders::new() + .header(CONTENT_TYPE, "0001") + .new_transform(ok_service()) + .await + .unwrap(); - let req = TestRequest::default().to_srv_request(); - let resp = mw.call(req).await.unwrap(); - assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); + let req = TestRequest::default().to_srv_request(); + let resp = mw.call(req).await.unwrap(); + assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); - let req = TestRequest::default().to_srv_request(); - let srv = |req: ServiceRequest| { - ok(req.into_response( - HttpResponse::Ok().header(CONTENT_TYPE, "0002").finish(), - )) - }; - let mut mw = DefaultHeaders::new() - .header(CONTENT_TYPE, "0001") - .new_transform(srv.into_service()) - .await - .unwrap(); - let resp = mw.call(req).await.unwrap(); - assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0002"); - }) + let req = TestRequest::default().to_srv_request(); + let srv = |req: ServiceRequest| { + ok(req + .into_response(HttpResponse::Ok().header(CONTENT_TYPE, "0002").finish())) + }; + let mut mw = DefaultHeaders::new() + .header(CONTENT_TYPE, "0001") + .new_transform(srv.into_service()) + .await + .unwrap(); + let resp = mw.call(req).await.unwrap(); + assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0002"); } - #[test] - fn test_content_type() { - block_on(async { - let srv = - |req: ServiceRequest| ok(req.into_response(HttpResponse::Ok().finish())); - let mut mw = DefaultHeaders::new() - .content_type() - .new_transform(srv.into_service()) - .await - .unwrap(); + #[actix_rt::test] + async fn test_content_type() { + let srv = + |req: ServiceRequest| ok(req.into_response(HttpResponse::Ok().finish())); + let mut mw = DefaultHeaders::new() + .content_type() + .new_transform(srv.into_service()) + .await + .unwrap(); - let req = TestRequest::default().to_srv_request(); - let resp = mw.call(req).await.unwrap(); - assert_eq!( - resp.headers().get(CONTENT_TYPE).unwrap(), - "application/octet-stream" - ); - }) + let req = TestRequest::default().to_srv_request(); + let resp = mw.call(req).await.unwrap(); + assert_eq!( + resp.headers().get(CONTENT_TYPE).unwrap(), + "application/octet-stream" + ); } } diff --git a/src/middleware/errhandlers.rs b/src/middleware/errhandlers.rs index c8a702857..3dc1f0828 100644 --- a/src/middleware/errhandlers.rs +++ b/src/middleware/errhandlers.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use std::task::{Context, Poll}; use actix_service::{Service, Transform}; -use futures::future::{err, ok, Either, Future, FutureExt, LocalBoxFuture, Ready}; +use futures::future::{ok, FutureExt, LocalBoxFuture, Ready}; use hashbrown::HashMap; use crate::dev::{ServiceRequest, ServiceResponse}; @@ -151,7 +151,7 @@ mod tests { use super::*; use crate::http::{header::CONTENT_TYPE, HeaderValue, StatusCode}; - use crate::test::{self, block_on, TestRequest}; + use crate::test::{self, TestRequest}; use crate::HttpResponse; fn render_500(mut res: ServiceResponse) -> Result> { @@ -161,24 +161,21 @@ mod tests { Ok(ErrorHandlerResponse::Response(res)) } - #[test] - fn test_handler() { - block_on(async { - let srv = |req: ServiceRequest| { - ok(req.into_response(HttpResponse::InternalServerError().finish())) - }; + #[actix_rt::test] + async fn test_handler() { + let srv = |req: ServiceRequest| { + ok(req.into_response(HttpResponse::InternalServerError().finish())) + }; - let mut mw = ErrorHandlers::new() - .handler(StatusCode::INTERNAL_SERVER_ERROR, render_500) - .new_transform(srv.into_service()) - .await - .unwrap(); + let mut mw = ErrorHandlers::new() + .handler(StatusCode::INTERNAL_SERVER_ERROR, render_500) + .new_transform(srv.into_service()) + .await + .unwrap(); - let resp = - test::call_service(&mut mw, TestRequest::default().to_srv_request()) - .await; - assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); - }) + let resp = + test::call_service(&mut mw, TestRequest::default().to_srv_request()).await; + assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); } fn render_500_async( @@ -190,23 +187,20 @@ mod tests { Ok(ErrorHandlerResponse::Future(ok(res).boxed_local())) } - #[test] - fn test_handler_async() { - block_on(async { - let srv = |req: ServiceRequest| { - ok(req.into_response(HttpResponse::InternalServerError().finish())) - }; + #[actix_rt::test] + async fn test_handler_async() { + let srv = |req: ServiceRequest| { + ok(req.into_response(HttpResponse::InternalServerError().finish())) + }; - let mut mw = ErrorHandlers::new() - .handler(StatusCode::INTERNAL_SERVER_ERROR, render_500_async) - .new_transform(srv.into_service()) - .await - .unwrap(); + let mut mw = ErrorHandlers::new() + .handler(StatusCode::INTERNAL_SERVER_ERROR, render_500_async) + .new_transform(srv.into_service()) + .await + .unwrap(); - let resp = - test::call_service(&mut mw, TestRequest::default().to_srv_request()) - .await; - assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); - }) + let resp = + test::call_service(&mut mw, TestRequest::default().to_srv_request()).await; + assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); } } diff --git a/src/middleware/logger.rs b/src/middleware/logger.rs index 45df4bf34..a57ea2961 100644 --- a/src/middleware/logger.rs +++ b/src/middleware/logger.rs @@ -479,10 +479,10 @@ mod tests { use super::*; use crate::http::{header, StatusCode}; - use crate::test::{block_on, TestRequest}; + use crate::test::TestRequest; - #[test] - fn test_logger() { + #[actix_rt::test] + async fn test_logger() { let srv = |req: ServiceRequest| { ok(req.into_response( HttpResponse::build(StatusCode::OK) @@ -492,18 +492,18 @@ mod tests { }; let logger = Logger::new("%% %{User-Agent}i %{X-Test}o %{HOME}e %D test"); - let mut srv = block_on(logger.new_transform(srv.into_service())).unwrap(); + let mut srv = logger.new_transform(srv.into_service()).await.unwrap(); let req = TestRequest::with_header( header::USER_AGENT, header::HeaderValue::from_static("ACTIX-WEB"), ) .to_srv_request(); - let _res = block_on(srv.call(req)); + let _res = srv.call(req).await; } - #[test] - fn test_url_path() { + #[actix_rt::test] + async fn test_url_path() { let mut format = Format::new("%T %U"); let req = TestRequest::with_header( header::USER_AGENT, @@ -533,8 +533,8 @@ mod tests { assert!(s.contains("/test/route/yeah")); } - #[test] - fn test_default_format() { + #[actix_rt::test] + async fn test_default_format() { let mut format = Format::default(); let req = TestRequest::with_header( @@ -566,8 +566,8 @@ mod tests { assert!(s.contains("ACTIX-WEB")); } - #[test] - fn test_request_time_format() { + #[actix_rt::test] + async fn test_request_time_format() { let mut format = Format::new("%t"); let req = TestRequest::default().to_srv_request(); diff --git a/src/middleware/normalize.rs b/src/middleware/normalize.rs index b7eb1384a..2926eacc9 100644 --- a/src/middleware/normalize.rs +++ b/src/middleware/normalize.rs @@ -105,62 +105,56 @@ mod tests { use super::*; use crate::dev::ServiceRequest; - use crate::test::{block_on, call_service, init_service, TestRequest}; + use crate::test::{call_service, init_service, TestRequest}; use crate::{web, App, HttpResponse}; - #[test] - fn test_wrap() { - block_on(async { - let mut app = init_service( - App::new() - .wrap(NormalizePath::default()) - .service(web::resource("/v1/something/").to(|| HttpResponse::Ok())), - ) - .await; + #[actix_rt::test] + async fn test_wrap() { + let mut app = init_service( + App::new() + .wrap(NormalizePath::default()) + .service(web::resource("/v1/something/").to(|| HttpResponse::Ok())), + ) + .await; - let req = TestRequest::with_uri("/v1//something////").to_request(); - let res = call_service(&mut app, req).await; - assert!(res.status().is_success()); - }) + let req = TestRequest::with_uri("/v1//something////").to_request(); + let res = call_service(&mut app, req).await; + assert!(res.status().is_success()); } - #[test] - fn test_in_place_normalization() { - block_on(async { - let srv = |req: ServiceRequest| { - assert_eq!("/v1/something/", req.path()); - ok(req.into_response(HttpResponse::Ok().finish())) - }; + #[actix_rt::test] + async fn test_in_place_normalization() { + let srv = |req: ServiceRequest| { + assert_eq!("/v1/something/", req.path()); + ok(req.into_response(HttpResponse::Ok().finish())) + }; - let mut normalize = NormalizePath - .new_transform(srv.into_service()) - .await - .unwrap(); + let mut normalize = NormalizePath + .new_transform(srv.into_service()) + .await + .unwrap(); - let req = TestRequest::with_uri("/v1//something////").to_srv_request(); - let res = normalize.call(req).await.unwrap(); - assert!(res.status().is_success()); - }) + let req = TestRequest::with_uri("/v1//something////").to_srv_request(); + let res = normalize.call(req).await.unwrap(); + assert!(res.status().is_success()); } - #[test] - fn should_normalize_nothing() { - block_on(async { - const URI: &str = "/v1/something/"; + #[actix_rt::test] + async fn should_normalize_nothing() { + const URI: &str = "/v1/something/"; - let srv = |req: ServiceRequest| { - assert_eq!(URI, req.path()); - ok(req.into_response(HttpResponse::Ok().finish())) - }; + let srv = |req: ServiceRequest| { + assert_eq!(URI, req.path()); + ok(req.into_response(HttpResponse::Ok().finish())) + }; - let mut normalize = NormalizePath - .new_transform(srv.into_service()) - .await - .unwrap(); + let mut normalize = NormalizePath + .new_transform(srv.into_service()) + .await + .unwrap(); - let req = TestRequest::with_uri(URI).to_srv_request(); - let res = normalize.call(req).await.unwrap(); - assert!(res.status().is_success()); - }) + let req = TestRequest::with_uri(URI).to_srv_request(); + let res = normalize.call(req).await.unwrap(); + assert!(res.status().is_success()); } } diff --git a/src/request.rs b/src/request.rs index 19072fcb1..84f0503c0 100644 --- a/src/request.rs +++ b/src/request.rs @@ -350,7 +350,7 @@ mod tests { use super::*; use crate::dev::{ResourceDef, ResourceMap}; use crate::http::{header, StatusCode}; - use crate::test::{block_on, call_service, init_service, TestRequest}; + use crate::test::{call_service, init_service, TestRequest}; use crate::{web, App, HttpResponse}; #[test] @@ -466,16 +466,62 @@ mod tests { ); } - #[test] - fn test_app_data() { - block_on(async { - let mut srv = init_service(App::new().data(10usize).service( - web::resource("/").to(|req: HttpRequest| { - if req.app_data::().is_some() { - HttpResponse::Ok() - } else { - HttpResponse::BadRequest() - } + #[actix_rt::test] + async fn test_app_data() { + let mut srv = init_service(App::new().data(10usize).service( + web::resource("/").to(|req: HttpRequest| { + if req.app_data::().is_some() { + HttpResponse::Ok() + } else { + HttpResponse::BadRequest() + } + }), + )) + .await; + + let req = TestRequest::default().to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + + let mut srv = init_service(App::new().data(10u32).service( + web::resource("/").to(|req: HttpRequest| { + if req.app_data::().is_some() { + HttpResponse::Ok() + } else { + HttpResponse::BadRequest() + } + }), + )) + .await; + + let req = TestRequest::default().to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::BAD_REQUEST); + } + + #[actix_rt::test] + async fn test_extensions_dropped() { + struct Tracker { + pub dropped: bool, + } + struct Foo { + tracker: Rc>, + } + impl Drop for Foo { + fn drop(&mut self) { + self.tracker.borrow_mut().dropped = true; + } + } + + let tracker = Rc::new(RefCell::new(Tracker { dropped: false })); + { + let tracker2 = Rc::clone(&tracker); + let mut srv = init_service(App::new().data(10u32).service( + web::resource("/").to(move |req: HttpRequest| { + req.extensions_mut().insert(Foo { + tracker: Rc::clone(&tracker2), + }); + HttpResponse::Ok() }), )) .await; @@ -483,58 +529,8 @@ mod tests { let req = TestRequest::default().to_request(); let resp = call_service(&mut srv, req).await; assert_eq!(resp.status(), StatusCode::OK); + } - let mut srv = init_service(App::new().data(10u32).service( - web::resource("/").to(|req: HttpRequest| { - if req.app_data::().is_some() { - HttpResponse::Ok() - } else { - HttpResponse::BadRequest() - } - }), - )) - .await; - - let req = TestRequest::default().to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::BAD_REQUEST); - }) - } - - #[test] - fn test_extensions_dropped() { - block_on(async { - struct Tracker { - pub dropped: bool, - } - struct Foo { - tracker: Rc>, - } - impl Drop for Foo { - fn drop(&mut self) { - self.tracker.borrow_mut().dropped = true; - } - } - - let tracker = Rc::new(RefCell::new(Tracker { dropped: false })); - { - let tracker2 = Rc::clone(&tracker); - let mut srv = init_service(App::new().data(10u32).service( - web::resource("/").to(move |req: HttpRequest| { - req.extensions_mut().insert(Foo { - tracker: Rc::clone(&tracker2), - }); - HttpResponse::Ok() - }), - )) - .await; - - let req = TestRequest::default().to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); - } - - assert!(tracker.borrow().dropped); - }) + assert!(tracker.borrow().dropped); } } diff --git a/src/resource.rs b/src/resource.rs index 758e2f282..866cbecf5 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -6,12 +6,11 @@ use std::rc::Rc; use std::task::{Context, Poll}; use actix_http::{Error, Extensions, Response}; -use actix_service::boxed::{self, BoxedNewService, BoxedService}; +use actix_service::boxed::{self, BoxService, BoxServiceFactory}; use actix_service::{ apply, apply_fn_factory, IntoServiceFactory, Service, ServiceFactory, Transform, }; use futures::future::{ok, Either, LocalBoxFuture, Ready}; -use pin_project::pin_project; use crate::data::Data; use crate::dev::{insert_slash, AppService, HttpServiceFactory, ResourceDef}; @@ -22,8 +21,8 @@ use crate::responder::Responder; use crate::route::{CreateRouteService, Route, RouteService}; use crate::service::{ServiceRequest, ServiceResponse}; -type HttpService = BoxedService; -type HttpNewService = BoxedNewService<(), ServiceRequest, ServiceResponse, Error, ()>; +type HttpService = BoxService; +type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>; /// *Resource* is an entry in resources table which corresponds to requested URL. /// @@ -585,40 +584,20 @@ impl ServiceFactory for ResourceEndpoint { mod tests { use std::time::Duration; + use actix_rt::time::delay_for; use actix_service::Service; - use futures::future::{ok, Future}; - use tokio_timer::delay_for; + use futures::future::ok; use crate::http::{header, HeaderValue, Method, StatusCode}; use crate::middleware::DefaultHeaders; - use crate::service::{ServiceRequest, ServiceResponse}; - use crate::test::{block_on, call_service, init_service, TestRequest}; + use crate::service::ServiceRequest; + use crate::test::{call_service, init_service, TestRequest}; use crate::{guard, web, App, Error, HttpResponse}; - fn md( - req: ServiceRequest, - srv: &mut S, - ) -> impl Future, Error>> - where - S: Service< - Request = ServiceRequest, - Response = ServiceResponse, - Error = Error, - >, - { - let fut = srv.call(req); - async move { - let mut res = fut.await?; - res.headers_mut() - .insert(header::CONTENT_TYPE, HeaderValue::from_static("0001")); - Ok(res) - } - } - - #[test] - fn test_middleware() { - block_on(async { - let mut srv = init_service( + #[actix_rt::test] + async fn test_middleware() { + let mut srv = + init_service( App::new().service( web::resource("/test") .name("test") @@ -630,185 +609,173 @@ mod tests { ), ) .await; - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - HeaderValue::from_static("0001") - ); - }) + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + HeaderValue::from_static("0001") + ); } - #[test] - fn test_middleware_fn() { - block_on(async { - let mut srv = init_service( - App::new().service( - web::resource("/test") - .wrap_fn(|req, srv| { - let fut = srv.call(req); - async { - fut.await.map(|mut res| { - res.headers_mut().insert( - header::CONTENT_TYPE, - HeaderValue::from_static("0001"), - ); - res - }) - } - }) - .route(web::get().to(|| HttpResponse::Ok())), - ), - ) + #[actix_rt::test] + async fn test_middleware_fn() { + let mut srv = init_service( + App::new().service( + web::resource("/test") + .wrap_fn(|req, srv| { + let fut = srv.call(req); + async { + fut.await.map(|mut res| { + res.headers_mut().insert( + header::CONTENT_TYPE, + HeaderValue::from_static("0001"), + ); + res + }) + } + }) + .route(web::get().to(|| HttpResponse::Ok())), + ), + ) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + HeaderValue::from_static("0001") + ); + } + + #[actix_rt::test] + async fn test_to() { + let mut srv = + init_service(App::new().service(web::resource("/test").to(|| { + async { + delay_for(Duration::from_millis(100)).await; + Ok::<_, Error>(HttpResponse::Ok()) + } + }))) .await; - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - HeaderValue::from_static("0001") - ); - }) + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); } - #[test] - fn test_to() { - block_on(async { - let mut srv = - init_service(App::new().service(web::resource("/test").to(|| { - async { - delay_for(Duration::from_millis(100)).await; - Ok::<_, Error>(HttpResponse::Ok()) - } - }))) - .await; - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); - }) - } + #[actix_rt::test] + async fn test_default_resource() { + let mut srv = init_service( + App::new() + .service( + web::resource("/test").route(web::get().to(|| HttpResponse::Ok())), + ) + .default_service(|r: ServiceRequest| { + ok(r.into_response(HttpResponse::BadRequest())) + }), + ) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); - #[test] - fn test_default_resource() { - block_on(async { - let mut srv = init_service( - App::new() - .service( - web::resource("/test") - .route(web::get().to(|| HttpResponse::Ok())), - ) + let req = TestRequest::with_uri("/test") + .method(Method::POST) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); + + let mut srv = init_service( + App::new().service( + web::resource("/test") + .route(web::get().to(|| HttpResponse::Ok())) .default_service(|r: ServiceRequest| { ok(r.into_response(HttpResponse::BadRequest())) }), - ) - .await; - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); + ), + ) + .await; - let req = TestRequest::with_uri("/test") - .method(Method::POST) - .to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); - let mut srv = init_service( - App::new().service( - web::resource("/test") - .route(web::get().to(|| HttpResponse::Ok())) - .default_service(|r: ServiceRequest| { - ok(r.into_response(HttpResponse::BadRequest())) - }), + let req = TestRequest::with_uri("/test") + .method(Method::POST) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::BAD_REQUEST); + } + + #[actix_rt::test] + async fn test_resource_guards() { + let mut srv = init_service( + App::new() + .service( + web::resource("/test/{p}") + .guard(guard::Get()) + .to(|| HttpResponse::Ok()), + ) + .service( + web::resource("/test/{p}") + .guard(guard::Put()) + .to(|| HttpResponse::Created()), + ) + .service( + web::resource("/test/{p}") + .guard(guard::Delete()) + .to(|| HttpResponse::NoContent()), ), - ) - .await; + ) + .await; - let req = TestRequest::with_uri("/test").to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/test/it") + .method(Method::GET) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); - let req = TestRequest::with_uri("/test") - .method(Method::POST) - .to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::BAD_REQUEST); - }) + let req = TestRequest::with_uri("/test/it") + .method(Method::PUT) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::CREATED); + + let req = TestRequest::with_uri("/test/it") + .method(Method::DELETE) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::NO_CONTENT); } - #[test] - fn test_resource_guards() { - block_on(async { - let mut srv = init_service( - App::new() - .service( - web::resource("/test/{p}") - .guard(guard::Get()) - .to(|| HttpResponse::Ok()), - ) - .service( - web::resource("/test/{p}") - .guard(guard::Put()) - .to(|| HttpResponse::Created()), - ) - .service( - web::resource("/test/{p}") - .guard(guard::Delete()) - .to(|| HttpResponse::NoContent()), - ), - ) - .await; + #[actix_rt::test] + async fn test_data() { + let mut srv = init_service( + App::new() + .data(1.0f64) + .data(1usize) + .register_data(web::Data::new('-')) + .service( + web::resource("/test") + .data(10usize) + .register_data(web::Data::new('*')) + .guard(guard::Get()) + .to( + |data1: web::Data, + data2: web::Data, + data3: web::Data| { + assert_eq!(*data1, 10); + assert_eq!(*data2, '*'); + assert_eq!(*data3, 1.0); + HttpResponse::Ok() + }, + ), + ), + ) + .await; - let req = TestRequest::with_uri("/test/it") - .method(Method::GET) - .to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); - - let req = TestRequest::with_uri("/test/it") - .method(Method::PUT) - .to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::CREATED); - - let req = TestRequest::with_uri("/test/it") - .method(Method::DELETE) - .to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::NO_CONTENT); - }) - } - - #[test] - fn test_data() { - block_on(async { - let mut srv = init_service( - App::new() - .data(1.0f64) - .data(1usize) - .register_data(web::Data::new('-')) - .service( - web::resource("/test") - .data(10usize) - .register_data(web::Data::new('*')) - .guard(guard::Get()) - .to( - |data1: web::Data, - data2: web::Data, - data3: web::Data| { - assert_eq!(*data1, 10); - assert_eq!(*data2, '*'); - assert_eq!(*data3, 1.0); - HttpResponse::Ok() - }, - ), - ), - ) - .await; - - let req = TestRequest::get().uri("/test").to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); - }) + let req = TestRequest::get().uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); } } diff --git a/src/responder.rs b/src/responder.rs index fd86bb686..7b30315f5 100644 --- a/src/responder.rs +++ b/src/responder.rs @@ -10,7 +10,7 @@ use actix_http::http::{ }; use actix_http::{Error, Response, ResponseBuilder}; use bytes::{Bytes, BytesMut}; -use futures::future::{err, ok, Either as EitherFuture, LocalBoxFuture, Ready}; +use futures::future::{err, ok, Either as EitherFuture, Ready}; use futures::ready; use pin_project::{pin_project, project}; @@ -457,37 +457,34 @@ pub(crate) mod tests { use super::*; use crate::dev::{Body, ResponseBody}; use crate::http::{header::CONTENT_TYPE, HeaderValue, StatusCode}; - use crate::test::{block_on, init_service, TestRequest}; + use crate::test::{init_service, TestRequest}; use crate::{error, web, App, HttpResponse}; - #[test] - fn test_option_responder() { - block_on(async { - let mut srv = init_service( - App::new() - .service( - web::resource("/none") - .to(|| async { Option::<&'static str>::None }), - ) - .service(web::resource("/some").to(|| async { Some("some") })), - ) - .await; + #[actix_rt::test] + async fn test_option_responder() { + let mut srv = init_service( + App::new() + .service( + web::resource("/none").to(|| async { Option::<&'static str>::None }), + ) + .service(web::resource("/some").to(|| async { Some("some") })), + ) + .await; - let req = TestRequest::with_uri("/none").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); + let req = TestRequest::with_uri("/none").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); - let req = TestRequest::with_uri("/some").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - match resp.response().body() { - ResponseBody::Body(Body::Bytes(ref b)) => { - let bytes: Bytes = b.clone().into(); - assert_eq!(bytes, Bytes::from_static(b"some")); - } - _ => panic!(), + let req = TestRequest::with_uri("/some").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + match resp.response().body() { + ResponseBody::Body(Body::Bytes(ref b)) => { + let bytes: Bytes = b.clone().into(); + assert_eq!(bytes, Bytes::from_static(b"some")); } - }) + _ => panic!(), + } } pub(crate) trait BodyTest { @@ -516,153 +513,142 @@ pub(crate) mod tests { } } - #[test] - fn test_responder() { - block_on(async { - let req = TestRequest::default().to_http_request(); + #[actix_rt::test] + async fn test_responder() { + let req = TestRequest::default().to_http_request(); - let resp: HttpResponse = "test".respond_to(&req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!(resp.body().bin_ref(), b"test"); - assert_eq!( - resp.headers().get(CONTENT_TYPE).unwrap(), - HeaderValue::from_static("text/plain; charset=utf-8") - ); + let resp: HttpResponse = "test".respond_to(&req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.body().bin_ref(), b"test"); + assert_eq!( + resp.headers().get(CONTENT_TYPE).unwrap(), + HeaderValue::from_static("text/plain; charset=utf-8") + ); - let resp: HttpResponse = b"test".respond_to(&req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!(resp.body().bin_ref(), b"test"); - assert_eq!( - resp.headers().get(CONTENT_TYPE).unwrap(), - HeaderValue::from_static("application/octet-stream") - ); + let resp: HttpResponse = b"test".respond_to(&req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.body().bin_ref(), b"test"); + assert_eq!( + resp.headers().get(CONTENT_TYPE).unwrap(), + HeaderValue::from_static("application/octet-stream") + ); - let resp: HttpResponse = "test".to_string().respond_to(&req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!(resp.body().bin_ref(), b"test"); - assert_eq!( - resp.headers().get(CONTENT_TYPE).unwrap(), - HeaderValue::from_static("text/plain; charset=utf-8") - ); + let resp: HttpResponse = "test".to_string().respond_to(&req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.body().bin_ref(), b"test"); + assert_eq!( + resp.headers().get(CONTENT_TYPE).unwrap(), + HeaderValue::from_static("text/plain; charset=utf-8") + ); - let resp: HttpResponse = - (&"test".to_string()).respond_to(&req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!(resp.body().bin_ref(), b"test"); - assert_eq!( - resp.headers().get(CONTENT_TYPE).unwrap(), - HeaderValue::from_static("text/plain; charset=utf-8") - ); + let resp: HttpResponse = (&"test".to_string()).respond_to(&req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.body().bin_ref(), b"test"); + assert_eq!( + resp.headers().get(CONTENT_TYPE).unwrap(), + HeaderValue::from_static("text/plain; charset=utf-8") + ); - let resp: HttpResponse = - Bytes::from_static(b"test").respond_to(&req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!(resp.body().bin_ref(), b"test"); - assert_eq!( - resp.headers().get(CONTENT_TYPE).unwrap(), - HeaderValue::from_static("application/octet-stream") - ); + let resp: HttpResponse = + Bytes::from_static(b"test").respond_to(&req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.body().bin_ref(), b"test"); + assert_eq!( + resp.headers().get(CONTENT_TYPE).unwrap(), + HeaderValue::from_static("application/octet-stream") + ); - let resp: HttpResponse = BytesMut::from(b"test".as_ref()) - .respond_to(&req) - .await - .unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!(resp.body().bin_ref(), b"test"); - assert_eq!( - resp.headers().get(CONTENT_TYPE).unwrap(), - HeaderValue::from_static("application/octet-stream") - ); - - // InternalError - let resp: HttpResponse = - error::InternalError::new("err", StatusCode::BAD_REQUEST) - .respond_to(&req) - .await - .unwrap(); - assert_eq!(resp.status(), StatusCode::BAD_REQUEST); - }) - } - - #[test] - fn test_result_responder() { - block_on(async { - let req = TestRequest::default().to_http_request(); - - // Result - let resp: HttpResponse = Ok::<_, Error>("test".to_string()) - .respond_to(&req) - .await - .unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!(resp.body().bin_ref(), b"test"); - assert_eq!( - resp.headers().get(CONTENT_TYPE).unwrap(), - HeaderValue::from_static("text/plain; charset=utf-8") - ); - - let res = Err::(error::InternalError::new( - "err", - StatusCode::BAD_REQUEST, - )) + let resp: HttpResponse = BytesMut::from(b"test".as_ref()) .respond_to(&req) - .await; - assert!(res.is_err()); - }) + .await + .unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.body().bin_ref(), b"test"); + assert_eq!( + resp.headers().get(CONTENT_TYPE).unwrap(), + HeaderValue::from_static("application/octet-stream") + ); + + // InternalError + let resp: HttpResponse = + error::InternalError::new("err", StatusCode::BAD_REQUEST) + .respond_to(&req) + .await + .unwrap(); + assert_eq!(resp.status(), StatusCode::BAD_REQUEST); } - #[test] - fn test_custom_responder() { - block_on(async { - let req = TestRequest::default().to_http_request(); - let res = "test" - .to_string() - .with_status(StatusCode::BAD_REQUEST) - .respond_to(&req) - .await - .unwrap(); - assert_eq!(res.status(), StatusCode::BAD_REQUEST); - assert_eq!(res.body().bin_ref(), b"test"); + #[actix_rt::test] + async fn test_result_responder() { + let req = TestRequest::default().to_http_request(); - let res = "test" - .to_string() - .with_header("content-type", "json") - .respond_to(&req) - .await - .unwrap(); + // Result + let resp: HttpResponse = Ok::<_, Error>("test".to_string()) + .respond_to(&req) + .await + .unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!(resp.body().bin_ref(), b"test"); + assert_eq!( + resp.headers().get(CONTENT_TYPE).unwrap(), + HeaderValue::from_static("text/plain; charset=utf-8") + ); - assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.body().bin_ref(), b"test"); - assert_eq!( - res.headers().get(CONTENT_TYPE).unwrap(), - HeaderValue::from_static("json") - ); - }) + let res = + Err::(error::InternalError::new("err", StatusCode::BAD_REQUEST)) + .respond_to(&req) + .await; + assert!(res.is_err()); } - #[test] - fn test_tuple_responder_with_status_code() { - block_on(async { - let req = TestRequest::default().to_http_request(); - let res = ("test".to_string(), StatusCode::BAD_REQUEST) - .respond_to(&req) - .await - .unwrap(); - assert_eq!(res.status(), StatusCode::BAD_REQUEST); - assert_eq!(res.body().bin_ref(), b"test"); + #[actix_rt::test] + async fn test_custom_responder() { + let req = TestRequest::default().to_http_request(); + let res = "test" + .to_string() + .with_status(StatusCode::BAD_REQUEST) + .respond_to(&req) + .await + .unwrap(); + assert_eq!(res.status(), StatusCode::BAD_REQUEST); + assert_eq!(res.body().bin_ref(), b"test"); - let req = TestRequest::default().to_http_request(); - let res = ("test".to_string(), StatusCode::OK) - .with_header("content-type", "json") - .respond_to(&req) - .await - .unwrap(); - assert_eq!(res.status(), StatusCode::OK); - assert_eq!(res.body().bin_ref(), b"test"); - assert_eq!( - res.headers().get(CONTENT_TYPE).unwrap(), - HeaderValue::from_static("json") - ); - }) + let res = "test" + .to_string() + .with_header("content-type", "json") + .respond_to(&req) + .await + .unwrap(); + + assert_eq!(res.status(), StatusCode::OK); + assert_eq!(res.body().bin_ref(), b"test"); + assert_eq!( + res.headers().get(CONTENT_TYPE).unwrap(), + HeaderValue::from_static("json") + ); + } + + #[actix_rt::test] + async fn test_tuple_responder_with_status_code() { + let req = TestRequest::default().to_http_request(); + let res = ("test".to_string(), StatusCode::BAD_REQUEST) + .respond_to(&req) + .await + .unwrap(); + assert_eq!(res.status(), StatusCode::BAD_REQUEST); + assert_eq!(res.body().bin_ref(), b"test"); + + let req = TestRequest::default().to_http_request(); + let res = ("test".to_string(), StatusCode::OK) + .with_header("content-type", "json") + .respond_to(&req) + .await + .unwrap(); + assert_eq!(res.status(), StatusCode::OK); + assert_eq!(res.body().bin_ref(), b"test"); + assert_eq!( + res.headers().get(CONTENT_TYPE).unwrap(), + HeaderValue::from_static("json") + ); } } diff --git a/src/route.rs b/src/route.rs index 3ebfc3f52..93f88bfe2 100644 --- a/src/route.rs +++ b/src/route.rs @@ -5,7 +5,7 @@ use std::task::{Context, Poll}; use actix_http::{http::Method, Error}; use actix_service::{Service, ServiceFactory}; -use futures::future::{ok, ready, Either, FutureExt, LocalBoxFuture, Ready}; +use futures::future::{ready, FutureExt, LocalBoxFuture}; use crate::extract::FromRequest; use crate::guard::{self, Guard}; @@ -342,93 +342,90 @@ where mod tests { use std::time::Duration; + use actix_rt::time::delay_for; use bytes::Bytes; - use futures::Future; use serde_derive::Serialize; - use tokio_timer::delay_for; use crate::http::{Method, StatusCode}; - use crate::test::{block_on, call_service, init_service, read_body, TestRequest}; - use crate::{error, web, App, Error, HttpResponse}; + use crate::test::{call_service, init_service, read_body, TestRequest}; + use crate::{error, web, App, HttpResponse}; #[derive(Serialize, PartialEq, Debug)] struct MyObject { name: String, } - #[test] - fn test_route() { - block_on(async { - let mut srv = init_service( - App::new() - .service( - web::resource("/test") - .route(web::get().to(|| HttpResponse::Ok())) - .route(web::put().to(|| { - async { - Err::(error::ErrorBadRequest("err")) - } - })) - .route(web::post().to(|| { - async { - delay_for(Duration::from_millis(100)).await; - HttpResponse::Created() - } - })) - .route(web::delete().to(|| { - async { - delay_for(Duration::from_millis(100)).await; - Err::(error::ErrorBadRequest("err")) - } - })), - ) - .service(web::resource("/json").route(web::get().to(|| { - async { - delay_for(Duration::from_millis(25)).await; - web::Json(MyObject { - name: "test".to_string(), - }) - } - }))), - ) - .await; + #[actix_rt::test] + async fn test_route() { + let mut srv = init_service( + App::new() + .service( + web::resource("/test") + .route(web::get().to(|| HttpResponse::Ok())) + .route(web::put().to(|| { + async { + Err::(error::ErrorBadRequest("err")) + } + })) + .route(web::post().to(|| { + async { + delay_for(Duration::from_millis(100)).await; + HttpResponse::Created() + } + })) + .route(web::delete().to(|| { + async { + delay_for(Duration::from_millis(100)).await; + Err::(error::ErrorBadRequest("err")) + } + })), + ) + .service(web::resource("/json").route(web::get().to(|| { + async { + delay_for(Duration::from_millis(25)).await; + web::Json(MyObject { + name: "test".to_string(), + }) + } + }))), + ) + .await; - let req = TestRequest::with_uri("/test") - .method(Method::GET) - .to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/test") + .method(Method::GET) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); - let req = TestRequest::with_uri("/test") - .method(Method::POST) - .to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::CREATED); + let req = TestRequest::with_uri("/test") + .method(Method::POST) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::CREATED); - let req = TestRequest::with_uri("/test") - .method(Method::PUT) - .to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::BAD_REQUEST); + let req = TestRequest::with_uri("/test") + .method(Method::PUT) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::BAD_REQUEST); - let req = TestRequest::with_uri("/test") - .method(Method::DELETE) - .to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::BAD_REQUEST); + let req = TestRequest::with_uri("/test") + .method(Method::DELETE) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::BAD_REQUEST); - let req = TestRequest::with_uri("/test") - .method(Method::HEAD) - .to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); + let req = TestRequest::with_uri("/test") + .method(Method::HEAD) + .to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); - let req = TestRequest::with_uri("/json").to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/json").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); - let body = read_body(resp).await; - assert_eq!(body, Bytes::from_static(b"{\"name\":\"test\"}")); - }) + let body = read_body(resp).await; + assert_eq!(body, Bytes::from_static(b"{\"name\":\"test\"}")); } } diff --git a/src/scope.rs b/src/scope.rs index 9bec0a1ff..db6f5da57 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -6,7 +6,7 @@ use std::task::{Context, Poll}; use actix_http::{Extensions, Response}; use actix_router::{ResourceDef, ResourceInfo, Router}; -use actix_service::boxed::{self, BoxedNewService, BoxedService}; +use actix_service::boxed::{self, BoxService, BoxServiceFactory}; use actix_service::{ apply, apply_fn_factory, IntoServiceFactory, Service, ServiceFactory, Transform, }; @@ -25,8 +25,8 @@ use crate::service::{ }; type Guards = Vec>; -type HttpService = BoxedService; -type HttpNewService = BoxedNewService<(), ServiceRequest, ServiceResponse, Error, ()>; +type HttpService = BoxService; +type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>; type BoxedResponse = LocalBoxFuture<'static, Result>; /// Resources scope. @@ -666,443 +666,389 @@ mod tests { use actix_service::Service; use bytes::Bytes; use futures::future::ok; - use futures::Future; use crate::dev::{Body, ResponseBody}; use crate::http::{header, HeaderValue, Method, StatusCode}; use crate::middleware::DefaultHeaders; - use crate::service::{ServiceRequest, ServiceResponse}; - use crate::test::{block_on, call_service, init_service, read_body, TestRequest}; - use crate::{guard, web, App, Error, HttpRequest, HttpResponse}; + use crate::service::ServiceRequest; + use crate::test::{call_service, init_service, read_body, TestRequest}; + use crate::{guard, web, App, HttpRequest, HttpResponse}; - #[test] - fn test_scope() { - block_on(async { - let mut srv = init_service( - App::new().service( - web::scope("/app") - .service(web::resource("/path1").to(|| HttpResponse::Ok())), - ), - ) - .await; + #[actix_rt::test] + async fn test_scope() { + let mut srv = init_service( + App::new().service( + web::scope("/app") + .service(web::resource("/path1").to(|| HttpResponse::Ok())), + ), + ) + .await; - let req = TestRequest::with_uri("/app/path1").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - }) + let req = TestRequest::with_uri("/app/path1").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); } - #[test] - fn test_scope_root() { - block_on(async { - let mut srv = init_service( - App::new().service( - web::scope("/app") + #[actix_rt::test] + async fn test_scope_root() { + let mut srv = init_service( + App::new().service( + web::scope("/app") + .service(web::resource("").to(|| HttpResponse::Ok())) + .service(web::resource("/").to(|| HttpResponse::Created())), + ), + ) + .await; + + let req = TestRequest::with_uri("/app").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + + let req = TestRequest::with_uri("/app/").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::CREATED); + } + + #[actix_rt::test] + async fn test_scope_root2() { + let mut srv = init_service(App::new().service( + web::scope("/app/").service(web::resource("").to(|| HttpResponse::Ok())), + )) + .await; + + let req = TestRequest::with_uri("/app").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + + let req = TestRequest::with_uri("/app/").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + } + + #[actix_rt::test] + async fn test_scope_root3() { + let mut srv = init_service(App::new().service( + web::scope("/app/").service(web::resource("/").to(|| HttpResponse::Ok())), + )) + .await; + + let req = TestRequest::with_uri("/app").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + + let req = TestRequest::with_uri("/app/").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + } + + #[actix_rt::test] + async fn test_scope_route() { + let mut srv = init_service( + App::new().service( + web::scope("app") + .route("/path1", web::get().to(|| HttpResponse::Ok())) + .route("/path1", web::delete().to(|| HttpResponse::Ok())), + ), + ) + .await; + + let req = TestRequest::with_uri("/app/path1").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + + let req = TestRequest::with_uri("/app/path1") + .method(Method::DELETE) + .to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + + let req = TestRequest::with_uri("/app/path1") + .method(Method::POST) + .to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + } + + #[actix_rt::test] + async fn test_scope_route_without_leading_slash() { + let mut srv = init_service( + App::new().service( + web::scope("app").service( + web::resource("path1") + .route(web::get().to(|| HttpResponse::Ok())) + .route(web::delete().to(|| HttpResponse::Ok())), + ), + ), + ) + .await; + + let req = TestRequest::with_uri("/app/path1").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + + let req = TestRequest::with_uri("/app/path1") + .method(Method::DELETE) + .to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + + let req = TestRequest::with_uri("/app/path1") + .method(Method::POST) + .to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); + } + + #[actix_rt::test] + async fn test_scope_guard() { + let mut srv = init_service( + App::new().service( + web::scope("/app") + .guard(guard::Get()) + .service(web::resource("/path1").to(|| HttpResponse::Ok())), + ), + ) + .await; + + let req = TestRequest::with_uri("/app/path1") + .method(Method::POST) + .to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + + let req = TestRequest::with_uri("/app/path1") + .method(Method::GET) + .to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + } + + #[actix_rt::test] + async fn test_scope_variable_segment() { + let mut srv = + init_service(App::new().service(web::scope("/ab-{project}").service( + web::resource("/path1").to(|r: HttpRequest| { + async move { + HttpResponse::Ok() + .body(format!("project: {}", &r.match_info()["project"])) + } + }), + ))) + .await; + + let req = TestRequest::with_uri("/ab-project1/path1").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + + match resp.response().body() { + ResponseBody::Body(Body::Bytes(ref b)) => { + let bytes: Bytes = b.clone().into(); + assert_eq!(bytes, Bytes::from_static(b"project: project1")); + } + _ => panic!(), + } + + let req = TestRequest::with_uri("/aa-project1/path1").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + } + + #[actix_rt::test] + async fn test_nested_scope() { + let mut srv = init_service( + App::new().service( + web::scope("/app") + .service(web::scope("/t1").service( + web::resource("/path1").to(|| HttpResponse::Created()), + )), + ), + ) + .await; + + let req = TestRequest::with_uri("/app/t1/path1").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::CREATED); + } + + #[actix_rt::test] + async fn test_nested_scope_no_slash() { + let mut srv = init_service( + App::new().service( + web::scope("/app") + .service(web::scope("t1").service( + web::resource("/path1").to(|| HttpResponse::Created()), + )), + ), + ) + .await; + + let req = TestRequest::with_uri("/app/t1/path1").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::CREATED); + } + + #[actix_rt::test] + async fn test_nested_scope_root() { + let mut srv = init_service( + App::new().service( + web::scope("/app").service( + web::scope("/t1") .service(web::resource("").to(|| HttpResponse::Ok())) .service(web::resource("/").to(|| HttpResponse::Created())), ), - ) - .await; + ), + ) + .await; - let req = TestRequest::with_uri("/app").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/app/t1").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); - let req = TestRequest::with_uri("/app/").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::CREATED); - }) + let req = TestRequest::with_uri("/app/t1/").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::CREATED); } - #[test] - fn test_scope_root2() { - block_on(async { - let mut srv = init_service(App::new().service( - web::scope("/app/").service(web::resource("").to(|| HttpResponse::Ok())), - )) - .await; - - let req = TestRequest::with_uri("/app").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); - - let req = TestRequest::with_uri("/app/").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - }) - } - - #[test] - fn test_scope_root3() { - block_on(async { - let mut srv = init_service( - App::new().service( - web::scope("/app/") - .service(web::resource("/").to(|| HttpResponse::Ok())), - ), - ) - .await; - - let req = TestRequest::with_uri("/app").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); - - let req = TestRequest::with_uri("/app/").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); - }) - } - - #[test] - fn test_scope_route() { - block_on(async { - let mut srv = init_service( - App::new().service( - web::scope("app") - .route("/path1", web::get().to(|| HttpResponse::Ok())) - .route("/path1", web::delete().to(|| HttpResponse::Ok())), - ), - ) - .await; - - let req = TestRequest::with_uri("/app/path1").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - - let req = TestRequest::with_uri("/app/path1") - .method(Method::DELETE) - .to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - - let req = TestRequest::with_uri("/app/path1") - .method(Method::POST) - .to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); - }) - } - - #[test] - fn test_scope_route_without_leading_slash() { - block_on(async { - let mut srv = init_service( - App::new().service( - web::scope("app").service( - web::resource("path1") - .route(web::get().to(|| HttpResponse::Ok())) - .route(web::delete().to(|| HttpResponse::Ok())), - ), - ), - ) - .await; - - let req = TestRequest::with_uri("/app/path1").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - - let req = TestRequest::with_uri("/app/path1") - .method(Method::DELETE) - .to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - - let req = TestRequest::with_uri("/app/path1") - .method(Method::POST) - .to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); - }) - } - - #[test] - fn test_scope_guard() { - block_on(async { - let mut srv = init_service( - App::new().service( - web::scope("/app") + #[actix_rt::test] + async fn test_nested_scope_filter() { + let mut srv = init_service( + App::new().service( + web::scope("/app").service( + web::scope("/t1") .guard(guard::Get()) .service(web::resource("/path1").to(|| HttpResponse::Ok())), ), - ) - .await; + ), + ) + .await; - let req = TestRequest::with_uri("/app/path1") - .method(Method::POST) - .to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); + let req = TestRequest::with_uri("/app/t1/path1") + .method(Method::POST) + .to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); - let req = TestRequest::with_uri("/app/path1") - .method(Method::GET) - .to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - }) + let req = TestRequest::with_uri("/app/t1/path1") + .method(Method::GET) + .to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); } - #[test] - fn test_scope_variable_segment() { - block_on(async { - let mut srv = - init_service(App::new().service(web::scope("/ab-{project}").service( - web::resource("/path1").to(|r: HttpRequest| { - async move { - HttpResponse::Ok() - .body(format!("project: {}", &r.match_info()["project"])) - } - }), - ))) - .await; + #[actix_rt::test] + async fn test_nested_scope_with_variable_segment() { + let mut srv = init_service(App::new().service(web::scope("/app").service( + web::scope("/{project_id}").service(web::resource("/path1").to( + |r: HttpRequest| { + async move { + HttpResponse::Created() + .body(format!("project: {}", &r.match_info()["project_id"])) + } + }, + )), + ))) + .await; - let req = TestRequest::with_uri("/ab-project1/path1").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/app/project_1/path1").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::CREATED); - match resp.response().body() { - ResponseBody::Body(Body::Bytes(ref b)) => { - let bytes: Bytes = b.clone().into(); - assert_eq!(bytes, Bytes::from_static(b"project: project1")); - } - _ => panic!(), + match resp.response().body() { + ResponseBody::Body(Body::Bytes(ref b)) => { + let bytes: Bytes = b.clone().into(); + assert_eq!(bytes, Bytes::from_static(b"project: project_1")); } - - let req = TestRequest::with_uri("/aa-project1/path1").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); - }) - } - - #[test] - fn test_nested_scope() { - block_on(async { - let mut srv = - init_service(App::new().service( - web::scope("/app").service(web::scope("/t1").service( - web::resource("/path1").to(|| HttpResponse::Created()), - )), - )) - .await; - - let req = TestRequest::with_uri("/app/t1/path1").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::CREATED); - }) - } - - #[test] - fn test_nested_scope_no_slash() { - block_on(async { - let mut srv = - init_service(App::new().service( - web::scope("/app").service(web::scope("t1").service( - web::resource("/path1").to(|| HttpResponse::Created()), - )), - )) - .await; - - let req = TestRequest::with_uri("/app/t1/path1").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::CREATED); - }) - } - - #[test] - fn test_nested_scope_root() { - block_on(async { - let mut srv = init_service( - App::new().service( - web::scope("/app").service( - web::scope("/t1") - .service(web::resource("").to(|| HttpResponse::Ok())) - .service(web::resource("/").to(|| HttpResponse::Created())), - ), - ), - ) - .await; - - let req = TestRequest::with_uri("/app/t1").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - - let req = TestRequest::with_uri("/app/t1/").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::CREATED); - }) - } - - #[test] - fn test_nested_scope_filter() { - block_on(async { - let mut srv = init_service( - App::new().service( - web::scope("/app").service( - web::scope("/t1") - .guard(guard::Get()) - .service(web::resource("/path1").to(|| HttpResponse::Ok())), - ), - ), - ) - .await; - - let req = TestRequest::with_uri("/app/t1/path1") - .method(Method::POST) - .to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); - - let req = TestRequest::with_uri("/app/t1/path1") - .method(Method::GET) - .to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - }) - } - - #[test] - fn test_nested_scope_with_variable_segment() { - block_on(async { - let mut srv = init_service(App::new().service(web::scope("/app").service( - web::scope("/{project_id}").service(web::resource("/path1").to( - |r: HttpRequest| { - async move { - HttpResponse::Created().body(format!( - "project: {}", - &r.match_info()["project_id"] - )) - } - }, - )), - ))) - .await; - - let req = TestRequest::with_uri("/app/project_1/path1").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::CREATED); - - match resp.response().body() { - ResponseBody::Body(Body::Bytes(ref b)) => { - let bytes: Bytes = b.clone().into(); - assert_eq!(bytes, Bytes::from_static(b"project: project_1")); - } - _ => panic!(), - } - }) - } - - #[test] - fn test_nested2_scope_with_variable_segment() { - block_on(async { - let mut srv = init_service(App::new().service(web::scope("/app").service( - web::scope("/{project}").service(web::scope("/{id}").service( - web::resource("/path1").to(|r: HttpRequest| { - async move { - HttpResponse::Created().body(format!( - "project: {} - {}", - &r.match_info()["project"], - &r.match_info()["id"], - )) - } - }), - )), - ))) - .await; - - let req = TestRequest::with_uri("/app/test/1/path1").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::CREATED); - - match resp.response().body() { - ResponseBody::Body(Body::Bytes(ref b)) => { - let bytes: Bytes = b.clone().into(); - assert_eq!(bytes, Bytes::from_static(b"project: test - 1")); - } - _ => panic!(), - } - - let req = TestRequest::with_uri("/app/test/1/path2").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); - }) - } - - #[test] - fn test_default_resource() { - block_on(async { - let mut srv = init_service( - App::new().service( - web::scope("/app") - .service(web::resource("/path1").to(|| HttpResponse::Ok())) - .default_service(|r: ServiceRequest| { - ok(r.into_response(HttpResponse::BadRequest())) - }), - ), - ) - .await; - - let req = TestRequest::with_uri("/app/path2").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::BAD_REQUEST); - - let req = TestRequest::with_uri("/path2").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::NOT_FOUND); - }) - } - - #[test] - fn test_default_resource_propagation() { - block_on(async { - let mut srv = init_service( - App::new() - .service(web::scope("/app1").default_service( - web::resource("").to(|| HttpResponse::BadRequest()), - )) - .service(web::scope("/app2")) - .default_service(|r: ServiceRequest| { - ok(r.into_response(HttpResponse::MethodNotAllowed())) - }), - ) - .await; - - let req = TestRequest::with_uri("/non-exist").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); - - let req = TestRequest::with_uri("/app1/non-exist").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::BAD_REQUEST); - - let req = TestRequest::with_uri("/app2/non-exist").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); - }) - } - - fn md( - req: ServiceRequest, - srv: &mut S, - ) -> impl Future, Error>> - where - S: Service< - Request = ServiceRequest, - Response = ServiceResponse, - Error = Error, - >, - { - let fut = srv.call(req); - async move { - let mut res = fut.await?; - res.headers_mut() - .insert(header::CONTENT_TYPE, HeaderValue::from_static("0001")); - Ok(res) + _ => panic!(), } } - #[test] - fn test_middleware() { - block_on(async { - let mut srv = init_service( + #[actix_rt::test] + async fn test_nested2_scope_with_variable_segment() { + let mut srv = init_service(App::new().service(web::scope("/app").service( + web::scope("/{project}").service(web::scope("/{id}").service( + web::resource("/path1").to(|r: HttpRequest| { + async move { + HttpResponse::Created().body(format!( + "project: {} - {}", + &r.match_info()["project"], + &r.match_info()["id"], + )) + } + }), + )), + ))) + .await; + + let req = TestRequest::with_uri("/app/test/1/path1").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::CREATED); + + match resp.response().body() { + ResponseBody::Body(Body::Bytes(ref b)) => { + let bytes: Bytes = b.clone().into(); + assert_eq!(bytes, Bytes::from_static(b"project: test - 1")); + } + _ => panic!(), + } + + let req = TestRequest::with_uri("/app/test/1/path2").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + } + + #[actix_rt::test] + async fn test_default_resource() { + let mut srv = init_service( + App::new().service( + web::scope("/app") + .service(web::resource("/path1").to(|| HttpResponse::Ok())) + .default_service(|r: ServiceRequest| { + ok(r.into_response(HttpResponse::BadRequest())) + }), + ), + ) + .await; + + let req = TestRequest::with_uri("/app/path2").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::BAD_REQUEST); + + let req = TestRequest::with_uri("/path2").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::NOT_FOUND); + } + + #[actix_rt::test] + async fn test_default_resource_propagation() { + let mut srv = init_service( + App::new() + .service(web::scope("/app1").default_service( + web::resource("").to(|| HttpResponse::BadRequest()), + )) + .service(web::scope("/app2")) + .default_service(|r: ServiceRequest| { + ok(r.into_response(HttpResponse::MethodNotAllowed())) + }), + ) + .await; + + let req = TestRequest::with_uri("/non-exist").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); + + let req = TestRequest::with_uri("/app1/non-exist").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::BAD_REQUEST); + + let req = TestRequest::with_uri("/app2/non-exist").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); + } + + #[actix_rt::test] + async fn test_middleware() { + let mut srv = + init_service( App::new().service( web::scope("app") .wrap(DefaultHeaders::new().header( @@ -1117,186 +1063,169 @@ mod tests { ) .await; - let req = TestRequest::with_uri("/app/test").to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - HeaderValue::from_static("0001") - ); - }) + let req = TestRequest::with_uri("/app/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + HeaderValue::from_static("0001") + ); } - #[test] - fn test_middleware_fn() { - block_on(async { - let mut srv = init_service( - App::new().service( - web::scope("app") - .wrap_fn(|req, srv| { - let fut = srv.call(req); - async move { - let mut res = fut.await?; - res.headers_mut().insert( - header::CONTENT_TYPE, - HeaderValue::from_static("0001"), - ); - Ok(res) - } - }) - .route("/test", web::get().to(|| HttpResponse::Ok())), - ), - ) - .await; - - let req = TestRequest::with_uri("/app/test").to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - HeaderValue::from_static("0001") - ); - }) - } - - #[test] - fn test_override_data() { - block_on(async { - let mut srv = init_service(App::new().data(1usize).service( - web::scope("app").data(10usize).route( - "/t", - web::get().to(|data: web::Data| { - assert_eq!(*data, 10); - let _ = data.clone(); - HttpResponse::Ok() - }), - ), - )) - .await; - - let req = TestRequest::with_uri("/app/t").to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); - }) - } - - #[test] - fn test_override_register_data() { - block_on(async { - let mut srv = init_service( - App::new().register_data(web::Data::new(1usize)).service( - web::scope("app") - .register_data(web::Data::new(10usize)) - .route( - "/t", - web::get().to(|data: web::Data| { - assert_eq!(*data, 10); - let _ = data.clone(); - HttpResponse::Ok() - }), - ), - ), - ) - .await; - - let req = TestRequest::with_uri("/app/t").to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); - }) - } - - #[test] - fn test_scope_config() { - block_on(async { - let mut srv = - init_service(App::new().service(web::scope("/app").configure(|s| { - s.route("/path1", web::get().to(|| HttpResponse::Ok())); - }))) - .await; - - let req = TestRequest::with_uri("/app/path1").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - }) - } - - #[test] - fn test_scope_config_2() { - block_on(async { - let mut srv = - init_service(App::new().service(web::scope("/app").configure(|s| { - s.service(web::scope("/v1").configure(|s| { - s.route("/", web::get().to(|| HttpResponse::Ok())); - })); - }))) - .await; - - let req = TestRequest::with_uri("/app/v1/").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - }) - } - - #[test] - fn test_url_for_external() { - block_on(async { - let mut srv = - init_service(App::new().service(web::scope("/app").configure(|s| { - s.service(web::scope("/v1").configure(|s| { - s.external_resource( - "youtube", - "https://youtube.com/watch/{video_id}", - ); - s.route( - "/", - web::get().to(|req: HttpRequest| { - async move { - HttpResponse::Ok().body(format!( - "{}", - req.url_for("youtube", &["xxxxxx"]) - .unwrap() - .as_str() - )) - } - }), - ); - })); - }))) - .await; - - let req = TestRequest::with_uri("/app/v1/").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - let body = read_body(resp).await; - assert_eq!(body, &b"https://youtube.com/watch/xxxxxx"[..]); - }) - } - - #[test] - fn test_url_for_nested() { - block_on(async { - let mut srv = init_service(App::new().service(web::scope("/a").service( - web::scope("/b").service(web::resource("/c/{stuff}").name("c").route( - web::get().to(|req: HttpRequest| { + #[actix_rt::test] + async fn test_middleware_fn() { + let mut srv = init_service( + App::new().service( + web::scope("app") + .wrap_fn(|req, srv| { + let fut = srv.call(req); async move { - HttpResponse::Ok().body(format!( - "{}", - req.url_for("c", &["12345"]).unwrap() - )) + let mut res = fut.await?; + res.headers_mut().insert( + header::CONTENT_TYPE, + HeaderValue::from_static("0001"), + ); + Ok(res) } - }), - )), - ))) + }) + .route("/test", web::get().to(|| HttpResponse::Ok())), + ), + ) + .await; + + let req = TestRequest::with_uri("/app/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + HeaderValue::from_static("0001") + ); + } + + #[actix_rt::test] + async fn test_override_data() { + let mut srv = init_service(App::new().data(1usize).service( + web::scope("app").data(10usize).route( + "/t", + web::get().to(|data: web::Data| { + assert_eq!(*data, 10); + let _ = data.clone(); + HttpResponse::Ok() + }), + ), + )) + .await; + + let req = TestRequest::with_uri("/app/t").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + } + + #[actix_rt::test] + async fn test_override_register_data() { + let mut srv = init_service( + App::new().register_data(web::Data::new(1usize)).service( + web::scope("app") + .register_data(web::Data::new(10usize)) + .route( + "/t", + web::get().to(|data: web::Data| { + assert_eq!(*data, 10); + let _ = data.clone(); + HttpResponse::Ok() + }), + ), + ), + ) + .await; + + let req = TestRequest::with_uri("/app/t").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + } + + #[actix_rt::test] + async fn test_scope_config() { + let mut srv = + init_service(App::new().service(web::scope("/app").configure(|s| { + s.route("/path1", web::get().to(|| HttpResponse::Ok())); + }))) .await; - let req = TestRequest::with_uri("/a/b/c/test").to_request(); - let resp = call_service(&mut srv, req).await; - assert_eq!(resp.status(), StatusCode::OK); - let body = read_body(resp).await; - assert_eq!( - body, - Bytes::from_static(b"http://localhost:8080/a/b/c/12345") - ); - }) + let req = TestRequest::with_uri("/app/path1").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + } + + #[actix_rt::test] + async fn test_scope_config_2() { + let mut srv = + init_service(App::new().service(web::scope("/app").configure(|s| { + s.service(web::scope("/v1").configure(|s| { + s.route("/", web::get().to(|| HttpResponse::Ok())); + })); + }))) + .await; + + let req = TestRequest::with_uri("/app/v1/").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + } + + #[actix_rt::test] + async fn test_url_for_external() { + let mut srv = + init_service(App::new().service(web::scope("/app").configure(|s| { + s.service(web::scope("/v1").configure(|s| { + s.external_resource( + "youtube", + "https://youtube.com/watch/{video_id}", + ); + s.route( + "/", + web::get().to(|req: HttpRequest| { + async move { + HttpResponse::Ok().body(format!( + "{}", + req.url_for("youtube", &["xxxxxx"]) + .unwrap() + .as_str() + )) + } + }), + ); + })); + }))) + .await; + + let req = TestRequest::with_uri("/app/v1/").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + let body = read_body(resp).await; + assert_eq!(body, &b"https://youtube.com/watch/xxxxxx"[..]); + } + + #[actix_rt::test] + async fn test_url_for_nested() { + let mut srv = init_service(App::new().service(web::scope("/a").service( + web::scope("/b").service(web::resource("/c/{stuff}").name("c").route( + web::get().to(|req: HttpRequest| { + async move { + HttpResponse::Ok() + .body(format!("{}", req.url_for("c", &["12345"]).unwrap())) + } + }), + )), + ))) + .await; + + let req = TestRequest::with_uri("/a/b/c/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + let body = read_body(resp).await; + assert_eq!( + body, + Bytes::from_static(b"http://localhost:8080/a/b/c/12345") + ); } } diff --git a/src/service.rs b/src/service.rs index 39540b067..b392e6e8b 100644 --- a/src/service.rs +++ b/src/service.rs @@ -10,7 +10,6 @@ use actix_http::{ }; use actix_router::{Path, Resource, ResourceDef, Url}; use actix_service::{IntoServiceFactory, ServiceFactory}; -use futures::future::{ok, Ready}; use crate::config::{AppConfig, AppService}; use crate::data::Data; @@ -529,9 +528,10 @@ where #[cfg(test)] mod tests { use super::*; - use crate::test::{block_on, init_service, TestRequest}; + use crate::test::{init_service, TestRequest}; use crate::{guard, http, web, App, HttpResponse}; use actix_service::Service; + use futures::future::ok; #[test] fn test_service_request() { @@ -554,35 +554,29 @@ mod tests { assert!(ServiceRequest::from_request(r).is_err()); } - #[test] - fn test_service() { - block_on(async { - let mut srv = init_service( - App::new().service(web::service("/test").name("test").finish( - |req: ServiceRequest| { - ok(req.into_response(HttpResponse::Ok().finish())) - }, - )), - ) - .await; - let req = TestRequest::with_uri("/test").to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), http::StatusCode::OK); + #[actix_rt::test] + async fn test_service() { + let mut srv = init_service( + App::new().service(web::service("/test").name("test").finish( + |req: ServiceRequest| ok(req.into_response(HttpResponse::Ok().finish())), + )), + ) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), http::StatusCode::OK); - let mut srv = init_service(App::new().service( - web::service("/test").guard(guard::Get()).finish( - |req: ServiceRequest| { - ok(req.into_response(HttpResponse::Ok().finish())) - }, - ), - )) - .await; - let req = TestRequest::with_uri("/test") - .method(http::Method::PUT) - .to_request(); - let resp = srv.call(req).await.unwrap(); - assert_eq!(resp.status(), http::StatusCode::NOT_FOUND); - }) + let mut srv = init_service( + App::new().service(web::service("/test").guard(guard::Get()).finish( + |req: ServiceRequest| ok(req.into_response(HttpResponse::Ok().finish())), + )), + ) + .await; + let req = TestRequest::with_uri("/test") + .method(http::Method::PUT) + .to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), http::StatusCode::NOT_FOUND); } #[test] diff --git a/src/test.rs b/src/test.rs index 0776b0f15..e19393156 100644 --- a/src/test.rs +++ b/src/test.rs @@ -9,14 +9,13 @@ use actix_router::{Path, ResourceDef, Url}; use actix_server_config::ServerConfig; use actix_service::{IntoService, IntoServiceFactory, Service, ServiceFactory}; use bytes::{Bytes, BytesMut}; -use futures::future::{ok, Future, FutureExt}; +use futures::future::ok; use futures::stream::{Stream, StreamExt}; use serde::de::DeserializeOwned; use serde::Serialize; use serde_json; pub use actix_http::test::TestBuffer; -pub use actix_testing::{block_fn, block_on, run_on}; use crate::config::{AppConfig, AppConfigInner}; use crate::data::Data; @@ -51,8 +50,8 @@ pub fn default_service( /// use actix_service::Service; /// use actix_web::{test, web, App, HttpResponse, http::StatusCode}; /// -/// #[test] -/// fn test_init_service() { +/// #[actix_rt::test] +/// async fn test_init_service() { /// let mut app = test::init_service( /// App::new() /// .service(web::resource("/test").to(|| async { HttpResponse::Ok() })) @@ -62,7 +61,7 @@ pub fn default_service( /// let req = test::TestRequest::with_uri("/test").to_request(); /// /// // Execute application -/// let resp = test::block_on(app.call(req)).unwrap(); +/// let resp = app.call(req).await.unwrap(); /// assert_eq!(resp.status(), StatusCode::OK); /// } /// ``` @@ -116,14 +115,13 @@ where } /// Helper function that returns a response body of a TestRequest -/// This function blocks the current thread until futures complete. /// /// ```rust /// use actix_web::{test, web, App, HttpResponse, http::header}; /// use bytes::Bytes; /// -/// #[test] -/// fn test_index() { +/// #[actix_rt::test] +/// async fn test_index() { /// let mut app = test::init_service( /// App::new().service( /// web::resource("/index.html") @@ -149,7 +147,7 @@ where let mut resp = app .call(req) .await - .unwrap_or_else(|_| panic!("read_response failed at block_on unwrap")); + .unwrap_or_else(|_| panic!("read_response failed at application call")); let mut body = resp.take_body(); let mut bytes = BytesMut::new(); @@ -160,14 +158,13 @@ where } /// Helper function that returns a response body of a ServiceResponse. -/// This function blocks the current thread until futures complete. /// /// ```rust /// use actix_web::{test, web, App, HttpResponse, http::header}; /// use bytes::Bytes; /// -/// #[test] -/// fn test_index() { +/// #[actix_rt::test] +/// async fn test_index() { /// let mut app = test::init_service( /// App::new().service( /// web::resource("/index.html") @@ -210,7 +207,6 @@ where } /// Helper function that returns a deserialized response body of a TestRequest -/// This function blocks the current thread until futures complete. /// /// ```rust /// use actix_web::{App, test, web, HttpResponse, http::header}; @@ -222,8 +218,8 @@ where /// name: String /// } /// -/// #[test] -/// fn test_add_person() { +/// #[actix_rt::test] +/// async fn test_add_person() { /// let mut app = test::init_service( /// App::new().service( /// web::resource("/people") @@ -512,90 +508,81 @@ mod tests { use super::*; use crate::{http::header, web, App, HttpResponse}; - #[test] - fn test_basics() { - block_on(async { - let req = TestRequest::with_hdr(header::ContentType::json()) - .version(Version::HTTP_2) - .set(header::Date(SystemTime::now().into())) - .param("test", "123") - .data(10u32) - .to_http_request(); - assert!(req.headers().contains_key(header::CONTENT_TYPE)); - assert!(req.headers().contains_key(header::DATE)); - assert_eq!(&req.match_info()["test"], "123"); - assert_eq!(req.version(), Version::HTTP_2); - let data = req.get_app_data::().unwrap(); - assert!(req.get_app_data::().is_none()); - assert_eq!(*data, 10); - assert_eq!(*data.get_ref(), 10); + #[actix_rt::test] + async fn test_basics() { + let req = TestRequest::with_hdr(header::ContentType::json()) + .version(Version::HTTP_2) + .set(header::Date(SystemTime::now().into())) + .param("test", "123") + .data(10u32) + .to_http_request(); + assert!(req.headers().contains_key(header::CONTENT_TYPE)); + assert!(req.headers().contains_key(header::DATE)); + assert_eq!(&req.match_info()["test"], "123"); + assert_eq!(req.version(), Version::HTTP_2); + let data = req.get_app_data::().unwrap(); + assert!(req.get_app_data::().is_none()); + assert_eq!(*data, 10); + assert_eq!(*data.get_ref(), 10); - assert!(req.app_data::().is_none()); - let data = req.app_data::().unwrap(); - assert_eq!(*data, 10); - }) + assert!(req.app_data::().is_none()); + let data = req.app_data::().unwrap(); + assert_eq!(*data, 10); } - #[test] - fn test_request_methods() { - block_on(async { - let mut app = init_service( - App::new().service( - web::resource("/index.html") - .route( - web::put().to(|| async { HttpResponse::Ok().body("put!") }), - ) - .route( - web::patch() - .to(|| async { HttpResponse::Ok().body("patch!") }), - ) - .route( - web::delete() - .to(|| async { HttpResponse::Ok().body("delete!") }), - ), - ), - ) + #[actix_rt::test] + async fn test_request_methods() { + let mut app = init_service( + App::new().service( + web::resource("/index.html") + .route(web::put().to(|| async { HttpResponse::Ok().body("put!") })) + .route( + web::patch().to(|| async { HttpResponse::Ok().body("patch!") }), + ) + .route( + web::delete() + .to(|| async { HttpResponse::Ok().body("delete!") }), + ), + ), + ) + .await; + + let put_req = TestRequest::put() + .uri("/index.html") + .header(header::CONTENT_TYPE, "application/json") + .to_request(); + + let result = read_response(&mut app, put_req).await; + assert_eq!(result, Bytes::from_static(b"put!")); + + let patch_req = TestRequest::patch() + .uri("/index.html") + .header(header::CONTENT_TYPE, "application/json") + .to_request(); + + let result = read_response(&mut app, patch_req).await; + assert_eq!(result, Bytes::from_static(b"patch!")); + + let delete_req = TestRequest::delete().uri("/index.html").to_request(); + let result = read_response(&mut app, delete_req).await; + assert_eq!(result, Bytes::from_static(b"delete!")); + } + + #[actix_rt::test] + async fn test_response() { + let mut app = + init_service(App::new().service(web::resource("/index.html").route( + web::post().to(|| async { HttpResponse::Ok().body("welcome!") }), + ))) .await; - let put_req = TestRequest::put() - .uri("/index.html") - .header(header::CONTENT_TYPE, "application/json") - .to_request(); + let req = TestRequest::post() + .uri("/index.html") + .header(header::CONTENT_TYPE, "application/json") + .to_request(); - let result = read_response(&mut app, put_req).await; - assert_eq!(result, Bytes::from_static(b"put!")); - - let patch_req = TestRequest::patch() - .uri("/index.html") - .header(header::CONTENT_TYPE, "application/json") - .to_request(); - - let result = read_response(&mut app, patch_req).await; - assert_eq!(result, Bytes::from_static(b"patch!")); - - let delete_req = TestRequest::delete().uri("/index.html").to_request(); - let result = read_response(&mut app, delete_req).await; - assert_eq!(result, Bytes::from_static(b"delete!")); - }) - } - - #[test] - fn test_response() { - block_on(async { - let mut app = - init_service(App::new().service(web::resource("/index.html").route( - web::post().to(|| async { HttpResponse::Ok().body("welcome!") }), - ))) - .await; - - let req = TestRequest::post() - .uri("/index.html") - .header(header::CONTENT_TYPE, "application/json") - .to_request(); - - let result = read_response(&mut app, req).await; - assert_eq!(result, Bytes::from_static(b"welcome!")); - }) + let result = read_response(&mut app, req).await; + assert_eq!(result, Bytes::from_static(b"welcome!")); } #[derive(Serialize, Deserialize)] @@ -604,114 +591,103 @@ mod tests { name: String, } - #[test] - fn test_response_json() { - block_on(async { - let mut app = - init_service(App::new().service(web::resource("/people").route( - web::post().to(|person: web::Json| { - async { HttpResponse::Ok().json(person.into_inner()) } - }), - ))) - .await; + #[actix_rt::test] + async fn test_response_json() { + let mut app = init_service(App::new().service(web::resource("/people").route( + web::post().to(|person: web::Json| { + async { HttpResponse::Ok().json(person.into_inner()) } + }), + ))) + .await; - let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes(); + let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes(); - let req = TestRequest::post() - .uri("/people") - .header(header::CONTENT_TYPE, "application/json") - .set_payload(payload) - .to_request(); + let req = TestRequest::post() + .uri("/people") + .header(header::CONTENT_TYPE, "application/json") + .set_payload(payload) + .to_request(); - let result: Person = read_response_json(&mut app, req).await; - assert_eq!(&result.id, "12345"); - }) + let result: Person = read_response_json(&mut app, req).await; + assert_eq!(&result.id, "12345"); } - #[test] - fn test_request_response_form() { - block_on(async { - let mut app = - init_service(App::new().service(web::resource("/people").route( - web::post().to(|person: web::Form| { - async { HttpResponse::Ok().json(person.into_inner()) } - }), - ))) - .await; + #[actix_rt::test] + async fn test_request_response_form() { + let mut app = init_service(App::new().service(web::resource("/people").route( + web::post().to(|person: web::Form| { + async { HttpResponse::Ok().json(person.into_inner()) } + }), + ))) + .await; - let payload = Person { - id: "12345".to_string(), - name: "User name".to_string(), - }; + let payload = Person { + id: "12345".to_string(), + name: "User name".to_string(), + }; - let req = TestRequest::post() - .uri("/people") - .set_form(&payload) - .to_request(); + let req = TestRequest::post() + .uri("/people") + .set_form(&payload) + .to_request(); - assert_eq!(req.content_type(), "application/x-www-form-urlencoded"); + assert_eq!(req.content_type(), "application/x-www-form-urlencoded"); - let result: Person = read_response_json(&mut app, req).await; - assert_eq!(&result.id, "12345"); - assert_eq!(&result.name, "User name"); - }) + let result: Person = read_response_json(&mut app, req).await; + assert_eq!(&result.id, "12345"); + assert_eq!(&result.name, "User name"); } - #[test] - fn test_request_response_json() { - block_on(async { - let mut app = - init_service(App::new().service(web::resource("/people").route( - web::post().to(|person: web::Json| { - async { HttpResponse::Ok().json(person.into_inner()) } - }), - ))) - .await; + #[actix_rt::test] + async fn test_request_response_json() { + let mut app = init_service(App::new().service(web::resource("/people").route( + web::post().to(|person: web::Json| { + async { HttpResponse::Ok().json(person.into_inner()) } + }), + ))) + .await; - let payload = Person { - id: "12345".to_string(), - name: "User name".to_string(), - }; + let payload = Person { + id: "12345".to_string(), + name: "User name".to_string(), + }; - let req = TestRequest::post() - .uri("/people") - .set_json(&payload) - .to_request(); + let req = TestRequest::post() + .uri("/people") + .set_json(&payload) + .to_request(); - assert_eq!(req.content_type(), "application/json"); + assert_eq!(req.content_type(), "application/json"); - let result: Person = read_response_json(&mut app, req).await; - assert_eq!(&result.id, "12345"); - assert_eq!(&result.name, "User name"); - }) + let result: Person = read_response_json(&mut app, req).await; + assert_eq!(&result.id, "12345"); + assert_eq!(&result.name, "User name"); } - #[test] - fn test_async_with_block() { - block_on(async { - async fn async_with_block() -> Result { - let res = web::block(move || Some(4usize).ok_or("wrong")).await; + #[actix_rt::test] + async fn test_async_with_block() { + async fn async_with_block() -> Result { + let res = web::block(move || Some(4usize).ok_or("wrong")).await; - match res? { - Ok(value) => Ok(HttpResponse::Ok() - .content_type("text/plain") - .body(format!("Async with block value: {}", value))), - Err(_) => panic!("Unexpected"), - } + match res? { + Ok(value) => Ok(HttpResponse::Ok() + .content_type("text/plain") + .body(format!("Async with block value: {}", value))), + Err(_) => panic!("Unexpected"), } + } - let mut app = init_service( - App::new().service(web::resource("/index.html").to(async_with_block)), - ) - .await; + let mut app = init_service( + App::new().service(web::resource("/index.html").to(async_with_block)), + ) + .await; - let req = TestRequest::post().uri("/index.html").to_request(); - let res = app.call(req).await.unwrap(); - assert!(res.status().is_success()); - }) + let req = TestRequest::post().uri("/index.html").to_request(); + let res = app.call(req).await.unwrap(); + assert!(res.status().is_success()); } - // #[test] + // #[actix_rt::test] // fn test_actor() { // use actix::Actor; diff --git a/src/types/form.rs b/src/types/form.rs index c20dc7a05..e1bd52375 100644 --- a/src/types/form.rs +++ b/src/types/form.rs @@ -10,7 +10,7 @@ use actix_http::{Error, HttpMessage, Payload, Response}; use bytes::BytesMut; use encoding_rs::{Encoding, UTF_8}; use futures::future::{err, ok, FutureExt, LocalBoxFuture, Ready}; -use futures::{Stream, StreamExt}; +use futures::StreamExt; use serde::de::DeserializeOwned; use serde::Serialize; @@ -370,7 +370,7 @@ mod tests { use super::*; use crate::http::header::{HeaderValue, CONTENT_TYPE}; - use crate::test::{block_on, TestRequest}; + use crate::test::TestRequest; #[derive(Deserialize, Serialize, Debug, PartialEq)] struct Info { @@ -378,26 +378,22 @@ mod tests { counter: i64, } - #[test] - fn test_form() { - block_on(async { - let (req, mut pl) = TestRequest::with_header( - CONTENT_TYPE, - "application/x-www-form-urlencoded", - ) - .header(CONTENT_LENGTH, "11") - .set_payload(Bytes::from_static(b"hello=world&counter=123")) - .to_http_parts(); + #[actix_rt::test] + async fn test_form() { + let (req, mut pl) = + TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded") + .header(CONTENT_LENGTH, "11") + .set_payload(Bytes::from_static(b"hello=world&counter=123")) + .to_http_parts(); - let Form(s) = Form::::from_request(&req, &mut pl).await.unwrap(); - assert_eq!( - s, - Info { - hello: "world".into(), - counter: 123 - } - ); - }) + let Form(s) = Form::::from_request(&req, &mut pl).await.unwrap(); + assert_eq!( + s, + Info { + hello: "world".into(), + counter: 123 + } + ); } fn eq(err: UrlencodedError, other: UrlencodedError) -> bool { @@ -418,95 +414,83 @@ mod tests { } } - #[test] - fn test_urlencoded_error() { - block_on(async { - let (req, mut pl) = TestRequest::with_header( - CONTENT_TYPE, - "application/x-www-form-urlencoded", - ) - .header(CONTENT_LENGTH, "xxxx") - .to_http_parts(); - let info = UrlEncoded::::new(&req, &mut pl).await; - assert!(eq(info.err().unwrap(), UrlencodedError::UnknownLength)); - - let (req, mut pl) = TestRequest::with_header( - CONTENT_TYPE, - "application/x-www-form-urlencoded", - ) - .header(CONTENT_LENGTH, "1000000") - .to_http_parts(); - let info = UrlEncoded::::new(&req, &mut pl).await; - assert!(eq( - info.err().unwrap(), - UrlencodedError::Overflow { size: 0, limit: 0 } - )); - - let (req, mut pl) = TestRequest::with_header(CONTENT_TYPE, "text/plain") - .header(CONTENT_LENGTH, "10") + #[actix_rt::test] + async fn test_urlencoded_error() { + let (req, mut pl) = + TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded") + .header(CONTENT_LENGTH, "xxxx") .to_http_parts(); - let info = UrlEncoded::::new(&req, &mut pl).await; - assert!(eq(info.err().unwrap(), UrlencodedError::ContentType)); - }) + let info = UrlEncoded::::new(&req, &mut pl).await; + assert!(eq(info.err().unwrap(), UrlencodedError::UnknownLength)); + + let (req, mut pl) = + TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded") + .header(CONTENT_LENGTH, "1000000") + .to_http_parts(); + let info = UrlEncoded::::new(&req, &mut pl).await; + assert!(eq( + info.err().unwrap(), + UrlencodedError::Overflow { size: 0, limit: 0 } + )); + + let (req, mut pl) = TestRequest::with_header(CONTENT_TYPE, "text/plain") + .header(CONTENT_LENGTH, "10") + .to_http_parts(); + let info = UrlEncoded::::new(&req, &mut pl).await; + assert!(eq(info.err().unwrap(), UrlencodedError::ContentType)); } - #[test] - fn test_urlencoded() { - block_on(async { - let (req, mut pl) = TestRequest::with_header( - CONTENT_TYPE, - "application/x-www-form-urlencoded", - ) - .header(CONTENT_LENGTH, "11") - .set_payload(Bytes::from_static(b"hello=world&counter=123")) - .to_http_parts(); + #[actix_rt::test] + async fn test_urlencoded() { + let (req, mut pl) = + TestRequest::with_header(CONTENT_TYPE, "application/x-www-form-urlencoded") + .header(CONTENT_LENGTH, "11") + .set_payload(Bytes::from_static(b"hello=world&counter=123")) + .to_http_parts(); - let info = UrlEncoded::::new(&req, &mut pl).await.unwrap(); - assert_eq!( - info, - Info { - hello: "world".to_owned(), - counter: 123 - } - ); + let info = UrlEncoded::::new(&req, &mut pl).await.unwrap(); + assert_eq!( + info, + Info { + hello: "world".to_owned(), + counter: 123 + } + ); - let (req, mut pl) = TestRequest::with_header( - CONTENT_TYPE, - "application/x-www-form-urlencoded; charset=utf-8", - ) - .header(CONTENT_LENGTH, "11") - .set_payload(Bytes::from_static(b"hello=world&counter=123")) - .to_http_parts(); + let (req, mut pl) = TestRequest::with_header( + CONTENT_TYPE, + "application/x-www-form-urlencoded; charset=utf-8", + ) + .header(CONTENT_LENGTH, "11") + .set_payload(Bytes::from_static(b"hello=world&counter=123")) + .to_http_parts(); - let info = UrlEncoded::::new(&req, &mut pl).await.unwrap(); - assert_eq!( - info, - Info { - hello: "world".to_owned(), - counter: 123 - } - ); - }) + let info = UrlEncoded::::new(&req, &mut pl).await.unwrap(); + assert_eq!( + info, + Info { + hello: "world".to_owned(), + counter: 123 + } + ); } - #[test] - fn test_responder() { - block_on(async { - let req = TestRequest::default().to_http_request(); + #[actix_rt::test] + async fn test_responder() { + let req = TestRequest::default().to_http_request(); - let form = Form(Info { - hello: "world".to_string(), - counter: 123, - }); - let resp = form.respond_to(&req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!( - resp.headers().get(CONTENT_TYPE).unwrap(), - HeaderValue::from_static("application/x-www-form-urlencoded") - ); + let form = Form(Info { + hello: "world".to_string(), + counter: 123, + }); + let resp = form.respond_to(&req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!( + resp.headers().get(CONTENT_TYPE).unwrap(), + HeaderValue::from_static("application/x-www-form-urlencoded") + ); - use crate::responder::tests::BodyTest; - assert_eq!(resp.body().bin_ref(), b"hello=world&counter=123"); - }) + use crate::responder::tests::BodyTest; + assert_eq!(resp.body().bin_ref(), b"hello=world&counter=123"); } } diff --git a/src/types/json.rs b/src/types/json.rs index 206a4e425..028092d1a 100644 --- a/src/types/json.rs +++ b/src/types/json.rs @@ -8,7 +8,7 @@ use std::{fmt, ops}; use bytes::BytesMut; use futures::future::{err, ok, FutureExt, LocalBoxFuture, Ready}; -use futures::{Stream, StreamExt}; +use futures::StreamExt; use serde::de::DeserializeOwned; use serde::Serialize; use serde_json; @@ -402,7 +402,7 @@ mod tests { use super::*; use crate::error::InternalError; use crate::http::header; - use crate::test::{block_on, load_stream, TestRequest}; + use crate::test::{load_stream, TestRequest}; use crate::HttpResponse; #[derive(Serialize, Deserialize, PartialEq, Debug)] @@ -424,236 +424,222 @@ mod tests { } } - #[test] - fn test_responder() { - block_on(async { - let req = TestRequest::default().to_http_request(); + #[actix_rt::test] + async fn test_responder() { + let req = TestRequest::default().to_http_request(); - let j = Json(MyObject { - name: "test".to_string(), - }); - let resp = j.respond_to(&req).await.unwrap(); - assert_eq!(resp.status(), StatusCode::OK); - assert_eq!( - resp.headers().get(header::CONTENT_TYPE).unwrap(), - header::HeaderValue::from_static("application/json") - ); + let j = Json(MyObject { + name: "test".to_string(), + }); + let resp = j.respond_to(&req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + assert_eq!( + resp.headers().get(header::CONTENT_TYPE).unwrap(), + header::HeaderValue::from_static("application/json") + ); - use crate::responder::tests::BodyTest; - assert_eq!(resp.body().bin_ref(), b"{\"name\":\"test\"}"); - }) + use crate::responder::tests::BodyTest; + assert_eq!(resp.body().bin_ref(), b"{\"name\":\"test\"}"); } - #[test] - fn test_custom_error_responder() { - block_on(async { - let (req, mut pl) = TestRequest::default() - .header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/json"), - ) - .header( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("16"), - ) - .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .data(JsonConfig::default().limit(10).error_handler(|err, _| { - let msg = MyObject { - name: "invalid request".to_string(), - }; - let resp = HttpResponse::BadRequest() - .body(serde_json::to_string(&msg).unwrap()); - InternalError::from_response(err, resp).into() - })) - .to_http_parts(); - - let s = Json::::from_request(&req, &mut pl).await; - let mut resp = Response::from_error(s.err().unwrap().into()); - assert_eq!(resp.status(), StatusCode::BAD_REQUEST); - - let body = load_stream(resp.take_body()).await.unwrap(); - let msg: MyObject = serde_json::from_slice(&body).unwrap(); - assert_eq!(msg.name, "invalid request"); - }) - } - - #[test] - fn test_extract() { - block_on(async { - let (req, mut pl) = TestRequest::default() - .header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/json"), - ) - .header( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("16"), - ) - .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .to_http_parts(); - - let s = Json::::from_request(&req, &mut pl).await.unwrap(); - assert_eq!(s.name, "test"); - assert_eq!( - s.into_inner(), - MyObject { - name: "test".to_string() - } - ); - - let (req, mut pl) = TestRequest::default() - .header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/json"), - ) - .header( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("16"), - ) - .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .data(JsonConfig::default().limit(10)) - .to_http_parts(); - - let s = Json::::from_request(&req, &mut pl).await; - assert!(format!("{}", s.err().unwrap()) - .contains("Json payload size is bigger than allowed")); - - let (req, mut pl) = TestRequest::default() - .header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/json"), - ) - .header( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("16"), - ) - .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .data( - JsonConfig::default() - .limit(10) - .error_handler(|_, _| JsonPayloadError::ContentType.into()), - ) - .to_http_parts(); - let s = Json::::from_request(&req, &mut pl).await; - assert!(format!("{}", s.err().unwrap()).contains("Content type error")); - }) - } - - #[test] - fn test_json_body() { - block_on(async { - let (req, mut pl) = TestRequest::default().to_http_parts(); - let json = JsonBody::::new(&req, &mut pl, None).await; - assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); - - let (req, mut pl) = TestRequest::default() - .header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/text"), - ) - .to_http_parts(); - let json = JsonBody::::new(&req, &mut pl, None).await; - assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); - - let (req, mut pl) = TestRequest::default() - .header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/json"), - ) - .header( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("10000"), - ) - .to_http_parts(); - - let json = JsonBody::::new(&req, &mut pl, None) - .limit(100) - .await; - assert!(json_eq(json.err().unwrap(), JsonPayloadError::Overflow)); - - let (req, mut pl) = TestRequest::default() - .header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/json"), - ) - .header( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("16"), - ) - .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .to_http_parts(); - - let json = JsonBody::::new(&req, &mut pl, None).await; - assert_eq!( - json.ok().unwrap(), - MyObject { - name: "test".to_owned() - } - ); - }) - } - - #[test] - fn test_with_json_and_bad_content_type() { - block_on(async { - let (req, mut pl) = TestRequest::with_header( + #[actix_rt::test] + async fn test_custom_error_responder() { + let (req, mut pl) = TestRequest::default() + .header( header::CONTENT_TYPE, - header::HeaderValue::from_static("text/plain"), + header::HeaderValue::from_static("application/json"), ) .header( header::CONTENT_LENGTH, header::HeaderValue::from_static("16"), ) .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .data(JsonConfig::default().limit(4096)) - .to_http_parts(); - - let s = Json::::from_request(&req, &mut pl).await; - assert!(s.is_err()) - }) - } - - #[test] - fn test_with_json_and_good_custom_content_type() { - block_on(async { - let (req, mut pl) = TestRequest::with_header( - header::CONTENT_TYPE, - header::HeaderValue::from_static("text/plain"), - ) - .header( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("16"), - ) - .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .data(JsonConfig::default().content_type(|mime: mime::Mime| { - mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN + .data(JsonConfig::default().limit(10).error_handler(|err, _| { + let msg = MyObject { + name: "invalid request".to_string(), + }; + let resp = HttpResponse::BadRequest() + .body(serde_json::to_string(&msg).unwrap()); + InternalError::from_response(err, resp).into() })) .to_http_parts(); - let s = Json::::from_request(&req, &mut pl).await; - assert!(s.is_ok()) - }) + let s = Json::::from_request(&req, &mut pl).await; + let mut resp = Response::from_error(s.err().unwrap().into()); + assert_eq!(resp.status(), StatusCode::BAD_REQUEST); + + let body = load_stream(resp.take_body()).await.unwrap(); + let msg: MyObject = serde_json::from_slice(&body).unwrap(); + assert_eq!(msg.name, "invalid request"); } - #[test] - fn test_with_json_and_bad_custom_content_type() { - block_on(async { - let (req, mut pl) = TestRequest::with_header( + #[actix_rt::test] + async fn test_extract() { + let (req, mut pl) = TestRequest::default() + .header( header::CONTENT_TYPE, - header::HeaderValue::from_static("text/html"), + header::HeaderValue::from_static("application/json"), ) .header( header::CONTENT_LENGTH, header::HeaderValue::from_static("16"), ) .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .data(JsonConfig::default().content_type(|mime: mime::Mime| { - mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN - })) .to_http_parts(); - let s = Json::::from_request(&req, &mut pl).await; - assert!(s.is_err()) - }) + let s = Json::::from_request(&req, &mut pl).await.unwrap(); + assert_eq!(s.name, "test"); + assert_eq!( + s.into_inner(), + MyObject { + name: "test".to_string() + } + ); + + let (req, mut pl) = TestRequest::default() + .header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/json"), + ) + .header( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("16"), + ) + .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) + .data(JsonConfig::default().limit(10)) + .to_http_parts(); + + let s = Json::::from_request(&req, &mut pl).await; + assert!(format!("{}", s.err().unwrap()) + .contains("Json payload size is bigger than allowed")); + + let (req, mut pl) = TestRequest::default() + .header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/json"), + ) + .header( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("16"), + ) + .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) + .data( + JsonConfig::default() + .limit(10) + .error_handler(|_, _| JsonPayloadError::ContentType.into()), + ) + .to_http_parts(); + let s = Json::::from_request(&req, &mut pl).await; + assert!(format!("{}", s.err().unwrap()).contains("Content type error")); + } + + #[actix_rt::test] + async fn test_json_body() { + let (req, mut pl) = TestRequest::default().to_http_parts(); + let json = JsonBody::::new(&req, &mut pl, None).await; + assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); + + let (req, mut pl) = TestRequest::default() + .header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/text"), + ) + .to_http_parts(); + let json = JsonBody::::new(&req, &mut pl, None).await; + assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); + + let (req, mut pl) = TestRequest::default() + .header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/json"), + ) + .header( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("10000"), + ) + .to_http_parts(); + + let json = JsonBody::::new(&req, &mut pl, None) + .limit(100) + .await; + assert!(json_eq(json.err().unwrap(), JsonPayloadError::Overflow)); + + let (req, mut pl) = TestRequest::default() + .header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/json"), + ) + .header( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("16"), + ) + .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) + .to_http_parts(); + + let json = JsonBody::::new(&req, &mut pl, None).await; + assert_eq!( + json.ok().unwrap(), + MyObject { + name: "test".to_owned() + } + ); + } + + #[actix_rt::test] + async fn test_with_json_and_bad_content_type() { + let (req, mut pl) = TestRequest::with_header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("text/plain"), + ) + .header( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("16"), + ) + .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) + .data(JsonConfig::default().limit(4096)) + .to_http_parts(); + + let s = Json::::from_request(&req, &mut pl).await; + assert!(s.is_err()) + } + + #[actix_rt::test] + async fn test_with_json_and_good_custom_content_type() { + let (req, mut pl) = TestRequest::with_header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("text/plain"), + ) + .header( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("16"), + ) + .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) + .data(JsonConfig::default().content_type(|mime: mime::Mime| { + mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN + })) + .to_http_parts(); + + let s = Json::::from_request(&req, &mut pl).await; + assert!(s.is_ok()) + } + + #[actix_rt::test] + async fn test_with_json_and_bad_custom_content_type() { + let (req, mut pl) = TestRequest::with_header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("text/html"), + ) + .header( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("16"), + ) + .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) + .data(JsonConfig::default().content_type(|mime: mime::Mime| { + mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN + })) + .to_http_parts(); + + let s = Json::::from_request(&req, &mut pl).await; + assert!(s.is_err()) } } diff --git a/src/types/mod.rs b/src/types/mod.rs index 43a189e2c..b32711e2a 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -12,3 +12,4 @@ pub use self::json::{Json, JsonConfig}; pub use self::path::{Path, PathConfig}; pub use self::payload::{Payload, PayloadConfig}; pub use self::query::{Query, QueryConfig}; +pub use self::readlines::Readlines; diff --git a/src/types/path.rs b/src/types/path.rs index 29a574feb..404759300 100644 --- a/src/types/path.rs +++ b/src/types/path.rs @@ -1,5 +1,4 @@ //! Path extractor - use std::sync::Arc; use std::{fmt, ops}; @@ -253,7 +252,7 @@ mod tests { use serde_derive::Deserialize; use super::*; - use crate::test::{block_on, TestRequest}; + use crate::test::TestRequest; use crate::{error, http, HttpResponse}; #[derive(Deserialize, Debug, Display)] @@ -269,118 +268,110 @@ mod tests { value: u32, } - #[test] - fn test_extract_path_single() { - block_on(async { - let resource = ResourceDef::new("/{value}/"); + #[actix_rt::test] + async fn test_extract_path_single() { + let resource = ResourceDef::new("/{value}/"); - let mut req = TestRequest::with_uri("/32/").to_srv_request(); - resource.match_path(req.match_info_mut()); + let mut req = TestRequest::with_uri("/32/").to_srv_request(); + resource.match_path(req.match_info_mut()); - let (req, mut pl) = req.into_parts(); - assert_eq!(*Path::::from_request(&req, &mut pl).await.unwrap(), 32); - assert!(Path::::from_request(&req, &mut pl).await.is_err()); - }) + let (req, mut pl) = req.into_parts(); + assert_eq!(*Path::::from_request(&req, &mut pl).await.unwrap(), 32); + assert!(Path::::from_request(&req, &mut pl).await.is_err()); } - #[test] - fn test_tuple_extract() { - block_on(async { - let resource = ResourceDef::new("/{key}/{value}/"); + #[actix_rt::test] + async fn test_tuple_extract() { + let resource = ResourceDef::new("/{key}/{value}/"); - let mut req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request(); - resource.match_path(req.match_info_mut()); + let mut req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request(); + resource.match_path(req.match_info_mut()); - let (req, mut pl) = req.into_parts(); - let res = <(Path<(String, String)>,)>::from_request(&req, &mut pl) - .await - .unwrap(); - assert_eq!((res.0).0, "name"); - assert_eq!((res.0).1, "user1"); - - let res = <(Path<(String, String)>, Path<(String, String)>)>::from_request( - &req, &mut pl, - ) + let (req, mut pl) = req.into_parts(); + let res = <(Path<(String, String)>,)>::from_request(&req, &mut pl) .await .unwrap(); - assert_eq!((res.0).0, "name"); - assert_eq!((res.0).1, "user1"); - assert_eq!((res.1).0, "name"); - assert_eq!((res.1).1, "user1"); + assert_eq!((res.0).0, "name"); + assert_eq!((res.0).1, "user1"); - let () = <()>::from_request(&req, &mut pl).await.unwrap(); - }) + let res = <(Path<(String, String)>, Path<(String, String)>)>::from_request( + &req, &mut pl, + ) + .await + .unwrap(); + assert_eq!((res.0).0, "name"); + assert_eq!((res.0).1, "user1"); + assert_eq!((res.1).0, "name"); + assert_eq!((res.1).1, "user1"); + + let () = <()>::from_request(&req, &mut pl).await.unwrap(); } - #[test] - fn test_request_extract() { - block_on(async { - let mut req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request(); + #[actix_rt::test] + async fn test_request_extract() { + let mut req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request(); - let resource = ResourceDef::new("/{key}/{value}/"); - resource.match_path(req.match_info_mut()); + let resource = ResourceDef::new("/{key}/{value}/"); + resource.match_path(req.match_info_mut()); - let (req, mut pl) = req.into_parts(); - let mut s = Path::::from_request(&req, &mut pl).await.unwrap(); - assert_eq!(s.key, "name"); - assert_eq!(s.value, "user1"); - s.value = "user2".to_string(); - assert_eq!(s.value, "user2"); - assert_eq!( - format!("{}, {:?}", s, s), - "MyStruct(name, user2), MyStruct { key: \"name\", value: \"user2\" }" - ); - let s = s.into_inner(); - assert_eq!(s.value, "user2"); + let (req, mut pl) = req.into_parts(); + let mut s = Path::::from_request(&req, &mut pl).await.unwrap(); + assert_eq!(s.key, "name"); + assert_eq!(s.value, "user1"); + s.value = "user2".to_string(); + assert_eq!(s.value, "user2"); + assert_eq!( + format!("{}, {:?}", s, s), + "MyStruct(name, user2), MyStruct { key: \"name\", value: \"user2\" }" + ); + let s = s.into_inner(); + assert_eq!(s.value, "user2"); - let s = Path::<(String, String)>::from_request(&req, &mut pl) - .await - .unwrap(); - assert_eq!(s.0, "name"); - assert_eq!(s.1, "user1"); + let s = Path::<(String, String)>::from_request(&req, &mut pl) + .await + .unwrap(); + assert_eq!(s.0, "name"); + assert_eq!(s.1, "user1"); - let mut req = TestRequest::with_uri("/name/32/").to_srv_request(); - let resource = ResourceDef::new("/{key}/{value}/"); - resource.match_path(req.match_info_mut()); + let mut req = TestRequest::with_uri("/name/32/").to_srv_request(); + let resource = ResourceDef::new("/{key}/{value}/"); + resource.match_path(req.match_info_mut()); - let (req, mut pl) = req.into_parts(); - let s = Path::::from_request(&req, &mut pl).await.unwrap(); - assert_eq!(s.as_ref().key, "name"); - assert_eq!(s.value, 32); + let (req, mut pl) = req.into_parts(); + let s = Path::::from_request(&req, &mut pl).await.unwrap(); + assert_eq!(s.as_ref().key, "name"); + assert_eq!(s.value, 32); - let s = Path::<(String, u8)>::from_request(&req, &mut pl) - .await - .unwrap(); - assert_eq!(s.0, "name"); - assert_eq!(s.1, 32); + let s = Path::<(String, u8)>::from_request(&req, &mut pl) + .await + .unwrap(); + assert_eq!(s.0, "name"); + assert_eq!(s.1, 32); - let res = Path::>::from_request(&req, &mut pl) - .await - .unwrap(); - assert_eq!(res[0], "name".to_owned()); - assert_eq!(res[1], "32".to_owned()); - }) + let res = Path::>::from_request(&req, &mut pl) + .await + .unwrap(); + assert_eq!(res[0], "name".to_owned()); + assert_eq!(res[1], "32".to_owned()); } - #[test] - fn test_custom_err_handler() { - block_on(async { - let (req, mut pl) = TestRequest::with_uri("/name/user1/") - .data(PathConfig::default().error_handler(|err, _| { - error::InternalError::from_response( - err, - HttpResponse::Conflict().finish(), - ) - .into() - })) - .to_http_parts(); + #[actix_rt::test] + async fn test_custom_err_handler() { + let (req, mut pl) = TestRequest::with_uri("/name/user1/") + .data(PathConfig::default().error_handler(|err, _| { + error::InternalError::from_response( + err, + HttpResponse::Conflict().finish(), + ) + .into() + })) + .to_http_parts(); - let s = Path::<(usize,)>::from_request(&req, &mut pl) - .await - .unwrap_err(); - let res: HttpResponse = s.into(); + let s = Path::<(usize,)>::from_request(&req, &mut pl) + .await + .unwrap_err(); + let res: HttpResponse = s.into(); - assert_eq!(res.status(), http::StatusCode::CONFLICT); - }) + assert_eq!(res.status(), http::StatusCode::CONFLICT); } } diff --git a/src/types/payload.rs b/src/types/payload.rs index ee7e11667..2969e385a 100644 --- a/src/types/payload.rs +++ b/src/types/payload.rs @@ -395,10 +395,10 @@ mod tests { use super::*; use crate::http::header; - use crate::test::{block_on, TestRequest}; + use crate::test::TestRequest; - #[test] - fn test_payload_config() { + #[actix_rt::test] + async fn test_payload_config() { let req = TestRequest::default().to_http_request(); let cfg = PayloadConfig::default().mimetype(mime::APPLICATION_JSON); assert!(cfg.check_mimetype(&req).is_err()); @@ -415,32 +415,32 @@ mod tests { assert!(cfg.check_mimetype(&req).is_ok()); } - #[test] - fn test_bytes() { + #[actix_rt::test] + async fn test_bytes() { let (req, mut pl) = TestRequest::with_header(header::CONTENT_LENGTH, "11") .set_payload(Bytes::from_static(b"hello=world")) .to_http_parts(); - let s = block_on(Bytes::from_request(&req, &mut pl)).unwrap(); + let s = Bytes::from_request(&req, &mut pl).await.unwrap(); assert_eq!(s, Bytes::from_static(b"hello=world")); } - #[test] - fn test_string() { + #[actix_rt::test] + async fn test_string() { let (req, mut pl) = TestRequest::with_header(header::CONTENT_LENGTH, "11") .set_payload(Bytes::from_static(b"hello=world")) .to_http_parts(); - let s = block_on(String::from_request(&req, &mut pl)).unwrap(); + let s = String::from_request(&req, &mut pl).await.unwrap(); assert_eq!(s, "hello=world"); } - #[test] - fn test_message_body() { + #[actix_rt::test] + async fn test_message_body() { let (req, mut pl) = TestRequest::with_header(header::CONTENT_LENGTH, "xxxx") .to_srv_request() .into_parts(); - let res = block_on(HttpMessageBody::new(&req, &mut pl)); + let res = HttpMessageBody::new(&req, &mut pl).await; match res.err().unwrap() { PayloadError::UnknownLength => (), _ => unreachable!("error"), @@ -449,7 +449,7 @@ mod tests { let (req, mut pl) = TestRequest::with_header(header::CONTENT_LENGTH, "1000000") .to_srv_request() .into_parts(); - let res = block_on(HttpMessageBody::new(&req, &mut pl)); + let res = HttpMessageBody::new(&req, &mut pl).await; match res.err().unwrap() { PayloadError::Overflow => (), _ => unreachable!("error"), @@ -458,13 +458,13 @@ mod tests { let (req, mut pl) = TestRequest::default() .set_payload(Bytes::from_static(b"test")) .to_http_parts(); - let res = block_on(HttpMessageBody::new(&req, &mut pl)); + let res = HttpMessageBody::new(&req, &mut pl).await; assert_eq!(res.ok().unwrap(), Bytes::from_static(b"test")); let (req, mut pl) = TestRequest::default() .set_payload(Bytes::from_static(b"11111111111111")) .to_http_parts(); - let res = block_on(HttpMessageBody::new(&req, &mut pl).limit(5)); + let res = HttpMessageBody::new(&req, &mut pl).limit(5).await; match res.err().unwrap() { PayloadError::Overflow => (), _ => unreachable!("error"), diff --git a/src/types/query.rs b/src/types/query.rs index e442f1c31..b1f4572fa 100644 --- a/src/types/query.rs +++ b/src/types/query.rs @@ -228,7 +228,7 @@ mod tests { use super::*; use crate::error::InternalError; - use crate::test::{block_on, TestRequest}; + use crate::test::TestRequest; use crate::HttpResponse; #[derive(Deserialize, Debug, Display)] @@ -236,8 +236,8 @@ mod tests { id: String, } - #[test] - fn test_service_request_extract() { + #[actix_rt::test] + async fn test_service_request_extract() { let req = TestRequest::with_uri("/name/user1/").to_srv_request(); assert!(Query::::from_query(&req.query_string()).is_err()); @@ -252,48 +252,44 @@ mod tests { assert_eq!(s.id, "test1"); } - #[test] - fn test_request_extract() { - block_on(async { - let req = TestRequest::with_uri("/name/user1/").to_srv_request(); - let (req, mut pl) = req.into_parts(); - assert!(Query::::from_request(&req, &mut pl).await.is_err()); + #[actix_rt::test] + async fn test_request_extract() { + let req = TestRequest::with_uri("/name/user1/").to_srv_request(); + let (req, mut pl) = req.into_parts(); + assert!(Query::::from_request(&req, &mut pl).await.is_err()); - let req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request(); - let (req, mut pl) = req.into_parts(); + let req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request(); + let (req, mut pl) = req.into_parts(); - let mut s = Query::::from_request(&req, &mut pl).await.unwrap(); - assert_eq!(s.id, "test"); - assert_eq!(format!("{}, {:?}", s, s), "test, Id { id: \"test\" }"); + let mut s = Query::::from_request(&req, &mut pl).await.unwrap(); + assert_eq!(s.id, "test"); + assert_eq!(format!("{}, {:?}", s, s), "test, Id { id: \"test\" }"); - s.id = "test1".to_string(); - let s = s.into_inner(); - assert_eq!(s.id, "test1"); - }) + s.id = "test1".to_string(); + let s = s.into_inner(); + assert_eq!(s.id, "test1"); } - #[test] - fn test_custom_error_responder() { - block_on(async { - let req = TestRequest::with_uri("/name/user1/") - .data(QueryConfig::default().error_handler(|e, _| { - let resp = HttpResponse::UnprocessableEntity().finish(); - InternalError::from_response(e, resp).into() - })) - .to_srv_request(); + #[actix_rt::test] + async fn test_custom_error_responder() { + let req = TestRequest::with_uri("/name/user1/") + .data(QueryConfig::default().error_handler(|e, _| { + let resp = HttpResponse::UnprocessableEntity().finish(); + InternalError::from_response(e, resp).into() + })) + .to_srv_request(); - let (req, mut pl) = req.into_parts(); - let query = Query::::from_request(&req, &mut pl).await; + let (req, mut pl) = req.into_parts(); + let query = Query::::from_request(&req, &mut pl).await; - assert!(query.is_err()); - assert_eq!( - query - .unwrap_err() - .as_response_error() - .error_response() - .status(), - StatusCode::UNPROCESSABLE_ENTITY - ); - }) + assert!(query.is_err()); + assert_eq!( + query + .unwrap_err() + .as_response_error() + .error_response() + .status(), + StatusCode::UNPROCESSABLE_ENTITY + ); } } diff --git a/src/types/readlines.rs b/src/types/readlines.rs index e2b3f9c1d..123f8102b 100644 --- a/src/types/readlines.rs +++ b/src/types/readlines.rs @@ -1,5 +1,4 @@ use std::borrow::Cow; -use std::future::Future; use std::pin::Pin; use std::str; use std::task::{Context, Poll}; @@ -7,7 +6,6 @@ use std::task::{Context, Poll}; use bytes::{Bytes, BytesMut}; use encoding_rs::{Encoding, UTF_8}; use futures::Stream; -use pin_project::pin_project; use crate::dev::Payload; use crate::error::{PayloadError, ReadlinesError}; @@ -174,12 +172,11 @@ mod tests { use futures::stream::StreamExt; use super::*; - use crate::test::{block_on, TestRequest}; + use crate::test::TestRequest; - #[test] - fn test_readlines() { - block_on(async { - let mut req = TestRequest::default() + #[actix_rt::test] + async fn test_readlines() { + let mut req = TestRequest::default() .set_payload(Bytes::from_static( b"Lorem Ipsum is simply dummy text of the printing and typesetting\n\ industry. Lorem Ipsum has been the industry's standard dummy\n\ @@ -187,21 +184,20 @@ mod tests { )) .to_request(); - let mut stream = Readlines::new(&mut req); - assert_eq!( - stream.next().await.unwrap().unwrap(), - "Lorem Ipsum is simply dummy text of the printing and typesetting\n" - ); + let mut stream = Readlines::new(&mut req); + assert_eq!( + stream.next().await.unwrap().unwrap(), + "Lorem Ipsum is simply dummy text of the printing and typesetting\n" + ); - assert_eq!( - stream.next().await.unwrap().unwrap(), - "industry. Lorem Ipsum has been the industry's standard dummy\n" - ); + assert_eq!( + stream.next().await.unwrap().unwrap(), + "industry. Lorem Ipsum has been the industry's standard dummy\n" + ); - assert_eq!( - stream.next().await.unwrap().unwrap(), - "Contrary to popular belief, Lorem Ipsum is not simply random text." - ); - }) + assert_eq!( + stream.next().await.unwrap().unwrap(), + "Contrary to popular belief, Lorem Ipsum is not simply random text." + ); } } diff --git a/src/web.rs b/src/web.rs index 22630ae81..7f1e8d8f6 100644 --- a/src/web.rs +++ b/src/web.rs @@ -6,7 +6,6 @@ pub use actix_http::Response as HttpResponse; pub use bytes::{Bytes, BytesMut}; pub use futures::channel::oneshot::Canceled; -use crate::error::Error; use crate::extract::FromRequest; use crate::handler::Factory; use crate::resource::Resource; diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index a2b03ffc2..e59e439fe 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -54,8 +54,6 @@ slab = "0.4" serde_urlencoded = "0.6.1" time = "0.1" tokio-net = "0.2.0-alpha.6" -tokio-timer = "0.3.0-alpha.6" - open-ssl = { version="0.10", package="openssl", optional = true } [dev-dependencies] diff --git a/test-server/src/lib.rs b/test-server/src/lib.rs index 1911c75d6..9ad06397c 100644 --- a/test-server/src/lib.rs +++ b/test-server/src/lib.rs @@ -23,26 +23,25 @@ pub use actix_testing::*; /// /// ```rust /// use actix_http::HttpService; -/// use actix_http_test::{block_on, TestServer}; +/// use actix_http_test::TestServer; /// use actix_web::{web, App, HttpResponse, Error}; /// /// async fn my_handler() -> Result { /// Ok(HttpResponse::Ok().into()) /// } /// -/// fn main() { -/// block_on( async { -/// let mut srv = TestServer::start( -/// || HttpService::new( -/// App::new().service( -/// web::resource("/").to(my_handler)) -/// ) -/// ); +/// #[actix_rt::test] +/// async fn test_example() { +/// let mut srv = TestServer::start( +/// || HttpService::new( +/// App::new().service( +/// web::resource("/").to(my_handler)) +/// ) +/// ); /// -/// let req = srv.get("/"); -/// let response = req.send().await.unwrap(); -/// assert!(response.status().is_success()); -/// }) +/// let req = srv.get("/"); +/// let response = req.send().await.unwrap(); +/// assert!(response.status().is_success()); /// } /// ``` pub struct TestServer; diff --git a/tests/test_httpserver.rs b/tests/test_httpserver.rs index 122f79baf..d19c46ee7 100644 --- a/tests/test_httpserver.rs +++ b/tests/test_httpserver.rs @@ -6,7 +6,7 @@ use std::{net, thread, time::Duration}; use open_ssl::ssl::SslAcceptorBuilder; use actix_http::Response; -use actix_web::{test, web, App, HttpServer}; +use actix_web::{web, App, HttpServer}; fn unused_addr() -> net::SocketAddr { let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); @@ -17,9 +17,9 @@ fn unused_addr() -> net::SocketAddr { tcp.local_addr().unwrap() } -#[test] #[cfg(unix)] -fn test_start() { +#[actix_rt::test] +async fn test_start() { let addr = unused_addr(); let (tx, rx) = mpsc::channel(); @@ -53,21 +53,18 @@ fn test_start() { #[cfg(feature = "client")] { use actix_http::client; - use actix_web::test; - test::block_on(async { - let client = awc::Client::build() - .connector( - client::Connector::new() - .timeout(Duration::from_millis(100)) - .finish(), - ) - .finish(); + let client = awc::Client::build() + .connector( + client::Connector::new() + .timeout(Duration::from_millis(100)) + .finish(), + ) + .finish(); - let host = format!("http://{}", addr); - let response = client.get(host.clone()).send().await.unwrap(); - assert!(response.status().is_success()); - }); + let host = format!("http://{}", addr); + let response = client.get(host.clone()).send().await.unwrap(); + assert!(response.status().is_success()); } // stop @@ -91,9 +88,9 @@ fn ssl_acceptor() -> std::io::Result { Ok(builder) } -#[test] +#[actix_rt::test] #[cfg(feature = "openssl")] -fn test_start_ssl() { +async fn test_start_ssl() { let addr = unused_addr(); let (tx, rx) = mpsc::channel(); @@ -119,27 +116,25 @@ fn test_start_ssl() { }); let (srv, sys) = rx.recv().unwrap(); - test::block_on(async move { - use open_ssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; - let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); - builder.set_verify(SslVerifyMode::NONE); - let _ = builder - .set_alpn_protos(b"\x02h2\x08http/1.1") - .map_err(|e| log::error!("Can not set alpn protocol: {:?}", e)); + use open_ssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; + let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); + builder.set_verify(SslVerifyMode::NONE); + let _ = builder + .set_alpn_protos(b"\x02h2\x08http/1.1") + .map_err(|e| log::error!("Can not set alpn protocol: {:?}", e)); - let client = awc::Client::build() - .connector( - awc::Connector::new() - .ssl(builder.build()) - .timeout(Duration::from_millis(100)) - .finish(), - ) - .finish(); + let client = awc::Client::build() + .connector( + awc::Connector::new() + .ssl(builder.build()) + .timeout(Duration::from_millis(100)) + .finish(), + ) + .finish(); - let host = format!("https://{}", addr); - let response = client.get(host.clone()).send().await.unwrap(); - assert!(response.status().is_success()); - }); + let host = format!("https://{}", addr); + let response = client.get(host.clone()).send().await.unwrap(); + assert!(response.status().is_success()); // stop let _ = srv.stop(false); diff --git a/tests/test_server.rs b/tests/test_server.rs index 0114b21ff..bfdf3f0ee 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -5,7 +5,7 @@ use actix_http::http::header::{ TRANSFER_ENCODING, }; use actix_http::{h1, Error, HttpService, Response}; -use actix_http_test::{block_on, TestServer}; +use actix_http_test::TestServer; use brotli2::write::{BrotliDecoder, BrotliEncoder}; use bytes::Bytes; use flate2::read::GzDecoder; @@ -39,728 +39,676 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World"; -#[test] -fn test_body() { - block_on(async { - let srv = - TestServer::start(|| { - h1::H1Service::new(App::new().service( - web::resource("/").route(web::to(|| Response::Ok().body(STR))), - )) - }); +#[actix_rt::test] +async fn test_body() { + let srv = TestServer::start(|| { + h1::H1Service::new( + App::new() + .service(web::resource("/").route(web::to(|| Response::Ok().body(STR)))), + ) + }); - let mut response = srv.get("/").send().await.unwrap(); - assert!(response.status().is_success()); + let mut response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = response.body().await.unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - }) + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); } #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] -#[test] -fn test_body_gzip() { - block_on(async { - let srv = TestServer::start(|| { - h1::H1Service::new( - App::new() - .wrap(Compress::new(ContentEncoding::Gzip)) - .service( - web::resource("/").route(web::to(|| Response::Ok().body(STR))), - ), - ) - }); +#[actix_rt::test] +async fn test_body_gzip() { + let srv = TestServer::start(|| { + h1::H1Service::new( + App::new() + .wrap(Compress::new(ContentEncoding::Gzip)) + .service(web::resource("/").route(web::to(|| Response::Ok().body(STR)))), + ) + }); - let mut response = srv - .get("/") - .no_decompress() - .header(ACCEPT_ENCODING, "gzip") - .send() - .await - .unwrap(); - assert!(response.status().is_success()); + let mut response = srv + .get("/") + .no_decompress() + .header(ACCEPT_ENCODING, "gzip") + .send() + .await + .unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = response.body().await.unwrap(); + // read response + let bytes = response.body().await.unwrap(); - // decode - let mut e = GzDecoder::new(&bytes[..]); - let mut dec = Vec::new(); - e.read_to_end(&mut dec).unwrap(); - assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); - }) + // decode + let mut e = GzDecoder::new(&bytes[..]); + let mut dec = Vec::new(); + e.read_to_end(&mut dec).unwrap(); + assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); } #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] -#[test] -fn test_body_gzip2() { - block_on(async { - let srv = TestServer::start(|| { - h1::H1Service::new( - App::new() - .wrap(Compress::new(ContentEncoding::Gzip)) - .service(web::resource("/").route(web::to(|| { - Response::Ok().body(STR).into_body::() - }))), - ) - }); +#[actix_rt::test] +async fn test_body_gzip2() { + let srv = TestServer::start(|| { + h1::H1Service::new( + App::new() + .wrap(Compress::new(ContentEncoding::Gzip)) + .service(web::resource("/").route(web::to(|| { + Response::Ok().body(STR).into_body::() + }))), + ) + }); - let mut response = srv - .get("/") - .no_decompress() - .header(ACCEPT_ENCODING, "gzip") - .send() - .await - .unwrap(); - assert!(response.status().is_success()); + let mut response = srv + .get("/") + .no_decompress() + .header(ACCEPT_ENCODING, "gzip") + .send() + .await + .unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = response.body().await.unwrap(); + // read response + let bytes = response.body().await.unwrap(); - // decode - let mut e = GzDecoder::new(&bytes[..]); - let mut dec = Vec::new(); - e.read_to_end(&mut dec).unwrap(); - assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); - }) + // decode + let mut e = GzDecoder::new(&bytes[..]); + let mut dec = Vec::new(); + e.read_to_end(&mut dec).unwrap(); + assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); } #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] -#[test] -fn test_body_encoding_override() { - block_on(async { - let srv = TestServer::start(|| { - h1::H1Service::new( - App::new() - .wrap(Compress::new(ContentEncoding::Gzip)) - .service(web::resource("/").route(web::to(|| { - Response::Ok().encoding(ContentEncoding::Deflate).body(STR) - }))) - .service(web::resource("/raw").route(web::to(|| { - let body = actix_web::dev::Body::Bytes(STR.into()); - let mut response = - Response::with_body(actix_web::http::StatusCode::OK, body); +#[actix_rt::test] +async fn test_body_encoding_override() { + let srv = TestServer::start(|| { + h1::H1Service::new( + App::new() + .wrap(Compress::new(ContentEncoding::Gzip)) + .service(web::resource("/").route(web::to(|| { + Response::Ok().encoding(ContentEncoding::Deflate).body(STR) + }))) + .service(web::resource("/raw").route(web::to(|| { + let body = actix_web::dev::Body::Bytes(STR.into()); + let mut response = + Response::with_body(actix_web::http::StatusCode::OK, body); - response.encoding(ContentEncoding::Deflate); + response.encoding(ContentEncoding::Deflate); - response - }))), - ) - }); + response + }))), + ) + }); - // Builder - let mut response = srv - .get("/") - .no_decompress() - .header(ACCEPT_ENCODING, "deflate") - .send() - .await - .unwrap(); - assert!(response.status().is_success()); + // Builder + let mut response = srv + .get("/") + .no_decompress() + .header(ACCEPT_ENCODING, "deflate") + .send() + .await + .unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = response.body().await.unwrap(); + // read response + let bytes = response.body().await.unwrap(); - // decode - let mut e = ZlibDecoder::new(Vec::new()); - e.write_all(bytes.as_ref()).unwrap(); - let dec = e.finish().unwrap(); - assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); + // decode + let mut e = ZlibDecoder::new(Vec::new()); + e.write_all(bytes.as_ref()).unwrap(); + let dec = e.finish().unwrap(); + assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); - // Raw Response - let mut response = srv - .request(actix_web::http::Method::GET, srv.url("/raw")) - .no_decompress() - .header(ACCEPT_ENCODING, "deflate") - .send() - .await - .unwrap(); - assert!(response.status().is_success()); + // Raw Response + let mut response = srv + .request(actix_web::http::Method::GET, srv.url("/raw")) + .no_decompress() + .header(ACCEPT_ENCODING, "deflate") + .send() + .await + .unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = response.body().await.unwrap(); + // read response + let bytes = response.body().await.unwrap(); - // decode - let mut e = ZlibDecoder::new(Vec::new()); - e.write_all(bytes.as_ref()).unwrap(); - let dec = e.finish().unwrap(); - assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); - }) + // decode + let mut e = ZlibDecoder::new(Vec::new()); + e.write_all(bytes.as_ref()).unwrap(); + let dec = e.finish().unwrap(); + assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); } #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] -#[test] -fn test_body_gzip_large() { - block_on(async { - let data = STR.repeat(10); - let srv_data = data.clone(); +#[actix_rt::test] +async fn test_body_gzip_large() { + let data = STR.repeat(10); + let srv_data = data.clone(); - let srv = TestServer::start(move || { - let data = srv_data.clone(); - h1::H1Service::new( - App::new() - .wrap(Compress::new(ContentEncoding::Gzip)) - .service( - web::resource("/") - .route(web::to(move || Response::Ok().body(data.clone()))), - ), - ) - }); - - let mut response = srv - .get("/") - .no_decompress() - .header(ACCEPT_ENCODING, "gzip") - .send() - .await - .unwrap(); - assert!(response.status().is_success()); - - // read response - let bytes = response.body().await.unwrap(); - - // decode - let mut e = GzDecoder::new(&bytes[..]); - let mut dec = Vec::new(); - e.read_to_end(&mut dec).unwrap(); - assert_eq!(Bytes::from(dec), Bytes::from(data)); - }) -} - -#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] -#[test] -fn test_body_gzip_large_random() { - block_on(async { - let data = rand::thread_rng() - .sample_iter(&Alphanumeric) - .take(70_000) - .collect::(); - let srv_data = data.clone(); - - let srv = TestServer::start(move || { - let data = srv_data.clone(); - h1::H1Service::new( - App::new() - .wrap(Compress::new(ContentEncoding::Gzip)) - .service( - web::resource("/") - .route(web::to(move || Response::Ok().body(data.clone()))), - ), - ) - }); - - let mut response = srv - .get("/") - .no_decompress() - .header(ACCEPT_ENCODING, "gzip") - .send() - .await - .unwrap(); - assert!(response.status().is_success()); - - // read response - let bytes = response.body().await.unwrap(); - - // decode - let mut e = GzDecoder::new(&bytes[..]); - let mut dec = Vec::new(); - e.read_to_end(&mut dec).unwrap(); - assert_eq!(dec.len(), data.len()); - assert_eq!(Bytes::from(dec), Bytes::from(data)); - }) -} - -#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] -#[test] -fn test_body_chunked_implicit() { - block_on(async { - let srv = TestServer::start(move || { - h1::H1Service::new( - App::new() - .wrap(Compress::new(ContentEncoding::Gzip)) - .service(web::resource("/").route(web::get().to(move || { - Response::Ok().streaming(once(ok::<_, Error>( - Bytes::from_static(STR.as_ref()), - ))) - }))), - ) - }); - - let mut response = srv - .get("/") - .no_decompress() - .header(ACCEPT_ENCODING, "gzip") - .send() - .await - .unwrap(); - assert!(response.status().is_success()); - assert_eq!( - response.headers().get(TRANSFER_ENCODING).unwrap(), - &b"chunked"[..] - ); - - // read response - let bytes = response.body().await.unwrap(); - - // decode - let mut e = GzDecoder::new(&bytes[..]); - let mut dec = Vec::new(); - e.read_to_end(&mut dec).unwrap(); - assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); - }) -} - -#[test] -#[cfg(feature = "brotli")] -fn test_body_br_streaming() { - block_on(async { - let srv = TestServer::start(move || { - h1::H1Service::new( - App::new().wrap(Compress::new(ContentEncoding::Br)).service( - web::resource("/").route(web::to(move || { - Response::Ok().streaming(once(ok::<_, Error>( - Bytes::from_static(STR.as_ref()), - ))) - })), + let srv = TestServer::start(move || { + let data = srv_data.clone(); + h1::H1Service::new( + App::new() + .wrap(Compress::new(ContentEncoding::Gzip)) + .service( + web::resource("/") + .route(web::to(move || Response::Ok().body(data.clone()))), ), - ) - }); + ) + }); - let mut response = srv - .get("/") - .header(ACCEPT_ENCODING, "br") - .no_decompress() - .send() - .await - .unwrap(); - assert!(response.status().is_success()); + let mut response = srv + .get("/") + .no_decompress() + .header(ACCEPT_ENCODING, "gzip") + .send() + .await + .unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = response.body().await.unwrap(); + // read response + let bytes = response.body().await.unwrap(); - // decode br - let mut e = BrotliDecoder::new(Vec::with_capacity(2048)); - e.write_all(bytes.as_ref()).unwrap(); - let dec = e.finish().unwrap(); - assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); - }) + // decode + let mut e = GzDecoder::new(&bytes[..]); + let mut dec = Vec::new(); + e.read_to_end(&mut dec).unwrap(); + assert_eq!(Bytes::from(dec), Bytes::from(data)); } -#[test] -fn test_head_binary() { - block_on(async { - let srv = TestServer::start(move || { - h1::H1Service::new(App::new().service(web::resource("/").route( - web::head().to(move || Response::Ok().content_length(100).body(STR)), - ))) - }); - - let mut response = srv.head("/").send().await.unwrap(); - assert!(response.status().is_success()); - - { - let len = response.headers().get(CONTENT_LENGTH).unwrap(); - assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); - } - - // read response - let bytes = response.body().await.unwrap(); - assert!(bytes.is_empty()); - }) -} - -#[test] -fn test_no_chunking() { - block_on(async { - let srv = TestServer::start(move || { - h1::H1Service::new(App::new().service(web::resource("/").route(web::to( - move || { - Response::Ok() - .no_chunking() - .content_length(STR.len() as u64) - .streaming(once(ok::<_, Error>(Bytes::from_static( - STR.as_ref(), - )))) - }, - )))) - }); - - let mut response = srv.get("/").send().await.unwrap(); - assert!(response.status().is_success()); - assert!(!response.headers().contains_key(TRANSFER_ENCODING)); - - // read response - let bytes = response.body().await.unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - }) -} - -#[test] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] -fn test_body_deflate() { - block_on(async { - let srv = TestServer::start(move || { - h1::H1Service::new( - App::new() - .wrap(Compress::new(ContentEncoding::Deflate)) - .service( - web::resource("/") - .route(web::to(move || Response::Ok().body(STR))), - ), - ) - }); +#[actix_rt::test] +async fn test_body_gzip_large_random() { + let data = rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(70_000) + .collect::(); + let srv_data = data.clone(); - // client request - let mut response = srv - .get("/") - .header(ACCEPT_ENCODING, "deflate") - .no_decompress() - .send() - .await - .unwrap(); - assert!(response.status().is_success()); + let srv = TestServer::start(move || { + let data = srv_data.clone(); + h1::H1Service::new( + App::new() + .wrap(Compress::new(ContentEncoding::Gzip)) + .service( + web::resource("/") + .route(web::to(move || Response::Ok().body(data.clone()))), + ), + ) + }); - // read response - let bytes = response.body().await.unwrap(); + let mut response = srv + .get("/") + .no_decompress() + .header(ACCEPT_ENCODING, "gzip") + .send() + .await + .unwrap(); + assert!(response.status().is_success()); - let mut e = ZlibDecoder::new(Vec::new()); - e.write_all(bytes.as_ref()).unwrap(); - let dec = e.finish().unwrap(); - assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); - }) + // read response + let bytes = response.body().await.unwrap(); + + // decode + let mut e = GzDecoder::new(&bytes[..]); + let mut dec = Vec::new(); + e.read_to_end(&mut dec).unwrap(); + assert_eq!(dec.len(), data.len()); + assert_eq!(Bytes::from(dec), Bytes::from(data)); } -#[test] -#[cfg(any(feature = "brotli"))] -fn test_body_brotli() { - block_on(async { - let srv = TestServer::start(move || { - h1::H1Service::new( - App::new().wrap(Compress::new(ContentEncoding::Br)).service( +#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] +#[actix_rt::test] +async fn test_body_chunked_implicit() { + let srv = TestServer::start(move || { + h1::H1Service::new( + App::new() + .wrap(Compress::new(ContentEncoding::Gzip)) + .service(web::resource("/").route(web::get().to(move || { + Response::Ok().streaming(once(ok::<_, Error>(Bytes::from_static( + STR.as_ref(), + )))) + }))), + ) + }); + + let mut response = srv + .get("/") + .no_decompress() + .header(ACCEPT_ENCODING, "gzip") + .send() + .await + .unwrap(); + assert!(response.status().is_success()); + assert_eq!( + response.headers().get(TRANSFER_ENCODING).unwrap(), + &b"chunked"[..] + ); + + // read response + let bytes = response.body().await.unwrap(); + + // decode + let mut e = GzDecoder::new(&bytes[..]); + let mut dec = Vec::new(); + e.read_to_end(&mut dec).unwrap(); + assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); +} + +#[actix_rt::test] +#[cfg(feature = "brotli")] +async fn test_body_br_streaming() { + let srv = TestServer::start(move || { + h1::H1Service::new(App::new().wrap(Compress::new(ContentEncoding::Br)).service( + web::resource("/").route(web::to(move || { + Response::Ok() + .streaming(once(ok::<_, Error>(Bytes::from_static(STR.as_ref())))) + })), + )) + }); + + let mut response = srv + .get("/") + .header(ACCEPT_ENCODING, "br") + .no_decompress() + .send() + .await + .unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = response.body().await.unwrap(); + + // decode br + let mut e = BrotliDecoder::new(Vec::with_capacity(2048)); + e.write_all(bytes.as_ref()).unwrap(); + let dec = e.finish().unwrap(); + assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); +} + +#[actix_rt::test] +async fn test_head_binary() { + let srv = TestServer::start(move || { + h1::H1Service::new(App::new().service(web::resource("/").route( + web::head().to(move || Response::Ok().content_length(100).body(STR)), + ))) + }); + + let mut response = srv.head("/").send().await.unwrap(); + assert!(response.status().is_success()); + + { + let len = response.headers().get(CONTENT_LENGTH).unwrap(); + assert_eq!(format!("{}", STR.len()), len.to_str().unwrap()); + } + + // read response + let bytes = response.body().await.unwrap(); + assert!(bytes.is_empty()); +} + +#[actix_rt::test] +async fn test_no_chunking() { + let srv = TestServer::start(move || { + h1::H1Service::new(App::new().service(web::resource("/").route(web::to( + move || { + Response::Ok() + .no_chunking() + .content_length(STR.len() as u64) + .streaming(once(ok::<_, Error>(Bytes::from_static(STR.as_ref())))) + }, + )))) + }); + + let mut response = srv.get("/").send().await.unwrap(); + assert!(response.status().is_success()); + assert!(!response.headers().contains_key(TRANSFER_ENCODING)); + + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); +} + +#[actix_rt::test] +#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] +async fn test_body_deflate() { + let srv = TestServer::start(move || { + h1::H1Service::new( + App::new() + .wrap(Compress::new(ContentEncoding::Deflate)) + .service( web::resource("/").route(web::to(move || Response::Ok().body(STR))), ), - ) - }); + ) + }); - // client request - let mut response = srv - .get("/") - .header(ACCEPT_ENCODING, "br") - .no_decompress() - .send() - .await - .unwrap(); - assert!(response.status().is_success()); + // client request + let mut response = srv + .get("/") + .header(ACCEPT_ENCODING, "deflate") + .no_decompress() + .send() + .await + .unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = response.body().await.unwrap(); + // read response + let bytes = response.body().await.unwrap(); - // decode brotli - let mut e = BrotliDecoder::new(Vec::with_capacity(2048)); - e.write_all(bytes.as_ref()).unwrap(); - let dec = e.finish().unwrap(); - assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); - }) + let mut e = ZlibDecoder::new(Vec::new()); + e.write_all(bytes.as_ref()).unwrap(); + let dec = e.finish().unwrap(); + assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); } -#[test] +#[actix_rt::test] +#[cfg(any(feature = "brotli"))] +async fn test_body_brotli() { + let srv = TestServer::start(move || { + h1::H1Service::new(App::new().wrap(Compress::new(ContentEncoding::Br)).service( + web::resource("/").route(web::to(move || Response::Ok().body(STR))), + )) + }); + + // client request + let mut response = srv + .get("/") + .header(ACCEPT_ENCODING, "br") + .no_decompress() + .send() + .await + .unwrap(); + assert!(response.status().is_success()); + + // read response + let bytes = response.body().await.unwrap(); + + // decode brotli + let mut e = BrotliDecoder::new(Vec::with_capacity(2048)); + e.write_all(bytes.as_ref()).unwrap(); + let dec = e.finish().unwrap(); + assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); +} + +#[actix_rt::test] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] -fn test_encoding() { - block_on(async { - let srv = TestServer::start(move || { - HttpService::new( - App::new().wrap(Compress::default()).service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) - }); +async fn test_encoding() { + let srv = TestServer::start(move || { + HttpService::new( + App::new().wrap(Compress::default()).service( + web::resource("/") + .route(web::to(move |body: Bytes| Response::Ok().body(body))), + ), + ) + }); - // client request - let mut e = GzEncoder::new(Vec::new(), Compression::default()); - e.write_all(STR.as_ref()).unwrap(); - let enc = e.finish().unwrap(); + // client request + let mut e = GzEncoder::new(Vec::new(), Compression::default()); + e.write_all(STR.as_ref()).unwrap(); + let enc = e.finish().unwrap(); - let request = srv - .post("/") - .header(CONTENT_ENCODING, "gzip") - .send_body(enc.clone()); - let mut response = request.await.unwrap(); - assert!(response.status().is_success()); + let request = srv + .post("/") + .header(CONTENT_ENCODING, "gzip") + .send_body(enc.clone()); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = response.body().await.unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - }) + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); } -#[test] +#[actix_rt::test] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] -fn test_gzip_encoding() { - block_on(async { - let srv = TestServer::start(move || { - HttpService::new( - App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) - }); +async fn test_gzip_encoding() { + let srv = TestServer::start(move || { + HttpService::new( + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| Response::Ok().body(body))), + ), + ) + }); - // client request - let mut e = GzEncoder::new(Vec::new(), Compression::default()); - e.write_all(STR.as_ref()).unwrap(); - let enc = e.finish().unwrap(); + // client request + let mut e = GzEncoder::new(Vec::new(), Compression::default()); + e.write_all(STR.as_ref()).unwrap(); + let enc = e.finish().unwrap(); - let request = srv - .post("/") - .header(CONTENT_ENCODING, "gzip") - .send_body(enc.clone()); - let mut response = request.await.unwrap(); - assert!(response.status().is_success()); + let request = srv + .post("/") + .header(CONTENT_ENCODING, "gzip") + .send_body(enc.clone()); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = response.body().await.unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - }) + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); } -#[test] +#[actix_rt::test] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] -fn test_gzip_encoding_large() { - block_on(async { - let data = STR.repeat(10); - let srv = TestServer::start(move || { - h1::H1Service::new( - App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) - }); +async fn test_gzip_encoding_large() { + let data = STR.repeat(10); + let srv = TestServer::start(move || { + h1::H1Service::new( + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| Response::Ok().body(body))), + ), + ) + }); - // client request - let mut e = GzEncoder::new(Vec::new(), Compression::default()); - e.write_all(data.as_ref()).unwrap(); - let enc = e.finish().unwrap(); + // client request + let mut e = GzEncoder::new(Vec::new(), Compression::default()); + e.write_all(data.as_ref()).unwrap(); + let enc = e.finish().unwrap(); - let request = srv - .post("/") - .header(CONTENT_ENCODING, "gzip") - .send_body(enc.clone()); - let mut response = request.await.unwrap(); - assert!(response.status().is_success()); + let request = srv + .post("/") + .header(CONTENT_ENCODING, "gzip") + .send_body(enc.clone()); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = response.body().await.unwrap(); - assert_eq!(bytes, Bytes::from(data)); - }) + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from(data)); } -#[test] +#[actix_rt::test] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] -fn test_reading_gzip_encoding_large_random() { - block_on(async { - let data = rand::thread_rng() - .sample_iter(&Alphanumeric) - .take(60_000) - .collect::(); +async fn test_reading_gzip_encoding_large_random() { + let data = rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(60_000) + .collect::(); - let srv = TestServer::start(move || { - HttpService::new( - App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) - }); + let srv = TestServer::start(move || { + HttpService::new( + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| Response::Ok().body(body))), + ), + ) + }); - // client request - let mut e = GzEncoder::new(Vec::new(), Compression::default()); - e.write_all(data.as_ref()).unwrap(); - let enc = e.finish().unwrap(); + // client request + let mut e = GzEncoder::new(Vec::new(), Compression::default()); + e.write_all(data.as_ref()).unwrap(); + let enc = e.finish().unwrap(); - let request = srv - .post("/") - .header(CONTENT_ENCODING, "gzip") - .send_body(enc.clone()); - let mut response = request.await.unwrap(); - assert!(response.status().is_success()); + let request = srv + .post("/") + .header(CONTENT_ENCODING, "gzip") + .send_body(enc.clone()); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = response.body().await.unwrap(); - assert_eq!(bytes.len(), data.len()); - assert_eq!(bytes, Bytes::from(data)); - }) + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes.len(), data.len()); + assert_eq!(bytes, Bytes::from(data)); } -#[test] +#[actix_rt::test] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] -fn test_reading_deflate_encoding() { - block_on(async { - let srv = TestServer::start(move || { - h1::H1Service::new( - App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) - }); +async fn test_reading_deflate_encoding() { + let srv = TestServer::start(move || { + h1::H1Service::new( + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| Response::Ok().body(body))), + ), + ) + }); - let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); - e.write_all(STR.as_ref()).unwrap(); - let enc = e.finish().unwrap(); + let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); + e.write_all(STR.as_ref()).unwrap(); + let enc = e.finish().unwrap(); - // client request - let request = srv - .post("/") - .header(CONTENT_ENCODING, "deflate") - .send_body(enc.clone()); - let mut response = request.await.unwrap(); - assert!(response.status().is_success()); + // client request + let request = srv + .post("/") + .header(CONTENT_ENCODING, "deflate") + .send_body(enc.clone()); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = response.body().await.unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - }) + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); } -#[test] +#[actix_rt::test] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] -fn test_reading_deflate_encoding_large() { - block_on(async { - let data = STR.repeat(10); - let srv = TestServer::start(move || { - h1::H1Service::new( - App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) - }); +async fn test_reading_deflate_encoding_large() { + let data = STR.repeat(10); + let srv = TestServer::start(move || { + h1::H1Service::new( + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| Response::Ok().body(body))), + ), + ) + }); - let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); - e.write_all(data.as_ref()).unwrap(); - let enc = e.finish().unwrap(); + let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); + e.write_all(data.as_ref()).unwrap(); + let enc = e.finish().unwrap(); - // client request - let request = srv - .post("/") - .header(CONTENT_ENCODING, "deflate") - .send_body(enc.clone()); - let mut response = request.await.unwrap(); - assert!(response.status().is_success()); + // client request + let request = srv + .post("/") + .header(CONTENT_ENCODING, "deflate") + .send_body(enc.clone()); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = response.body().await.unwrap(); - assert_eq!(bytes, Bytes::from(data)); - }) + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from(data)); } -#[test] +#[actix_rt::test] #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] -fn test_reading_deflate_encoding_large_random() { - block_on(async { - let data = rand::thread_rng() - .sample_iter(&Alphanumeric) - .take(160_000) - .collect::(); +async fn test_reading_deflate_encoding_large_random() { + let data = rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(160_000) + .collect::(); - let srv = TestServer::start(move || { - h1::H1Service::new( - App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) - }); + let srv = TestServer::start(move || { + h1::H1Service::new( + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| Response::Ok().body(body))), + ), + ) + }); - let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); - e.write_all(data.as_ref()).unwrap(); - let enc = e.finish().unwrap(); + let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); + e.write_all(data.as_ref()).unwrap(); + let enc = e.finish().unwrap(); - // client request - let request = srv - .post("/") - .header(CONTENT_ENCODING, "deflate") - .send_body(enc.clone()); - let mut response = request.await.unwrap(); - assert!(response.status().is_success()); + // client request + let request = srv + .post("/") + .header(CONTENT_ENCODING, "deflate") + .send_body(enc.clone()); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = response.body().await.unwrap(); - assert_eq!(bytes.len(), data.len()); - assert_eq!(bytes, Bytes::from(data)); - }) + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes.len(), data.len()); + assert_eq!(bytes, Bytes::from(data)); } -#[test] +#[actix_rt::test] #[cfg(feature = "brotli")] -fn test_brotli_encoding() { - block_on(async { - let srv = TestServer::start(move || { - h1::H1Service::new( - App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) - }); +async fn test_brotli_encoding() { + let srv = TestServer::start(move || { + h1::H1Service::new( + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| Response::Ok().body(body))), + ), + ) + }); - let mut e = BrotliEncoder::new(Vec::new(), 5); - e.write_all(STR.as_ref()).unwrap(); - let enc = e.finish().unwrap(); + let mut e = BrotliEncoder::new(Vec::new(), 5); + e.write_all(STR.as_ref()).unwrap(); + let enc = e.finish().unwrap(); - // client request - let request = srv - .post("/") - .header(CONTENT_ENCODING, "br") - .send_body(enc.clone()); - let mut response = request.await.unwrap(); - assert!(response.status().is_success()); + // client request + let request = srv + .post("/") + .header(CONTENT_ENCODING, "br") + .send_body(enc.clone()); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = response.body().await.unwrap(); - assert_eq!(bytes, Bytes::from_static(STR.as_ref())); - }) + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from_static(STR.as_ref())); } #[cfg(feature = "brotli")] -#[test] -fn test_brotli_encoding_large() { - block_on(async { - let data = STR.repeat(10); - let srv = TestServer::start(move || { - h1::H1Service::new( - App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) - }); +#[actix_rt::test] +async fn test_brotli_encoding_large() { + let data = STR.repeat(10); + let srv = TestServer::start(move || { + h1::H1Service::new( + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| Response::Ok().body(body))), + ), + ) + }); - let mut e = BrotliEncoder::new(Vec::new(), 5); - e.write_all(data.as_ref()).unwrap(); - let enc = e.finish().unwrap(); + let mut e = BrotliEncoder::new(Vec::new(), 5); + e.write_all(data.as_ref()).unwrap(); + let enc = e.finish().unwrap(); - // client request - let request = srv - .post("/") - .header(CONTENT_ENCODING, "br") - .send_body(enc.clone()); - let mut response = request.await.unwrap(); - assert!(response.status().is_success()); + // client request + let request = srv + .post("/") + .header(CONTENT_ENCODING, "br") + .send_body(enc.clone()); + let mut response = request.await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = response.body().await.unwrap(); - assert_eq!(bytes, Bytes::from(data)); - }) + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from(data)); } // #[cfg(all(feature = "brotli", feature = "ssl"))] -// #[test] -// fn test_brotli_encoding_large_ssl() { +// #[actix_rt::test] +// async fn test_brotli_encoding_large_ssl() { // use actix::{Actor, System}; // use openssl::ssl::{ // SslAcceptor, SslConnector, SslFiletype, SslMethod, SslVerifyMode, @@ -819,91 +767,89 @@ fn test_brotli_encoding_large() { feature = "openssl", any(feature = "flate2-zlib", feature = "flate2-rust") ))] -#[test] -fn test_reading_deflate_encoding_large_random_ssl() { - block_on(async { - use open_ssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; - use rust_tls::internal::pemfile::{certs, pkcs8_private_keys}; - use rust_tls::{NoClientAuth, ServerConfig}; - use std::fs::File; - use std::io::BufReader; - use std::sync::mpsc; +#[actix_rt::test] +async fn test_reading_deflate_encoding_large_random_ssl() { + use open_ssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; + use rust_tls::internal::pemfile::{certs, pkcs8_private_keys}; + use rust_tls::{NoClientAuth, ServerConfig}; + use std::fs::File; + use std::io::BufReader; + use std::sync::mpsc; - let addr = TestServer::unused_addr(); - let (tx, rx) = mpsc::channel(); + let addr = TestServer::unused_addr(); + let (tx, rx) = mpsc::channel(); - let data = rand::thread_rng() - .sample_iter(&Alphanumeric) - .take(160_000) - .collect::(); + let data = rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(160_000) + .collect::(); - std::thread::spawn(move || { - let sys = actix_rt::System::new("test"); + std::thread::spawn(move || { + let sys = actix_rt::System::new("test"); - // load ssl keys - let mut config = ServerConfig::new(NoClientAuth::new()); - let cert_file = &mut BufReader::new(File::open("tests/cert.pem").unwrap()); - let key_file = &mut BufReader::new(File::open("tests/key.pem").unwrap()); - let cert_chain = certs(cert_file).unwrap(); - let mut keys = pkcs8_private_keys(key_file).unwrap(); - config.set_single_cert(cert_chain, keys.remove(0)).unwrap(); + // load ssl keys + let mut config = ServerConfig::new(NoClientAuth::new()); + let cert_file = &mut BufReader::new(File::open("tests/cert.pem").unwrap()); + let key_file = &mut BufReader::new(File::open("tests/key.pem").unwrap()); + let cert_chain = certs(cert_file).unwrap(); + let mut keys = pkcs8_private_keys(key_file).unwrap(); + config.set_single_cert(cert_chain, keys.remove(0)).unwrap(); - let srv = HttpServer::new(|| { - App::new().service(web::resource("/").route(web::to(|bytes: Bytes| { - async move { - Ok::<_, Error>( - HttpResponse::Ok() - .encoding(http::ContentEncoding::Identity) - .body(bytes), - ) - } - }))) - }) - .bind_rustls(addr, config) - .unwrap() - .start(); + let srv = HttpServer::new(|| { + App::new().service(web::resource("/").route(web::to(|bytes: Bytes| { + async move { + Ok::<_, Error>( + HttpResponse::Ok() + .encoding(http::ContentEncoding::Identity) + .body(bytes), + ) + } + }))) + }) + .bind_rustls(addr, config) + .unwrap() + .start(); - let _ = tx.send((srv, actix_rt::System::current())); - let _ = sys.run(); - }); - let (srv, _sys) = rx.recv().unwrap(); - let client = { - let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); - builder.set_verify(SslVerifyMode::NONE); - let _ = builder.set_alpn_protos(b"\x02h2\x08http/1.1").unwrap(); + let _ = tx.send((srv, actix_rt::System::current())); + let _ = sys.run(); + }); + let (srv, _sys) = rx.recv().unwrap(); + let client = { + let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); + builder.set_verify(SslVerifyMode::NONE); + let _ = builder.set_alpn_protos(b"\x02h2\x08http/1.1").unwrap(); - awc::Client::build() - .connector( - awc::Connector::new() - .timeout(std::time::Duration::from_millis(500)) - .ssl(builder.build()) - .finish(), - ) - .finish() - }; + awc::Client::build() + .connector( + awc::Connector::new() + .timeout(std::time::Duration::from_millis(500)) + .ssl(builder.build()) + .finish(), + ) + .finish() + }; - // encode data - let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); - e.write_all(data.as_ref()).unwrap(); - let enc = e.finish().unwrap(); + // encode data + let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); + e.write_all(data.as_ref()).unwrap(); + let enc = e.finish().unwrap(); - // client request - let req = client - .post(format!("https://localhost:{}/", addr.port())) - .header(http::header::CONTENT_ENCODING, "deflate") - .send_body(enc); + // client request + let req = client + .post(format!("https://localhost:{}/", addr.port())) + .header(http::header::CONTENT_ENCODING, "deflate") + .send_body(enc); - let mut response = req.await.unwrap(); - assert!(response.status().is_success()); + let mut response = req.await.unwrap(); + assert!(response.status().is_success()); - // read response - let bytes = response.body().await.unwrap(); - assert_eq!(bytes.len(), data.len()); - assert_eq!(bytes, Bytes::from(data)); + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes.len(), data.len()); + assert_eq!(bytes, Bytes::from(data)); - // stop - let _ = srv.stop(false); - }) + // stop + let _ = srv.stop(false); } // #[cfg(all(feature = "tls", feature = "ssl"))] From f73f97353b812bbe3d872932c8ca51d5de2a1f84 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Tue, 26 Nov 2019 16:07:39 +0600 Subject: [PATCH 073/176] refactor ResponseError trait --- MIGRATION.md | 50 ++++++++++---------- README.md | 7 ++- actix-cors/src/lib.rs | 4 ++ actix-files/src/error.rs | 4 +- actix-http/src/client/error.rs | 12 ++--- actix-http/src/error.rs | 86 ++++++++++++++++++---------------- actix-http/src/response.rs | 2 +- actix-multipart/src/error.rs | 7 +-- awc/src/error.rs | 8 +--- src/error.rs | 34 +++++--------- src/lib.rs | 2 +- 11 files changed, 105 insertions(+), 111 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 675dc61ed..dd3a1b043 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -4,6 +4,8 @@ replace `fn` with `async fn` to convert sync handler to async +* `TestServer::new()` renamed to `TestServer::start()` + ## 1.0.1 @@ -41,52 +43,52 @@ * Extractor configuration. In version 1.0 this is handled with the new `Data` mechanism for both setting and retrieving the configuration instead of - + ```rust - + #[derive(Default)] struct ExtractorConfig { config: String, } - + impl FromRequest for YourExtractor { type Config = ExtractorConfig; type Result = Result; - + fn from_request(req: &HttpRequest, cfg: &Self::Config) -> Self::Result { println!("use the config: {:?}", cfg.config); ... } } - + App::new().resource("/route_with_config", |r| { r.post().with_config(handler_fn, |cfg| { cfg.0.config = "test".to_string(); }) }) - + ``` - + use the HttpRequest to get the configuration like any other `Data` with `req.app_data::()` and set it with the `data()` method on the `resource` - + ```rust #[derive(Default)] struct ExtractorConfig { config: String, } - + impl FromRequest for YourExtractor { type Error = Error; type Future = Result; type Config = ExtractorConfig; - + fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { let cfg = req.app_data::(); println!("config data?: {:?}", cfg.unwrap().role); ... } } - + App::new().service( resource("/route_with_config") .data(ExtractorConfig { @@ -95,7 +97,7 @@ .route(post().to(handler_fn)), ) ``` - + * Resource registration. 1.0 version uses generalized resource registration via `.service()` method. @@ -386,9 +388,9 @@ * `HttpRequest` does not implement `Stream` anymore. If you need to read request payload use `HttpMessage::payload()` method. - + instead of - + ```rust fn index(req: HttpRequest) -> impl Responder { req @@ -414,8 +416,8 @@ trait uses `&HttpRequest` instead of `&mut HttpRequest`. * Removed `Route::with2()` and `Route::with3()` use tuple of extractors instead. - - instead of + + instead of ```rust fn index(query: Query<..>, info: Json impl Responder {} @@ -431,7 +433,7 @@ * `Handler::handle()` accepts reference to `HttpRequest<_>` instead of value -* Removed deprecated `HttpServer::threads()`, use +* Removed deprecated `HttpServer::threads()`, use [HttpServer::workers()](https://actix.rs/actix-web/actix_web/server/struct.HttpServer.html#method.workers) instead. * Renamed `client::ClientConnectorError::Connector` to @@ -440,7 +442,7 @@ * `Route::with()` does not return `ExtractorConfig`, to configure extractor use `Route::with_config()` - instead of + instead of ```rust fn main() { @@ -451,11 +453,11 @@ }); } ``` - - use - + + use + ```rust - + fn main() { let app = App::new().resource("/index.html", |r| { r.method(http::Method::GET) @@ -485,12 +487,12 @@ * `HttpRequest::extensions()` returns read only reference to the request's Extension `HttpRequest::extensions_mut()` returns mutable reference. -* Instead of +* Instead of `use actix_web::middleware::{ CookieSessionBackend, CookieSessionError, RequestSession, Session, SessionBackend, SessionImpl, SessionStorage};` - + use `actix_web::middleware::session` `use actix_web::middleware::session{CookieSessionBackend, CookieSessionError, diff --git a/README.md b/README.md index 00bb3ec4e..4c0553e38 100644 --- a/README.md +++ b/README.md @@ -26,16 +26,15 @@ Actix web is a simple, pragmatic and extremely fast web framework for Rust. ## Example ```rust -use actix_web::{web, App, HttpServer, Responder}; +use actix_web::{get, App, HttpServer, Responder}; +#[get("/{id}/{name}/index.html")] async fn index(info: web::Path<(u32, String)>) -> impl Responder { format!("Hello {}! id:{}", info.1, info.0) } fn main() -> std::io::Result<()> { - HttpServer::new( - || App::new().service( - web::resource("/{id}/{name}/index.html").to(index))) + HttpServer::new(|| App::new().service(index)) .bind("127.0.0.1:8080")? .run() } diff --git a/actix-cors/src/lib.rs b/actix-cors/src/lib.rs index 551e3bb4d..d3607aa8e 100644 --- a/actix-cors/src/lib.rs +++ b/actix-cors/src/lib.rs @@ -93,6 +93,10 @@ pub enum CorsError { } impl ResponseError for CorsError { + fn status_code(&self) -> StatusCode { + StatusCode::BAD_REQUEST + } + fn error_response(&self) -> HttpResponse { HttpResponse::with_body(StatusCode::BAD_REQUEST, format!("{}", self).into()) } diff --git a/actix-files/src/error.rs b/actix-files/src/error.rs index ca99fa813..49a46e58d 100644 --- a/actix-files/src/error.rs +++ b/actix-files/src/error.rs @@ -35,7 +35,7 @@ pub enum UriSegmentError { /// Return `BadRequest` for `UriSegmentError` impl ResponseError for UriSegmentError { - fn error_response(&self) -> HttpResponse { - HttpResponse::new(StatusCode::BAD_REQUEST) + fn status_code(&self) -> StatusCode { + StatusCode::BAD_REQUEST } } diff --git a/actix-http/src/client/error.rs b/actix-http/src/client/error.rs index 75f7935f2..ee568e8be 100644 --- a/actix-http/src/client/error.rs +++ b/actix-http/src/client/error.rs @@ -7,8 +7,7 @@ use trust_dns_resolver::error::ResolveError; use open_ssl::ssl::{Error as SslError, HandshakeError}; use crate::error::{Error, ParseError, ResponseError}; -use crate::http::Error as HttpError; -use crate::response::Response; +use crate::http::{Error as HttpError, StatusCode}; /// A set of errors that can occur while connecting to an HTTP host #[derive(Debug, Display, From)] @@ -117,15 +116,14 @@ pub enum SendRequestError { /// Convert `SendRequestError` to a server `Response` impl ResponseError for SendRequestError { - fn error_response(&self) -> Response { + fn status_code(&self) -> StatusCode { match *self { SendRequestError::Connect(ConnectError::Timeout) => { - Response::GatewayTimeout() + StatusCode::GATEWAY_TIMEOUT } - SendRequestError::Connect(_) => Response::BadGateway(), - _ => Response::InternalServerError(), + SendRequestError::Connect(_) => StatusCode::BAD_REQUEST, + _ => StatusCode::INTERNAL_SERVER_ERROR, } - .into() } } diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index f1767cf19..587849bde 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -59,16 +59,18 @@ impl Error { /// Error that can be converted to `Response` pub trait ResponseError: fmt::Debug + fmt::Display { + /// Response's status code + /// + /// Internal server error is generated by default. + fn status_code(&self) -> StatusCode { + StatusCode::INTERNAL_SERVER_ERROR + } + /// Create response for error /// /// Internal server error is generated by default. fn error_response(&self) -> Response { - Response::new(StatusCode::INTERNAL_SERVER_ERROR) - } - - /// Constructs an error response - fn render_response(&self) -> Response { - let mut resp = self.error_response(); + let mut resp = Response::new(self.status_code()); let mut buf = BytesMut::new(); let _ = write!(Writer(&mut buf), "{}", self); resp.headers_mut().insert( @@ -156,10 +158,10 @@ impl From for Error { /// Return `GATEWAY_TIMEOUT` for `TimeoutError` impl ResponseError for TimeoutError { - fn error_response(&self) -> Response { + fn status_code(&self) -> StatusCode { match self { - TimeoutError::Service(e) => e.error_response(), - TimeoutError::Timeout => Response::new(StatusCode::GATEWAY_TIMEOUT), + TimeoutError::Service(e) => e.status_code(), + TimeoutError::Timeout => StatusCode::GATEWAY_TIMEOUT, } } } @@ -187,8 +189,8 @@ impl ResponseError for open_ssl::ssl::HandshakeError {} /// Return `BAD_REQUEST` for `de::value::Error` impl ResponseError for DeError { - fn error_response(&self) -> Response { - Response::new(StatusCode::BAD_REQUEST) + fn status_code(&self) -> StatusCode { + StatusCode::BAD_REQUEST } } @@ -197,8 +199,8 @@ impl ResponseError for Canceled {} /// Return `BAD_REQUEST` for `Utf8Error` impl ResponseError for Utf8Error { - fn error_response(&self) -> Response { - Response::new(StatusCode::BAD_REQUEST) + fn status_code(&self) -> StatusCode { + StatusCode::BAD_REQUEST } } @@ -208,26 +210,26 @@ impl ResponseError for HttpError {} /// Return `InternalServerError` for `io::Error` impl ResponseError for io::Error { - fn error_response(&self) -> Response { + fn status_code(&self) -> StatusCode { match self.kind() { - io::ErrorKind::NotFound => Response::new(StatusCode::NOT_FOUND), - io::ErrorKind::PermissionDenied => Response::new(StatusCode::FORBIDDEN), - _ => Response::new(StatusCode::INTERNAL_SERVER_ERROR), + io::ErrorKind::NotFound => StatusCode::NOT_FOUND, + io::ErrorKind::PermissionDenied => StatusCode::FORBIDDEN, + _ => StatusCode::INTERNAL_SERVER_ERROR, } } } /// `BadRequest` for `InvalidHeaderValue` impl ResponseError for header::InvalidHeaderValue { - fn error_response(&self) -> Response { - Response::new(StatusCode::BAD_REQUEST) + fn status_code(&self) -> StatusCode { + StatusCode::BAD_REQUEST } } /// `BadRequest` for `InvalidHeaderValue` impl ResponseError for header::InvalidHeaderValueBytes { - fn error_response(&self) -> Response { - Response::new(StatusCode::BAD_REQUEST) + fn status_code(&self) -> StatusCode { + StatusCode::BAD_REQUEST } } @@ -270,8 +272,8 @@ pub enum ParseError { /// Return `BadRequest` for `ParseError` impl ResponseError for ParseError { - fn error_response(&self) -> Response { - Response::new(StatusCode::BAD_REQUEST) + fn status_code(&self) -> StatusCode { + StatusCode::BAD_REQUEST } } @@ -371,18 +373,18 @@ impl From for PayloadError { /// - `Overflow` returns `PayloadTooLarge` /// - Other errors returns `BadRequest` impl ResponseError for PayloadError { - fn error_response(&self) -> Response { + fn status_code(&self) -> StatusCode { match *self { - PayloadError::Overflow => Response::new(StatusCode::PAYLOAD_TOO_LARGE), - _ => Response::new(StatusCode::BAD_REQUEST), + PayloadError::Overflow => StatusCode::PAYLOAD_TOO_LARGE, + _ => StatusCode::BAD_REQUEST, } } } /// Return `BadRequest` for `cookie::ParseError` impl ResponseError for crate::cookie::ParseError { - fn error_response(&self) -> Response { - Response::new(StatusCode::BAD_REQUEST) + fn status_code(&self) -> StatusCode { + StatusCode::BAD_REQUEST } } @@ -446,8 +448,8 @@ pub enum ContentTypeError { /// Return `BadRequest` for `ContentTypeError` impl ResponseError for ContentTypeError { - fn error_response(&self) -> Response { - Response::new(StatusCode::BAD_REQUEST) + fn status_code(&self) -> StatusCode { + StatusCode::BAD_REQUEST } } @@ -517,6 +519,19 @@ impl ResponseError for InternalError where T: fmt::Debug + fmt::Display + 'static, { + fn status_code(&self) -> StatusCode { + match self.status { + InternalErrorType::Status(st) => st, + InternalErrorType::Response(ref resp) => { + if let Some(resp) = resp.borrow().as_ref() { + resp.head().status + } else { + StatusCode::INTERNAL_SERVER_ERROR + } + } + } + } + fn error_response(&self) -> Response { match self.status { InternalErrorType::Status(st) => { @@ -538,11 +553,6 @@ where } } } - - /// Constructs an error response - fn render_response(&self) -> Response { - self.error_response() - } } /// Convert Response to a Error @@ -947,11 +957,7 @@ mod failure_integration { use super::*; /// Compatibility for `failure::Error` - impl ResponseError for failure::Error { - fn error_response(&self) -> Response { - Response::new(StatusCode::INTERNAL_SERVER_ERROR) - } - } + impl ResponseError for failure::Error {} } #[cfg(test)] diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index 5eb0228dc..e9147aa4b 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -53,7 +53,7 @@ impl Response { /// Constructs an error response #[inline] pub fn from_error(error: Error) -> Response { - let mut resp = error.as_response_error().render_response(); + let mut resp = error.as_response_error().error_response(); if resp.head.status == StatusCode::INTERNAL_SERVER_ERROR { error!("Internal Server Error: {:?}", error); } diff --git a/actix-multipart/src/error.rs b/actix-multipart/src/error.rs index 32c740a1a..6677f69c7 100644 --- a/actix-multipart/src/error.rs +++ b/actix-multipart/src/error.rs @@ -1,7 +1,7 @@ //! Error and Result module use actix_web::error::{ParseError, PayloadError}; use actix_web::http::StatusCode; -use actix_web::{HttpResponse, ResponseError}; +use actix_web::ResponseError; use derive_more::{Display, From}; /// A set of errors that can occur during parsing multipart streams @@ -35,14 +35,15 @@ pub enum MultipartError { /// Return `BadRequest` for `MultipartError` impl ResponseError for MultipartError { - fn error_response(&self) -> HttpResponse { - HttpResponse::new(StatusCode::BAD_REQUEST) + fn status_code(&self) -> StatusCode { + StatusCode::BAD_REQUEST } } #[cfg(test)] mod tests { use super::*; + use actix_web::HttpResponse; #[test] fn test_multipart_error() { diff --git a/awc/src/error.rs b/awc/src/error.rs index eb8d03e2b..8816c4075 100644 --- a/awc/src/error.rs +++ b/awc/src/error.rs @@ -6,7 +6,7 @@ pub use actix_http::error::PayloadError; pub use actix_http::ws::HandshakeError as WsHandshakeError; pub use actix_http::ws::ProtocolError as WsProtocolError; -use actix_http::{Response, ResponseError}; +use actix_http::ResponseError; use serde_json::error::Error as JsonError; use actix_http::http::{header::HeaderValue, Error as HttpError, StatusCode}; @@ -68,8 +68,4 @@ pub enum JsonPayloadError { } /// Return `InternalServerError` for `JsonPayloadError` -impl ResponseError for JsonPayloadError { - fn error_response(&self) -> Response { - Response::new(StatusCode::INTERNAL_SERVER_ERROR) - } -} +impl ResponseError for JsonPayloadError {} diff --git a/src/error.rs b/src/error.rs index a60276a7a..2eec7c51b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -54,15 +54,11 @@ pub enum UrlencodedError { /// Return `BadRequest` for `UrlencodedError` impl ResponseError for UrlencodedError { - fn error_response(&self) -> HttpResponse { + fn status_code(&self) -> StatusCode { match *self { - UrlencodedError::Overflow { .. } => { - HttpResponse::new(StatusCode::PAYLOAD_TOO_LARGE) - } - UrlencodedError::UnknownLength => { - HttpResponse::new(StatusCode::LENGTH_REQUIRED) - } - _ => HttpResponse::new(StatusCode::BAD_REQUEST), + UrlencodedError::Overflow { .. } => StatusCode::PAYLOAD_TOO_LARGE, + UrlencodedError::UnknownLength => StatusCode::LENGTH_REQUIRED, + _ => StatusCode::BAD_REQUEST, } } } @@ -106,10 +102,8 @@ pub enum PathError { /// Return `BadRequest` for `PathError` impl ResponseError for PathError { - fn error_response(&self) -> HttpResponse { - match *self { - PathError::Deserialize(_) => HttpResponse::new(StatusCode::BAD_REQUEST), - } + fn status_code(&self) -> StatusCode { + StatusCode::BAD_REQUEST } } @@ -123,12 +117,8 @@ pub enum QueryPayloadError { /// Return `BadRequest` for `QueryPayloadError` impl ResponseError for QueryPayloadError { - fn error_response(&self) -> HttpResponse { - match *self { - QueryPayloadError::Deserialize(_) => { - HttpResponse::new(StatusCode::BAD_REQUEST) - } - } + fn status_code(&self) -> StatusCode { + StatusCode::BAD_REQUEST } } @@ -152,12 +142,10 @@ pub enum ReadlinesError { /// Return `BadRequest` for `ReadlinesError` impl ResponseError for ReadlinesError { - fn error_response(&self) -> HttpResponse { + fn status_code(&self) -> StatusCode { match *self { - ReadlinesError::LimitOverflow => { - HttpResponse::new(StatusCode::PAYLOAD_TOO_LARGE) - } - _ => HttpResponse::new(StatusCode::BAD_REQUEST), + ReadlinesError::LimitOverflow => StatusCode::PAYLOAD_TOO_LARGE, + _ => StatusCode::BAD_REQUEST, } } } diff --git a/src/lib.rs b/src/lib.rs index 4d1facd8d..b7fd8d155 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,7 +63,7 @@ //! * SSL support with OpenSSL or `native-tls` //! * Middlewares (`Logger`, `Session`, `CORS`, `DefaultHeaders`) //! * Supports [Actix actor framework](https://github.com/actix/actix) -//! * Supported Rust version: 1.36 or later +//! * Supported Rust version: 1.39 or later //! //! ## Package feature //! From f2b3dc5625e09e0cefc33983e4d87339f9780999 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Tue, 26 Nov 2019 17:16:33 +0600 Subject: [PATCH 074/176] update examples --- README.md | 6 ++++-- examples/basic.rs | 6 ++++-- examples/client.rs | 32 +++++++++++++++----------------- examples/uds.rs | 6 ++++-- 4 files changed, 27 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 4c0553e38..b7a1bf28f 100644 --- a/README.md +++ b/README.md @@ -33,10 +33,12 @@ async fn index(info: web::Path<(u32, String)>) -> impl Responder { format!("Hello {}! id:{}", info.1, info.0) } -fn main() -> std::io::Result<()> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { HttpServer::new(|| App::new().service(index)) .bind("127.0.0.1:8080")? - .run() + .start() + .await } ``` diff --git a/examples/basic.rs b/examples/basic.rs index 6d9a4dcd8..b5b69fce2 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -16,7 +16,8 @@ async fn no_params() -> &'static str { "Hello world!\r\n" } -fn main() -> std::io::Result<()> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info"); env_logger::init(); @@ -41,5 +42,6 @@ fn main() -> std::io::Result<()> { }) .bind("127.0.0.1:8080")? .workers(1) - .run() + .start() + .await } diff --git a/examples/client.rs b/examples/client.rs index 90a362fe3..874e08e1b 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -1,27 +1,25 @@ use actix_http::Error; -use actix_rt::System; -fn main() -> Result<(), Error> { +#[actix_rt::main] +async fn main() -> Result<(), Error> { std::env::set_var("RUST_LOG", "actix_http=trace"); env_logger::init(); - System::new("test").block_on(async { - let client = awc::Client::new(); + let client = awc::Client::new(); - // Create request builder, configure request and send - let mut response = client - .get("https://www.rust-lang.org/") - .header("User-Agent", "Actix-web") - .send() - .await?; + // Create request builder, configure request and send + let mut response = client + .get("https://www.rust-lang.org/") + .header("User-Agent", "Actix-web") + .send() + .await?; - // server http response - println!("Response: {:?}", response); + // server http response + println!("Response: {:?}", response); - // read response body - let body = response.body().await?; - println!("Downloaded: {:?} bytes", body.len()); + // read response body + let body = response.body().await?; + println!("Downloaded: {:?} bytes", body.len()); - Ok(()) - }) + Ok(()) } diff --git a/examples/uds.rs b/examples/uds.rs index fc6a58de1..8db4cf230 100644 --- a/examples/uds.rs +++ b/examples/uds.rs @@ -19,7 +19,8 @@ async fn no_params() -> &'static str { } #[cfg(unix)] -fn main() -> std::io::Result<()> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info"); env_logger::init(); @@ -44,7 +45,8 @@ fn main() -> std::io::Result<()> { }) .bind_uds("/Users/fafhrd91/uds-test")? .workers(1) - .run() + .start() + .await } #[cfg(not(unix))] From f43a7063642bebe8b76f472beb5a9d6dfdb42ed6 Mon Sep 17 00:00:00 2001 From: Folyd Date: Tue, 26 Nov 2019 19:40:29 +0800 Subject: [PATCH 075/176] Set name for each generated resource --- actix-web-codegen/src/route.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/actix-web-codegen/src/route.rs b/actix-web-codegen/src/route.rs index f8e2496c4..16d3e8157 100644 --- a/actix-web-codegen/src/route.rs +++ b/actix-web-codegen/src/route.rs @@ -184,6 +184,7 @@ impl Route { pub fn generate(&self) -> TokenStream { let name = &self.name; + let resource_name = name.to_string(); let guard = &self.guard; let ast = &self.ast; let path = &self.args.path; @@ -196,8 +197,8 @@ impl Route { impl actix_web::dev::HttpServiceFactory for #name { fn register(self, config: &mut actix_web::dev::AppService) { #ast - let resource = actix_web::Resource::new(#path) + .name(#resource_name) .guard(actix_web::guard::#guard()) #(.guard(actix_web::guard::fn_guard(#extra_guards)))* .#resource_type(#name); From 56b9f11c981f532556916b54205de2f5fc173fa7 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 27 Nov 2019 21:07:49 +0600 Subject: [PATCH 076/176] disable rustls --- Cargo.toml | 4 ++-- actix-http/Cargo.toml | 9 +++++---- awc/Cargo.toml | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a1875eb76..02e4ac0ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,7 +66,7 @@ fail = ["actix-http/fail"] openssl = ["open-ssl", "actix-server/openssl", "awc/openssl"] # rustls -rustls = ["rust-tls", "actix-server/rustls", "awc/rustls"] +# rustls = ["rust-tls", "actix-server/rustls", "awc/rustls"] [dependencies] actix-codec = "0.2.0-alpha.1" @@ -101,7 +101,7 @@ url = "2.1" # ssl support open-ssl = { version="0.10", package="openssl", optional = true } -rust-tls = { version = "0.16", package="rustls", optional = true } +# rust-tls = { version = "0.16", package="rustls", optional = true } [dev-dependencies] # actix = "0.8.3" diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index cfed0bf14..9a14abefe 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -29,7 +29,7 @@ default = [] openssl = ["open-ssl", "actix-connect/openssl", "tokio-openssl"] # rustls support -rustls = ["rust-tls", "webpki-roots", "actix-connect/rustls"] +# rustls = ["rust-tls", "webpki-roots", "actix-connect/rustls"] # brotli encoding, requires c compiler brotli = ["brotli2"] @@ -99,11 +99,12 @@ failure = { version = "0.1.5", optional = true } open-ssl = { version="0.10", package="openssl", optional = true } tokio-openssl = { version = "0.4.0-alpha.6", optional = true } -rust-tls = { version = "0.16.0", package="rustls", optional = true } -webpki-roots = { version = "0.18", optional = true } +# rust-tls = { version = "0.16.0", package="rustls", optional = true } +# webpki-roots = { version = "0.18", optional = true } [dev-dependencies] -actix-server = { version = "0.8.0-alpha.1", features=["openssl", "rustls"] } +#actix-server = { version = "0.8.0-alpha.1", features=["openssl", "rustls"] } +actix-server = { version = "0.8.0-alpha.1", features=["openssl"] } actix-connect = { version = "1.0.0-alpha.1", features=["openssl"] } actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } env_logger = "0.6" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 1b35c279b..e9268aac0 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -30,7 +30,7 @@ default = ["brotli", "flate2-zlib"] openssl = ["open-ssl", "actix-http/openssl"] # rustls -rustls = ["rust-tls", "actix-http/rustls"] +# rustls = ["rust-tls", "actix-http/rustls"] # brotli encoding, requires c compiler brotli = ["actix-http/brotli"] @@ -59,7 +59,7 @@ serde = "1.0" serde_json = "1.0" serde_urlencoded = "0.6.1" open-ssl = { version="0.10", package="openssl", optional = true } -rust-tls = { version = "0.16.0", package="rustls", optional = true, features = ["dangerous_configuration"] } +# rust-tls = { version = "0.16.0", package="rustls", optional = true, features = ["dangerous_configuration"] } [dev-dependencies] actix-connect = { version = "1.0.0-alpha.1", features=["openssl"] } From 33574403b50a61f7cec47103e3a666b002e4845b Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 28 Nov 2019 09:25:21 +0900 Subject: [PATCH 077/176] Remove `rustls` from `package.metadata.docs.rs` (#1182) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 02e4ac0ac..689f7b147 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] edition = "2018" [package.metadata.docs.rs] -features = ["openssl", "rustls", "brotli", "flate2-zlib", "secure-cookies", "client"] +features = ["openssl", "brotli", "flate2-zlib", "secure-cookies", "client"] [badges] travis-ci = { repository = "actix/actix-web", branch = "master" } From f4c01384ecc7fb863e6e41e5b345920431accc68 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Mon, 2 Dec 2019 17:33:11 +0600 Subject: [PATCH 078/176] update to latest actix-net --- Cargo.toml | 10 +- actix-framed/Cargo.toml | 3 +- actix-framed/src/app.rs | 5 +- actix-framed/tests/test_server.rs | 1 + actix-http/Cargo.toml | 9 +- actix-http/examples/echo.rs | 1 + actix-http/examples/echo2.rs | 2 +- actix-http/examples/hello-world.rs | 1 + actix-http/src/builder.rs | 51 ++- actix-http/src/config.rs | 24 +- actix-http/src/h1/dispatcher.rs | 17 +- actix-http/src/h1/expect.rs | 5 +- actix-http/src/h1/service.rs | 224 +++++++++----- actix-http/src/h1/upgrade.rs | 5 +- actix-http/src/h2/dispatcher.rs | 10 +- actix-http/src/h2/service.rs | 159 +++++++--- actix-http/src/lib.rs | 7 + actix-http/src/service.rs | 428 ++++++++++---------------- actix-http/src/test.rs | 15 - actix-http/tests/test_client.rs | 6 +- actix-http/tests/test_openssl.rs | 320 +++++++------------ actix-http/tests/test_server.rs | 157 ++++++---- actix-http/tests/test_ws.rs | 1 + actix-web-codegen/tests/test_macro.rs | 5 +- awc/Cargo.toml | 3 +- awc/tests/test_client.rs | 73 +++-- awc/tests/test_ssl_client.rs | 19 +- awc/tests/test_ws.rs | 1 + src/app_service.rs | 12 +- src/server.rs | 67 ++-- src/test.rs | 6 +- test-server/Cargo.toml | 3 +- tests/test_server.rs | 189 ++++++------ 33 files changed, 941 insertions(+), 898 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 689f7b147..441291d3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,7 @@ secure-cookies = ["actix-http/secure-cookies"] fail = ["actix-http/fail"] # openssl -openssl = ["open-ssl", "actix-server/openssl", "awc/openssl"] +openssl = ["open-ssl", "actix-tls/openssl", "awc/openssl"] # rustls # rustls = ["rust-tls", "actix-server/rustls", "awc/rustls"] @@ -76,10 +76,11 @@ actix-router = "0.1.5" actix-rt = "1.0.0-alpha.1" actix-web-codegen = "0.2.0-alpha.1" actix-http = "0.3.0-alpha.1" -actix-server = "0.8.0-alpha.1" -actix-server-config = "0.3.0-alpha.1" +actix-server = "0.8.0-alpha.2" actix-testing = "0.3.0-alpha.1" actix-threadpool = "0.2.0-alpha.1" +#actix-tls = "0.1.0-alpha.1" +actix-tls = { git = "https://github.com/actix/actix-net.git", optional = true } awc = { version = "0.3.0-alpha.1", optional = true } bytes = "0.4" @@ -123,7 +124,6 @@ actix-web = { path = "." } actix-http = { path = "actix-http" } actix-http-test = { path = "test-server" } actix-web-codegen = { path = "actix-web-codegen" } -# actix-web-actors = { path = "actix-web-actors" } actix-cors = { path = "actix-cors" } actix-identity = { path = "actix-identity" } actix-session = { path = "actix-session" } @@ -136,7 +136,7 @@ actix-connect = { git = "https://github.com/actix/actix-net.git" } actix-rt = { git = "https://github.com/actix/actix-net.git" } actix-macros = { git = "https://github.com/actix/actix-net.git" } actix-server = { git = "https://github.com/actix/actix-net.git" } -actix-server-config = { git = "https://github.com/actix/actix-net.git" } actix-service = { git = "https://github.com/actix/actix-net.git" } actix-testing = { git = "https://github.com/actix/actix-net.git" } +actix-tls = { git = "https://github.com/actix/actix-net.git" } actix-utils = { git = "https://github.com/actix/actix-net.git" } diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml index 4783daefd..24ca6400d 100644 --- a/actix-framed/Cargo.toml +++ b/actix-framed/Cargo.toml @@ -25,7 +25,6 @@ actix-service = "1.0.0-alpha.1" actix-router = "0.1.2" actix-rt = "1.0.0-alpha.1" actix-http = "0.3.0-alpha.1" -actix-server-config = "0.3.0-alpha.1" bytes = "0.4" futures = "0.3.1" @@ -33,7 +32,7 @@ pin-project = "0.4.6" log = "0.4" [dev-dependencies] -actix-server = { version = "0.8.0-alpha.1", features=["openssl"] } +actix-server = { version = "0.8.0-alpha.1" } actix-connect = { version = "0.3.0-alpha.1", features=["openssl"] } actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } actix-utils = "0.5.0-alpha.1" diff --git a/actix-framed/src/app.rs b/actix-framed/src/app.rs index f3e746e9f..2f8c800f8 100644 --- a/actix-framed/src/app.rs +++ b/actix-framed/src/app.rs @@ -7,7 +7,6 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_http::h1::{Codec, SendResponse}; use actix_http::{Error, Request, Response}; use actix_router::{Path, Router, Url}; -use actix_server_config::ServerConfig; use actix_service::{IntoServiceFactory, Service, ServiceFactory}; use futures::future::{ok, FutureExt, LocalBoxFuture}; @@ -97,7 +96,7 @@ where T: AsyncRead + AsyncWrite + Unpin + 'static, S: 'static, { - type Config = ServerConfig; + type Config = (); type Request = (Request, Framed); type Response = (); type Error = Error; @@ -105,7 +104,7 @@ where type Service = FramedAppService; type Future = CreateService; - fn new_service(&self, _: &ServerConfig) -> Self::Future { + fn new_service(&self, _: &()) -> Self::Future { CreateService { fut: self .services diff --git a/actix-framed/tests/test_server.rs b/actix-framed/tests/test_server.rs index 4d1028d31..c272f0f93 100644 --- a/actix-framed/tests/test_server.rs +++ b/actix-framed/tests/test_server.rs @@ -46,6 +46,7 @@ async fn test_simple() { FramedApp::new().service(FramedRoute::get("/index.html").to(ws_service)), ) .finish(|_| future::ok::<_, Error>(Response::NotFound())) + .tcp() }); assert!(srv.ws_at("/test").await.is_err()); diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 9a14abefe..ff47abd1e 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -26,7 +26,7 @@ path = "src/lib.rs" default = [] # openssl -openssl = ["open-ssl", "actix-connect/openssl", "tokio-openssl"] +openssl = ["open-ssl", "actix-tls/openssl", "actix-connect/openssl", "tokio-openssl"] # rustls support # rustls = ["rust-tls", "webpki-roots", "actix-connect/rustls"] @@ -51,9 +51,9 @@ actix-service = "1.0.0-alpha.1" actix-codec = "0.2.0-alpha.1" actix-connect = "1.0.0-alpha.1" actix-utils = "0.5.0-alpha.1" -actix-server-config = "0.3.0-alpha.1" actix-rt = "1.0.0-alpha.1" actix-threadpool = "0.2.0-alpha.1" +actix-tls = { git = "https://github.com/actix/actix-net.git", optional = true } base64 = "0.10" bitflags = "1.0" @@ -103,10 +103,11 @@ tokio-openssl = { version = "0.4.0-alpha.6", optional = true } # webpki-roots = { version = "0.18", optional = true } [dev-dependencies] -#actix-server = { version = "0.8.0-alpha.1", features=["openssl", "rustls"] } -actix-server = { version = "0.8.0-alpha.1", features=["openssl"] } +actix-server = { version = "0.8.0-alpha.1" } actix-connect = { version = "1.0.0-alpha.1", features=["openssl"] } actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } +#actix-tls = { version = "0.1.0-alpha.1", features=["openssl"] } +actix-tls = { git = "https://github.com/actix/actix-net.git", features=["openssl"] } env_logger = "0.6" serde_derive = "1.0" open-ssl = { version="0.10", package="openssl" } diff --git a/actix-http/examples/echo.rs b/actix-http/examples/echo.rs index ba81020ca..5b2894f89 100644 --- a/actix-http/examples/echo.rs +++ b/actix-http/examples/echo.rs @@ -34,6 +34,7 @@ fn main() -> io::Result<()> { ) } }) + .tcp() })? .run() } diff --git a/actix-http/examples/echo2.rs b/actix-http/examples/echo2.rs index 3776c7d58..07d181277 100644 --- a/actix-http/examples/echo2.rs +++ b/actix-http/examples/echo2.rs @@ -25,7 +25,7 @@ fn main() -> io::Result<()> { Server::build() .bind("echo", "127.0.0.1:8080", || { - HttpService::build().finish(handle_request) + HttpService::build().finish(handle_request).tcp() })? .run() } diff --git a/actix-http/examples/hello-world.rs b/actix-http/examples/hello-world.rs index 6e3820390..7d8576869 100644 --- a/actix-http/examples/hello-world.rs +++ b/actix-http/examples/hello-world.rs @@ -21,6 +21,7 @@ fn main() -> io::Result<()> { res.header("x-head", HeaderValue::from_static("dummy value!")); future::ok::<_, ()>(res.body("Hello world!")) }) + .tcp() })? .run() } diff --git a/actix-http/src/builder.rs b/actix-http/src/builder.rs index 7e1dae58f..271abd43f 100644 --- a/actix-http/src/builder.rs +++ b/actix-http/src/builder.rs @@ -1,9 +1,8 @@ -use std::fmt; use std::marker::PhantomData; use std::rc::Rc; +use std::{fmt, net}; use actix_codec::Framed; -use actix_server_config::ServerConfig as SrvConfig; use actix_service::{IntoServiceFactory, Service, ServiceFactory}; use crate::body::MessageBody; @@ -24,6 +23,8 @@ pub struct HttpServiceBuilder> { keep_alive: KeepAlive, client_timeout: u64, client_disconnect: u64, + secure: bool, + local_addr: Option, expect: X, upgrade: Option, on_connect: Option Box>>, @@ -32,7 +33,7 @@ pub struct HttpServiceBuilder> { impl HttpServiceBuilder> where - S: ServiceFactory, + S: ServiceFactory, S::Error: Into + 'static, S::InitError: fmt::Debug, ::Future: 'static, @@ -43,6 +44,8 @@ where keep_alive: KeepAlive::Timeout(5), client_timeout: 5000, client_disconnect: 0, + secure: false, + local_addr: None, expect: ExpectHandler, upgrade: None, on_connect: None, @@ -53,19 +56,15 @@ where impl HttpServiceBuilder where - S: ServiceFactory, + S: ServiceFactory, S::Error: Into + 'static, S::InitError: fmt::Debug, ::Future: 'static, - X: ServiceFactory, + X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, ::Future: 'static, - U: ServiceFactory< - Config = SrvConfig, - Request = (Request, Framed), - Response = (), - >, + U: ServiceFactory), Response = ()>, U::Error: fmt::Display, U::InitError: fmt::Debug, ::Future: 'static, @@ -78,6 +77,18 @@ where self } + /// Set connection secure state + pub fn secure(mut self) -> Self { + self.secure = true; + self + } + + /// Set the local address that this service is bound to. + pub fn local_addr(mut self, addr: net::SocketAddr) -> Self { + self.local_addr = Some(addr); + self + } + /// Set server client timeout in milliseconds for first request. /// /// Defines a timeout for reading client request header. If a client does not transmit @@ -113,7 +124,7 @@ where pub fn expect(self, expect: F) -> HttpServiceBuilder where F: IntoServiceFactory, - X1: ServiceFactory, + X1: ServiceFactory, X1::Error: Into, X1::InitError: fmt::Debug, ::Future: 'static, @@ -122,6 +133,8 @@ where keep_alive: self.keep_alive, client_timeout: self.client_timeout, client_disconnect: self.client_disconnect, + secure: self.secure, + local_addr: self.local_addr, expect: expect.into_factory(), upgrade: self.upgrade, on_connect: self.on_connect, @@ -137,7 +150,7 @@ where where F: IntoServiceFactory, U1: ServiceFactory< - Config = SrvConfig, + Config = (), Request = (Request, Framed), Response = (), >, @@ -149,6 +162,8 @@ where keep_alive: self.keep_alive, client_timeout: self.client_timeout, client_disconnect: self.client_disconnect, + secure: self.secure, + local_addr: self.local_addr, expect: self.expect, upgrade: Some(upgrade.into_factory()), on_connect: self.on_connect, @@ -170,7 +185,7 @@ where } /// Finish service configuration and create *http service* for HTTP/1 protocol. - pub fn h1(self, service: F) -> H1Service + pub fn h1(self, service: F) -> H1Service where B: MessageBody, F: IntoServiceFactory, @@ -182,6 +197,8 @@ where self.keep_alive, self.client_timeout, self.client_disconnect, + self.secure, + self.local_addr, ); H1Service::with_config(cfg, service.into_factory()) .expect(self.expect) @@ -190,7 +207,7 @@ where } /// Finish service configuration and create *http service* for HTTP/2 protocol. - pub fn h2(self, service: F) -> H2Service + pub fn h2(self, service: F) -> H2Service where B: MessageBody + 'static, F: IntoServiceFactory, @@ -203,12 +220,14 @@ where self.keep_alive, self.client_timeout, self.client_disconnect, + self.secure, + self.local_addr, ); H2Service::with_config(cfg, service.into_factory()).on_connect(self.on_connect) } /// Finish service configuration and create `HttpService` instance. - pub fn finish(self, service: F) -> HttpService + pub fn finish(self, service: F) -> HttpService where B: MessageBody + 'static, F: IntoServiceFactory, @@ -221,6 +240,8 @@ where self.keep_alive, self.client_timeout, self.client_disconnect, + self.secure, + self.local_addr, ); HttpService::with_config(cfg, service.into_factory()) .expect(self.expect) diff --git a/actix-http/src/config.rs b/actix-http/src/config.rs index bab3cdc6d..6ea75e565 100644 --- a/actix-http/src/config.rs +++ b/actix-http/src/config.rs @@ -1,8 +1,8 @@ use std::cell::UnsafeCell; -use std::fmt; use std::fmt::Write; use std::rc::Rc; use std::time::{Duration, Instant}; +use std::{fmt, net}; use actix_rt::time::{delay, delay_for, Delay}; use bytes::BytesMut; @@ -47,6 +47,8 @@ struct Inner { client_timeout: u64, client_disconnect: u64, ka_enabled: bool, + secure: bool, + local_addr: Option, timer: DateService, } @@ -58,7 +60,7 @@ impl Clone for ServiceConfig { impl Default for ServiceConfig { fn default() -> Self { - Self::new(KeepAlive::Timeout(5), 0, 0) + Self::new(KeepAlive::Timeout(5), 0, 0, false, None) } } @@ -68,6 +70,8 @@ impl ServiceConfig { keep_alive: KeepAlive, client_timeout: u64, client_disconnect: u64, + secure: bool, + local_addr: Option, ) -> ServiceConfig { let (keep_alive, ka_enabled) = match keep_alive { KeepAlive::Timeout(val) => (val as u64, true), @@ -85,10 +89,24 @@ impl ServiceConfig { ka_enabled, client_timeout, client_disconnect, + secure, + local_addr, timer: DateService::new(), })) } + #[inline] + /// Returns true if connection is secure(https) + pub fn secure(&self) -> bool { + self.0.secure + } + + #[inline] + /// Returns the local address that this server is bound to. + pub fn local_addr(&self) -> Option { + self.0.local_addr + } + #[inline] /// Keep alive duration if configured. pub fn keep_alive(&self) -> Option { @@ -271,7 +289,7 @@ mod tests { #[actix_rt::test] async fn test_date() { - let settings = ServiceConfig::new(KeepAlive::Os, 0, 0); + let settings = ServiceConfig::new(KeepAlive::Os, 0, 0, false, None); let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10); settings.set_date(&mut buf1); let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10); diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 154b3ed40..d5b3a8ed6 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -5,9 +5,8 @@ use std::task::{Context, Poll}; use std::time::Instant; use std::{fmt, io, net}; -use actix_codec::{AsyncRead, Decoder, Encoder, Framed, FramedParts}; +use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed, FramedParts}; use actix_rt::time::{delay, Delay}; -use actix_server_config::IoStream; use actix_service::Service; use bitflags::bitflags; use bytes::{BufMut, BytesMut}; @@ -168,7 +167,7 @@ impl PartialEq for PollResponse { impl Dispatcher where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into, S::Response: Into>, @@ -186,6 +185,7 @@ where expect: CloneableService, upgrade: Option>, on_connect: Option>, + peer_addr: Option, ) -> Self { Dispatcher::with_timeout( stream, @@ -197,6 +197,7 @@ where expect, upgrade, on_connect, + peer_addr, ) } @@ -211,6 +212,7 @@ where expect: CloneableService, upgrade: Option>, on_connect: Option>, + peer_addr: Option, ) -> Self { let keepalive = config.keep_alive_enabled(); let flags = if keepalive { @@ -234,7 +236,6 @@ where payload: None, state: State::None, error: None, - peer_addr: io.peer_addr(), messages: VecDeque::new(), io, codec, @@ -244,6 +245,7 @@ where upgrade, on_connect, flags, + peer_addr, ka_expire, ka_timer, }), @@ -253,7 +255,7 @@ where impl InnerDispatcher where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into, S::Response: Into>, @@ -682,7 +684,7 @@ where impl Unpin for Dispatcher where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into, S::Response: Into>, @@ -696,7 +698,7 @@ where impl Future for Dispatcher where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into, S::Response: Into>, @@ -907,6 +909,7 @@ mod tests { CloneableService::new(ExpectHandler), None, None, + None, ); match Pin::new(&mut h1).poll(cx) { Poll::Pending => panic!(), diff --git a/actix-http/src/h1/expect.rs b/actix-http/src/h1/expect.rs index d6b4a9f1e..576b7672e 100644 --- a/actix-http/src/h1/expect.rs +++ b/actix-http/src/h1/expect.rs @@ -1,6 +1,5 @@ use std::task::{Context, Poll}; -use actix_server_config::ServerConfig; use actix_service::{Service, ServiceFactory}; use futures::future::{ok, Ready}; @@ -10,7 +9,7 @@ use crate::request::Request; pub struct ExpectHandler; impl ServiceFactory for ExpectHandler { - type Config = ServerConfig; + type Config = (); type Request = Request; type Response = Request; type Error = Error; @@ -18,7 +17,7 @@ impl ServiceFactory for ExpectHandler { type InitError = Error; type Future = Ready>; - fn new_service(&self, _: &ServerConfig) -> Self::Future { + fn new_service(&self, _: &()) -> Self::Future { ok(ExpectHandler) } } diff --git a/actix-http/src/h1/service.rs b/actix-http/src/h1/service.rs index 197c92887..abc96e719 100644 --- a/actix-http/src/h1/service.rs +++ b/actix-http/src/h1/service.rs @@ -1,19 +1,19 @@ -use std::fmt; use std::future::Future; use std::marker::PhantomData; use std::pin::Pin; use std::rc::Rc; use std::task::{Context, Poll}; +use std::{fmt, net}; -use actix_codec::Framed; -use actix_server_config::{Io, IoStream, ServerConfig as SrvConfig}; -use actix_service::{IntoServiceFactory, Service, ServiceFactory}; +use actix_codec::{AsyncRead, AsyncWrite, Framed}; +use actix_rt::net::TcpStream; +use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory}; use futures::future::{ok, Ready}; use futures::ready; use crate::body::MessageBody; use crate::cloneable::CloneableService; -use crate::config::{KeepAlive, ServiceConfig}; +use crate::config::ServiceConfig; use crate::error::{DispatchError, Error, ParseError}; use crate::helpers::DataFactory; use crate::request::Request; @@ -24,39 +24,25 @@ use super::dispatcher::Dispatcher; use super::{ExpectHandler, Message, UpgradeHandler}; /// `ServiceFactory` implementation for HTTP1 transport -pub struct H1Service> { +pub struct H1Service> { srv: S, cfg: ServiceConfig, expect: X, upgrade: Option, on_connect: Option Box>>, - _t: PhantomData<(T, P, B)>, + _t: PhantomData<(T, B)>, } -impl H1Service +impl H1Service where - S: ServiceFactory, + S: ServiceFactory, S::Error: Into, S::InitError: fmt::Debug, S::Response: Into>, B: MessageBody, { - /// Create new `HttpService` instance with default config. - pub fn new>(service: F) -> Self { - let cfg = ServiceConfig::new(KeepAlive::Timeout(5), 5000, 0); - - H1Service { - cfg, - srv: service.into_factory(), - expect: ExpectHandler, - upgrade: None, - on_connect: None, - _t: PhantomData, - } - } - /// Create new `HttpService` instance with config. - pub fn with_config>( + pub(crate) fn with_config>( cfg: ServiceConfig, service: F, ) -> Self { @@ -71,15 +57,102 @@ where } } -impl H1Service +impl H1Service where - S: ServiceFactory, + S: ServiceFactory, + S::Error: Into, + S::InitError: fmt::Debug, + S::Response: Into>, + B: MessageBody, + X: ServiceFactory, + X::Error: Into, + X::InitError: fmt::Debug, + U: ServiceFactory< + Config = (), + Request = (Request, Framed), + Response = (), + >, + U::Error: fmt::Display, + U::InitError: fmt::Debug, +{ + /// Create simple tcp stream service + pub fn tcp( + self, + ) -> impl ServiceFactory< + Config = (), + Request = TcpStream, + Response = (), + Error = DispatchError, + InitError = (), + > { + pipeline_factory(|io: TcpStream| { + let peer_addr = io.peer_addr().ok(); + ok((io, peer_addr)) + }) + .and_then(self) + } +} + +#[cfg(feature = "openssl")] +mod openssl { + use super::*; + + use actix_tls::openssl::{Acceptor, SslStream}; + use actix_tls::{openssl::HandshakeError, SslError}; + use open_ssl::ssl::SslAcceptor; + + impl H1Service, S, B, X, U> + where + S: ServiceFactory, + S::Error: Into, + S::InitError: fmt::Debug, + S::Response: Into>, + B: MessageBody, + X: ServiceFactory, + X::Error: Into, + X::InitError: fmt::Debug, + U: ServiceFactory< + Config = (), + Request = (Request, Framed, Codec>), + Response = (), + >, + U::Error: fmt::Display, + U::InitError: fmt::Debug, + { + /// Create openssl based service + pub fn openssl( + self, + acceptor: SslAcceptor, + ) -> impl ServiceFactory< + Config = (), + Request = TcpStream, + Response = (), + Error = SslError, DispatchError>, + InitError = (), + > { + pipeline_factory( + Acceptor::new(acceptor) + .map_err(SslError::Ssl) + .map_init_err(|_| panic!()), + ) + .and_then(|io: SslStream| { + let peer_addr = io.get_ref().peer_addr().ok(); + ok((io, peer_addr)) + }) + .and_then(self.map_err(SslError::Service)) + } + } +} + +impl H1Service +where + S: ServiceFactory, S::Error: Into, S::Response: Into>, S::InitError: fmt::Debug, B: MessageBody, { - pub fn expect(self, expect: X1) -> H1Service + pub fn expect(self, expect: X1) -> H1Service where X1: ServiceFactory, X1::Error: Into, @@ -95,7 +168,7 @@ where } } - pub fn upgrade(self, upgrade: Option) -> H1Service + pub fn upgrade(self, upgrade: Option) -> H1Service where U1: ServiceFactory), Response = ()>, U1::Error: fmt::Display, @@ -121,34 +194,30 @@ where } } -impl ServiceFactory for H1Service +impl ServiceFactory for H1Service where - T: IoStream, - S: ServiceFactory, + T: AsyncRead + AsyncWrite + Unpin, + S: ServiceFactory, S::Error: Into, S::Response: Into>, S::InitError: fmt::Debug, B: MessageBody, - X: ServiceFactory, + X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, - U: ServiceFactory< - Config = SrvConfig, - Request = (Request, Framed), - Response = (), - >, + U: ServiceFactory), Response = ()>, U::Error: fmt::Display, U::InitError: fmt::Debug, { - type Config = SrvConfig; - type Request = Io; + type Config = (); + type Request = (T, Option); type Response = (); type Error = DispatchError; type InitError = (); - type Service = H1ServiceHandler; - type Future = H1ServiceResponse; + type Service = H1ServiceHandler; + type Future = H1ServiceResponse; - fn new_service(&self, cfg: &SrvConfig) -> Self::Future { + fn new_service(&self, cfg: &()) -> Self::Future { H1ServiceResponse { fut: self.srv.new_service(cfg), fut_ex: Some(self.expect.new_service(cfg)), @@ -164,7 +233,7 @@ where #[doc(hidden)] #[pin_project::pin_project] -pub struct H1ServiceResponse +pub struct H1ServiceResponse where S: ServiceFactory, S::Error: Into, @@ -186,12 +255,12 @@ where upgrade: Option, on_connect: Option Box>>, cfg: Option, - _t: PhantomData<(T, P, B)>, + _t: PhantomData<(T, B)>, } -impl Future for H1ServiceResponse +impl Future for H1ServiceResponse where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: ServiceFactory, S::Error: Into, S::Response: Into>, @@ -204,8 +273,7 @@ where U::Error: fmt::Display, U::InitError: fmt::Debug, { - type Output = - Result, ()>; + type Output = Result, ()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { let mut this = self.as_mut().project(); @@ -247,16 +315,16 @@ where } /// `Service` implementation for HTTP1 transport -pub struct H1ServiceHandler { +pub struct H1ServiceHandler { srv: CloneableService, expect: CloneableService, upgrade: Option>, on_connect: Option Box>>, cfg: ServiceConfig, - _t: PhantomData<(T, P, B)>, + _t: PhantomData<(T, B)>, } -impl H1ServiceHandler +impl H1ServiceHandler where S: Service, S::Error: Into, @@ -273,7 +341,7 @@ where expect: X, upgrade: Option, on_connect: Option Box>>, - ) -> H1ServiceHandler { + ) -> H1ServiceHandler { H1ServiceHandler { srv: CloneableService::new(srv), expect: CloneableService::new(expect), @@ -285,9 +353,9 @@ where } } -impl Service for H1ServiceHandler +impl Service for H1ServiceHandler where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into, S::Response: Into>, @@ -297,7 +365,7 @@ where U: Service), Response = ()>, U::Error: fmt::Display, { - type Request = Io; + type Request = (T, Option); type Response = (); type Error = DispatchError; type Future = Dispatcher; @@ -331,9 +399,7 @@ where } } - fn call(&mut self, req: Self::Request) -> Self::Future { - let io = req.into_parts().0; - + fn call(&mut self, (io, addr): Self::Request) -> Self::Future { let on_connect = if let Some(ref on_connect) = self.on_connect { Some(on_connect(&io)) } else { @@ -347,20 +413,21 @@ where self.expect.clone(), self.upgrade.clone(), on_connect, + addr, ) } } /// `ServiceFactory` implementation for `OneRequestService` service #[derive(Default)] -pub struct OneRequest { +pub struct OneRequest { config: ServiceConfig, - _t: PhantomData<(T, P)>, + _t: PhantomData, } -impl OneRequest +impl OneRequest where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, { /// Create new `H1SimpleService` instance. pub fn new() -> Self { @@ -371,38 +438,38 @@ where } } -impl ServiceFactory for OneRequest +impl ServiceFactory for OneRequest where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, { - type Config = SrvConfig; - type Request = Io; + type Config = (); + type Request = T; type Response = (Request, Framed); type Error = ParseError; type InitError = (); - type Service = OneRequestService; + type Service = OneRequestService; type Future = Ready>; - fn new_service(&self, _: &SrvConfig) -> Self::Future { + fn new_service(&self, _: &()) -> Self::Future { ok(OneRequestService { - config: self.config.clone(), _t: PhantomData, + config: self.config.clone(), }) } } /// `Service` implementation for HTTP1 transport. Reads one request and returns /// request and framed object. -pub struct OneRequestService { +pub struct OneRequestService { + _t: PhantomData, config: ServiceConfig, - _t: PhantomData<(T, P)>, } -impl Service for OneRequestService +impl Service for OneRequestService where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, { - type Request = Io; + type Request = T; type Response = (Request, Framed); type Error = ParseError; type Future = OneRequestServiceResponse; @@ -413,10 +480,7 @@ where fn call(&mut self, req: Self::Request) -> Self::Future { OneRequestServiceResponse { - framed: Some(Framed::new( - req.into_parts().0, - Codec::new(self.config.clone()), - )), + framed: Some(Framed::new(req, Codec::new(self.config.clone()))), } } } @@ -424,14 +488,14 @@ where #[doc(hidden)] pub struct OneRequestServiceResponse where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, { framed: Option>, } impl Future for OneRequestServiceResponse where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, { type Output = Result<(Request, Framed), ParseError>; diff --git a/actix-http/src/h1/upgrade.rs b/actix-http/src/h1/upgrade.rs index ce46fbe93..e84230ac7 100644 --- a/actix-http/src/h1/upgrade.rs +++ b/actix-http/src/h1/upgrade.rs @@ -2,7 +2,6 @@ use std::marker::PhantomData; use std::task::{Context, Poll}; use actix_codec::Framed; -use actix_server_config::ServerConfig; use actix_service::{Service, ServiceFactory}; use futures::future::Ready; @@ -13,7 +12,7 @@ use crate::request::Request; pub struct UpgradeHandler(PhantomData); impl ServiceFactory for UpgradeHandler { - type Config = ServerConfig; + type Config = (); type Request = (Request, Framed); type Response = (); type Error = Error; @@ -21,7 +20,7 @@ impl ServiceFactory for UpgradeHandler { type InitError = Error; type Future = Ready>; - fn new_service(&self, _: &ServerConfig) -> Self::Future { + fn new_service(&self, _: &()) -> Self::Future { unimplemented!() } } diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index 188553806..e6e8967df 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -8,7 +8,6 @@ use std::{fmt, mem, net}; use actix_codec::{AsyncRead, AsyncWrite}; use actix_rt::time::Delay; -use actix_server_config::IoStream; use actix_service::Service; use bitflags::bitflags; use bytes::{Bytes, BytesMut}; @@ -36,7 +35,10 @@ const CHUNK_SIZE: usize = 16_384; /// Dispatcher for HTTP/2 protocol #[pin_project::pin_project] -pub struct Dispatcher, B: MessageBody> { +pub struct Dispatcher, B: MessageBody> +where + T: AsyncRead + AsyncWrite + Unpin, +{ service: CloneableService, connection: Connection, on_connect: Option>, @@ -49,7 +51,7 @@ pub struct Dispatcher, B: MessageBody impl Dispatcher where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into, // S::Future: 'static, @@ -95,7 +97,7 @@ where impl Future for Dispatcher where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into + 'static, S::Future: 'static, diff --git a/actix-http/src/h2/service.rs b/actix-http/src/h2/service.rs index 860a61f73..a2c8275a1 100644 --- a/actix-http/src/h2/service.rs +++ b/actix-http/src/h2/service.rs @@ -6,8 +6,11 @@ use std::task::{Context, Poll}; use std::{io, net, rc}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; -use actix_server_config::{Io, IoStream, ServerConfig as SrvConfig}; -use actix_service::{IntoServiceFactory, Service, ServiceFactory}; +use actix_rt::net::TcpStream; +use actix_service::{ + factory_fn, pipeline_factory, service_fn2, IntoServiceFactory, Service, + ServiceFactory, +}; use bytes::Bytes; use futures::future::{ok, Ready}; use futures::{ready, Stream}; @@ -23,39 +26,28 @@ use crate::helpers::DataFactory; use crate::payload::Payload; use crate::request::Request; use crate::response::Response; +use crate::Protocol; use super::dispatcher::Dispatcher; /// `ServiceFactory` implementation for HTTP2 transport -pub struct H2Service { +pub struct H2Service { srv: S, cfg: ServiceConfig, on_connect: Option Box>>, - _t: PhantomData<(T, P, B)>, + _t: PhantomData<(T, B)>, } -impl H2Service +impl H2Service where - S: ServiceFactory, + S: ServiceFactory, S::Error: Into + 'static, S::Response: Into> + 'static, ::Future: 'static, B: MessageBody + 'static, { - /// Create new `HttpService` instance. - pub fn new>(service: F) -> Self { - let cfg = ServiceConfig::new(KeepAlive::Timeout(5), 5000, 0); - - H2Service { - cfg, - on_connect: None, - srv: service.into_factory(), - _t: PhantomData, - } - } - /// Create new `HttpService` instance with config. - pub fn with_config>( + pub(crate) fn with_config>( cfg: ServiceConfig, service: F, ) -> Self { @@ -77,24 +69,98 @@ where } } -impl ServiceFactory for H2Service +impl H2Service where - T: IoStream, - S: ServiceFactory, + S: ServiceFactory, S::Error: Into + 'static, S::Response: Into> + 'static, ::Future: 'static, B: MessageBody + 'static, { - type Config = SrvConfig; - type Request = Io; + /// Create simple tcp based service + pub fn tcp( + self, + ) -> impl ServiceFactory< + Config = (), + Request = TcpStream, + Response = (), + Error = DispatchError, + InitError = S::InitError, + > { + pipeline_factory(factory_fn(|| { + async { + Ok::<_, S::InitError>(service_fn2(|io: TcpStream| { + let peer_addr = io.peer_addr().ok(); + ok::<_, DispatchError>((io, peer_addr)) + })) + } + })) + .and_then(self) + } +} + +#[cfg(feature = "openssl")] +mod openssl { + use actix_service::{factory_fn, service_fn2}; + use actix_tls::openssl::{Acceptor, SslStream}; + use actix_tls::{openssl::HandshakeError, SslError}; + use open_ssl::ssl::SslAcceptor; + + use super::*; + + impl H2Service, S, B> + where + S: ServiceFactory, + S::Error: Into + 'static, + S::Response: Into> + 'static, + ::Future: 'static, + B: MessageBody + 'static, + { + /// Create ssl based service + pub fn openssl( + self, + acceptor: SslAcceptor, + ) -> impl ServiceFactory< + Config = (), + Request = TcpStream, + Response = (), + Error = SslError, DispatchError>, + InitError = S::InitError, + > { + pipeline_factory( + Acceptor::new(acceptor) + .map_err(SslError::Ssl) + .map_init_err(|_| panic!()), + ) + .and_then(factory_fn(|| { + ok::<_, S::InitError>(service_fn2(|io: SslStream| { + let peer_addr = io.get_ref().peer_addr().ok(); + ok((io, peer_addr)) + })) + })) + .and_then(self.map_err(SslError::Service)) + } + } +} + +impl ServiceFactory for H2Service +where + T: AsyncRead + AsyncWrite + Unpin, + S: ServiceFactory, + S::Error: Into + 'static, + S::Response: Into> + 'static, + ::Future: 'static, + B: MessageBody + 'static, +{ + type Config = (); + type Request = (T, Option); type Response = (); type Error = DispatchError; type InitError = S::InitError; - type Service = H2ServiceHandler; - type Future = H2ServiceResponse; + type Service = H2ServiceHandler; + type Future = H2ServiceResponse; - fn new_service(&self, cfg: &SrvConfig) -> Self::Future { + fn new_service(&self, cfg: &()) -> Self::Future { H2ServiceResponse { fut: self.srv.new_service(cfg), cfg: Some(self.cfg.clone()), @@ -106,24 +172,24 @@ where #[doc(hidden)] #[pin_project::pin_project] -pub struct H2ServiceResponse { +pub struct H2ServiceResponse { #[pin] fut: S::Future, cfg: Option, on_connect: Option Box>>, - _t: PhantomData<(T, P, B)>, + _t: PhantomData<(T, B)>, } -impl Future for H2ServiceResponse +impl Future for H2ServiceResponse where - T: IoStream, - S: ServiceFactory, + T: AsyncRead + AsyncWrite + Unpin, + S: ServiceFactory, S::Error: Into + 'static, S::Response: Into> + 'static, ::Future: 'static, B: MessageBody + 'static, { - type Output = Result, S::InitError>; + type Output = Result, S::InitError>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { let this = self.as_mut().project(); @@ -140,14 +206,14 @@ where } /// `Service` implementation for http/2 transport -pub struct H2ServiceHandler { +pub struct H2ServiceHandler { srv: CloneableService, cfg: ServiceConfig, on_connect: Option Box>>, - _t: PhantomData<(T, P, B)>, + _t: PhantomData<(T, B)>, } -impl H2ServiceHandler +impl H2ServiceHandler where S: Service, S::Error: Into + 'static, @@ -159,7 +225,7 @@ where cfg: ServiceConfig, on_connect: Option Box>>, srv: S, - ) -> H2ServiceHandler { + ) -> H2ServiceHandler { H2ServiceHandler { cfg, on_connect, @@ -169,16 +235,16 @@ where } } -impl Service for H2ServiceHandler +impl Service for H2ServiceHandler where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into + 'static, S::Future: 'static, S::Response: Into> + 'static, B: MessageBody + 'static, { - type Request = Io; + type Request = (T, Option); type Response = (); type Error = DispatchError; type Future = H2ServiceHandlerResponse; @@ -191,9 +257,7 @@ where }) } - fn call(&mut self, req: Self::Request) -> Self::Future { - let io = req.into_parts().0; - let peer_addr = io.peer_addr(); + fn call(&mut self, (io, addr): Self::Request) -> Self::Future { let on_connect = if let Some(ref on_connect) = self.on_connect { Some(on_connect(&io)) } else { @@ -204,7 +268,7 @@ where state: State::Handshake( Some(self.srv.clone()), Some(self.cfg.clone()), - peer_addr, + addr, on_connect, server::handshake(io), ), @@ -212,8 +276,9 @@ where } } -enum State, B: MessageBody> +enum State, B: MessageBody> where + T: AsyncRead + AsyncWrite + Unpin, S::Future: 'static, { Incoming(Dispatcher), @@ -228,7 +293,7 @@ where pub struct H2ServiceHandlerResponse where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into + 'static, S::Future: 'static, @@ -240,7 +305,7 @@ where impl Future for H2ServiceHandlerResponse where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into + 'static, S::Future: 'static, diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs index b57fdddce..e476623d1 100644 --- a/actix-http/src/lib.rs +++ b/actix-http/src/lib.rs @@ -64,3 +64,10 @@ pub mod http { pub use crate::header::ContentEncoding; pub use crate::message::ConnectionType; } + +/// Http protocol +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum Protocol { + Http1, + Http2, +} diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index 7340c15fd..8220421ab 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -1,15 +1,13 @@ use std::marker::PhantomData; use std::pin::Pin; use std::task::{Context, Poll}; -use std::{fmt, io, net, rc}; +use std::{fmt, net, rc}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; -use actix_server_config::{ - Io as ServerIo, IoStream, Protocol, ServerConfig as SrvConfig, -}; -use actix_service::{IntoServiceFactory, Service, ServiceFactory}; -use bytes::{BufMut, Bytes, BytesMut}; -use futures::{ready, Future}; +use actix_rt::net::TcpStream; +use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory}; +use bytes::Bytes; +use futures::{future::ok, ready, Future}; use h2::server::{self, Handshake}; use pin_project::{pin_project, project}; @@ -21,21 +19,21 @@ use crate::error::{DispatchError, Error}; use crate::helpers::DataFactory; use crate::request::Request; use crate::response::Response; -use crate::{h1, h2::Dispatcher}; +use crate::{h1, h2::Dispatcher, Protocol}; /// `ServiceFactory` HTTP1.1/HTTP2 transport implementation -pub struct HttpService> { +pub struct HttpService> { srv: S, cfg: ServiceConfig, expect: X, upgrade: Option, on_connect: Option Box>>, - _t: PhantomData<(T, P, B)>, + _t: PhantomData<(T, B)>, } -impl HttpService +impl HttpService where - S: ServiceFactory, + S: ServiceFactory, S::Error: Into + 'static, S::InitError: fmt::Debug, S::Response: Into> + 'static, @@ -48,9 +46,9 @@ where } } -impl HttpService +impl HttpService where - S: ServiceFactory, + S: ServiceFactory, S::Error: Into + 'static, S::InitError: fmt::Debug, S::Response: Into> + 'static, @@ -59,7 +57,7 @@ where { /// Create new `HttpService` instance. pub fn new>(service: F) -> Self { - let cfg = ServiceConfig::new(KeepAlive::Timeout(5), 5000, 0); + let cfg = ServiceConfig::new(KeepAlive::Timeout(5), 5000, 0, false, None); HttpService { cfg, @@ -87,9 +85,9 @@ where } } -impl HttpService +impl HttpService where - S: ServiceFactory, + S: ServiceFactory, S::Error: Into + 'static, S::InitError: fmt::Debug, S::Response: Into> + 'static, @@ -101,9 +99,9 @@ where /// Service get called with request that contains `EXPECT` header. /// Service must return request in case of success, in that case /// request will be forwarded to main service. - pub fn expect(self, expect: X1) -> HttpService + pub fn expect(self, expect: X1) -> HttpService where - X1: ServiceFactory, + X1: ServiceFactory, X1::Error: Into, X1::InitError: fmt::Debug, ::Future: 'static, @@ -122,10 +120,10 @@ where /// /// If service is provided then normal requests handling get halted /// and this service get called with original request and framed object. - pub fn upgrade(self, upgrade: Option) -> HttpService + pub fn upgrade(self, upgrade: Option) -> HttpService where U1: ServiceFactory< - Config = SrvConfig, + Config = (), Request = (Request, Framed), Response = (), >, @@ -153,21 +151,122 @@ where } } -impl ServiceFactory for HttpService +impl HttpService where - T: IoStream, - S: ServiceFactory, + S: ServiceFactory, S::Error: Into + 'static, S::InitError: fmt::Debug, S::Response: Into> + 'static, ::Future: 'static, B: MessageBody + 'static, - X: ServiceFactory, + X: ServiceFactory, X::Error: Into, X::InitError: fmt::Debug, ::Future: 'static, U: ServiceFactory< - Config = SrvConfig, + Config = (), + Request = (Request, Framed), + Response = (), + >, + U::Error: fmt::Display, + U::InitError: fmt::Debug, + ::Future: 'static, +{ + /// Create simple tcp stream service + pub fn tcp( + self, + ) -> impl ServiceFactory< + Config = (), + Request = TcpStream, + Response = (), + Error = DispatchError, + InitError = (), + > { + pipeline_factory(|io: TcpStream| { + let peer_addr = io.peer_addr().ok(); + ok((io, Protocol::Http1, peer_addr)) + }) + .and_then(self) + } +} + +#[cfg(feature = "openssl")] +mod openssl { + use super::*; + use actix_tls::openssl::{Acceptor, SslStream}; + use actix_tls::{openssl::HandshakeError, SslError}; + use open_ssl::ssl::SslAcceptor; + + impl HttpService, S, B, X, U> + where + S: ServiceFactory, + S::Error: Into + 'static, + S::InitError: fmt::Debug, + S::Response: Into> + 'static, + ::Future: 'static, + B: MessageBody + 'static, + X: ServiceFactory, + X::Error: Into, + X::InitError: fmt::Debug, + ::Future: 'static, + U: ServiceFactory< + Config = (), + Request = (Request, Framed, h1::Codec>), + Response = (), + >, + U::Error: fmt::Display, + U::InitError: fmt::Debug, + ::Future: 'static, + { + /// Create openssl based service + pub fn openssl( + self, + acceptor: SslAcceptor, + ) -> impl ServiceFactory< + Config = (), + Request = TcpStream, + Response = (), + Error = SslError, DispatchError>, + InitError = (), + > { + pipeline_factory( + Acceptor::new(acceptor) + .map_err(SslError::Ssl) + .map_init_err(|_| panic!()), + ) + .and_then(|io: SslStream| { + let proto = if let Some(protos) = io.ssl().selected_alpn_protocol() { + if protos.windows(2).any(|window| window == b"h2") { + Protocol::Http2 + } else { + Protocol::Http1 + } + } else { + Protocol::Http1 + }; + let peer_addr = io.get_ref().peer_addr().ok(); + ok((io, proto, peer_addr)) + }) + .and_then(self.map_err(SslError::Service)) + } + } +} + +impl ServiceFactory for HttpService +where + T: AsyncRead + AsyncWrite + Unpin, + S: ServiceFactory, + S::Error: Into + 'static, + S::InitError: fmt::Debug, + S::Response: Into> + 'static, + ::Future: 'static, + B: MessageBody + 'static, + X: ServiceFactory, + X::Error: Into, + X::InitError: fmt::Debug, + ::Future: 'static, + U: ServiceFactory< + Config = (), Request = (Request, Framed), Response = (), >, @@ -175,15 +274,15 @@ where U::InitError: fmt::Debug, ::Future: 'static, { - type Config = SrvConfig; - type Request = ServerIo; + type Config = (); + type Request = (T, Protocol, Option); type Response = (); type Error = DispatchError; type InitError = (); - type Service = HttpServiceHandler; - type Future = HttpServiceResponse; + type Service = HttpServiceHandler; + type Future = HttpServiceResponse; - fn new_service(&self, cfg: &SrvConfig) -> Self::Future { + fn new_service(&self, cfg: &()) -> Self::Future { HttpServiceResponse { fut: self.srv.new_service(cfg), fut_ex: Some(self.expect.new_service(cfg)), @@ -191,7 +290,7 @@ where expect: None, upgrade: None, on_connect: self.on_connect.clone(), - cfg: Some(self.cfg.clone()), + cfg: self.cfg.clone(), _t: PhantomData, } } @@ -201,7 +300,6 @@ where #[pin_project] pub struct HttpServiceResponse< T, - P, S: ServiceFactory, B, X: ServiceFactory, @@ -216,13 +314,13 @@ pub struct HttpServiceResponse< expect: Option, upgrade: Option, on_connect: Option Box>>, - cfg: Option, - _t: PhantomData<(T, P, B)>, + cfg: ServiceConfig, + _t: PhantomData<(T, B)>, } -impl Future for HttpServiceResponse +impl Future for HttpServiceResponse where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: ServiceFactory, S::Error: Into + 'static, S::InitError: fmt::Debug, @@ -239,7 +337,7 @@ where ::Future: 'static, { type Output = - Result, ()>; + Result, ()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { let mut this = self.as_mut().project(); @@ -269,7 +367,7 @@ where Poll::Ready(result.map(|service| { let this = self.as_mut().project(); HttpServiceHandler::new( - this.cfg.take().unwrap(), + this.cfg.clone(), service, this.expect.take().unwrap(), this.upgrade.take(), @@ -280,16 +378,16 @@ where } /// `Service` implementation for http transport -pub struct HttpServiceHandler { +pub struct HttpServiceHandler { srv: CloneableService, expect: CloneableService, upgrade: Option>, cfg: ServiceConfig, on_connect: Option Box>>, - _t: PhantomData<(T, P, B, X)>, + _t: PhantomData<(T, B, X)>, } -impl HttpServiceHandler +impl HttpServiceHandler where S: Service, S::Error: Into + 'static, @@ -307,7 +405,7 @@ where expect: X, upgrade: Option, on_connect: Option Box>>, - ) -> HttpServiceHandler { + ) -> HttpServiceHandler { HttpServiceHandler { cfg, on_connect, @@ -319,9 +417,9 @@ where } } -impl Service for HttpServiceHandler +impl Service for HttpServiceHandler where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into + 'static, S::Future: 'static, @@ -332,7 +430,7 @@ where U: Service), Response = ()>, U::Error: fmt::Display, { - type Request = ServerIo; + type Request = (T, Protocol, Option); type Response = (); type Error = DispatchError; type Future = HttpServiceHandlerResponse; @@ -366,9 +464,7 @@ where } } - fn call(&mut self, req: Self::Request) -> Self::Future { - let (io, _, proto) = req.into_parts(); - + fn call(&mut self, (io, proto, peer_addr): Self::Request) -> Self::Future { let on_connect = if let Some(ref on_connect) = self.on_connect { Some(on_connect(&io)) } else { @@ -376,23 +472,16 @@ where }; match proto { - Protocol::Http2 => { - let peer_addr = io.peer_addr(); - let io = Io { - inner: io, - unread: None, - }; - HttpServiceHandlerResponse { - state: State::Handshake(Some(( - server::handshake(io), - self.cfg.clone(), - self.srv.clone(), - peer_addr, - on_connect, - ))), - } - } - Protocol::Http10 | Protocol::Http11 => HttpServiceHandlerResponse { + Protocol::Http2 => HttpServiceHandlerResponse { + state: State::H2Handshake(Some(( + server::handshake(io), + self.cfg.clone(), + self.srv.clone(), + on_connect, + peer_addr, + ))), + }, + Protocol::Http1 => HttpServiceHandlerResponse { state: State::H1(h1::Dispatcher::new( io, self.cfg.clone(), @@ -400,19 +489,9 @@ where self.expect.clone(), self.upgrade.clone(), on_connect, + peer_addr, )), }, - _ => HttpServiceHandlerResponse { - state: State::Unknown(Some(( - io, - BytesMut::with_capacity(14), - self.cfg.clone(), - self.srv.clone(), - self.expect.clone(), - self.upgrade.clone(), - on_connect, - ))), - }, } } } @@ -423,7 +502,7 @@ where S: Service, S::Future: 'static, S::Error: Into, - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, B: MessageBody, X: Service, X::Error: Into, @@ -431,25 +510,14 @@ where U::Error: fmt::Display, { H1(#[pin] h1::Dispatcher), - H2(#[pin] Dispatcher, S, B>), - Unknown( + H2(#[pin] Dispatcher), + H2Handshake( Option<( - T, - BytesMut, + Handshake, ServiceConfig, CloneableService, - CloneableService, - Option>, Option>, - )>, - ), - Handshake( - Option<( - Handshake, Bytes>, - ServiceConfig, - CloneableService, Option, - Option>, )>, ), } @@ -457,7 +525,7 @@ where #[pin_project] pub struct HttpServiceHandlerResponse where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into + 'static, S::Future: 'static, @@ -472,11 +540,9 @@ where state: State, } -const HTTP2_PREFACE: [u8; 14] = *b"PRI * HTTP/2.0"; - impl Future for HttpServiceHandlerResponse where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into + 'static, S::Future: 'static, @@ -496,7 +562,7 @@ where impl State where - T: IoStream, + T: AsyncRead + AsyncWrite + Unpin, S: Service, S::Error: Into + 'static, S::Response: Into> + 'static, @@ -515,57 +581,7 @@ where match self.as_mut().project() { State::H1(disp) => disp.poll(cx), State::H2(disp) => disp.poll(cx), - State::Unknown(ref mut data) => { - if let Some(ref mut item) = data { - loop { - // Safety - we only write to the returned slice. - let b = unsafe { item.1.bytes_mut() }; - let n = ready!(Pin::new(&mut item.0).poll_read(cx, b))?; - if n == 0 { - return Poll::Ready(Ok(())); - } - // Safety - we know that 'n' bytes have - // been initialized via the contract of - // 'poll_read' - unsafe { item.1.advance_mut(n) }; - if item.1.len() >= HTTP2_PREFACE.len() { - break; - } - } - } else { - panic!() - } - let (io, buf, cfg, srv, expect, upgrade, on_connect) = - data.take().unwrap(); - if buf[..14] == HTTP2_PREFACE[..] { - let peer_addr = io.peer_addr(); - let io = Io { - inner: io, - unread: Some(buf), - }; - self.set(State::Handshake(Some(( - server::handshake(io), - cfg, - srv, - peer_addr, - on_connect, - )))); - } else { - self.set(State::H1(h1::Dispatcher::with_timeout( - io, - h1::Codec::new(cfg.clone()), - cfg, - buf, - None, - srv, - expect, - upgrade, - on_connect, - ))) - } - self.poll(cx) - } - State::Handshake(ref mut data) => { + State::H2Handshake(ref mut data) => { let conn = if let Some(ref mut item) = data { match Pin::new(&mut item.0).poll(cx) { Poll::Ready(Ok(conn)) => conn, @@ -578,7 +594,7 @@ where } else { panic!() }; - let (_, cfg, srv, peer_addr, on_connect) = data.take().unwrap(); + let (_, cfg, srv, on_connect, peer_addr) = data.take().unwrap(); self.set(State::H2(Dispatcher::new( srv, conn, on_connect, cfg, None, peer_addr, ))); @@ -587,117 +603,3 @@ where } } } - -/// Wrapper for `AsyncRead + AsyncWrite` types -#[pin_project::pin_project] -struct Io { - unread: Option, - #[pin] - inner: T, -} - -impl io::Read for Io { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - if let Some(mut bytes) = self.unread.take() { - let size = std::cmp::min(buf.len(), bytes.len()); - buf[..size].copy_from_slice(&bytes[..size]); - if bytes.len() > size { - bytes.split_to(size); - self.unread = Some(bytes); - } - Ok(size) - } else { - self.inner.read(buf) - } - } -} - -impl io::Write for Io { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.inner.write(buf) - } - fn flush(&mut self) -> io::Result<()> { - self.inner.flush() - } -} - -impl AsyncRead for Io { - // unsafe fn initializer(&self) -> io::Initializer { - // self.get_mut().inner.initializer() - // } - - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { - self.inner.prepare_uninitialized_buffer(buf) - } - - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - let this = self.project(); - - if let Some(mut bytes) = this.unread.take() { - let size = std::cmp::min(buf.len(), bytes.len()); - buf[..size].copy_from_slice(&bytes[..size]); - if bytes.len() > size { - bytes.split_to(size); - *this.unread = Some(bytes); - } - Poll::Ready(Ok(size)) - } else { - this.inner.poll_read(cx, buf) - } - } - - // fn poll_read_vectored( - // self: Pin<&mut Self>, - // cx: &mut Context<'_>, - // bufs: &mut [io::IoSliceMut<'_>], - // ) -> Poll> { - // self.get_mut().inner.poll_read_vectored(cx, bufs) - // } -} - -impl actix_codec::AsyncWrite for Io { - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - self.project().inner.poll_write(cx, buf) - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.project().inner.poll_flush(cx) - } - - fn poll_shutdown( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - self.project().inner.poll_shutdown(cx) - } -} - -impl actix_server_config::IoStream for Io { - #[inline] - fn peer_addr(&self) -> Option { - self.inner.peer_addr() - } - - #[inline] - fn set_nodelay(&mut self, nodelay: bool) -> io::Result<()> { - self.inner.set_nodelay(nodelay) - } - - #[inline] - fn set_linger(&mut self, dur: Option) -> io::Result<()> { - self.inner.set_linger(dur) - } - - #[inline] - fn set_keepalive(&mut self, dur: Option) -> io::Result<()> { - self.inner.set_keepalive(dur) - } -} diff --git a/actix-http/src/test.rs b/actix-http/src/test.rs index 744f057dc..ebb7bda37 100644 --- a/actix-http/src/test.rs +++ b/actix-http/src/test.rs @@ -6,7 +6,6 @@ use std::str::FromStr; use std::task::{Context, Poll}; use actix_codec::{AsyncRead, AsyncWrite}; -use actix_server_config::IoStream; use bytes::{Bytes, BytesMut}; use http::header::{self, HeaderName, HeaderValue}; use http::{HttpTryFrom, Method, Uri, Version}; @@ -272,17 +271,3 @@ impl AsyncWrite for TestBuffer { Poll::Ready(Ok(())) } } - -impl IoStream for TestBuffer { - fn set_nodelay(&mut self, _nodelay: bool) -> io::Result<()> { - Ok(()) - } - - fn set_linger(&mut self, _dur: Option) -> io::Result<()> { - Ok(()) - } - - fn set_keepalive(&mut self, _dur: Option) -> io::Result<()> { - Ok(()) - } -} diff --git a/actix-http/tests/test_client.rs b/actix-http/tests/test_client.rs index cdcaea028..711ee7afd 100644 --- a/actix-http/tests/test_client.rs +++ b/actix-http/tests/test_client.rs @@ -30,7 +30,9 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[actix_rt::test] async fn test_h1_v2() { let srv = TestServer::start(move || { - HttpService::build().finish(|_| future::ok::<_, ()>(Response::Ok().body(STR))) + HttpService::build() + .finish(|_| future::ok::<_, ()>(Response::Ok().body(STR))) + .tcp() }); let response = srv.get("/").send().await.unwrap(); @@ -57,6 +59,7 @@ async fn test_connection_close() { let srv = TestServer::start(move || { HttpService::build() .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) + .tcp() .map(|_| ()) }); @@ -75,6 +78,7 @@ async fn test_with_query_parameter() { ok::<_, ()>(Response::BadRequest().finish()) } }) + .tcp() .map(|_| ()) }); diff --git a/actix-http/tests/test_openssl.rs b/actix-http/tests/test_openssl.rs index 0fdddaa1c..35e234af9 100644 --- a/actix-http/tests/test_openssl.rs +++ b/actix-http/tests/test_openssl.rs @@ -1,11 +1,8 @@ #![cfg(feature = "openssl")] use std::io; -use actix_codec::{AsyncRead, AsyncWrite}; use actix_http_test::TestServer; -use actix_server::ssl::OpensslAcceptor; -use actix_server_config::ServerConfig; -use actix_service::{factory_fn_cfg, pipeline_factory, service_fn2, ServiceFactory}; +use actix_service::{service_fn, ServiceFactory}; use bytes::{Bytes, BytesMut}; use futures::future::{err, ok, ready}; @@ -36,7 +33,7 @@ where Ok(body) } -fn ssl_acceptor() -> io::Result> { +fn ssl_acceptor() -> SslAcceptor { // load ssl keys let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); builder @@ -47,30 +44,29 @@ fn ssl_acceptor() -> io::Result io::Result<()> { - let openssl = ssl_acceptor()?; let srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|_| ok::<_, Error>(Response::Ok().finish())) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| ok::<_, Error>(Response::Ok().finish())) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.sget("/").send().await.unwrap(); @@ -80,22 +76,15 @@ async fn test_h2() -> io::Result<()> { #[actix_rt::test] async fn test_h2_1() -> io::Result<()> { - let openssl = ssl_acceptor()?; let srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .finish(|req: Request| { - assert!(req.peer_addr().is_some()); - assert_eq!(req.version(), Version::HTTP_2); - ok::<_, Error>(Response::Ok().finish()) - }) - .map_err(|_| ()), - ) + HttpService::build() + .finish(|req: Request| { + assert!(req.peer_addr().is_some()); + assert_eq!(req.version(), Version::HTTP_2); + ok::<_, Error>(Response::Ok().finish()) + }) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.sget("/").send().await.unwrap(); @@ -106,23 +95,16 @@ async fn test_h2_1() -> io::Result<()> { #[actix_rt::test] async fn test_h2_body() -> io::Result<()> { let data = "HELLOWORLD".to_owned().repeat(64 * 1024); - let openssl = ssl_acceptor()?; let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|mut req: Request<_>| { - async move { - let body = load_body(req.take_payload()).await?; - Ok::<_, Error>(Response::Ok().body(body)) - } - }) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|mut req: Request<_>| { + async move { + let body = load_body(req.take_payload()).await?; + Ok::<_, Error>(Response::Ok().body(body)) + } + }) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.sget("/").send_body(data.clone()).await.unwrap(); @@ -135,30 +117,22 @@ async fn test_h2_body() -> io::Result<()> { #[actix_rt::test] async fn test_h2_content_length() { - let openssl = ssl_acceptor().unwrap(); - let srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|req: Request| { - let indx: usize = req.uri().path()[1..].parse().unwrap(); - let statuses = [ - StatusCode::NO_CONTENT, - StatusCode::CONTINUE, - StatusCode::SWITCHING_PROTOCOLS, - StatusCode::PROCESSING, - StatusCode::OK, - StatusCode::NOT_FOUND, - ]; - ok::<_, ()>(Response::new(statuses[indx])) - }) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|req: Request| { + let indx: usize = req.uri().path()[1..].parse().unwrap(); + let statuses = [ + StatusCode::NO_CONTENT, + StatusCode::CONTINUE, + StatusCode::SWITCHING_PROTOCOLS, + StatusCode::PROCESSING, + StatusCode::OK, + StatusCode::NOT_FOUND, + ]; + ok::<_, ()>(Response::new(statuses[indx])) + }) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let header = HeaderName::from_static("content-length"); @@ -193,14 +167,9 @@ async fn test_h2_content_length() { async fn test_h2_headers() { let data = STR.repeat(10); let data2 = data.clone(); - let openssl = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { let data = data.clone(); - pipeline_factory(openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e))) - .and_then( HttpService::build().h2(move |_| { let mut builder = Response::Ok(); for idx in 0..90 { @@ -222,7 +191,9 @@ async fn test_h2_headers() { ); } ok::<_, ()>(builder.body(data.clone())) - }).map_err(|_| ())) + }) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.sget("/").send().await.unwrap(); @@ -257,18 +228,11 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[actix_rt::test] async fn test_h2_body2() { - let openssl = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.sget("/").send().await.unwrap(); @@ -281,18 +245,11 @@ async fn test_h2_body2() { #[actix_rt::test] async fn test_h2_head_empty() { - let openssl = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) + HttpService::build() + .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.shead("/").send().await.unwrap(); @@ -311,22 +268,13 @@ async fn test_h2_head_empty() { #[actix_rt::test] async fn test_h2_head_binary() { - let openssl = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|_| { - ok::<_, ()>( - Response::Ok().content_length(STR.len() as u64).body(STR), - ) - }) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| { + ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) + }) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.shead("/").send().await.unwrap(); @@ -344,18 +292,11 @@ async fn test_h2_head_binary() { #[actix_rt::test] async fn test_h2_head_binary2() { - let openssl = ssl_acceptor().unwrap(); let srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.shead("/").send().await.unwrap(); @@ -369,24 +310,16 @@ async fn test_h2_head_binary2() { #[actix_rt::test] async fn test_h2_body_length() { - let openssl = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|_| { - let body = once(ok(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok() - .body(body::SizedStream::new(STR.len() as u64, body)), - ) - }) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| { + let body = once(ok(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok().body(body::SizedStream::new(STR.len() as u64, body)), + ) + }) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.sget("/").send().await.unwrap(); @@ -399,25 +332,18 @@ async fn test_h2_body_length() { #[actix_rt::test] async fn test_h2_body_chunked_explicit() { - let openssl = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|_| { - let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok() - .header(header::TRANSFER_ENCODING, "chunked") - .streaming(body), - ) - }) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| { + let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok() + .header(header::TRANSFER_ENCODING, "chunked") + .streaming(body), + ) + }) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.sget("/").send().await.unwrap(); @@ -433,28 +359,18 @@ async fn test_h2_body_chunked_explicit() { #[actix_rt::test] async fn test_h2_response_http_error_handling() { - let openssl = ssl_acceptor().unwrap(); - let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(factory_fn_cfg(|_: &ServerConfig| { - ok::<_, ()>(service_fn2(|_| { - let broken_header = Bytes::from_static(b"\0\0\0"); - ok::<_, ()>( - Response::Ok() - .header(header::CONTENT_TYPE, broken_header) - .body(STR), - ) - })) - })) - .map_err(|_| ()), - ) + HttpService::build() + .h2(service_fn(|_| { + let broken_header = Bytes::from_static(b"\0\0\0"); + ok::<_, ()>( + Response::Ok() + .header(header::CONTENT_TYPE, broken_header) + .body(STR), + ) + })) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.sget("/").send().await.unwrap(); @@ -467,19 +383,11 @@ async fn test_h2_response_http_error_handling() { #[actix_rt::test] async fn test_h2_service_error() { - let openssl = ssl_acceptor().unwrap(); - let mut srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .h2(|_| err::(ErrorBadRequest("error"))) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| err::(ErrorBadRequest("error"))) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.sget("/").send().await.unwrap(); @@ -492,23 +400,15 @@ async fn test_h2_service_error() { #[actix_rt::test] async fn test_h2_on_connect() { - let openssl = ssl_acceptor().unwrap(); - let srv = TestServer::start(move || { - pipeline_factory( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) - .and_then( - HttpService::build() - .on_connect(|_| 10usize) - .h2(|req: Request| { - assert!(req.extensions().contains::()); - ok::<_, ()>(Response::Ok().finish()) - }) - .map_err(|_| ()), - ) + HttpService::build() + .on_connect(|_| 10usize) + .h2(|req: Request| { + assert!(req.extensions().contains::()); + ok::<_, ()>(Response::Ok().finish()) + }) + .openssl(ssl_acceptor()) + .map_err(|_| ()) }); let response = srv.sget("/").send().await.unwrap(); diff --git a/actix-http/tests/test_server.rs b/actix-http/tests/test_server.rs index a3ce3f9cb..850d30a3f 100644 --- a/actix-http/tests/test_server.rs +++ b/actix-http/tests/test_server.rs @@ -4,8 +4,7 @@ use std::{net, thread}; use actix_http_test::TestServer; use actix_rt::time::delay_for; -use actix_server_config::ServerConfig; -use actix_service::{factory_fn_cfg, pipeline, service_fn, ServiceFactory}; +use actix_service::service_fn; use bytes::Bytes; use futures::future::{self, err, ok, ready, FutureExt}; use futures::stream::{once, StreamExt}; @@ -27,6 +26,7 @@ async fn test_h1() { assert!(req.peer_addr().is_some()); future::ok::<_, ()>(Response::Ok().finish()) }) + .tcp() }); let response = srv.get("/").send().await.unwrap(); @@ -45,7 +45,7 @@ async fn test_h1_2() { assert_eq!(req.version(), http::Version::HTTP_11); future::ok::<_, ()>(Response::Ok().finish()) }) - .map(|_| ()) + .tcp() }); let response = srv.get("/").send().await.unwrap(); @@ -64,6 +64,7 @@ async fn test_expect_continue() { } })) .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) + .tcp() }); let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); @@ -92,7 +93,8 @@ async fn test_expect_continue_h1() { } }) })) - .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + .h1(service_fn(|_| future::ok::<_, ()>(Response::Ok().finish()))) + .tcp() }); let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); @@ -114,18 +116,20 @@ async fn test_chunked_payload() { let total_size: usize = chunk_sizes.iter().sum(); let srv = TestServer::start(|| { - HttpService::build().h1(service_fn(|mut request: Request| { - request - .take_payload() - .map(|res| match res { - Ok(pl) => pl, - Err(e) => panic!(format!("Error reading payload: {}", e)), - }) - .fold(0usize, |acc, chunk| ready(acc + chunk.len())) - .map(|req_size| { - Ok::<_, Error>(Response::Ok().body(format!("size={}", req_size))) - }) - })) + HttpService::build() + .h1(service_fn(|mut request: Request| { + request + .take_payload() + .map(|res| match res { + Ok(pl) => pl, + Err(e) => panic!(format!("Error reading payload: {}", e)), + }) + .fold(0usize, |acc, chunk| ready(acc + chunk.len())) + .map(|req_size| { + Ok::<_, Error>(Response::Ok().body(format!("size={}", req_size))) + }) + })) + .tcp() }); let returned_size = { @@ -167,6 +171,7 @@ async fn test_slow_request() { HttpService::build() .client_timeout(100) .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) + .tcp() }); let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); @@ -179,7 +184,9 @@ async fn test_slow_request() { #[actix_rt::test] async fn test_http1_malformed_request() { let srv = TestServer::start(|| { - HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + HttpService::build() + .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + .tcp() }); let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); @@ -192,7 +199,9 @@ async fn test_http1_malformed_request() { #[actix_rt::test] async fn test_http1_keepalive() { let srv = TestServer::start(|| { - HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + HttpService::build() + .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + .tcp() }); let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); @@ -213,6 +222,7 @@ async fn test_http1_keepalive_timeout() { HttpService::build() .keep_alive(1) .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + .tcp() }); let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); @@ -230,7 +240,9 @@ async fn test_http1_keepalive_timeout() { #[actix_rt::test] async fn test_http1_keepalive_close() { let srv = TestServer::start(|| { - HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + HttpService::build() + .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + .tcp() }); let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); @@ -248,7 +260,9 @@ async fn test_http1_keepalive_close() { #[actix_rt::test] async fn test_http10_keepalive_default_close() { let srv = TestServer::start(|| { - HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + HttpService::build() + .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + .tcp() }); let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); @@ -265,7 +279,9 @@ async fn test_http10_keepalive_default_close() { #[actix_rt::test] async fn test_http10_keepalive() { let srv = TestServer::start(|| { - HttpService::build().h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + HttpService::build() + .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + .tcp() }); let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); @@ -292,6 +308,7 @@ async fn test_http1_keepalive_disabled() { HttpService::build() .keep_alive(KeepAlive::Disabled) .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) + .tcp() }); let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); @@ -313,18 +330,20 @@ async fn test_content_length() { }; let srv = TestServer::start(|| { - HttpService::build().h1(|req: Request| { - let indx: usize = req.uri().path()[1..].parse().unwrap(); - let statuses = [ - StatusCode::NO_CONTENT, - StatusCode::CONTINUE, - StatusCode::SWITCHING_PROTOCOLS, - StatusCode::PROCESSING, - StatusCode::OK, - StatusCode::NOT_FOUND, - ]; - future::ok::<_, ()>(Response::new(statuses[indx])) - }) + HttpService::build() + .h1(|req: Request| { + let indx: usize = req.uri().path()[1..].parse().unwrap(); + let statuses = [ + StatusCode::NO_CONTENT, + StatusCode::CONTINUE, + StatusCode::SWITCHING_PROTOCOLS, + StatusCode::PROCESSING, + StatusCode::OK, + StatusCode::NOT_FOUND, + ]; + future::ok::<_, ()>(Response::new(statuses[indx])) + }) + .tcp() }); let header = HeaderName::from_static("content-length"); @@ -377,7 +396,7 @@ async fn test_h1_headers() { ); } future::ok::<_, ()>(builder.body(data.clone())) - }) + }).tcp() }); let response = srv.get("/").send().await.unwrap(); @@ -413,7 +432,9 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[actix_rt::test] async fn test_h1_body() { let mut srv = TestServer::start(|| { - HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) + HttpService::build() + .h1(|_| ok::<_, ()>(Response::Ok().body(STR))) + .tcp() }); let response = srv.get("/").send().await.unwrap(); @@ -427,7 +448,9 @@ async fn test_h1_body() { #[actix_rt::test] async fn test_h1_head_empty() { let mut srv = TestServer::start(|| { - HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) + HttpService::build() + .h1(|_| ok::<_, ()>(Response::Ok().body(STR))) + .tcp() }); let response = srv.head("/").send().await.unwrap(); @@ -449,9 +472,11 @@ async fn test_h1_head_empty() { #[actix_rt::test] async fn test_h1_head_binary() { let mut srv = TestServer::start(|| { - HttpService::build().h1(|_| { - ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) - }) + HttpService::build() + .h1(|_| { + ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) + }) + .tcp() }); let response = srv.head("/").send().await.unwrap(); @@ -473,7 +498,9 @@ async fn test_h1_head_binary() { #[actix_rt::test] async fn test_h1_head_binary2() { let srv = TestServer::start(|| { - HttpService::build().h1(|_| ok::<_, ()>(Response::Ok().body(STR))) + HttpService::build() + .h1(|_| ok::<_, ()>(Response::Ok().body(STR))) + .tcp() }); let response = srv.head("/").send().await.unwrap(); @@ -491,12 +518,14 @@ async fn test_h1_head_binary2() { #[actix_rt::test] async fn test_h1_body_length() { let mut srv = TestServer::start(|| { - HttpService::build().h1(|_| { - let body = once(ok(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok().body(body::SizedStream::new(STR.len() as u64, body)), - ) - }) + HttpService::build() + .h1(|_| { + let body = once(ok(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok().body(body::SizedStream::new(STR.len() as u64, body)), + ) + }) + .tcp() }); let response = srv.get("/").send().await.unwrap(); @@ -510,14 +539,16 @@ async fn test_h1_body_length() { #[actix_rt::test] async fn test_h1_body_chunked_explicit() { let mut srv = TestServer::start(|| { - HttpService::build().h1(|_| { - let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok() - .header(header::TRANSFER_ENCODING, "chunked") - .streaming(body), - ) - }) + HttpService::build() + .h1(|_| { + let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok() + .header(header::TRANSFER_ENCODING, "chunked") + .streaming(body), + ) + }) + .tcp() }); let response = srv.get("/").send().await.unwrap(); @@ -542,10 +573,12 @@ async fn test_h1_body_chunked_explicit() { #[actix_rt::test] async fn test_h1_body_chunked_implicit() { let mut srv = TestServer::start(|| { - HttpService::build().h1(|_| { - let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>(Response::Ok().streaming(body)) - }) + HttpService::build() + .h1(|_| { + let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>(Response::Ok().streaming(body)) + }) + .tcp() }); let response = srv.get("/").send().await.unwrap(); @@ -568,8 +601,8 @@ async fn test_h1_body_chunked_implicit() { #[actix_rt::test] async fn test_h1_response_http_error_handling() { let mut srv = TestServer::start(|| { - HttpService::build().h1(factory_fn_cfg(|_: &ServerConfig| { - ok::<_, ()>(pipeline(|_| { + HttpService::build() + .h1(service_fn(|_| { let broken_header = Bytes::from_static(b"\0\0\0"); ok::<_, ()>( Response::Ok() @@ -577,7 +610,7 @@ async fn test_h1_response_http_error_handling() { .body(STR), ) })) - })) + .tcp() }); let response = srv.get("/").send().await.unwrap(); @@ -593,6 +626,7 @@ async fn test_h1_service_error() { let mut srv = TestServer::start(|| { HttpService::build() .h1(|_| future::err::(error::ErrorBadRequest("error"))) + .tcp() }); let response = srv.get("/").send().await.unwrap(); @@ -612,6 +646,7 @@ async fn test_h1_on_connect() { assert!(req.extensions().contains::()); future::ok::<_, ()>(Response::Ok().finish()) }) + .tcp() }); let response = srv.get("/").send().await.unwrap(); diff --git a/actix-http/tests/test_ws.rs b/actix-http/tests/test_ws.rs index aa81bc41b..5ac5fcaaf 100644 --- a/actix-http/tests/test_ws.rs +++ b/actix-http/tests/test_ws.rs @@ -40,6 +40,7 @@ async fn test_simple() { HttpService::build() .upgrade(actix_service::service_fn(ws_service)) .finish(|_| future::ok::<_, ()>(Response::NotFound())) + .tcp() }); // client service diff --git a/actix-web-codegen/tests/test_macro.rs b/actix-web-codegen/tests/test_macro.rs index b6ac6dd18..c4f2d7e89 100644 --- a/actix-web-codegen/tests/test_macro.rs +++ b/actix-web-codegen/tests/test_macro.rs @@ -78,6 +78,7 @@ async fn test_params() { .service(put_param_test) .service(delete_param_test), ) + .tcp() }); let request = srv.request(http::Method::GET, srv.url("/test/it")); @@ -107,6 +108,7 @@ async fn test_body() { .service(patch_test) .service(test), ) + .tcp() }); let request = srv.request(http::Method::GET, srv.url("/test")); let response = request.send().await.unwrap(); @@ -149,7 +151,8 @@ async fn test_body() { #[actix_rt::test] async fn test_auto_async() { - let srv = TestServer::start(|| HttpService::new(App::new().service(auto_async))); + let srv = + TestServer::start(|| HttpService::new(App::new().service(auto_async)).tcp()); let request = srv.request(http::Method::GET, srv.url("/test")); let response = request.send().await.unwrap(); diff --git a/awc/Cargo.toml b/awc/Cargo.toml index e9268aac0..883b275da 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -67,7 +67,8 @@ actix-web = { version = "2.0.0-alpha.1", features=["openssl"] } actix-http = { version = "0.3.0-alpha.1", features=["openssl"] } actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } actix-utils = "0.5.0-alpha.1" -actix-server = { version = "0.8.0-alpha.1", features=["openssl"] } +actix-server = { version = "0.8.0-alpha.2" } +#actix-tls = { version = "0.1.0-alpha.1", features=["openssl"] } brotli2 = { version="0.3.2" } flate2 = { version="1.0.2" } env_logger = "0.6" diff --git a/awc/tests/test_client.rs b/awc/tests/test_client.rs index 15e9a07ac..6bd39973f 100644 --- a/awc/tests/test_client.rs +++ b/awc/tests/test_client.rs @@ -49,6 +49,7 @@ async fn test_simple() { HttpService::new(App::new().service( web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))), )) + .tcp() }); let request = srv.get("/").header("x-test", "111").send(); @@ -77,6 +78,7 @@ async fn test_json() { HttpService::new(App::new().service( web::resource("/").route(web::to(|_: web::Json| HttpResponse::Ok())), )) + .tcp() }); let request = srv @@ -93,6 +95,7 @@ async fn test_form() { HttpService::new(App::new().service(web::resource("/").route(web::to( |_: web::Form>| HttpResponse::Ok(), )))) + .tcp() }); let mut data = HashMap::new(); @@ -112,6 +115,7 @@ async fn test_timeout() { Ok::<_, Error>(HttpResponse::Ok().body(STR)) } })))) + .tcp() }); let connector = awc::Connector::new() @@ -142,6 +146,7 @@ async fn test_timeout_override() { Ok::<_, Error>(HttpResponse::Ok().body(STR)) } })))) + .tcp() }); let client = awc::Client::build() @@ -168,9 +173,13 @@ async fn test_connection_reuse() { num2.fetch_add(1, Ordering::Relaxed); ok(io) }) - .and_then(HttpService::new( - App::new().service(web::resource("/").route(web::to(|| HttpResponse::Ok()))), - )) + .and_then( + HttpService::new( + App::new() + .service(web::resource("/").route(web::to(|| HttpResponse::Ok()))), + ) + .tcp(), + ) }); let client = awc::Client::default(); @@ -200,9 +209,13 @@ async fn test_connection_force_close() { num2.fetch_add(1, Ordering::Relaxed); ok(io) }) - .and_then(HttpService::new( - App::new().service(web::resource("/").route(web::to(|| HttpResponse::Ok()))), - )) + .and_then( + HttpService::new( + App::new() + .service(web::resource("/").route(web::to(|| HttpResponse::Ok()))), + ) + .tcp(), + ) }); let client = awc::Client::default(); @@ -232,12 +245,15 @@ async fn test_connection_server_close() { num2.fetch_add(1, Ordering::Relaxed); ok(io) }) - .and_then(HttpService::new( - App::new().service( - web::resource("/") - .route(web::to(|| HttpResponse::Ok().force_close().finish())), - ), - )) + .and_then( + HttpService::new( + App::new().service( + web::resource("/") + .route(web::to(|| HttpResponse::Ok().force_close().finish())), + ), + ) + .tcp(), + ) }); let client = awc::Client::default(); @@ -267,9 +283,12 @@ async fn test_connection_wait_queue() { num2.fetch_add(1, Ordering::Relaxed); ok(io) }) - .and_then(HttpService::new(App::new().service( - web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))), - ))) + .and_then( + HttpService::new(App::new().service( + web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))), + )) + .tcp(), + ) }); let client = awc::Client::build() @@ -308,12 +327,15 @@ async fn test_connection_wait_queue_force_close() { num2.fetch_add(1, Ordering::Relaxed); ok(io) }) - .and_then(HttpService::new( - App::new().service( - web::resource("/") - .route(web::to(|| HttpResponse::Ok().force_close().body(STR))), - ), - )) + .and_then( + HttpService::new( + App::new().service( + web::resource("/") + .route(web::to(|| HttpResponse::Ok().force_close().body(STR))), + ), + ) + .tcp(), + ) }); let client = awc::Client::build() @@ -353,6 +375,7 @@ async fn test_with_query_parameter() { } }, ))) + .tcp() }); let res = awc::Client::new() @@ -373,6 +396,7 @@ async fn test_no_decompress() { res })), )) + .tcp() }); let mut res = awc::Client::new() @@ -419,6 +443,7 @@ async fn test_client_gzip_encoding() { .header("content-encoding", "gzip") .body(data) })))) + .tcp() }); // client request @@ -442,6 +467,7 @@ async fn test_client_gzip_encoding_large() { .header("content-encoding", "gzip") .body(data) })))) + .tcp() }); // client request @@ -471,6 +497,7 @@ async fn test_client_gzip_encoding_large_random() { .body(data) }, )))) + .tcp() }); // client request @@ -495,6 +522,7 @@ async fn test_client_brotli_encoding() { .body(data) }, )))) + .tcp() }); // client request @@ -707,6 +735,7 @@ async fn test_client_cookie_handling() { } }), )) + .tcp() }); let request = srv.get("/").cookie(cookie1.clone()).cookie(cookie2.clone()); @@ -768,6 +797,7 @@ async fn client_basic_auth() { } }), )) + .tcp() }); // set authorization header to Basic @@ -796,6 +826,7 @@ async fn client_bearer_auth() { } }), )) + .tcp() }); // set authorization header to Bearer diff --git a/awc/tests/test_ssl_client.rs b/awc/tests/test_ssl_client.rs index 1abb071a4..a9a7fa2fb 100644 --- a/awc/tests/test_ssl_client.rs +++ b/awc/tests/test_ssl_client.rs @@ -1,20 +1,16 @@ #![cfg(feature = "openssl")] -use open_ssl::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod, SslVerifyMode}; - -use std::io::Result; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; -use actix_codec::{AsyncRead, AsyncWrite}; use actix_http::HttpService; use actix_http_test::TestServer; -use actix_server::ssl::OpensslAcceptor; use actix_service::{pipeline_factory, ServiceFactory}; use actix_web::http::Version; use actix_web::{web, App, HttpResponse}; use futures::future::ok; +use open_ssl::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod, SslVerifyMode}; -fn ssl_acceptor() -> Result> { +fn ssl_acceptor() -> SslAcceptor { // load ssl keys let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); builder @@ -31,13 +27,12 @@ fn ssl_acceptor() -> Result> { Err(open_ssl::ssl::AlpnError::NOACK) } }); - builder.set_alpn_protos(b"\x02h2")?; - Ok(actix_server::ssl::OpensslAcceptor::new(builder.build())) + builder.set_alpn_protos(b"\x02h2").unwrap(); + builder.build() } #[actix_rt::test] async fn test_connection_reuse_h2() { - let openssl = ssl_acceptor().unwrap(); let num = Arc::new(AtomicUsize::new(0)); let num2 = num.clone(); @@ -47,15 +42,11 @@ async fn test_connection_reuse_h2() { num2.fetch_add(1, Ordering::Relaxed); ok(io) }) - .and_then( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) .and_then( HttpService::build() .h2(App::new() .service(web::resource("/").route(web::to(|| HttpResponse::Ok())))) + .openssl(ssl_acceptor()) .map_err(|_| ()), ) }); diff --git a/awc/tests/test_ws.rs b/awc/tests/test_ws.rs index 2e1d3981e..d90f55531 100644 --- a/awc/tests/test_ws.rs +++ b/awc/tests/test_ws.rs @@ -46,6 +46,7 @@ async fn test_simple() { } }) .finish(|_| ok::<_, Error>(Response::NotFound())) + .tcp() }); // client service diff --git a/src/app_service.rs b/src/app_service.rs index 3fa5a6eed..6c18cd542 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -7,7 +7,6 @@ use std::task::{Context, Poll}; use actix_http::{Extensions, Request, Response}; use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url}; -use actix_server_config::ServerConfig; use actix_service::boxed::{self, BoxService, BoxServiceFactory}; use actix_service::{service_fn, Service, ServiceFactory}; use futures::future::{ok, FutureExt, LocalBoxFuture}; @@ -59,7 +58,7 @@ where InitError = (), >, { - type Config = ServerConfig; + type Config = (); type Request = Request; type Response = ServiceResponse; type Error = T::Error; @@ -67,7 +66,7 @@ where type Service = AppInitService; type Future = AppInitResult; - fn new_service(&self, cfg: &ServerConfig) -> Self::Future { + fn new_service(&self, _: &()) -> Self::Future { // update resource default service let default = self.default.clone().unwrap_or_else(|| { Rc::new(boxed::factory(service_fn(|req: ServiceRequest| { @@ -76,13 +75,6 @@ where }); // App config - { - let mut c = self.config.borrow_mut(); - let loc_cfg = Rc::get_mut(&mut c.0).unwrap(); - loc_cfg.secure = cfg.secure(); - loc_cfg.addr = cfg.local_addr(); - } - let mut config = AppService::new( self.config.borrow().clone(), default.clone(), diff --git a/src/server.rs b/src/server.rs index a98d06275..f3ec550cf 100644 --- a/src/server.rs +++ b/src/server.rs @@ -2,11 +2,13 @@ use std::marker::PhantomData; use std::sync::Arc; use std::{fmt, io, net}; -use actix_http::{body::MessageBody, Error, HttpService, KeepAlive, Request, Response}; +use actix_http::{ + body::MessageBody, Error, HttpService, KeepAlive, Protocol, Request, Response, +}; use actix_rt::System; use actix_server::{Server, ServerBuilder}; -use actix_server_config::ServerConfig; -use actix_service::{IntoServiceFactory, Service, ServiceFactory}; +use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory}; +use futures::future::ok; use parking_lot::Mutex; use net2::TcpBuilder; @@ -52,7 +54,7 @@ pub struct HttpServer where F: Fn() -> I + Send + Clone + 'static, I: IntoServiceFactory, - S: ServiceFactory, + S: ServiceFactory, S::Error: Into, S::InitError: fmt::Debug, S::Response: Into>, @@ -71,7 +73,7 @@ impl HttpServer where F: Fn() -> I + Send + Clone + 'static, I: IntoServiceFactory, - S: ServiceFactory, + S: ServiceFactory, S::Error: Into + 'static, S::InitError: fmt::Debug, S::Response: Into> + 'static, @@ -137,8 +139,8 @@ where /// can be used to limit the global SSL CPU usage. /// /// By default max connections is set to a 256. - pub fn maxconnrate(mut self, num: usize) -> Self { - self.builder = self.builder.maxconnrate(num); + pub fn maxconnrate(self, num: usize) -> Self { + actix_tls::max_concurrent_ssl_connect(num); self } @@ -247,7 +249,9 @@ where HttpService::build() .keep_alive(c.keep_alive) .client_timeout(c.client_timeout) + .local_addr(addr) .finish(factory()) + .tcp() }, )?; Ok(self) @@ -271,10 +275,6 @@ where lst: net::TcpListener, acceptor: SslAcceptor, ) -> io::Result { - use actix_server::ssl::{OpensslAcceptor, SslError}; - use actix_service::pipeline_factory; - - let acceptor = OpensslAcceptor::new(acceptor); let factory = self.factory.clone(); let cfg = self.config.clone(); let addr = lst.local_addr().unwrap(); @@ -288,15 +288,12 @@ where lst, move || { let c = cfg.lock(); - pipeline_factory(acceptor.clone().map_err(SslError::Ssl)).and_then( - HttpService::build() - .keep_alive(c.keep_alive) - .client_timeout(c.client_timeout) - .client_disconnect(c.client_shutdown) - .finish(factory()) - .map_err(SslError::Service) - .map_init_err(|_| ()), - ) + HttpService::build() + .keep_alive(c.keep_alive) + .client_timeout(c.client_timeout) + .client_disconnect(c.client_shutdown) + .finish(factory()) + .openssl(acceptor.clone()) }, )?; Ok(self) @@ -444,6 +441,8 @@ where mut self, lst: std::os::unix::net::UnixListener, ) -> io::Result { + use actix_rt::net::UnixStream; + let cfg = self.config.clone(); let factory = self.factory.clone(); // todo duplicated: @@ -459,10 +458,12 @@ where self.builder = self.builder.listen_uds(addr, lst, move || { let c = cfg.lock(); - HttpService::build() - .keep_alive(c.keep_alive) - .client_timeout(c.client_timeout) - .finish(factory()) + pipeline_factory(|io: UnixStream| ok((io, Protocol::Http1, None))).and_then( + HttpService::build() + .keep_alive(c.keep_alive) + .client_timeout(c.client_timeout) + .finish(factory()), + ) })?; Ok(self) } @@ -475,6 +476,8 @@ where where A: AsRef, { + use actix_rt::net::UnixStream; + let cfg = self.config.clone(); let factory = self.factory.clone(); self.sockets.push(Socket { @@ -490,10 +493,13 @@ where addr, move || { let c = cfg.lock(); - HttpService::build() - .keep_alive(c.keep_alive) - .client_timeout(c.client_timeout) - .finish(factory()) + pipeline_factory(|io: UnixStream| ok((io, Protocol::Http1, None))) + .and_then( + HttpService::build() + .keep_alive(c.keep_alive) + .client_timeout(c.client_timeout) + .finish(factory()), + ) }, )?; Ok(self) @@ -504,7 +510,7 @@ impl HttpServer where F: Fn() -> I + Send + Clone + 'static, I: IntoServiceFactory, - S: ServiceFactory, + S: ServiceFactory, S::Error: Into, S::InitError: fmt::Debug, S::Response: Into>, @@ -585,8 +591,11 @@ fn openssl_acceptor(mut builder: SslAcceptorBuilder) -> io::Result builder.set_alpn_select_callback(|_, protos| { const H2: &[u8] = b"\x02h2"; + const H11: &[u8] = b"\x08http/1.1"; if protos.windows(3).any(|window| window == H2) { Ok(b"h2") + } else if protos.windows(9).any(|window| window == H11) { + Ok(b"http/1.1") } else { Err(AlpnError::NOACK) } diff --git a/src/test.rs b/src/test.rs index e19393156..9ded3f9f8 100644 --- a/src/test.rs +++ b/src/test.rs @@ -6,7 +6,6 @@ use actix_http::http::{HttpTryFrom, Method, StatusCode, Uri, Version}; use actix_http::test::TestRequest as HttpTestRequest; use actix_http::{cookie::Cookie, Extensions, Request}; use actix_router::{Path, ResourceDef, Url}; -use actix_server_config::ServerConfig; use actix_service::{IntoService, IntoServiceFactory, Service, ServiceFactory}; use bytes::{Bytes, BytesMut}; use futures::future::ok; @@ -71,16 +70,15 @@ pub async fn init_service( where R: IntoServiceFactory, S: ServiceFactory< - Config = ServerConfig, + Config = (), Request = Request, Response = ServiceResponse, Error = E, >, S::InitError: std::fmt::Debug, { - let cfg = ServerConfig::new("127.0.0.1:8080".parse().unwrap()); let srv = app.into_factory(); - srv.new_service(&cfg).await.unwrap() + srv.new_service(&()).await.unwrap() } /// Calls service and waits for response future completion. diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index e59e439fe..bb7a10040 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -27,7 +27,7 @@ path = "src/lib.rs" default = [] # openssl -openssl = ["open-ssl", "actix-server/openssl", "awc/openssl"] +openssl = ["open-ssl", "awc/openssl", ] # "actix-tls/openssl"] [dependencies] actix-service = "1.0.0-alpha.1" @@ -36,7 +36,6 @@ actix-connect = "1.0.0-alpha.1" actix-utils = "0.5.0-alpha.1" actix-rt = "1.0.0-alpha.1" actix-server = "0.8.0-alpha.1" -actix-server-config = "0.3.0-alpha.1" actix-testing = "0.3.0-alpha.1" awc = "0.3.0-alpha.1" diff --git a/tests/test_server.rs b/tests/test_server.rs index bfdf3f0ee..7cfda04ad 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -4,7 +4,7 @@ use actix_http::http::header::{ ContentEncoding, ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_LENGTH, TRANSFER_ENCODING, }; -use actix_http::{h1, Error, HttpService, Response}; +use actix_http::{Error, HttpService, Response}; use actix_http_test::TestServer; use brotli2::write::{BrotliDecoder, BrotliEncoder}; use bytes::Bytes; @@ -42,10 +42,10 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[actix_rt::test] async fn test_body() { let srv = TestServer::start(|| { - h1::H1Service::new( - App::new() - .service(web::resource("/").route(web::to(|| Response::Ok().body(STR)))), - ) + HttpService::build() + .h1(App::new() + .service(web::resource("/").route(web::to(|| Response::Ok().body(STR))))) + .tcp() }); let mut response = srv.get("/").send().await.unwrap(); @@ -60,11 +60,11 @@ async fn test_body() { #[actix_rt::test] async fn test_body_gzip() { let srv = TestServer::start(|| { - h1::H1Service::new( - App::new() + HttpService::build() + .h1(App::new() .wrap(Compress::new(ContentEncoding::Gzip)) - .service(web::resource("/").route(web::to(|| Response::Ok().body(STR)))), - ) + .service(web::resource("/").route(web::to(|| Response::Ok().body(STR))))) + .tcp() }); let mut response = srv @@ -90,13 +90,13 @@ async fn test_body_gzip() { #[actix_rt::test] async fn test_body_gzip2() { let srv = TestServer::start(|| { - h1::H1Service::new( - App::new() + HttpService::build() + .h1(App::new() .wrap(Compress::new(ContentEncoding::Gzip)) .service(web::resource("/").route(web::to(|| { Response::Ok().body(STR).into_body::() - }))), - ) + })))) + .tcp() }); let mut response = srv @@ -122,8 +122,8 @@ async fn test_body_gzip2() { #[actix_rt::test] async fn test_body_encoding_override() { let srv = TestServer::start(|| { - h1::H1Service::new( - App::new() + HttpService::build() + .h1(App::new() .wrap(Compress::new(ContentEncoding::Gzip)) .service(web::resource("/").route(web::to(|| { Response::Ok().encoding(ContentEncoding::Deflate).body(STR) @@ -136,8 +136,8 @@ async fn test_body_encoding_override() { response.encoding(ContentEncoding::Deflate); response - }))), - ) + })))) + .tcp() }); // Builder @@ -187,14 +187,14 @@ async fn test_body_gzip_large() { let srv = TestServer::start(move || { let data = srv_data.clone(); - h1::H1Service::new( - App::new() + HttpService::build() + .h1(App::new() .wrap(Compress::new(ContentEncoding::Gzip)) .service( web::resource("/") .route(web::to(move || Response::Ok().body(data.clone()))), - ), - ) + )) + .tcp() }); let mut response = srv @@ -227,14 +227,14 @@ async fn test_body_gzip_large_random() { let srv = TestServer::start(move || { let data = srv_data.clone(); - h1::H1Service::new( - App::new() + HttpService::build() + .h1(App::new() .wrap(Compress::new(ContentEncoding::Gzip)) .service( web::resource("/") .route(web::to(move || Response::Ok().body(data.clone()))), - ), - ) + )) + .tcp() }); let mut response = srv @@ -261,15 +261,15 @@ async fn test_body_gzip_large_random() { #[actix_rt::test] async fn test_body_chunked_implicit() { let srv = TestServer::start(move || { - h1::H1Service::new( - App::new() + HttpService::build() + .h1(App::new() .wrap(Compress::new(ContentEncoding::Gzip)) .service(web::resource("/").route(web::get().to(move || { Response::Ok().streaming(once(ok::<_, Error>(Bytes::from_static( STR.as_ref(), )))) - }))), - ) + })))) + .tcp() }); let mut response = srv @@ -299,12 +299,15 @@ async fn test_body_chunked_implicit() { #[cfg(feature = "brotli")] async fn test_body_br_streaming() { let srv = TestServer::start(move || { - h1::H1Service::new(App::new().wrap(Compress::new(ContentEncoding::Br)).service( - web::resource("/").route(web::to(move || { - Response::Ok() - .streaming(once(ok::<_, Error>(Bytes::from_static(STR.as_ref())))) - })), - )) + HttpService::build() + .h1(App::new().wrap(Compress::new(ContentEncoding::Br)).service( + web::resource("/").route(web::to(move || { + Response::Ok().streaming(once(ok::<_, Error>(Bytes::from_static( + STR.as_ref(), + )))) + })), + )) + .tcp() }); let mut response = srv @@ -329,9 +332,11 @@ async fn test_body_br_streaming() { #[actix_rt::test] async fn test_head_binary() { let srv = TestServer::start(move || { - h1::H1Service::new(App::new().service(web::resource("/").route( - web::head().to(move || Response::Ok().content_length(100).body(STR)), - ))) + HttpService::build() + .h1(App::new().service(web::resource("/").route( + web::head().to(move || Response::Ok().content_length(100).body(STR)), + ))) + .tcp() }); let mut response = srv.head("/").send().await.unwrap(); @@ -350,14 +355,18 @@ async fn test_head_binary() { #[actix_rt::test] async fn test_no_chunking() { let srv = TestServer::start(move || { - h1::H1Service::new(App::new().service(web::resource("/").route(web::to( - move || { - Response::Ok() - .no_chunking() - .content_length(STR.len() as u64) - .streaming(once(ok::<_, Error>(Bytes::from_static(STR.as_ref())))) - }, - )))) + HttpService::build() + .h1( + App::new().service(web::resource("/").route(web::to(move || { + Response::Ok() + .no_chunking() + .content_length(STR.len() as u64) + .streaming(once(ok::<_, Error>(Bytes::from_static( + STR.as_ref(), + )))) + }))), + ) + .tcp() }); let mut response = srv.get("/").send().await.unwrap(); @@ -373,13 +382,13 @@ async fn test_no_chunking() { #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] async fn test_body_deflate() { let srv = TestServer::start(move || { - h1::H1Service::new( - App::new() + HttpService::build() + .h1(App::new() .wrap(Compress::new(ContentEncoding::Deflate)) .service( web::resource("/").route(web::to(move || Response::Ok().body(STR))), - ), - ) + )) + .tcp() }); // client request @@ -405,9 +414,11 @@ async fn test_body_deflate() { #[cfg(any(feature = "brotli"))] async fn test_body_brotli() { let srv = TestServer::start(move || { - h1::H1Service::new(App::new().wrap(Compress::new(ContentEncoding::Br)).service( - web::resource("/").route(web::to(move || Response::Ok().body(STR))), - )) + HttpService::build() + .h1(App::new().wrap(Compress::new(ContentEncoding::Br)).service( + web::resource("/").route(web::to(move || Response::Ok().body(STR))), + )) + .tcp() }); // client request @@ -434,12 +445,12 @@ async fn test_body_brotli() { #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] async fn test_encoding() { let srv = TestServer::start(move || { - HttpService::new( - App::new().wrap(Compress::default()).service( + HttpService::build() + .h1(App::new().wrap(Compress::default()).service( web::resource("/") .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) + )) + .tcp() }); // client request @@ -463,12 +474,12 @@ async fn test_encoding() { #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] async fn test_gzip_encoding() { let srv = TestServer::start(move || { - HttpService::new( - App::new().service( + HttpService::build() + .h1(App::new().service( web::resource("/") .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) + )) + .tcp() }); // client request @@ -493,12 +504,12 @@ async fn test_gzip_encoding() { async fn test_gzip_encoding_large() { let data = STR.repeat(10); let srv = TestServer::start(move || { - h1::H1Service::new( - App::new().service( + HttpService::build() + .h1(App::new().service( web::resource("/") .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) + )) + .tcp() }); // client request @@ -527,12 +538,12 @@ async fn test_reading_gzip_encoding_large_random() { .collect::(); let srv = TestServer::start(move || { - HttpService::new( - App::new().service( + HttpService::build() + .h1(App::new().service( web::resource("/") .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) + )) + .tcp() }); // client request @@ -557,12 +568,12 @@ async fn test_reading_gzip_encoding_large_random() { #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] async fn test_reading_deflate_encoding() { let srv = TestServer::start(move || { - h1::H1Service::new( - App::new().service( + HttpService::build() + .h1(App::new().service( web::resource("/") .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) + )) + .tcp() }); let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); @@ -587,12 +598,12 @@ async fn test_reading_deflate_encoding() { async fn test_reading_deflate_encoding_large() { let data = STR.repeat(10); let srv = TestServer::start(move || { - h1::H1Service::new( - App::new().service( + HttpService::build() + .h1(App::new().service( web::resource("/") .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) + )) + .tcp() }); let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); @@ -621,12 +632,12 @@ async fn test_reading_deflate_encoding_large_random() { .collect::(); let srv = TestServer::start(move || { - h1::H1Service::new( - App::new().service( + HttpService::build() + .h1(App::new().service( web::resource("/") .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) + )) + .tcp() }); let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); @@ -651,12 +662,12 @@ async fn test_reading_deflate_encoding_large_random() { #[cfg(feature = "brotli")] async fn test_brotli_encoding() { let srv = TestServer::start(move || { - h1::H1Service::new( - App::new().service( + HttpService::build() + .h1(App::new().service( web::resource("/") .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) + )) + .tcp() }); let mut e = BrotliEncoder::new(Vec::new(), 5); @@ -681,12 +692,12 @@ async fn test_brotli_encoding() { async fn test_brotli_encoding_large() { let data = STR.repeat(10); let srv = TestServer::start(move || { - h1::H1Service::new( - App::new().service( + HttpService::build() + .h1(App::new().service( web::resource("/") .route(web::to(move |body: Bytes| Response::Ok().body(body))), - ), - ) + )) + .tcp() }); let mut e = BrotliEncoder::new(Vec::new(), 5); From 068f047dd5244981e962a4d551e152477e57c581 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Mon, 2 Dec 2019 21:37:13 +0600 Subject: [PATCH 079/176] update service factory config --- Cargo.toml | 2 +- actix-files/src/lib.rs | 6 +++--- actix-framed/src/app.rs | 4 ++-- actix-framed/src/helpers.rs | 4 ++-- actix-framed/src/route.rs | 2 +- actix-framed/src/service.rs | 4 ++-- actix-http/src/h1/expect.rs | 2 +- actix-http/src/h1/service.rs | 10 +++++----- actix-http/src/h1/upgrade.rs | 2 +- actix-http/src/h2/service.rs | 4 ++-- actix-http/src/service.rs | 8 ++++---- src/app_service.rs | 14 +++++++------- src/handler.rs | 2 +- src/resource.rs | 10 +++++----- src/route.rs | 8 ++++---- src/scope.rs | 10 +++++----- src/test.rs | 2 +- 17 files changed, 47 insertions(+), 47 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 441291d3b..e04b2974b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -139,4 +139,4 @@ actix-server = { git = "https://github.com/actix/actix-net.git" } actix-service = { git = "https://github.com/actix/actix-net.git" } actix-testing = { git = "https://github.com/actix/actix-net.git" } actix-tls = { git = "https://github.com/actix/actix-net.git" } -actix-utils = { git = "https://github.com/actix/actix-net.git" } +actix-utils = { git = "https://github.com/actix/actix-net.git" } diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index ed8b6c3b9..94bb0e376 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -415,7 +415,7 @@ impl ServiceFactory for Files { type InitError = (); type Future = LocalBoxFuture<'static, Result>; - fn new_service(&self, _: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { let mut srv = FilesService { directory: self.directory.clone(), index: self.index.clone(), @@ -430,7 +430,7 @@ impl ServiceFactory for Files { if let Some(ref default) = *self.default.borrow() { default - .new_service(&()) + .new_service(()) .map(move |result| match result { Ok(default) => { srv.default = Some(default); @@ -1262,7 +1262,7 @@ mod tests { .default_handler(|req: ServiceRequest| { ok(req.into_response(HttpResponse::Ok().body("default content"))) }) - .new_service(&()) + .new_service(()) .await .unwrap(); let req = TestRequest::with_uri("/missing").to_srv_request(); diff --git a/actix-framed/src/app.rs b/actix-framed/src/app.rs index 2f8c800f8..e4b91e6c4 100644 --- a/actix-framed/src/app.rs +++ b/actix-framed/src/app.rs @@ -104,7 +104,7 @@ where type Service = FramedAppService; type Future = CreateService; - fn new_service(&self, _: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { CreateService { fut: self .services @@ -112,7 +112,7 @@ where .map(|(path, service)| { CreateServiceItem::Future( Some(path.clone()), - service.new_service(&()), + service.new_service(()), ) }) .collect(), diff --git a/actix-framed/src/helpers.rs b/actix-framed/src/helpers.rs index b654f9cd7..d08ca25ac 100644 --- a/actix-framed/src/helpers.rs +++ b/actix-framed/src/helpers.rs @@ -56,8 +56,8 @@ where type Service = BoxedHttpService; type Future = LocalBoxFuture<'static, Result>; - fn new_service(&self, _: &()) -> Self::Future { - let fut = self.0.new_service(&()); + fn new_service(&self, _: ()) -> Self::Future { + let fut = self.0.new_service(()); async move { fut.await.map_err(|_| ()).map(|service| { diff --git a/actix-framed/src/route.rs b/actix-framed/src/route.rs index 783039684..03e48e4d2 100644 --- a/actix-framed/src/route.rs +++ b/actix-framed/src/route.rs @@ -113,7 +113,7 @@ where type Service = FramedRouteService; type Future = Ready>; - fn new_service(&self, _: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { ok(FramedRouteService { handler: self.handler.clone(), methods: self.methods.clone(), diff --git a/actix-framed/src/service.rs b/actix-framed/src/service.rs index ed3a75ff5..92393ca75 100644 --- a/actix-framed/src/service.rs +++ b/actix-framed/src/service.rs @@ -33,7 +33,7 @@ impl ServiceFactory for VerifyWebSockets { type Service = VerifyWebSockets; type Future = Ready>; - fn new_service(&self, _: &C) -> Self::Future { + fn new_service(&self, _: C) -> Self::Future { ok(VerifyWebSockets { _t: PhantomData }) } } @@ -83,7 +83,7 @@ where type Service = SendError; type Future = Ready>; - fn new_service(&self, _: &C) -> Self::Future { + fn new_service(&self, _: C) -> Self::Future { ok(SendError(PhantomData)) } } diff --git a/actix-http/src/h1/expect.rs b/actix-http/src/h1/expect.rs index 576b7672e..109491bac 100644 --- a/actix-http/src/h1/expect.rs +++ b/actix-http/src/h1/expect.rs @@ -17,7 +17,7 @@ impl ServiceFactory for ExpectHandler { type InitError = Error; type Future = Ready>; - fn new_service(&self, _: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { ok(ExpectHandler) } } diff --git a/actix-http/src/h1/service.rs b/actix-http/src/h1/service.rs index abc96e719..354fed482 100644 --- a/actix-http/src/h1/service.rs +++ b/actix-http/src/h1/service.rs @@ -217,11 +217,11 @@ where type Service = H1ServiceHandler; type Future = H1ServiceResponse; - fn new_service(&self, cfg: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { H1ServiceResponse { - fut: self.srv.new_service(cfg), - fut_ex: Some(self.expect.new_service(cfg)), - fut_upg: self.upgrade.as_ref().map(|f| f.new_service(cfg)), + fut: self.srv.new_service(()), + fut_ex: Some(self.expect.new_service(())), + fut_upg: self.upgrade.as_ref().map(|f| f.new_service(())), expect: None, upgrade: None, on_connect: self.on_connect.clone(), @@ -450,7 +450,7 @@ where type Service = OneRequestService; type Future = Ready>; - fn new_service(&self, _: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { ok(OneRequestService { _t: PhantomData, config: self.config.clone(), diff --git a/actix-http/src/h1/upgrade.rs b/actix-http/src/h1/upgrade.rs index e84230ac7..e3ce66521 100644 --- a/actix-http/src/h1/upgrade.rs +++ b/actix-http/src/h1/upgrade.rs @@ -20,7 +20,7 @@ impl ServiceFactory for UpgradeHandler { type InitError = Error; type Future = Ready>; - fn new_service(&self, _: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { unimplemented!() } } diff --git a/actix-http/src/h2/service.rs b/actix-http/src/h2/service.rs index a2c8275a1..864070eb3 100644 --- a/actix-http/src/h2/service.rs +++ b/actix-http/src/h2/service.rs @@ -160,9 +160,9 @@ where type Service = H2ServiceHandler; type Future = H2ServiceResponse; - fn new_service(&self, cfg: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { H2ServiceResponse { - fut: self.srv.new_service(cfg), + fut: self.srv.new_service(()), cfg: Some(self.cfg.clone()), on_connect: self.on_connect.clone(), _t: PhantomData, diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index 8220421ab..de5fd0c55 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -282,11 +282,11 @@ where type Service = HttpServiceHandler; type Future = HttpServiceResponse; - fn new_service(&self, cfg: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { HttpServiceResponse { - fut: self.srv.new_service(cfg), - fut_ex: Some(self.expect.new_service(cfg)), - fut_upg: self.upgrade.as_ref().map(|f| f.new_service(cfg)), + fut: self.srv.new_service(()), + fut_ex: Some(self.expect.new_service(())), + fut_upg: self.upgrade.as_ref().map(|f| f.new_service(())), expect: None, upgrade: None, on_connect: self.on_connect.clone(), diff --git a/src/app_service.rs b/src/app_service.rs index 6c18cd542..6a91fa079 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -66,7 +66,7 @@ where type Service = AppInitService; type Future = AppInitResult; - fn new_service(&self, _: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { // update resource default service let default = self.default.clone().unwrap_or_else(|| { Rc::new(boxed::factory(service_fn(|req: ServiceRequest| { @@ -115,7 +115,7 @@ where AppInitResult { endpoint: None, - endpoint_fut: self.endpoint.new_service(&()), + endpoint_fut: self.endpoint.new_service(()), data: self.data.clone(), data_factories: Vec::new(), data_factories_fut: self.data_factories.iter().map(|f| f()).collect(), @@ -273,7 +273,7 @@ impl ServiceFactory for AppRoutingFactory { type Service = AppRouting; type Future = AppRoutingFactoryResponse; - fn new_service(&self, _: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { AppRoutingFactoryResponse { fut: self .services @@ -282,12 +282,12 @@ impl ServiceFactory for AppRoutingFactory { CreateAppRoutingItem::Future( Some(path.clone()), guards.borrow_mut().take(), - service.new_service(&()).boxed_local(), + service.new_service(()).boxed_local(), ) }) .collect(), default: None, - default_fut: Some(self.default.new_service(&())), + default_fut: Some(self.default.new_service(())), } } } @@ -432,8 +432,8 @@ impl ServiceFactory for AppEntry { type Service = AppRouting; type Future = AppRoutingFactoryResponse; - fn new_service(&self, _: &()) -> Self::Future { - self.factory.borrow_mut().as_mut().unwrap().new_service(&()) + fn new_service(&self, _: ()) -> Self::Future { + self.factory.borrow_mut().as_mut().unwrap().new_service(()) } } diff --git a/src/handler.rs b/src/handler.rs index a7023422b..d1b070d88 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -177,7 +177,7 @@ where type Service = ExtractService; type Future = Ready>; - fn new_service(&self, _: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { ok(ExtractService { _t: PhantomData, service: self.service.clone(), diff --git a/src/resource.rs b/src/resource.rs index 866cbecf5..7ee0506a3 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -435,9 +435,9 @@ impl ServiceFactory for ResourceFactory { type Service = ResourceService; type Future = CreateResourceService; - fn new_service(&self, _: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { let default_fut = if let Some(ref default) = *self.default.borrow() { - Some(default.new_service(&())) + Some(default.new_service(())) } else { None }; @@ -446,7 +446,7 @@ impl ServiceFactory for ResourceFactory { fut: self .routes .iter() - .map(|route| CreateRouteServiceItem::Future(route.new_service(&()))) + .map(|route| CreateRouteServiceItem::Future(route.new_service(()))) .collect(), data: self.data.clone(), default: None, @@ -575,8 +575,8 @@ impl ServiceFactory for ResourceEndpoint { type Service = ResourceService; type Future = CreateResourceService; - fn new_service(&self, _: &()) -> Self::Future { - self.factory.borrow_mut().as_mut().unwrap().new_service(&()) + fn new_service(&self, _: ()) -> Self::Future { + self.factory.borrow_mut().as_mut().unwrap().new_service(()) } } diff --git a/src/route.rs b/src/route.rs index 93f88bfe2..2c643099b 100644 --- a/src/route.rs +++ b/src/route.rs @@ -69,9 +69,9 @@ impl ServiceFactory for Route { type Service = RouteService; type Future = CreateRouteService; - fn new_service(&self, _: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { CreateRouteService { - fut: self.service.new_service(&()), + fut: self.service.new_service(()), guards: self.guards.clone(), } } @@ -280,9 +280,9 @@ where type Service = BoxedRouteService; type Future = LocalBoxFuture<'static, Result>; - fn new_service(&self, _: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { self.service - .new_service(&()) + .new_service(()) .map(|result| match result { Ok(service) => { let service: BoxedRouteService<_, _> = diff --git a/src/scope.rs b/src/scope.rs index db6f5da57..d6b88577b 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -488,9 +488,9 @@ impl ServiceFactory for ScopeFactory { type Service = ScopeService; type Future = ScopeFactoryResponse; - fn new_service(&self, _: &()) -> Self::Future { + fn new_service(&self, _: ()) -> Self::Future { let default_fut = if let Some(ref default) = *self.default.borrow() { - Some(default.new_service(&())) + Some(default.new_service(())) } else { None }; @@ -503,7 +503,7 @@ impl ServiceFactory for ScopeFactory { CreateScopeServiceItem::Future( Some(path.clone()), guards.borrow_mut().take(), - service.new_service(&()), + service.new_service(()), ) }) .collect(), @@ -656,8 +656,8 @@ impl ServiceFactory for ScopeEndpoint { type Service = ScopeService; type Future = ScopeFactoryResponse; - fn new_service(&self, _: &()) -> Self::Future { - self.factory.borrow_mut().as_mut().unwrap().new_service(&()) + fn new_service(&self, _: ()) -> Self::Future { + self.factory.borrow_mut().as_mut().unwrap().new_service(()) } } diff --git a/src/test.rs b/src/test.rs index 9ded3f9f8..a33374800 100644 --- a/src/test.rs +++ b/src/test.rs @@ -78,7 +78,7 @@ where S::InitError: std::fmt::Debug, { let srv = app.into_factory(); - srv.new_service(&()).await.unwrap() + srv.new_service(()).await.unwrap() } /// Calls service and waits for response future completion. From 14075ebf7fc73e92473d97085522d37b9bb82b61 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Mon, 2 Dec 2019 23:33:39 +0600 Subject: [PATCH 080/176] use released versions of actix-net --- Cargo.toml | 49 +++++++++++++++--------------- actix-cors/Cargo.toml | 4 +-- actix-files/Cargo.toml | 4 +-- actix-files/src/lib.rs | 21 +++++++------ actix-framed/Cargo.toml | 16 +++++----- actix-http/Cargo.toml | 31 +++++++++---------- actix-http/src/client/connector.rs | 2 +- actix-http/src/encoding/decoder.rs | 5 ++- actix-http/src/encoding/encoder.rs | 5 ++- actix-http/src/error.rs | 19 ++++++++---- actix-identity/Cargo.toml | 8 ++--- actix-multipart/Cargo.toml | 10 +++--- actix-session/Cargo.toml | 8 ++--- actix-web-codegen/Cargo.toml | 10 +++--- awc/Cargo.toml | 22 +++++++------- src/test.rs | 2 +- src/web.rs | 10 +++--- test-server/Cargo.toml | 25 ++++++++------- test-server/src/lib.rs | 3 +- 19 files changed, 128 insertions(+), 126 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e04b2974b..72efbde68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "2.0.0-alpha.1" +version = "2.0.0-alpha.2" authors = ["Nikolay Kim "] description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." readme = "README.md" @@ -69,19 +69,18 @@ openssl = ["open-ssl", "actix-tls/openssl", "awc/openssl"] # rustls = ["rust-tls", "actix-server/rustls", "awc/rustls"] [dependencies] -actix-codec = "0.2.0-alpha.1" -actix-service = "1.0.0-alpha.1" -actix-utils = "0.5.0-alpha.1" +actix-codec = "0.2.0-alpha.2" +actix-service = "1.0.0-alpha.2" +actix-utils = "1.0.0-alpha.2" actix-router = "0.1.5" -actix-rt = "1.0.0-alpha.1" -actix-web-codegen = "0.2.0-alpha.1" -actix-http = "0.3.0-alpha.1" -actix-server = "0.8.0-alpha.2" -actix-testing = "0.3.0-alpha.1" -actix-threadpool = "0.2.0-alpha.1" -#actix-tls = "0.1.0-alpha.1" -actix-tls = { git = "https://github.com/actix/actix-net.git", optional = true } -awc = { version = "0.3.0-alpha.1", optional = true } +actix-rt = "1.0.0-alpha.2" +actix-web-codegen = "0.2.0-alpha.2" +actix-http = "0.3.0-alpha.2" +actix-server = "1.0.0-alpha.2" +actix-testing = "1.0.0-alpha.2" +actix-threadpool = "0.3.0" +actix-tls = { version = "1.0.0-alpha.1", optional = true } +awc = { version = "0.3.0-alpha.2", optional = true } bytes = "0.4" derive_more = "0.99.2" @@ -92,7 +91,7 @@ log = "0.4" mime = "0.3" net2 = "0.2.33" parking_lot = "0.9" -pin-project = "0.4.5" +pin-project = "0.4.6" regex = "1.0" serde = { version = "1.0", features=["derive"] } serde_json = "1.0" @@ -106,8 +105,8 @@ open-ssl = { version="0.10", package="openssl", optional = true } [dev-dependencies] # actix = "0.8.3" -actix-connect = "0.3.0-alpha.1" -actix-http-test = "0.3.0-alpha.1" +actix-connect = "1.0.0-alpha.2" +actix-http-test = "0.3.0-alpha.2" rand = "0.7" env_logger = "0.6" serde_derive = "1.0" @@ -131,12 +130,12 @@ actix-files = { path = "actix-files" } actix-multipart = { path = "actix-multipart" } awc = { path = "awc" } -actix-codec = { git = "https://github.com/actix/actix-net.git" } -actix-connect = { git = "https://github.com/actix/actix-net.git" } -actix-rt = { git = "https://github.com/actix/actix-net.git" } -actix-macros = { git = "https://github.com/actix/actix-net.git" } -actix-server = { git = "https://github.com/actix/actix-net.git" } -actix-service = { git = "https://github.com/actix/actix-net.git" } -actix-testing = { git = "https://github.com/actix/actix-net.git" } -actix-tls = { git = "https://github.com/actix/actix-net.git" } -actix-utils = { git = "https://github.com/actix/actix-net.git" } +# actix-codec = { git = "https://github.com/actix/actix-net.git" } +# actix-connect = { git = "https://github.com/actix/actix-net.git" } +# actix-rt = { git = "https://github.com/actix/actix-net.git" } +# actix-macros = { git = "https://github.com/actix/actix-net.git" } +# actix-server = { git = "https://github.com/actix/actix-net.git" } +# actix-service = { git = "https://github.com/actix/actix-net.git" } +# actix-testing = { git = "https://github.com/actix/actix-net.git" } +# actix-tls = { git = "https://github.com/actix/actix-net.git" } +# actix-utils = { git = "https://github.com/actix/actix-net.git" } diff --git a/actix-cors/Cargo.toml b/actix-cors/Cargo.toml index ddb5f307e..bc8c7d805 100644 --- a/actix-cors/Cargo.toml +++ b/actix-cors/Cargo.toml @@ -18,9 +18,9 @@ path = "src/lib.rs" [dependencies] actix-web = "2.0.0-alpha.1" -actix-service = "1.0.0-alpha.1" +actix-service = "1.0.0-alpha.2" derive_more = "0.99.2" futures = "0.3.1" [dev-dependencies] -actix-rt = "1.0.0-alpha.1" +actix-rt = "1.0.0-alpha.2" diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 19366b902..e4cb12deb 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -20,7 +20,7 @@ path = "src/lib.rs" [dependencies] actix-web = { version = "2.0.0-alpha.1", default-features = false } actix-http = "0.3.0-alpha.1" -actix-service = "1.0.0-alpha.1" +actix-service = "1.0.0-alpha.2" bitflags = "1" bytes = "0.4" futures = "0.3.1" @@ -32,5 +32,5 @@ percent-encoding = "2.1" v_htmlescape = "0.4" [dev-dependencies] -actix-rt = "1.0.0-alpha.1" +actix-rt = "1.0.0-alpha.2" actix-web = { version = "2.0.0-alpha.1", features=["openssl"] } diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index 94bb0e376..c33367d71 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -18,7 +18,7 @@ use actix_web::dev::{ AppService, HttpServiceFactory, Payload, ResourceDef, ServiceRequest, ServiceResponse, }; -use actix_web::error::{Canceled, Error, ErrorInternalServerError}; +use actix_web::error::{BlockingError, Error, ErrorInternalServerError}; use actix_web::guard::Guard; use actix_web::http::header::{self, DispositionType}; use actix_web::http::Method; @@ -50,6 +50,12 @@ pub fn file_extension_to_mime(ext: &str) -> mime::Mime { from_ext(ext).first_or_octet_stream() } +fn handle_error(err: BlockingError) -> Error { + match err { + BlockingError::Error(err) => err.into(), + BlockingError::Canceled => ErrorInternalServerError("Unexpected error"), + } +} #[doc(hidden)] /// A helper created from a `std::fs::File` which reads the file /// chunk-by-chunk on a `ThreadPool`. @@ -57,9 +63,8 @@ pub struct ChunkedReadFile { size: u64, offset: u64, file: Option, - fut: Option< - LocalBoxFuture<'static, Result, Canceled>>, - >, + fut: + Option>>>, counter: u64, } @@ -72,18 +77,14 @@ impl Stream for ChunkedReadFile { ) -> Poll> { if let Some(ref mut fut) = self.fut { return match Pin::new(fut).poll(cx) { - Poll::Ready(Err(_)) => Poll::Ready(Some(Err(ErrorInternalServerError( - "Unexpected error", - ) - .into()))), - Poll::Ready(Ok(Ok((file, bytes)))) => { + Poll::Ready(Ok((file, bytes))) => { self.fut.take(); self.file = Some(file); self.offset += bytes.len() as u64; self.counter += bytes.len() as u64; Poll::Ready(Some(Ok(bytes))) } - Poll::Ready(Ok(Err(e))) => Poll::Ready(Some(Err(e.into()))), + Poll::Ready(Err(e)) => Poll::Ready(Some(Err(handle_error(e)))), Poll::Pending => Poll::Pending, }; } diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml index 24ca6400d..55bc1a24c 100644 --- a/actix-framed/Cargo.toml +++ b/actix-framed/Cargo.toml @@ -20,11 +20,11 @@ name = "actix_framed" path = "src/lib.rs" [dependencies] -actix-codec = "0.2.0-alpha.1" -actix-service = "1.0.0-alpha.1" +actix-codec = "0.2.0-alpha.2" +actix-service = "1.0.0-alpha.2" actix-router = "0.1.2" -actix-rt = "1.0.0-alpha.1" -actix-http = "0.3.0-alpha.1" +actix-rt = "1.0.0-alpha.2" +actix-http = "0.3.0-alpha.2" bytes = "0.4" futures = "0.3.1" @@ -32,7 +32,7 @@ pin-project = "0.4.6" log = "0.4" [dev-dependencies] -actix-server = { version = "0.8.0-alpha.1" } -actix-connect = { version = "0.3.0-alpha.1", features=["openssl"] } -actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } -actix-utils = "0.5.0-alpha.1" +actix-server = { version = "1.0.0-alpha.2" } +actix-connect = { version = "1.0.0-alpha.2", features=["openssl"] } +actix-http-test = { version = "0.3.0-alpha.2", features=["openssl"] } +actix-utils = "1.0.0-alpha.2" diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index ff47abd1e..54e83fbc5 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "0.3.0-alpha.1" +version = "0.3.0-alpha.2" authors = ["Nikolay Kim "] description = "Actix http primitives" readme = "README.md" @@ -47,15 +47,15 @@ fail = ["failure"] secure-cookies = ["ring"] [dependencies] -actix-service = "1.0.0-alpha.1" -actix-codec = "0.2.0-alpha.1" -actix-connect = "1.0.0-alpha.1" -actix-utils = "0.5.0-alpha.1" -actix-rt = "1.0.0-alpha.1" -actix-threadpool = "0.2.0-alpha.1" -actix-tls = { git = "https://github.com/actix/actix-net.git", optional = true } +actix-service = "1.0.0-alpha.2" +actix-codec = "0.2.0-alpha.2" +actix-connect = "1.0.0-alpha.2" +actix-utils = "1.0.0-alpha.2" +actix-rt = "1.0.0-alpha.2" +actix-threadpool = "0.3.0" +actix-tls = { version = "1.0.0-alpha.1", optional = true } -base64 = "0.10" +base64 = "0.11" bitflags = "1.0" bytes = "0.4" copyless = "0.1.4" @@ -74,7 +74,7 @@ language-tags = "0.2" log = "0.4" mime = "0.3" percent-encoding = "2.1" -pin-project = "0.4.5" +pin-project = "0.4.6" rand = "0.7" regex = "1.0" serde = "1.0" @@ -83,8 +83,6 @@ sha1 = "0.6" slab = "0.4" serde_urlencoded = "0.6.1" time = "0.1.42" - -tokio-net = "=0.2.0-alpha.6" trust-dns-resolver = { version="0.18.0-alpha.1", default-features = false } # for secure cookie @@ -103,11 +101,10 @@ tokio-openssl = { version = "0.4.0-alpha.6", optional = true } # webpki-roots = { version = "0.18", optional = true } [dev-dependencies] -actix-server = { version = "0.8.0-alpha.1" } -actix-connect = { version = "1.0.0-alpha.1", features=["openssl"] } -actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } -#actix-tls = { version = "0.1.0-alpha.1", features=["openssl"] } -actix-tls = { git = "https://github.com/actix/actix-net.git", features=["openssl"] } +actix-server = { version = "1.0.0-alpha.2" } +actix-connect = { version = "1.0.0-alpha.2", features=["openssl"] } +actix-http-test = { version = "0.3.0-alpha.2", features=["openssl"] } +actix-tls = { version = "1.0.0-alpha.1", features=["openssl"] } env_logger = "0.6" serde_derive = "1.0" open-ssl = { version="0.10", package="openssl" } diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index eaa3d97e4..fb43733cf 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -6,10 +6,10 @@ use actix_codec::{AsyncRead, AsyncWrite}; use actix_connect::{ default_connector, Connect as TcpConnect, Connection as TcpConnection, }; +use actix_rt::net::TcpStream; use actix_service::{apply_fn, Service}; use actix_utils::timeout::{TimeoutError, TimeoutService}; use http::Uri; -use tokio_net::tcp::TcpStream; use super::connection::Connection; use super::error::ConnectError; diff --git a/actix-http/src/encoding/decoder.rs b/actix-http/src/encoding/decoder.rs index 1e51e8b56..dca774838 100644 --- a/actix-http/src/encoding/decoder.rs +++ b/actix-http/src/encoding/decoder.rs @@ -21,7 +21,7 @@ pub struct Decoder { decoder: Option, stream: S, eof: bool, - fut: Option, ContentDecoder), io::Error>>>, + fut: Option, ContentDecoder), io::Error>>, } impl Decoder @@ -85,8 +85,7 @@ where loop { if let Some(ref mut fut) = self.fut { let (chunk, decoder) = match ready!(Pin::new(fut).poll(cx)) { - Ok(Ok(item)) => item, - Ok(Err(e)) => return Poll::Ready(Some(Err(e.into()))), + Ok(item) => item, Err(e) => return Poll::Ready(Some(Err(e.into()))), }; self.decoder = Some(decoder); diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index 295d99a2a..f561ab0c4 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -24,7 +24,7 @@ pub struct Encoder { eof: bool, body: EncoderBody, encoder: Option, - fut: Option>>, + fut: Option>, } impl Encoder { @@ -104,8 +104,7 @@ impl MessageBody for Encoder { if let Some(ref mut fut) = self.fut { let mut encoder = match futures::ready!(Pin::new(fut).poll(cx)) { - Ok(Ok(item)) => item, - Ok(Err(e)) => return Poll::Ready(Some(Err(e.into()))), + Ok(item) => item, Err(e) => return Poll::Ready(Some(Err(e.into()))), }; let chunk = encoder.take(); diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index 587849bde..2c09142e0 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -6,6 +6,7 @@ use std::str::Utf8Error; use std::string::FromUtf8Error; use std::{fmt, io, result}; +pub use actix_threadpool::BlockingError; use actix_utils::timeout::TimeoutError; use bytes::BytesMut; use derive_more::{Display, From}; @@ -197,6 +198,9 @@ impl ResponseError for DeError { /// `InternalServerError` for `Canceled` impl ResponseError for Canceled {} +/// `InternalServerError` for `BlockingError` +impl ResponseError for BlockingError {} + /// Return `BAD_REQUEST` for `Utf8Error` impl ResponseError for Utf8Error { fn status_code(&self) -> StatusCode { @@ -359,12 +363,15 @@ impl From for PayloadError { } } -impl From for PayloadError { - fn from(_: Canceled) -> Self { - PayloadError::Io(io::Error::new( - io::ErrorKind::Other, - "Operation is canceled", - )) +impl From> for PayloadError { + fn from(err: BlockingError) -> Self { + match err { + BlockingError::Error(e) => PayloadError::Io(e), + BlockingError::Canceled => PayloadError::Io(io::Error::new( + io::ErrorKind::Other, + "Operation is canceled", + )), + } } } diff --git a/actix-identity/Cargo.toml b/actix-identity/Cargo.toml index d05b37685..f9c664672 100644 --- a/actix-identity/Cargo.toml +++ b/actix-identity/Cargo.toml @@ -17,14 +17,14 @@ name = "actix_identity" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-alpha.1", default-features = false, features = ["secure-cookies"] } -actix-service = "1.0.0-alpha.1" +actix-web = { version = "2.0.0-alpha.2", default-features = false, features = ["secure-cookies"] } +actix-service = "1.0.0-alpha.2" futures = "0.3.1" serde = "1.0" serde_json = "1.0" time = "0.1.42" [dev-dependencies] -actix-rt = "1.0.0-alpha.1" -actix-http = "0.3.0-alpha.1" +actix-rt = "1.0.0-alpha.2" +actix-http = "0.3.0-alpha.2" bytes = "0.4" \ No newline at end of file diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index 52b33d582..20e9000d5 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -18,9 +18,9 @@ name = "actix_multipart" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-alpha.1", default-features = false } -actix-service = "1.0.0-alpha.1" -actix-utils = "0.5.0-alpha.1" +actix-web = { version = "2.0.0-alpha.2", default-features = false } +actix-service = "1.0.0-alpha.2" +actix-utils = "1.0.0-alpha.2" bytes = "0.4" derive_more = "0.99.2" httparse = "1.3" @@ -31,5 +31,5 @@ time = "0.1" twoway = "0.2" [dev-dependencies] -actix-rt = "1.0.0-alpha.1" -actix-http = "0.3.0-alpha.1" \ No newline at end of file +actix-rt = "1.0.0-alpha.2" +actix-http = "0.3.0-alpha.2" \ No newline at end of file diff --git a/actix-session/Cargo.toml b/actix-session/Cargo.toml index a4c53e563..96c88251a 100644 --- a/actix-session/Cargo.toml +++ b/actix-session/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-session" -version = "0.3.0-alpha.1" +version = "0.3.0-alpha.2" authors = ["Nikolay Kim "] description = "Session for actix web framework." readme = "README.md" @@ -24,8 +24,8 @@ default = ["cookie-session"] cookie-session = ["actix-web/secure-cookies"] [dependencies] -actix-web = "2.0.0-alpha.1" -actix-service = "1.0.0-alpha.1" +actix-web = "2.0.0-alpha.2" +actix-service = "1.0.0-alpha.2" bytes = "0.4" derive_more = "0.99.2" futures = "0.3.1" @@ -35,4 +35,4 @@ serde_json = "1.0" time = "0.1.42" [dev-dependencies] -actix-rt = "1.0.0-alpha.1" +actix-rt = "1.0.0-alpha.2" diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 95883363a..6a8a8a792 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web-codegen" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" description = "Actix web proc macros" readme = "README.md" authors = ["Nikolay Kim "] @@ -17,8 +17,8 @@ syn = { version = "^1", features = ["full", "parsing"] } proc-macro2 = "^1" [dev-dependencies] -actix-rt = { version = "1.0.0-alpha.1" } -actix-web = { version = "2.0.0-alpha.1" } -actix-http = { version = "0.3.0-alpha.1", features=["openssl"] } -actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } +actix-rt = { version = "1.0.0-alpha.2" } +actix-web = { version = "2.0.0-alpha.2" } +actix-http = { version = "0.3.0-alpha.2", features=["openssl"] } +actix-http-test = { version = "0.3.0-alpha.2", features=["openssl"] } futures = { version = "0.3.1" } diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 883b275da..7c1816901 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "awc" -version = "0.3.0-alpha.1" +version = "0.3.0-alpha.2" authors = ["Nikolay Kim "] description = "Actix http client." readme = "README.md" @@ -42,10 +42,10 @@ flate2-zlib = ["actix-http/flate2-zlib"] flate2-rust = ["actix-http/flate2-rust"] [dependencies] -actix-codec = "0.2.0-alpha.1" -actix-service = "1.0.0-alpha.1" -actix-http = "0.3.0-alpha.1" -actix-rt = "1.0.0-alpha.1" +actix-codec = "0.2.0-alpha.2" +actix-service = "1.0.0-alpha.2" +actix-http = "0.3.0-alpha.2" +actix-rt = "1.0.0-alpha.2" base64 = "0.10.1" bytes = "0.4" @@ -62,12 +62,12 @@ open-ssl = { version="0.10", package="openssl", optional = true } # rust-tls = { version = "0.16.0", package="rustls", optional = true, features = ["dangerous_configuration"] } [dev-dependencies] -actix-connect = { version = "1.0.0-alpha.1", features=["openssl"] } -actix-web = { version = "2.0.0-alpha.1", features=["openssl"] } -actix-http = { version = "0.3.0-alpha.1", features=["openssl"] } -actix-http-test = { version = "0.3.0-alpha.1", features=["openssl"] } -actix-utils = "0.5.0-alpha.1" -actix-server = { version = "0.8.0-alpha.2" } +actix-connect = { version = "1.0.0-alpha.2", features=["openssl"] } +actix-web = { version = "2.0.0-alpha.2", features=["openssl"] } +actix-http = { version = "0.3.0-alpha.2", features=["openssl"] } +actix-http-test = { version = "0.3.0-alpha.2", features=["openssl"] } +actix-utils = "1.0.0-alpha.2" +actix-server = { version = "1.0.0-alpha.2" } #actix-tls = { version = "0.1.0-alpha.1", features=["openssl"] } brotli2 = { version="0.3.2" } flate2 = { version="1.0.2" } diff --git a/src/test.rs b/src/test.rs index a33374800..82ba97fec 100644 --- a/src/test.rs +++ b/src/test.rs @@ -667,7 +667,7 @@ mod tests { async fn async_with_block() -> Result { let res = web::block(move || Some(4usize).ok_or("wrong")).await; - match res? { + match res { Ok(value) => Ok(HttpResponse::Ok() .content_type("text/plain") .body(format!("Async with block value: {}", value))), diff --git a/src/web.rs b/src/web.rs index 7f1e8d8f6..2a66a132d 100644 --- a/src/web.rs +++ b/src/web.rs @@ -6,6 +6,7 @@ pub use actix_http::Response as HttpResponse; pub use bytes::{Bytes, BytesMut}; pub use futures::channel::oneshot::Canceled; +use crate::error::BlockingError; use crate::extract::FromRequest; use crate::handler::Factory; use crate::resource::Resource; @@ -274,10 +275,11 @@ pub fn service(path: &str) -> WebService { /// Execute blocking function on a thread pool, returns future that resolves /// to result of the function execution. -pub fn block(f: F) -> impl Future> +pub async fn block(f: F) -> Result> where - F: FnOnce() -> R + Send + 'static, - R: Send + 'static, + F: FnOnce() -> Result + Send + 'static, + I: Send + 'static, + E: Send + std::fmt::Debug + 'static, { - actix_threadpool::run(f) + actix_threadpool::run(f).await } diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index bb7a10040..b3a225f3d 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http-test" -version = "0.3.0-alpha.1" +version = "0.3.0-alpha.2" authors = ["Nikolay Kim "] description = "Actix http test server" readme = "README.md" @@ -30,16 +30,16 @@ default = [] openssl = ["open-ssl", "awc/openssl", ] # "actix-tls/openssl"] [dependencies] -actix-service = "1.0.0-alpha.1" -actix-codec = "0.2.0-alpha.1" -actix-connect = "1.0.0-alpha.1" -actix-utils = "0.5.0-alpha.1" -actix-rt = "1.0.0-alpha.1" -actix-server = "0.8.0-alpha.1" -actix-testing = "0.3.0-alpha.1" -awc = "0.3.0-alpha.1" +actix-service = "1.0.0-alpha.2" +actix-codec = "0.2.0-alpha.2" +actix-connect = "1.0.0-alpha.2" +actix-utils = "1.0.0-alpha.2" +actix-rt = "1.0.0-alpha.2" +actix-server = "1.0.0-alpha.2" +actix-testing = "1.0.0-alpha.2" +awc = "0.3.0-alpha.2" -base64 = "0.10" +base64 = "0.11" bytes = "0.4" futures = "0.3.1" http = "0.1.8" @@ -52,9 +52,8 @@ sha1 = "0.6" slab = "0.4" serde_urlencoded = "0.6.1" time = "0.1" -tokio-net = "0.2.0-alpha.6" open-ssl = { version="0.10", package="openssl", optional = true } [dev-dependencies] -actix-web = "2.0.0-alpha.1" -actix-http = "0.3.0-alpha.1" +actix-web = "2.0.0-alpha.2" +actix-http = "0.3.0-alpha.2" diff --git a/test-server/src/lib.rs b/test-server/src/lib.rs index 9ad06397c..a28811486 100644 --- a/test-server/src/lib.rs +++ b/test-server/src/lib.rs @@ -3,14 +3,13 @@ use std::sync::mpsc; use std::{net, thread, time}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; -use actix_rt::System; +use actix_rt::{net::TcpStream, System}; use actix_server::{Server, ServiceFactory}; use awc::{error::PayloadError, ws, Client, ClientRequest, ClientResponse, Connector}; use bytes::Bytes; use futures::Stream; use http::Method; use net2::TcpBuilder; -use tokio_net::tcp::TcpStream; pub use actix_testing::*; From cf30eafb49c62c5f4a7aa39fa0a8ddd9d54bc936 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Tue, 3 Dec 2019 00:49:12 +0600 Subject: [PATCH 081/176] update md --- Cargo.toml | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 72efbde68..4093ca3b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,12 +74,13 @@ actix-service = "1.0.0-alpha.2" actix-utils = "1.0.0-alpha.2" actix-router = "0.1.5" actix-rt = "1.0.0-alpha.2" -actix-web-codegen = "0.2.0-alpha.2" -actix-http = "0.3.0-alpha.2" actix-server = "1.0.0-alpha.2" actix-testing = "1.0.0-alpha.2" actix-threadpool = "0.3.0" -actix-tls = { version = "1.0.0-alpha.1", optional = true } +actix-tls = { version = "1.0.0-alpha.1" } + +actix-web-codegen = "0.2.0-alpha.2" +actix-http = "0.3.0-alpha.2" awc = { version = "0.3.0-alpha.2", optional = true } bytes = "0.4" @@ -129,13 +130,3 @@ actix-session = { path = "actix-session" } actix-files = { path = "actix-files" } actix-multipart = { path = "actix-multipart" } awc = { path = "awc" } - -# actix-codec = { git = "https://github.com/actix/actix-net.git" } -# actix-connect = { git = "https://github.com/actix/actix-net.git" } -# actix-rt = { git = "https://github.com/actix/actix-net.git" } -# actix-macros = { git = "https://github.com/actix/actix-net.git" } -# actix-server = { git = "https://github.com/actix/actix-net.git" } -# actix-service = { git = "https://github.com/actix/actix-net.git" } -# actix-testing = { git = "https://github.com/actix/actix-net.git" } -# actix-tls = { git = "https://github.com/actix/actix-net.git" } -# actix-utils = { git = "https://github.com/actix/actix-net.git" } From c7ed6d34282abf1272ad1043d4aea57024aa95aa Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Tue, 3 Dec 2019 16:35:31 +0600 Subject: [PATCH 082/176] update version --- actix-files/CHANGES.md | 4 ++++ actix-files/Cargo.toml | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index 5ec56593c..29e774e0f 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [0.2.0-alpha.2] - 2019-12-03 + +* Migrate to `std::future` + ## [0.1.7] - 2019-11-06 * Add an additional `filename*` param in the `Content-Disposition` header of `actix_files::NamedFile` to be more compatible. (#1151) diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index e4cb12deb..55078adb7 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-files" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" authors = ["Nikolay Kim "] description = "Static files support for actix web." readme = "README.md" @@ -18,8 +18,8 @@ name = "actix_files" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-alpha.1", default-features = false } -actix-http = "0.3.0-alpha.1" +actix-web = { version = "2.0.0-alpha.2", default-features = false } +actix-http = "0.3.0-alpha.2" actix-service = "1.0.0-alpha.2" bitflags = "1" bytes = "0.4" @@ -33,4 +33,4 @@ v_htmlescape = "0.4" [dev-dependencies] actix-rt = "1.0.0-alpha.2" -actix-web = { version = "2.0.0-alpha.1", features=["openssl"] } +actix-web = { version = "2.0.0-alpha.2", features=["openssl"] } From 0015a204aa223440405eb7ba97a50eac72a851a4 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Tue, 3 Dec 2019 19:03:53 +0600 Subject: [PATCH 083/176] update version --- actix-multipart/CHANGES.md | 4 ++++ actix-multipart/Cargo.toml | 2 +- actix-multipart/README.md | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/actix-multipart/CHANGES.md b/actix-multipart/CHANGES.md index ca61176c7..ae6f5bf14 100644 --- a/actix-multipart/CHANGES.md +++ b/actix-multipart/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [0.2.0-alpha.2] - 2019-12-03 + +* Migrate to `std::future` + ## [0.1.4] - 2019-09-12 * Multipart handling now parses requests which do not end in CRLF #1038 diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index 20e9000d5..2107af5a0 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-multipart" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" authors = ["Nikolay Kim "] description = "Multipart support for actix web framework." readme = "README.md" diff --git a/actix-multipart/README.md b/actix-multipart/README.md index ac0d05640..a453f489e 100644 --- a/actix-multipart/README.md +++ b/actix-multipart/README.md @@ -5,4 +5,4 @@ * [API Documentation](https://docs.rs/actix-multipart/) * [Chat on gitter](https://gitter.im/actix/actix) * Cargo package: [actix-multipart](https://crates.io/crates/actix-multipart) -* Minimum supported Rust version: 1.33 or later +* Minimum supported Rust version: 1.39 or later From b45c6cd66b5fcb37a7b4b3afcd4cb35cc25f2d9b Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 4 Dec 2019 18:32:18 +0600 Subject: [PATCH 084/176] replace hashbrown with std hashmap --- Cargo.toml | 2 +- actix-http/Cargo.toml | 2 +- actix-http/src/client/pool.rs | 6 +++--- actix-http/src/extensions.rs | 6 +++--- actix-http/src/header/map.rs | 11 ++++++----- actix-session/Cargo.toml | 1 - actix-session/src/lib.rs | 2 +- awc/Cargo.toml | 1 - src/middleware/errhandlers.rs | 8 ++++---- src/rmap.rs | 6 +++--- 10 files changed, 22 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4093ca3b1..d5b16844d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,7 +87,7 @@ bytes = "0.4" derive_more = "0.99.2" encoding_rs = "0.8" futures = "0.3.1" -hashbrown = "0.6.3" +fxhash = "0.2.1" log = "0.4" mime = "0.3" net2 = "0.2.33" diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 54e83fbc5..d81c46119 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -64,7 +64,7 @@ derive_more = "0.99.2" either = "1.5.2" encoding_rs = "0.8" futures = "0.3.1" -hashbrown = "0.6.3" +fxhash = "0.2.1" h2 = "0.2.0-alpha.3" http = "0.1.17" httparse = "1.3" diff --git a/actix-http/src/client/pool.rs b/actix-http/src/client/pool.rs index c61039866..938ccb5d0 100644 --- a/actix-http/src/client/pool.rs +++ b/actix-http/src/client/pool.rs @@ -12,8 +12,8 @@ use actix_service::Service; use actix_utils::{oneshot, task::LocalWaker}; use bytes::Bytes; use futures::future::{poll_fn, FutureExt, LocalBoxFuture}; +use fxhash::FxHashMap; use h2::client::{handshake, Connection, SendRequest}; -use hashbrown::HashMap; use http::uri::Authority; use indexmap::IndexSet; use slab::Slab; @@ -66,7 +66,7 @@ where acquired: 0, waiters: Slab::new(), waiters_queue: IndexSet::new(), - available: HashMap::new(), + available: FxHashMap::default(), waker: LocalWaker::new(), })), ) @@ -259,7 +259,7 @@ pub(crate) struct Inner { disconnect_timeout: Option, limit: usize, acquired: usize, - available: HashMap>>, + available: FxHashMap>>, waiters: Slab< Option<( Connect, diff --git a/actix-http/src/extensions.rs b/actix-http/src/extensions.rs index c6266f56e..066e0a3ca 100644 --- a/actix-http/src/extensions.rs +++ b/actix-http/src/extensions.rs @@ -1,12 +1,12 @@ use std::any::{Any, TypeId}; use std::fmt; -use hashbrown::HashMap; +use fxhash::FxHashMap; #[derive(Default)] /// A type map of request extensions. pub struct Extensions { - map: HashMap>, + map: FxHashMap>, } impl Extensions { @@ -14,7 +14,7 @@ impl Extensions { #[inline] pub fn new() -> Extensions { Extensions { - map: HashMap::default(), + map: FxHashMap::default(), } } diff --git a/actix-http/src/header/map.rs b/actix-http/src/header/map.rs index f2f1ba51c..5fcf79f75 100644 --- a/actix-http/src/header/map.rs +++ b/actix-http/src/header/map.rs @@ -1,6 +1,7 @@ +use std::collections::hash_map::{self, Entry}; + use either::Either; -use hashbrown::hash_map::{self, Entry}; -use hashbrown::HashMap; +use fxhash::FxHashMap; use http::header::{HeaderName, HeaderValue}; use http::HttpTryFrom; @@ -11,7 +12,7 @@ use http::HttpTryFrom; /// [`HeaderName`]: struct.HeaderName.html #[derive(Debug, Clone)] pub struct HeaderMap { - pub(crate) inner: HashMap, + pub(crate) inner: FxHashMap, } #[derive(Debug, Clone)] @@ -56,7 +57,7 @@ impl HeaderMap { /// allocate. pub fn new() -> Self { HeaderMap { - inner: HashMap::new(), + inner: FxHashMap::default(), } } @@ -70,7 +71,7 @@ impl HeaderMap { /// More capacity than requested may be allocated. pub fn with_capacity(capacity: usize) -> HeaderMap { HeaderMap { - inner: HashMap::with_capacity(capacity), + inner: FxHashMap::with_capacity_and_hasher(capacity, Default::default()), } } diff --git a/actix-session/Cargo.toml b/actix-session/Cargo.toml index 96c88251a..d396a787a 100644 --- a/actix-session/Cargo.toml +++ b/actix-session/Cargo.toml @@ -29,7 +29,6 @@ actix-service = "1.0.0-alpha.2" bytes = "0.4" derive_more = "0.99.2" futures = "0.3.1" -hashbrown = "0.6.3" serde = "1.0" serde_json = "1.0" time = "0.1.42" diff --git a/actix-session/src/lib.rs b/actix-session/src/lib.rs index def35a1e9..6f23ef913 100644 --- a/actix-session/src/lib.rs +++ b/actix-session/src/lib.rs @@ -43,12 +43,12 @@ //! } //! ``` use std::cell::RefCell; +use std::collections::HashMap; use std::rc::Rc; use actix_web::dev::{Extensions, Payload, ServiceRequest, ServiceResponse}; use actix_web::{Error, FromRequest, HttpMessage, HttpRequest}; use futures::future::{ok, Ready}; -use hashbrown::HashMap; use serde::de::DeserializeOwned; use serde::Serialize; use serde_json; diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 7c1816901..e884aaddd 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -72,5 +72,4 @@ actix-server = { version = "1.0.0-alpha.2" } brotli2 = { version="0.3.2" } flate2 = { version="1.0.2" } env_logger = "0.6" -rand = "0.7" webpki = { version = "0.21" } diff --git a/src/middleware/errhandlers.rs b/src/middleware/errhandlers.rs index 3dc1f0828..7a8684936 100644 --- a/src/middleware/errhandlers.rs +++ b/src/middleware/errhandlers.rs @@ -4,7 +4,7 @@ use std::task::{Context, Poll}; use actix_service::{Service, Transform}; use futures::future::{ok, FutureExt, LocalBoxFuture, Ready}; -use hashbrown::HashMap; +use fxhash::FxHashMap; use crate::dev::{ServiceRequest, ServiceResponse}; use crate::error::{Error, Result}; @@ -52,13 +52,13 @@ type ErrorHandler = dyn Fn(ServiceResponse) -> Result { - handlers: Rc>>>, + handlers: Rc>>>, } impl Default for ErrorHandlers { fn default() -> Self { ErrorHandlers { - handlers: Rc::new(HashMap::new()), + handlers: Rc::new(FxHashMap::default()), } } } @@ -105,7 +105,7 @@ where #[doc(hidden)] pub struct ErrorHandlersMiddleware { service: S, - handlers: Rc>>>, + handlers: Rc>>>, } impl Service for ErrorHandlersMiddleware diff --git a/src/rmap.rs b/src/rmap.rs index 42ddb1349..47092608c 100644 --- a/src/rmap.rs +++ b/src/rmap.rs @@ -2,7 +2,7 @@ use std::cell::RefCell; use std::rc::Rc; use actix_router::ResourceDef; -use hashbrown::HashMap; +use fxhash::FxHashMap; use url::Url; use crate::error::UrlGenerationError; @@ -12,7 +12,7 @@ use crate::request::HttpRequest; pub struct ResourceMap { root: ResourceDef, parent: RefCell>>, - named: HashMap, + named: FxHashMap, patterns: Vec<(ResourceDef, Option>)>, } @@ -21,7 +21,7 @@ impl ResourceMap { ResourceMap { root, parent: RefCell::new(None), - named: HashMap::new(), + named: FxHashMap::default(), patterns: Vec::new(), } } From 205a964d8f5a3fe750567ad4710c81195b16414d Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 5 Dec 2019 23:35:43 +0600 Subject: [PATCH 085/176] upgrade to tokio 0.2 --- CHANGES.md | 7 + Cargo.toml | 34 ++- actix-cors/CHANGES.md | 6 +- actix-cors/Cargo.toml | 2 +- actix-cors/src/lib.rs | 15 +- actix-files/Cargo.toml | 6 +- actix-framed/Cargo.toml | 20 +- actix-framed/src/request.rs | 4 +- actix-framed/src/test.rs | 9 +- actix-framed/tests/test_server.rs | 6 +- actix-http/Cargo.toml | 45 ++- actix-http/src/body.rs | 4 +- actix-http/src/client/connection.rs | 7 +- actix-http/src/client/connector.rs | 18 +- actix-http/src/client/error.rs | 17 +- actix-http/src/client/h1proto.rs | 12 +- actix-http/src/client/h2proto.rs | 3 +- actix-http/src/client/pool.rs | 4 +- actix-http/src/config.rs | 8 +- actix-http/src/encoding/encoder.rs | 4 +- actix-http/src/encoding/mod.rs | 2 +- actix-http/src/error.rs | 23 +- actix-http/src/h1/decoder.rs | 13 +- actix-http/src/h1/dispatcher.rs | 5 +- actix-http/src/h1/encoder.rs | 109 ++++--- actix-http/src/h1/service.rs | 53 +++- actix-http/src/h2/dispatcher.rs | 10 +- actix-http/src/h2/mod.rs | 2 +- actix-http/src/h2/service.rs | 48 ++- actix-http/src/header/common/cache_control.rs | 4 +- .../src/header/common/content_disposition.rs | 15 +- actix-http/src/header/common/content_range.rs | 6 +- actix-http/src/header/common/if_range.rs | 6 +- actix-http/src/header/common/mod.rs | 14 +- actix-http/src/header/map.rs | 2 +- actix-http/src/header/mod.rs | 27 +- actix-http/src/header/shared/entity.rs | 6 +- actix-http/src/header/shared/httpdate.rs | 8 +- actix-http/src/helpers.rs | 22 +- actix-http/src/lib.rs | 2 +- actix-http/src/request.rs | 2 +- actix-http/src/response.rs | 19 +- actix-http/src/service.rs | 68 ++++- actix-http/src/test.rs | 9 +- actix-http/src/ws/frame.rs | 4 +- actix-http/tests/test_rustls.rs | 278 ++++++++---------- actix-http/tests/test_ws.rs | 4 +- actix-identity/Cargo.toml | 2 +- actix-multipart/Cargo.toml | 2 +- actix-multipart/src/server.rs | 12 +- actix-session/Cargo.toml | 2 +- awc/Cargo.toml | 34 +-- awc/src/builder.rs | 7 +- awc/src/connect.rs | 7 +- awc/src/error.rs | 3 +- awc/src/frozen.rs | 11 +- awc/src/lib.rs | 33 ++- awc/src/request.rs | 36 ++- awc/src/response.rs | 2 +- awc/src/test.rs | 9 +- awc/src/ws.rs | 30 +- awc/tests/test_rustls_client.rs | 19 +- awc/tests/test_ws.rs | 5 +- src/guard.rs | 5 +- src/info.rs | 4 +- src/middleware/defaultheaders.rs | 9 +- src/middleware/logger.rs | 3 +- src/middleware/normalize.rs | 8 +- src/responder.rs | 10 +- src/server.rs | 39 +-- src/test.rs | 9 +- test-server/Cargo.toml | 26 +- 72 files changed, 764 insertions(+), 555 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index a7569862d..600dc8e15 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,12 @@ # Changes +## [2.0.0-alpha.3] - 2019-12-xx + +### Changed + +* Migrate to tokio 0.2 + + ## [2.0.0-alpha.1] - 2019-11-22 ### Changed diff --git a/Cargo.toml b/Cargo.toml index d5b16844d..ee3158a15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "2.0.0-alpha.2" +version = "2.0.0-alpha.3" authors = ["Nikolay Kim "] description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." readme = "README.md" @@ -66,24 +66,24 @@ fail = ["actix-http/fail"] openssl = ["open-ssl", "actix-tls/openssl", "awc/openssl"] # rustls -# rustls = ["rust-tls", "actix-server/rustls", "awc/rustls"] +rustls = ["rust-tls", "actix-tls/rustls", "awc/rustls"] [dependencies] -actix-codec = "0.2.0-alpha.2" -actix-service = "1.0.0-alpha.2" -actix-utils = "1.0.0-alpha.2" -actix-router = "0.1.5" -actix-rt = "1.0.0-alpha.2" -actix-server = "1.0.0-alpha.2" -actix-testing = "1.0.0-alpha.2" +actix-codec = "0.2.0-alpha.3" +actix-service = "1.0.0-alpha.3" +actix-utils = "1.0.0-alpha.3" +actix-router = "0.2.0" +actix-rt = "1.0.0-alpha.3" +actix-server = "1.0.0-alpha.3" +actix-testing = "1.0.0-alpha.3" actix-threadpool = "0.3.0" -actix-tls = { version = "1.0.0-alpha.1" } +actix-tls = { version = "1.0.0-alpha.3" } actix-web-codegen = "0.2.0-alpha.2" actix-http = "0.3.0-alpha.2" awc = { version = "0.3.0-alpha.2", optional = true } -bytes = "0.4" +bytes = "0.5.2" derive_more = "0.99.2" encoding_rs = "0.8" futures = "0.3.1" @@ -102,7 +102,7 @@ url = "2.1" # ssl support open-ssl = { version="0.10", package="openssl", optional = true } -# rust-tls = { version = "0.16", package="rustls", optional = true } +rust-tls = { version = "0.16", package="rustls", optional = true } [dev-dependencies] # actix = "0.8.3" @@ -130,3 +130,13 @@ actix-session = { path = "actix-session" } actix-files = { path = "actix-files" } actix-multipart = { path = "actix-multipart" } awc = { path = "awc" } + +actix-codec = { git = "https://github.com/actix/actix-net.git" } +actix-connect = { git = "https://github.com/actix/actix-net.git" } +actix-rt = { git = "https://github.com/actix/actix-net.git" } +actix-server = { git = "https://github.com/actix/actix-net.git" } +actix-service = { git = "https://github.com/actix/actix-net.git" } +actix-testing = { git = "https://github.com/actix/actix-net.git" } +actix-tls = { git = "https://github.com/actix/actix-net.git" } +actix-utils = { git = "https://github.com/actix/actix-net.git" } +actix-router = { git = "https://github.com/actix/actix-net.git" } diff --git a/actix-cors/CHANGES.md b/actix-cors/CHANGES.md index 10e408ede..92e3b697b 100644 --- a/actix-cors/CHANGES.md +++ b/actix-cors/CHANGES.md @@ -1,8 +1,10 @@ # Changes -## [0.1.1] - unreleased +## [0.2.0-alpha.3] - unreleased -* Bump `derive_more` crate version to 0.15.0 +* Migrate to actix-web 2.0.0 + +* Bump `derive_more` crate version to 0.99.0 ## [0.1.0] - 2019-06-15 diff --git a/actix-cors/Cargo.toml b/actix-cors/Cargo.toml index bc8c7d805..976d0be7f 100644 --- a/actix-cors/Cargo.toml +++ b/actix-cors/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-cors" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.3" authors = ["Nikolay Kim "] description = "Cross-origin resource sharing (CORS) for Actix applications." readme = "README.md" diff --git a/actix-cors/src/lib.rs b/actix-cors/src/lib.rs index d3607aa8e..71d98f896 100644 --- a/actix-cors/src/lib.rs +++ b/actix-cors/src/lib.rs @@ -40,6 +40,7 @@ //! //! Cors middleware automatically handle *OPTIONS* preflight request. use std::collections::HashSet; +use std::convert::TryFrom; use std::iter::FromIterator; use std::rc::Rc; use std::task::{Context, Poll}; @@ -48,7 +49,7 @@ use actix_service::{Service, Transform}; use actix_web::dev::{RequestHead, ServiceRequest, ServiceResponse}; use actix_web::error::{Error, ResponseError, Result}; use actix_web::http::header::{self, HeaderName, HeaderValue}; -use actix_web::http::{self, HttpTryFrom, Method, StatusCode, Uri}; +use actix_web::http::{self, Error as HttpError, Method, StatusCode, Uri}; use actix_web::HttpResponse; use derive_more::Display; use futures::future::{ok, Either, FutureExt, LocalBoxFuture, Ready}; @@ -274,7 +275,8 @@ impl Cors { pub fn allowed_methods(mut self, methods: U) -> Cors where U: IntoIterator, - Method: HttpTryFrom, + Method: TryFrom, + >::Error: Into, { self.methods = true; if let Some(cors) = cors(&mut self.cors, &self.error) { @@ -296,7 +298,8 @@ impl Cors { /// Set an allowed header pub fn allowed_header(mut self, header: H) -> Cors where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, { if let Some(cors) = cors(&mut self.cors, &self.error) { match HeaderName::try_from(header) { @@ -328,7 +331,8 @@ impl Cors { pub fn allowed_headers(mut self, headers: U) -> Cors where U: IntoIterator, - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, { if let Some(cors) = cors(&mut self.cors, &self.error) { for h in headers { @@ -362,7 +366,8 @@ impl Cors { pub fn expose_headers(mut self, headers: U) -> Cors where U: IntoIterator, - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, { for h in headers { match HeaderName::try_from(h) { diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 55078adb7..261bf14e3 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-files" -version = "0.2.0-alpha.2" +version = "0.2.0-alpha.3" authors = ["Nikolay Kim "] description = "Static files support for actix web." readme = "README.md" @@ -20,9 +20,9 @@ path = "src/lib.rs" [dependencies] actix-web = { version = "2.0.0-alpha.2", default-features = false } actix-http = "0.3.0-alpha.2" -actix-service = "1.0.0-alpha.2" +actix-service = "1.0.0-alpha.3" bitflags = "1" -bytes = "0.4" +bytes = "0.5.2" futures = "0.3.1" derive_more = "0.99.2" log = "0.4" diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml index 55bc1a24c..0b80266aa 100644 --- a/actix-framed/Cargo.toml +++ b/actix-framed/Cargo.toml @@ -20,19 +20,19 @@ name = "actix_framed" path = "src/lib.rs" [dependencies] -actix-codec = "0.2.0-alpha.2" -actix-service = "1.0.0-alpha.2" -actix-router = "0.1.2" -actix-rt = "1.0.0-alpha.2" -actix-http = "0.3.0-alpha.2" +actix-codec = "0.2.0-alpha.3" +actix-service = "1.0.0-alpha.3" +actix-router = "0.2.0" +actix-rt = "1.0.0-alpha.3" +actix-http = "0.3.0-alpha.3" -bytes = "0.4" +bytes = "0.5.2" futures = "0.3.1" pin-project = "0.4.6" log = "0.4" [dev-dependencies] -actix-server = { version = "1.0.0-alpha.2" } -actix-connect = { version = "1.0.0-alpha.2", features=["openssl"] } -actix-http-test = { version = "0.3.0-alpha.2", features=["openssl"] } -actix-utils = "1.0.0-alpha.2" +actix-server = { version = "1.0.0-alpha.3" } +actix-connect = { version = "1.0.0-alpha.3", features=["openssl"] } +actix-http-test = { version = "0.3.0-alpha.3", features=["openssl"] } +actix-utils = "1.0.0-alpha.3" diff --git a/actix-framed/src/request.rs b/actix-framed/src/request.rs index bdcdd7026..1872dcc25 100644 --- a/actix-framed/src/request.rs +++ b/actix-framed/src/request.rs @@ -123,7 +123,9 @@ impl FramedRequest { #[cfg(test)] mod tests { - use actix_http::http::{HeaderName, HeaderValue, HttpTryFrom}; + use std::convert::TryFrom; + + use actix_http::http::{HeaderName, HeaderValue}; use actix_http::test::{TestBuffer, TestRequest}; use super::*; diff --git a/actix-framed/src/test.rs b/actix-framed/src/test.rs index 7969d51ff..b8029531e 100644 --- a/actix-framed/src/test.rs +++ b/actix-framed/src/test.rs @@ -1,10 +1,11 @@ //! Various helpers for Actix applications to use during testing. +use std::convert::TryFrom; use std::future::Future; use actix_codec::Framed; use actix_http::h1::Codec; use actix_http::http::header::{Header, HeaderName, IntoHeaderValue}; -use actix_http::http::{HttpTryFrom, Method, Uri, Version}; +use actix_http::http::{Error as HttpError, Method, Uri, Version}; use actix_http::test::{TestBuffer, TestRequest as HttpTestRequest}; use actix_router::{Path, Url}; @@ -41,7 +42,8 @@ impl TestRequest<()> { /// Create TestRequest and set header pub fn with_header(key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { Self::default().header(key, value) @@ -96,7 +98,8 @@ impl TestRequest { /// Set a header pub fn header(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { self.req.header(key, value); diff --git a/actix-framed/tests/test_server.rs b/actix-framed/tests/test_server.rs index c272f0f93..40e698c70 100644 --- a/actix-framed/tests/test_server.rs +++ b/actix-framed/tests/test_server.rs @@ -3,7 +3,7 @@ use actix_http::{body, http::StatusCode, ws, Error, HttpService, Response}; use actix_http_test::TestServer; use actix_service::{pipeline_factory, IntoServiceFactory, ServiceFactory}; use actix_utils::framed::FramedTransport; -use bytes::{Bytes, BytesMut}; +use bytes::BytesMut; use futures::{future, SinkExt, StreamExt}; use actix_framed::{FramedApp, FramedRequest, FramedRoute, SendError, VerifyWebSockets}; @@ -70,7 +70,7 @@ async fn test_simple() { let (item, mut framed) = framed.into_future().await; assert_eq!( item.unwrap().unwrap(), - ws::Frame::Binary(Some(Bytes::from_static(b"text").into())) + ws::Frame::Binary(Some(BytesMut::from(&b"text"[..]))) ); framed.send(ws::Message::Ping("text".into())).await.unwrap(); @@ -136,7 +136,7 @@ async fn test_service() { let (item, mut framed) = framed.into_future().await; assert_eq!( item.unwrap().unwrap(), - ws::Frame::Binary(Some(Bytes::from_static(b"text").into())) + ws::Frame::Binary(Some(BytesMut::from(&b"text"[..]))) ); framed.send(ws::Message::Ping("text".into())).await.unwrap(); diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index d81c46119..aadef9e3c 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "0.3.0-alpha.2" +version = "0.3.0-alpha.3" authors = ["Nikolay Kim "] description = "Actix http primitives" readme = "README.md" @@ -16,7 +16,7 @@ edition = "2018" workspace = ".." [package.metadata.docs.rs] -features = ["openssl", "fail", "brotli", "flate2-zlib", "secure-cookies"] +features = ["openssl", "rustls", "fail", "brotli", "flate2-zlib", "secure-cookies"] [lib] name = "actix_http" @@ -26,10 +26,10 @@ path = "src/lib.rs" default = [] # openssl -openssl = ["open-ssl", "actix-tls/openssl", "actix-connect/openssl", "tokio-openssl"] +openssl = ["actix-tls/openssl", "actix-connect/openssl"] # rustls support -# rustls = ["rust-tls", "webpki-roots", "actix-connect/rustls"] +rustls = ["actix-tls/rustls", "actix-connect/rustls"] # brotli encoding, requires c compiler brotli = ["brotli2"] @@ -47,17 +47,17 @@ fail = ["failure"] secure-cookies = ["ring"] [dependencies] -actix-service = "1.0.0-alpha.2" -actix-codec = "0.2.0-alpha.2" -actix-connect = "1.0.0-alpha.2" -actix-utils = "1.0.0-alpha.2" -actix-rt = "1.0.0-alpha.2" +actix-service = "1.0.0-alpha.3" +actix-codec = "0.2.0-alpha.3" +actix-connect = "1.0.0-alpha.3" +actix-utils = "1.0.0-alpha.3" +actix-rt = "1.0.0-alpha.3" actix-threadpool = "0.3.0" -actix-tls = { version = "1.0.0-alpha.1", optional = true } +actix-tls = { version = "1.0.0-alpha.3", optional = true } base64 = "0.11" bitflags = "1.0" -bytes = "0.4" +bytes = "0.5.2" copyless = "0.1.4" chrono = "0.4.6" derive_more = "0.99.2" @@ -65,8 +65,8 @@ either = "1.5.2" encoding_rs = "0.8" futures = "0.3.1" fxhash = "0.2.1" -h2 = "0.2.0-alpha.3" -http = "0.1.17" +h2 = "0.2.0" +http = "0.2.0" httparse = "1.3" indexmap = "1.2" lazy_static = "1.0" @@ -76,14 +76,13 @@ mime = "0.3" percent-encoding = "2.1" pin-project = "0.4.6" rand = "0.7" -regex = "1.0" +regex = "1.3" serde = "1.0" serde_json = "1.0" sha1 = "0.6" slab = "0.4" serde_urlencoded = "0.6.1" time = "0.1.42" -trust-dns-resolver = { version="0.18.0-alpha.1", default-features = false } # for secure cookie ring = { version = "0.16.9", optional = true } @@ -94,17 +93,13 @@ flate2 = { version="1.0.7", optional = true, default-features = false } # optional deps failure = { version = "0.1.5", optional = true } -open-ssl = { version="0.10", package="openssl", optional = true } -tokio-openssl = { version = "0.4.0-alpha.6", optional = true } - -# rust-tls = { version = "0.16.0", package="rustls", optional = true } -# webpki-roots = { version = "0.18", optional = true } [dev-dependencies] -actix-server = { version = "1.0.0-alpha.2" } -actix-connect = { version = "1.0.0-alpha.2", features=["openssl"] } -actix-http-test = { version = "0.3.0-alpha.2", features=["openssl"] } -actix-tls = { version = "1.0.0-alpha.1", features=["openssl"] } +actix-server = { version = "1.0.0-alpha.3" } +actix-connect = { version = "1.0.0-alpha.3", features=["openssl"] } +actix-http-test = { version = "0.3.0-alpha.3", features=["openssl"] } +actix-tls = { version = "1.0.0-alpha.3", features=["openssl"] } env_logger = "0.6" serde_derive = "1.0" -open-ssl = { version="0.10", package="openssl" } +open-ssl = { version="0.10", package = "openssl" } +rust-tls = { version="0.16", package = "rustls" } diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs index b69c21eaa..ff7235626 100644 --- a/actix-http/src/body.rs +++ b/actix-http/src/body.rs @@ -133,7 +133,7 @@ pub enum Body { impl Body { /// Create body from slice (copy) pub fn from_slice(s: &[u8]) -> Body { - Body::Bytes(Bytes::from(s)) + Body::Bytes(Bytes::copy_from_slice(s)) } /// Create body from generic message body. @@ -226,7 +226,7 @@ impl From for Body { impl<'a> From<&'a String> for Body { fn from(s: &'a String) -> Body { - Body::Bytes(Bytes::from(AsRef::<[u8]>::as_ref(&s))) + Body::Bytes(Bytes::copy_from_slice(AsRef::<[u8]>::as_ref(&s))) } } diff --git a/actix-http/src/client/connection.rs b/actix-http/src/client/connection.rs index 75d393b1b..9b590690f 100644 --- a/actix-http/src/client/connection.rs +++ b/actix-http/src/client/connection.rs @@ -1,6 +1,6 @@ use std::pin::Pin; use std::task::{Context, Poll}; -use std::{fmt, io, time}; +use std::{fmt, io, mem, time}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; use bytes::{Buf, Bytes}; @@ -228,7 +228,10 @@ where } } - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { + unsafe fn prepare_uninitialized_buffer( + &self, + buf: &mut [mem::MaybeUninit], + ) -> bool { match self { EitherIo::A(ref val) => val.prepare_uninitialized_buffer(buf), EitherIo::B(ref val) => val.prepare_uninitialized_buffer(buf), diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index fb43733cf..2710252e3 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -17,10 +17,10 @@ use super::pool::{ConnectionPool, Protocol}; use super::Connect; #[cfg(feature = "openssl")] -use open_ssl::ssl::SslConnector as OpensslConnector; +use actix_connect::ssl::openssl::SslConnector as OpensslConnector; #[cfg(feature = "rustls")] -use rust_tls::ClientConfig; +use actix_connect::ssl::rustls::ClientConfig; #[cfg(feature = "rustls")] use std::sync::Arc; @@ -74,7 +74,7 @@ impl Connector<(), ()> { let ssl = { #[cfg(feature = "openssl")] { - use open_ssl::ssl::SslMethod; + use actix_connect::ssl::openssl::SslMethod; let mut ssl = OpensslConnector::builder(SslMethod::tls()).unwrap(); let _ = ssl @@ -87,9 +87,9 @@ impl Connector<(), ()> { let protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; let mut config = ClientConfig::new(); config.set_protocols(&protos); - config - .root_store - .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); + config.root_store.add_server_trust_anchors( + &actix_connect::ssl::rustls::TLS_SERVER_ROOTS, + ); SslConnector::Rustls(Arc::new(config)) } #[cfg(not(any(feature = "openssl", feature = "rustls")))] @@ -242,12 +242,10 @@ where { const H2: &[u8] = b"h2"; #[cfg(feature = "openssl")] - use actix_connect::ssl::OpensslConnector; + use actix_connect::ssl::openssl::OpensslConnector; #[cfg(feature = "rustls")] - use actix_connect::ssl::RustlsConnector; + use actix_connect::ssl::rustls::{RustlsConnector, Session}; use actix_service::{boxed::service, pipeline}; - #[cfg(feature = "rustls")] - use rust_tls::Session; let ssl_service = TimeoutService::new( self.timeout, diff --git a/actix-http/src/client/error.rs b/actix-http/src/client/error.rs index ee568e8be..42ea47ee8 100644 --- a/actix-http/src/client/error.rs +++ b/actix-http/src/client/error.rs @@ -1,10 +1,10 @@ use std::io; +use actix_connect::resolver::ResolveError; use derive_more::{Display, From}; -use trust_dns_resolver::error::ResolveError; #[cfg(feature = "openssl")] -use open_ssl::ssl::{Error as SslError, HandshakeError}; +use actix_connect::ssl::openssl::{HandshakeError, SslError}; use crate::error::{Error, ParseError, ResponseError}; use crate::http::{Error as HttpError, StatusCode}; @@ -21,6 +21,11 @@ pub enum ConnectError { #[display(fmt = "{}", _0)] SslError(SslError), + /// SSL Handshake error + #[cfg(feature = "openssl")] + #[display(fmt = "{}", _0)] + SslHandshakeError(String), + /// Failed to resolve the hostname #[display(fmt = "Failed resolving hostname: {}", _0)] Resolver(ResolveError), @@ -63,13 +68,9 @@ impl From for ConnectError { } #[cfg(feature = "openssl")] -impl From> for ConnectError { +impl From> for ConnectError { fn from(err: HandshakeError) -> ConnectError { - match err { - HandshakeError::SetupFailure(stack) => SslError::from(stack).into(), - HandshakeError::Failure(stream) => stream.into_error().into(), - HandshakeError::WouldBlock(stream) => stream.into_error().into(), - } + ConnectError::SslHandshakeError(format!("{:?}", err)) } } diff --git a/actix-http/src/client/h1proto.rs b/actix-http/src/client/h1proto.rs index ddfc7a314..048d3795c 100644 --- a/actix-http/src/client/h1proto.rs +++ b/actix-http/src/client/h1proto.rs @@ -1,10 +1,11 @@ use std::io::Write; use std::pin::Pin; use std::task::{Context, Poll}; -use std::{io, time}; +use std::{io, mem, time}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; -use bytes::{BufMut, Bytes, BytesMut}; +use bytes::buf::BufMutExt; +use bytes::{Bytes, BytesMut}; use futures::future::poll_fn; use futures::{SinkExt, Stream, StreamExt}; @@ -43,7 +44,7 @@ where Some(port) => write!(wrt, "{}:{}", host, port), }; - match wrt.get_mut().take().freeze().try_into() { + match wrt.get_mut().split().freeze().try_into() { Ok(value) => match head { RequestHeadType::Owned(ref mut head) => { head.headers.insert(HOST, value) @@ -199,7 +200,10 @@ where } impl AsyncRead for H1Connection { - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { + unsafe fn prepare_uninitialized_buffer( + &self, + buf: &mut [mem::MaybeUninit], + ) -> bool { self.io.as_ref().unwrap().prepare_uninitialized_buffer(buf) } diff --git a/actix-http/src/client/h2proto.rs b/actix-http/src/client/h2proto.rs index a94562f2d..ff8f21d00 100644 --- a/actix-http/src/client/h2proto.rs +++ b/actix-http/src/client/h2proto.rs @@ -1,3 +1,4 @@ +use std::convert::TryFrom; use std::time; use actix_codec::{AsyncRead, AsyncWrite}; @@ -5,7 +6,7 @@ use bytes::Bytes; use futures::future::poll_fn; use h2::{client::SendRequest, SendStream}; use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, TRANSFER_ENCODING}; -use http::{request::Request, HttpTryFrom, Method, Version}; +use http::{request::Request, Method, Version}; use crate::body::{BodySize, MessageBody}; use crate::header::HeaderMap; diff --git a/actix-http/src/client/pool.rs b/actix-http/src/client/pool.rs index 938ccb5d0..ef5bd5965 100644 --- a/actix-http/src/client/pool.rs +++ b/actix-http/src/client/pool.rs @@ -108,7 +108,7 @@ where let inner = self.1.clone(); let fut = async move { - let key = if let Some(authority) = req.uri.authority_part() { + let key = if let Some(authority) = req.uri.authority() { authority.clone().into() } else { return Err(ConnectError::Unresolverd); @@ -299,7 +299,7 @@ where ) { let (tx, rx) = oneshot::channel(); - let key: Key = connect.uri.authority_part().unwrap().clone().into(); + let key: Key = connect.uri.authority().unwrap().clone().into(); let entry = self.waiters.vacant_entry(); let token = entry.key(); entry.insert(Some((connect, tx))); diff --git a/actix-http/src/config.rs b/actix-http/src/config.rs index 6ea75e565..77633bdc2 100644 --- a/actix-http/src/config.rs +++ b/actix-http/src/config.rs @@ -1,10 +1,10 @@ use std::cell::UnsafeCell; use std::fmt::Write; use std::rc::Rc; -use std::time::{Duration, Instant}; +use std::time::Duration; use std::{fmt, net}; -use actix_rt::time::{delay, delay_for, Delay}; +use actix_rt::time::{delay_for, delay_until, Delay, Instant}; use bytes::BytesMut; use futures::{future, FutureExt}; use time; @@ -124,7 +124,7 @@ impl ServiceConfig { pub fn client_timer(&self) -> Option { let delay_time = self.0.client_timeout; if delay_time != 0 { - Some(delay( + Some(delay_until( self.0.timer.now() + Duration::from_millis(delay_time), )) } else { @@ -156,7 +156,7 @@ impl ServiceConfig { /// Return keep-alive timer delay is configured. pub fn keep_alive_timer(&self) -> Option { if let Some(ka) = self.0.keep_alive { - Some(delay(self.0.timer.now() + ka)) + Some(delay_until(self.0.timer.now() + ka)) } else { None } diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index f561ab0c4..d1b64bfb8 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -13,7 +13,7 @@ use flate2::write::{GzEncoder, ZlibEncoder}; use crate::body::{Body, BodySize, MessageBody, ResponseBody}; use crate::http::header::{ContentEncoding, CONTENT_ENCODING}; -use crate::http::{HeaderValue, HttpTryFrom, StatusCode}; +use crate::http::{HeaderValue, StatusCode}; use crate::{Error, ResponseHead}; use super::Writer; @@ -168,7 +168,7 @@ impl MessageBody for Encoder { fn update_head(encoding: ContentEncoding, head: &mut ResponseHead) { head.headers_mut().insert( CONTENT_ENCODING, - HeaderValue::try_from(Bytes::from_static(encoding.as_str().as_bytes())).unwrap(), + HeaderValue::from_static(encoding.as_str()), ); } diff --git a/actix-http/src/encoding/mod.rs b/actix-http/src/encoding/mod.rs index b55a43a7c..9f65f800c 100644 --- a/actix-http/src/encoding/mod.rs +++ b/actix-http/src/encoding/mod.rs @@ -20,7 +20,7 @@ impl Writer { } } fn take(&mut self) -> Bytes { - self.buf.take().freeze() + self.buf.split().freeze() } } diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index 2c09142e0..c580f3846 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -113,12 +113,6 @@ impl fmt::Debug for Error { } } -impl From<()> for Error { - fn from(_: ()) -> Self { - Error::from(UnitError) - } -} - impl std::error::Error for Error { fn description(&self) -> &str { "actix-http::Error" @@ -133,6 +127,12 @@ impl std::error::Error for Error { } } +impl From<()> for Error { + fn from(_: ()) -> Self { + Error::from(UnitError) + } +} + impl From for Error { fn from(_: std::convert::Infallible) -> Self { // `std::convert::Infallible` indicates an error @@ -182,11 +182,11 @@ impl ResponseError for FormError {} #[cfg(feature = "openssl")] /// `InternalServerError` for `openssl::ssl::Error` -impl ResponseError for open_ssl::ssl::Error {} +impl ResponseError for actix_connect::ssl::openssl::SslError {} #[cfg(feature = "openssl")] /// `InternalServerError` for `openssl::ssl::HandshakeError` -impl ResponseError for open_ssl::ssl::HandshakeError {} +impl ResponseError for actix_tls::openssl::HandshakeError {} /// Return `BAD_REQUEST` for `de::value::Error` impl ResponseError for DeError { @@ -230,13 +230,6 @@ impl ResponseError for header::InvalidHeaderValue { } } -/// `BadRequest` for `InvalidHeaderValue` -impl ResponseError for header::InvalidHeaderValueBytes { - fn status_code(&self) -> StatusCode { - StatusCode::BAD_REQUEST - } -} - /// A set of errors that can occur during parsing HTTP streams #[derive(Debug, Display)] pub enum ParseError { diff --git a/actix-http/src/h1/decoder.rs b/actix-http/src/h1/decoder.rs index ffa00288f..32ec64ba3 100644 --- a/actix-http/src/h1/decoder.rs +++ b/actix-http/src/h1/decoder.rs @@ -1,3 +1,4 @@ +use std::convert::TryFrom; use std::io; use std::marker::PhantomData; use std::mem::MaybeUninit; @@ -6,7 +7,7 @@ use std::task::Poll; use actix_codec::Decoder; use bytes::{Bytes, BytesMut}; use http::header::{HeaderName, HeaderValue}; -use http::{header, HttpTryFrom, Method, StatusCode, Uri, Version}; +use http::{header, Method, StatusCode, Uri, Version}; use httparse; use log::{debug, error, trace}; @@ -79,8 +80,8 @@ pub(crate) trait MessageType: Sized { // Unsafe: httparse check header value for valid utf-8 let value = unsafe { - HeaderValue::from_shared_unchecked( - slice.slice(idx.value.0, idx.value.1), + HeaderValue::from_maybe_shared_unchecked( + slice.slice(idx.value.0..idx.value.1), ) }; match name { @@ -428,7 +429,7 @@ impl Decoder for PayloadDecoder { let len = src.len() as u64; let buf; if *remaining > len { - buf = src.take().freeze(); + buf = src.split().freeze(); *remaining -= len; } else { buf = src.split_to(*remaining as usize).freeze(); @@ -463,7 +464,7 @@ impl Decoder for PayloadDecoder { if src.is_empty() { Ok(None) } else { - Ok(Some(PayloadItem::Chunk(src.take().freeze()))) + Ok(Some(PayloadItem::Chunk(src.split().freeze()))) } } } @@ -581,7 +582,7 @@ impl ChunkedState { } else { let slice; if *rem > len { - slice = rdr.take().freeze(); + slice = rdr.split().freeze(); *rem -= len; } else { slice = rdr.split_to(*rem as usize).freeze(); diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index d5b3a8ed6..0e2a58346 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -2,11 +2,10 @@ use std::collections::VecDeque; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; -use std::time::Instant; use std::{fmt, io, net}; use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed, FramedParts}; -use actix_rt::time::{delay, Delay}; +use actix_rt::time::{delay_until, Delay, Instant}; use actix_service::Service; use bitflags::bitflags; use bytes::{BufMut, BytesMut}; @@ -610,7 +609,7 @@ where // shutdown timeout if self.flags.contains(Flags::SHUTDOWN) { if let Some(interval) = self.codec.config().client_disconnect_timer() { - self.ka_timer = Some(delay(interval)); + self.ka_timer = Some(delay_until(interval)); } else { self.flags.insert(Flags::READ_DISCONNECT); if let Some(mut payload) = self.payload.take() { diff --git a/actix-http/src/h1/encoder.rs b/actix-http/src/h1/encoder.rs index 6396f3b55..e83ce90cf 100644 --- a/actix-http/src/h1/encoder.rs +++ b/actix-http/src/h1/encoder.rs @@ -2,11 +2,13 @@ use std::fmt::Write as FmtWrite; use std::io::Write; use std::marker::PhantomData; +use std::ptr::copy_nonoverlapping; use std::rc::Rc; +use std::slice::from_raw_parts_mut; use std::str::FromStr; use std::{cmp, fmt, io, mem}; -use bytes::{BufMut, Bytes, BytesMut}; +use bytes::{buf::BufMutExt, BufMut, Bytes, BytesMut}; use crate::body::BodySize; use crate::config::ServiceConfig; @@ -144,8 +146,8 @@ pub(crate) trait MessageType: Sized { // write headers let mut pos = 0; let mut has_date = false; - let mut remaining = dst.remaining_mut(); - let mut buf = unsafe { &mut *(dst.bytes_mut() as *mut [u8]) }; + let mut remaining = dst.capacity() - dst.len(); + let mut buf = dst.bytes_mut().as_mut_ptr() as *mut u8; for (key, value) in headers { match *key { CONNECTION => continue, @@ -159,61 +161,67 @@ pub(crate) trait MessageType: Sized { match value { map::Value::One(ref val) => { let v = val.as_ref(); - let len = k.len() + v.len() + 4; + let v_len = v.len(); + let k_len = k.len(); + let len = k_len + v_len + 4; if len > remaining { unsafe { dst.advance_mut(pos); } pos = 0; dst.reserve(len * 2); - remaining = dst.remaining_mut(); - unsafe { - buf = &mut *(dst.bytes_mut() as *mut _); - } + remaining = dst.capacity() - dst.len(); + buf = dst.bytes_mut().as_mut_ptr() as *mut u8; } // use upper Camel-Case - if camel_case { - write_camel_case(k, &mut buf[pos..pos + k.len()]); - } else { - buf[pos..pos + k.len()].copy_from_slice(k); + unsafe { + if camel_case { + write_camel_case(k, from_raw_parts_mut(buf, k_len)) + } else { + write_data(k, buf, k_len) + } + buf = buf.add(k_len); + write_data(b": ", buf, 2); + buf = buf.add(2); + write_data(v, buf, v_len); + buf = buf.add(v_len); + write_data(b"\r\n", buf, 2); + buf = buf.add(2); + pos += len; + remaining -= len; } - pos += k.len(); - buf[pos..pos + 2].copy_from_slice(b": "); - pos += 2; - buf[pos..pos + v.len()].copy_from_slice(v); - pos += v.len(); - buf[pos..pos + 2].copy_from_slice(b"\r\n"); - pos += 2; - remaining -= len; } map::Value::Multi(ref vec) => { for val in vec { let v = val.as_ref(); - let len = k.len() + v.len() + 4; + let v_len = v.len(); + let k_len = k.len(); + let len = k_len + v_len + 4; if len > remaining { unsafe { dst.advance_mut(pos); } pos = 0; dst.reserve(len * 2); - remaining = dst.remaining_mut(); - unsafe { - buf = &mut *(dst.bytes_mut() as *mut _); - } + remaining = dst.capacity() - dst.len(); + buf = dst.bytes_mut().as_mut_ptr() as *mut u8; } // use upper Camel-Case - if camel_case { - write_camel_case(k, &mut buf[pos..pos + k.len()]); - } else { - buf[pos..pos + k.len()].copy_from_slice(k); - } - pos += k.len(); - buf[pos..pos + 2].copy_from_slice(b": "); - pos += 2; - buf[pos..pos + v.len()].copy_from_slice(v); - pos += v.len(); - buf[pos..pos + 2].copy_from_slice(b"\r\n"); - pos += 2; + unsafe { + if camel_case { + write_camel_case(k, from_raw_parts_mut(buf, k_len)); + } else { + write_data(k, buf, k_len); + } + buf = buf.add(k_len); + write_data(b": ", buf, 2); + buf = buf.add(2); + write_data(v, buf, v_len); + buf = buf.add(v_len); + write_data(b"\r\n", buf, 2); + buf = buf.add(2); + }; + pos += len; remaining -= len; } } @@ -298,6 +306,12 @@ impl MessageType for RequestHeadType { Version::HTTP_10 => "HTTP/1.0", Version::HTTP_11 => "HTTP/1.1", Version::HTTP_2 => "HTTP/2.0", + Version::HTTP_3 => "HTTP/3.0", + _ => + return Err(io::Error::new( + io::ErrorKind::Other, + "unsupported version" + )), } ) .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) @@ -479,6 +493,10 @@ impl<'a> io::Write for Writer<'a> { } } +unsafe fn write_data(value: &[u8], buf: *mut u8, len: usize) { + copy_nonoverlapping(value.as_ptr(), buf, len); +} + fn write_camel_case(value: &[u8], buffer: &mut [u8]) { let mut index = 0; let key = value; @@ -525,7 +543,7 @@ mod tests { assert!(enc.encode(b"", &mut bytes).ok().unwrap()); } assert_eq!( - bytes.take().freeze(), + bytes.split().freeze(), Bytes::from_static(b"4\r\ntest\r\n0\r\n\r\n") ); } @@ -548,7 +566,8 @@ mod tests { ConnectionType::Close, &ServiceConfig::default(), ); - let data = String::from_utf8(Vec::from(bytes.take().freeze().as_ref())).unwrap(); + let data = + String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap(); assert!(data.contains("Content-Length: 0\r\n")); assert!(data.contains("Connection: close\r\n")); assert!(data.contains("Content-Type: plain/text\r\n")); @@ -561,7 +580,8 @@ mod tests { ConnectionType::KeepAlive, &ServiceConfig::default(), ); - let data = String::from_utf8(Vec::from(bytes.take().freeze().as_ref())).unwrap(); + let data = + String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap(); assert!(data.contains("Transfer-Encoding: chunked\r\n")); assert!(data.contains("Content-Type: plain/text\r\n")); assert!(data.contains("Date: date\r\n")); @@ -573,7 +593,8 @@ mod tests { ConnectionType::KeepAlive, &ServiceConfig::default(), ); - let data = String::from_utf8(Vec::from(bytes.take().freeze().as_ref())).unwrap(); + let data = + String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap(); assert!(data.contains("Content-Length: 100\r\n")); assert!(data.contains("Content-Type: plain/text\r\n")); assert!(data.contains("Date: date\r\n")); @@ -594,7 +615,8 @@ mod tests { ConnectionType::KeepAlive, &ServiceConfig::default(), ); - let data = String::from_utf8(Vec::from(bytes.take().freeze().as_ref())).unwrap(); + let data = + String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap(); assert!(data.contains("transfer-encoding: chunked\r\n")); assert!(data.contains("content-type: xml\r\n")); assert!(data.contains("content-type: plain/text\r\n")); @@ -627,7 +649,8 @@ mod tests { ConnectionType::Close, &ServiceConfig::default(), ); - let data = String::from_utf8(Vec::from(bytes.take().freeze().as_ref())).unwrap(); + let data = + String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap(); assert!(data.contains("content-length: 0\r\n")); assert!(data.contains("connection: close\r\n")); assert!(data.contains("authorization: another authorization\r\n")); diff --git a/actix-http/src/h1/service.rs b/actix-http/src/h1/service.rs index 354fed482..d6494b5cb 100644 --- a/actix-http/src/h1/service.rs +++ b/actix-http/src/h1/service.rs @@ -97,9 +97,8 @@ where mod openssl { use super::*; - use actix_tls::openssl::{Acceptor, SslStream}; + use actix_tls::openssl::{Acceptor, SslAcceptor, SslStream}; use actix_tls::{openssl::HandshakeError, SslError}; - use open_ssl::ssl::SslAcceptor; impl H1Service, S, B, X, U> where @@ -144,6 +143,56 @@ mod openssl { } } +#[cfg(feature = "rustls")] +mod rustls { + use super::*; + use actix_tls::rustls::{Acceptor, ServerConfig, TlsStream}; + use actix_tls::SslError; + use std::{fmt, io}; + + impl H1Service, S, B, X, U> + where + S: ServiceFactory, + S::Error: Into, + S::InitError: fmt::Debug, + S::Response: Into>, + B: MessageBody, + X: ServiceFactory, + X::Error: Into, + X::InitError: fmt::Debug, + U: ServiceFactory< + Config = (), + Request = (Request, Framed, Codec>), + Response = (), + >, + U::Error: fmt::Display, + U::InitError: fmt::Debug, + { + /// Create rustls based service + pub fn rustls( + self, + config: ServerConfig, + ) -> impl ServiceFactory< + Config = (), + Request = TcpStream, + Response = (), + Error = SslError, + InitError = (), + > { + pipeline_factory( + Acceptor::new(config) + .map_err(SslError::Ssl) + .map_init_err(|_| panic!()), + ) + .and_then(|io: TlsStream| { + let peer_addr = io.get_ref().0.peer_addr().ok(); + ok((io, peer_addr)) + }) + .and_then(self.map_err(SslError::Service)) + } + } +} + impl H1Service where S: ServiceFactory, diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index e6e8967df..707ac7b9d 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -1,13 +1,13 @@ use std::collections::VecDeque; +use std::convert::TryFrom; use std::future::Future; use std::marker::PhantomData; use std::pin::Pin; use std::task::{Context, Poll}; -use std::time::Instant; use std::{fmt, mem, net}; use actix_codec::{AsyncRead, AsyncWrite}; -use actix_rt::time::Delay; +use actix_rt::time::{Delay, Instant}; use actix_service::Service; use bitflags::bitflags; use bytes::{Bytes, BytesMut}; @@ -17,7 +17,6 @@ use h2::{RecvStream, SendStream}; use http::header::{ HeaderValue, ACCEPT_ENCODING, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING, }; -use http::HttpTryFrom; use log::{debug, error, trace}; use crate::body::{Body, BodySize, MessageBody, ResponseBody}; @@ -235,8 +234,9 @@ where if !has_date { let mut bytes = BytesMut::with_capacity(29); self.config.set_date_header(&mut bytes); - res.headers_mut() - .insert(DATE, HeaderValue::try_from(bytes.freeze()).unwrap()); + res.headers_mut().insert(DATE, unsafe { + HeaderValue::from_maybe_shared_unchecked(bytes.freeze()) + }); } res diff --git a/actix-http/src/h2/mod.rs b/actix-http/src/h2/mod.rs index 9c902f18c..28dd75383 100644 --- a/actix-http/src/h2/mod.rs +++ b/actix-http/src/h2/mod.rs @@ -35,7 +35,7 @@ impl Stream for Payload { match Pin::new(&mut this.pl).poll_data(cx) { Poll::Ready(Some(Ok(chunk))) => { let len = chunk.len(); - if let Err(err) = this.pl.release_capacity().release_capacity(len) { + if let Err(err) = this.pl.flow_control().release_capacity(len) { Poll::Ready(Some(Err(err.into()))) } else { Poll::Ready(Some(Ok(chunk))) diff --git a/actix-http/src/h2/service.rs b/actix-http/src/h2/service.rs index 864070eb3..1bacc5c95 100644 --- a/actix-http/src/h2/service.rs +++ b/actix-http/src/h2/service.rs @@ -102,9 +102,8 @@ where #[cfg(feature = "openssl")] mod openssl { use actix_service::{factory_fn, service_fn2}; - use actix_tls::openssl::{Acceptor, SslStream}; + use actix_tls::openssl::{Acceptor, SslAcceptor, SslStream}; use actix_tls::{openssl::HandshakeError, SslError}; - use open_ssl::ssl::SslAcceptor; use super::*; @@ -143,6 +142,51 @@ mod openssl { } } +#[cfg(feature = "rustls")] +mod rustls { + use super::*; + use actix_tls::rustls::{Acceptor, ServerConfig, Session, TlsStream}; + use actix_tls::SslError; + use std::{fmt, io}; + + impl H2Service, S, B> + where + S: ServiceFactory, + S::Error: Into + 'static, + S::Response: Into> + 'static, + ::Future: 'static, + B: MessageBody + 'static, + { + /// Create openssl based service + pub fn rustls( + self, + mut config: ServerConfig, + ) -> impl ServiceFactory< + Config = (), + Request = TcpStream, + Response = (), + Error = SslError, + InitError = S::InitError, + > { + let protos = vec!["h2".to_string().into()]; + config.set_protocols(&protos); + + pipeline_factory( + Acceptor::new(config) + .map_err(SslError::Ssl) + .map_init_err(|_| panic!()), + ) + .and_then(factory_fn(|| { + ok::<_, S::InitError>(service_fn2(|io: TlsStream| { + let peer_addr = io.get_ref().0.peer_addr().ok(); + ok((io, peer_addr)) + })) + })) + .and_then(self.map_err(SslError::Service)) + } + } +} + impl ServiceFactory for H2Service where T: AsyncRead + AsyncWrite + Unpin, diff --git a/actix-http/src/header/common/cache_control.rs b/actix-http/src/header/common/cache_control.rs index 55774619b..a3253b85b 100644 --- a/actix-http/src/header/common/cache_control.rs +++ b/actix-http/src/header/common/cache_control.rs @@ -80,12 +80,12 @@ impl fmt::Display for CacheControl { } impl IntoHeaderValue for CacheControl { - type Error = header::InvalidHeaderValueBytes; + type Error = header::InvalidHeaderValue; fn try_into(self) -> Result { let mut writer = Writer::new(); let _ = write!(&mut writer, "{}", self); - header::HeaderValue::from_shared(writer.take()) + header::HeaderValue::from_maybe_shared(writer.take()) } } diff --git a/actix-http/src/header/common/content_disposition.rs b/actix-http/src/header/common/content_disposition.rs index b2b6f34d7..d09024f3f 100644 --- a/actix-http/src/header/common/content_disposition.rs +++ b/actix-http/src/header/common/content_disposition.rs @@ -462,12 +462,12 @@ impl ContentDisposition { } impl IntoHeaderValue for ContentDisposition { - type Error = header::InvalidHeaderValueBytes; + type Error = header::InvalidHeaderValue; fn try_into(self) -> Result { let mut writer = Writer::new(); let _ = write!(&mut writer, "{}", self); - header::HeaderValue::from_shared(writer.take()) + header::HeaderValue::from_maybe_shared(writer.take()) } } @@ -768,9 +768,8 @@ mod tests { Mainstream browsers like Firefox (gecko) and Chrome use UTF-8 directly as above. (And now, only UTF-8 is handled by this implementation.) */ - let a = - HeaderValue::from_str("form-data; name=upload; filename=\"文件.webp\"") - .unwrap(); + let a = HeaderValue::from_str("form-data; name=upload; filename=\"文件.webp\"") + .unwrap(); let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap(); let b = ContentDisposition { disposition: DispositionType::FormData, @@ -884,7 +883,11 @@ mod tests { assert!(ContentDisposition::from_raw(&a).is_err()); let a = HeaderValue::from_static("inline; filename=\"\""); - assert!(ContentDisposition::from_raw(&a).expect("parse cd").get_filename().expect("filename").is_empty()); + assert!(ContentDisposition::from_raw(&a) + .expect("parse cd") + .get_filename() + .expect("filename") + .is_empty()); } #[test] diff --git a/actix-http/src/header/common/content_range.rs b/actix-http/src/header/common/content_range.rs index cc7f27548..a3e4d49ba 100644 --- a/actix-http/src/header/common/content_range.rs +++ b/actix-http/src/header/common/content_range.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use crate::error::ParseError; use crate::header::{ - HeaderValue, IntoHeaderValue, InvalidHeaderValueBytes, Writer, CONTENT_RANGE, + HeaderValue, IntoHeaderValue, InvalidHeaderValue, Writer, CONTENT_RANGE, }; header! { @@ -198,11 +198,11 @@ impl Display for ContentRangeSpec { } impl IntoHeaderValue for ContentRangeSpec { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; fn try_into(self) -> Result { let mut writer = Writer::new(); let _ = write!(&mut writer, "{}", self); - HeaderValue::from_shared(writer.take()) + HeaderValue::from_maybe_shared(writer.take()) } } diff --git a/actix-http/src/header/common/if_range.rs b/actix-http/src/header/common/if_range.rs index e910ebd96..ea5d69a13 100644 --- a/actix-http/src/header/common/if_range.rs +++ b/actix-http/src/header/common/if_range.rs @@ -3,7 +3,7 @@ use std::fmt::{self, Display, Write}; use crate::error::ParseError; use crate::header::{ self, from_one_raw_str, EntityTag, Header, HeaderName, HeaderValue, HttpDate, - IntoHeaderValue, InvalidHeaderValueBytes, Writer, + IntoHeaderValue, InvalidHeaderValue, Writer, }; use crate::httpmessage::HttpMessage; @@ -96,12 +96,12 @@ impl Display for IfRange { } impl IntoHeaderValue for IfRange { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; fn try_into(self) -> Result { let mut writer = Writer::new(); let _ = write!(&mut writer, "{}", self); - HeaderValue::from_shared(writer.take()) + HeaderValue::from_maybe_shared(writer.take()) } } diff --git a/actix-http/src/header/common/mod.rs b/actix-http/src/header/common/mod.rs index 30dfcaa6d..814050b13 100644 --- a/actix-http/src/header/common/mod.rs +++ b/actix-http/src/header/common/mod.rs @@ -164,13 +164,13 @@ macro_rules! header { } } impl $crate::http::header::IntoHeaderValue for $id { - type Error = $crate::http::header::InvalidHeaderValueBytes; + type Error = $crate::http::header::InvalidHeaderValue; fn try_into(self) -> Result<$crate::http::header::HeaderValue, Self::Error> { use std::fmt::Write; let mut writer = $crate::http::header::Writer::new(); let _ = write!(&mut writer, "{}", self); - $crate::http::header::HeaderValue::from_shared(writer.take()) + $crate::http::header::HeaderValue::from_maybe_shared(writer.take()) } } }; @@ -200,13 +200,13 @@ macro_rules! header { } } impl $crate::http::header::IntoHeaderValue for $id { - type Error = $crate::http::header::InvalidHeaderValueBytes; + type Error = $crate::http::header::InvalidHeaderValue; fn try_into(self) -> Result<$crate::http::header::HeaderValue, Self::Error> { use std::fmt::Write; let mut writer = $crate::http::header::Writer::new(); let _ = write!(&mut writer, "{}", self); - $crate::http::header::HeaderValue::from_shared(writer.take()) + $crate::http::header::HeaderValue::from_maybe_shared(writer.take()) } } }; @@ -236,7 +236,7 @@ macro_rules! header { } } impl $crate::http::header::IntoHeaderValue for $id { - type Error = $crate::http::header::InvalidHeaderValueBytes; + type Error = $crate::http::header::InvalidHeaderValue; fn try_into(self) -> Result<$crate::http::header::HeaderValue, Self::Error> { self.0.try_into() @@ -285,13 +285,13 @@ macro_rules! header { } } impl $crate::http::header::IntoHeaderValue for $id { - type Error = $crate::http::header::InvalidHeaderValueBytes; + type Error = $crate::http::header::InvalidHeaderValue; fn try_into(self) -> Result<$crate::http::header::HeaderValue, Self::Error> { use std::fmt::Write; let mut writer = $crate::http::header::Writer::new(); let _ = write!(&mut writer, "{}", self); - $crate::http::header::HeaderValue::from_shared(writer.take()) + $crate::http::header::HeaderValue::from_maybe_shared(writer.take()) } } }; diff --git a/actix-http/src/header/map.rs b/actix-http/src/header/map.rs index 5fcf79f75..dc49d53f3 100644 --- a/actix-http/src/header/map.rs +++ b/actix-http/src/header/map.rs @@ -1,9 +1,9 @@ use std::collections::hash_map::{self, Entry}; +use std::convert::TryFrom; use either::Either; use fxhash::FxHashMap; use http::header::{HeaderName, HeaderValue}; -use http::HttpTryFrom; /// A set of HTTP headers /// diff --git a/actix-http/src/header/mod.rs b/actix-http/src/header/mod.rs index 59cbb11c4..6fd3c1b96 100644 --- a/actix-http/src/header/mod.rs +++ b/actix-http/src/header/mod.rs @@ -1,6 +1,7 @@ //! Various http headers // This is mostly copy of [hyper](https://github.com/hyperium/hyper/tree/master/src/header) +use std::convert::TryFrom; use std::{fmt, str::FromStr}; use bytes::{Bytes, BytesMut}; @@ -73,58 +74,58 @@ impl<'a> IntoHeaderValue for &'a [u8] { } impl IntoHeaderValue for Bytes { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; #[inline] fn try_into(self) -> Result { - HeaderValue::from_shared(self) + HeaderValue::from_maybe_shared(self) } } impl IntoHeaderValue for Vec { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; #[inline] fn try_into(self) -> Result { - HeaderValue::from_shared(Bytes::from(self)) + HeaderValue::try_from(self) } } impl IntoHeaderValue for String { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; #[inline] fn try_into(self) -> Result { - HeaderValue::from_shared(Bytes::from(self)) + HeaderValue::try_from(self) } } impl IntoHeaderValue for usize { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; #[inline] fn try_into(self) -> Result { let s = format!("{}", self); - HeaderValue::from_shared(Bytes::from(s)) + HeaderValue::try_from(s) } } impl IntoHeaderValue for u64 { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; #[inline] fn try_into(self) -> Result { let s = format!("{}", self); - HeaderValue::from_shared(Bytes::from(s)) + HeaderValue::try_from(s) } } impl IntoHeaderValue for Mime { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; #[inline] fn try_into(self) -> Result { - HeaderValue::from_shared(Bytes::from(format!("{}", self))) + HeaderValue::try_from(format!("{}", self)) } } @@ -204,7 +205,7 @@ impl Writer { } } fn take(&mut self) -> Bytes { - self.buf.take().freeze() + self.buf.split().freeze() } } diff --git a/actix-http/src/header/shared/entity.rs b/actix-http/src/header/shared/entity.rs index da02dc193..7ef51a7d6 100644 --- a/actix-http/src/header/shared/entity.rs +++ b/actix-http/src/header/shared/entity.rs @@ -1,7 +1,7 @@ use std::fmt::{self, Display, Write}; use std::str::FromStr; -use crate::header::{HeaderValue, IntoHeaderValue, InvalidHeaderValueBytes, Writer}; +use crate::header::{HeaderValue, IntoHeaderValue, InvalidHeaderValue, Writer}; /// check that each char in the slice is either: /// 1. `%x21`, or @@ -157,12 +157,12 @@ impl FromStr for EntityTag { } impl IntoHeaderValue for EntityTag { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; fn try_into(self) -> Result { let mut wrt = Writer::new(); write!(wrt, "{}", self).unwrap(); - HeaderValue::from_shared(wrt.take()) + HeaderValue::from_maybe_shared(wrt.take()) } } diff --git a/actix-http/src/header/shared/httpdate.rs b/actix-http/src/header/shared/httpdate.rs index 350f77bbe..c8d26ef54 100644 --- a/actix-http/src/header/shared/httpdate.rs +++ b/actix-http/src/header/shared/httpdate.rs @@ -3,8 +3,8 @@ use std::io::Write; use std::str::FromStr; use std::time::{Duration, SystemTime, UNIX_EPOCH}; -use bytes::{BufMut, BytesMut}; -use http::header::{HeaderValue, InvalidHeaderValueBytes}; +use bytes::{buf::BufMutExt, BytesMut}; +use http::header::{HeaderValue, InvalidHeaderValue}; use crate::error::ParseError; use crate::header::IntoHeaderValue; @@ -58,12 +58,12 @@ impl From for HttpDate { } impl IntoHeaderValue for HttpDate { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; fn try_into(self) -> Result { let mut wrt = BytesMut::with_capacity(29).writer(); write!(wrt, "{}", self.0.rfc822()).unwrap(); - HeaderValue::from_shared(wrt.get_mut().take().freeze()) + HeaderValue::from_maybe_shared(wrt.get_mut().split().freeze()) } } diff --git a/actix-http/src/helpers.rs b/actix-http/src/helpers.rs index 84403d8fd..58ebff61f 100644 --- a/actix-http/src/helpers.rs +++ b/actix-http/src/helpers.rs @@ -60,7 +60,7 @@ pub(crate) fn write_status_line(version: Version, mut n: u16, bytes: &mut BytesM bytes.put_slice(&buf); if four { - bytes.put(b' '); + bytes.put_u8(b' '); } } @@ -203,33 +203,33 @@ mod tests { let mut bytes = BytesMut::new(); bytes.reserve(50); write_content_length(0, &mut bytes); - assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 0\r\n"[..]); + assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 0\r\n"[..]); bytes.reserve(50); write_content_length(9, &mut bytes); - assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 9\r\n"[..]); + assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 9\r\n"[..]); bytes.reserve(50); write_content_length(10, &mut bytes); - assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 10\r\n"[..]); + assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 10\r\n"[..]); bytes.reserve(50); write_content_length(99, &mut bytes); - assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 99\r\n"[..]); + assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 99\r\n"[..]); bytes.reserve(50); write_content_length(100, &mut bytes); - assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 100\r\n"[..]); + assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 100\r\n"[..]); bytes.reserve(50); write_content_length(101, &mut bytes); - assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 101\r\n"[..]); + assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 101\r\n"[..]); bytes.reserve(50); write_content_length(998, &mut bytes); - assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 998\r\n"[..]); + assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 998\r\n"[..]); bytes.reserve(50); write_content_length(1000, &mut bytes); - assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 1000\r\n"[..]); + assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 1000\r\n"[..]); bytes.reserve(50); write_content_length(1001, &mut bytes); - assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 1001\r\n"[..]); + assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 1001\r\n"[..]); bytes.reserve(50); write_content_length(5909, &mut bytes); - assert_eq!(bytes.take().freeze(), b"\r\ncontent-length: 5909\r\n"[..]); + assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 5909\r\n"[..]); } } diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs index e476623d1..190d5fbdc 100644 --- a/actix-http/src/lib.rs +++ b/actix-http/src/lib.rs @@ -51,7 +51,7 @@ pub mod http { // re-exports pub use http::header::{HeaderName, HeaderValue}; pub use http::uri::PathAndQuery; - pub use http::{uri, Error, HttpTryFrom, Uri}; + pub use http::{uri, Error, Uri}; pub use http::{Method, StatusCode, Version}; pub use crate::cookie::{Cookie, CookieBuilder}; diff --git a/actix-http/src/request.rs b/actix-http/src/request.rs index 77ece01c5..371e2ccd8 100644 --- a/actix-http/src/request.rs +++ b/actix-http/src/request.rs @@ -187,7 +187,7 @@ impl

fmt::Debug for Request

{ #[cfg(test)] mod tests { use super::*; - use http::HttpTryFrom; + use std::convert::TryFrom; #[test] fn test_basics() { diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index e9147aa4b..e7f145d39 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -1,12 +1,12 @@ //! Http response use std::cell::{Ref, RefMut}; +use std::convert::TryFrom; use std::future::Future; -use std::io::Write; use std::pin::Pin; use std::task::{Context, Poll}; use std::{fmt, str}; -use bytes::{BufMut, Bytes, BytesMut}; +use bytes::{Bytes, BytesMut}; use futures::stream::Stream; use serde::Serialize; use serde_json; @@ -17,7 +17,7 @@ use crate::error::Error; use crate::extensions::Extensions; use crate::header::{Header, IntoHeaderValue}; use crate::http::header::{self, HeaderName, HeaderValue}; -use crate::http::{Error as HttpError, HeaderMap, HttpTryFrom, StatusCode}; +use crate::http::{Error as HttpError, HeaderMap, StatusCode}; use crate::message::{BoxedResponseHead, ConnectionType, ResponseHead}; /// An HTTP Response @@ -384,7 +384,8 @@ impl ResponseBuilder { /// ``` pub fn header(&mut self, key: K, value: V) -> &mut Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { if let Some(parts) = parts(&mut self.head, &self.err) { @@ -416,7 +417,8 @@ impl ResponseBuilder { /// ``` pub fn set_header(&mut self, key: K, value: V) -> &mut Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { if let Some(parts) = parts(&mut self.head, &self.err) { @@ -485,7 +487,8 @@ impl ResponseBuilder { #[inline] pub fn content_type(&mut self, value: V) -> &mut Self where - HeaderValue: HttpTryFrom, + HeaderValue: TryFrom, + >::Error: Into, { if let Some(parts) = parts(&mut self.head, &self.err) { match HeaderValue::try_from(value) { @@ -501,9 +504,7 @@ impl ResponseBuilder { /// Set content length #[inline] pub fn content_length(&mut self, len: u64) -> &mut Self { - let mut wrt = BytesMut::new().writer(); - let _ = write!(wrt, "{}", len); - self.header(header::CONTENT_LENGTH, wrt.get_mut().take().freeze()) + self.header(header::CONTENT_LENGTH, len) } /// Set a cookie diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index de5fd0c55..3624060bf 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -193,9 +193,8 @@ where #[cfg(feature = "openssl")] mod openssl { use super::*; - use actix_tls::openssl::{Acceptor, SslStream}; + use actix_tls::openssl::{Acceptor, SslAcceptor, SslStream}; use actix_tls::{openssl::HandshakeError, SslError}; - use open_ssl::ssl::SslAcceptor; impl HttpService, S, B, X, U> where @@ -252,6 +251,71 @@ mod openssl { } } +#[cfg(feature = "rustls")] +mod rustls { + use super::*; + use actix_tls::rustls::{Acceptor, ServerConfig, Session, TlsStream}; + use actix_tls::SslError; + use std::io; + + impl HttpService, S, B, X, U> + where + S: ServiceFactory, + S::Error: Into + 'static, + S::InitError: fmt::Debug, + S::Response: Into> + 'static, + ::Future: 'static, + B: MessageBody + 'static, + X: ServiceFactory, + X::Error: Into, + X::InitError: fmt::Debug, + ::Future: 'static, + U: ServiceFactory< + Config = (), + Request = (Request, Framed, h1::Codec>), + Response = (), + >, + U::Error: fmt::Display, + U::InitError: fmt::Debug, + ::Future: 'static, + { + /// Create openssl based service + pub fn rustls( + self, + mut config: ServerConfig, + ) -> impl ServiceFactory< + Config = (), + Request = TcpStream, + Response = (), + Error = SslError, + InitError = (), + > { + let protos = vec!["h2".to_string().into(), "http/1.1".to_string().into()]; + config.set_protocols(&protos); + + pipeline_factory( + Acceptor::new(config) + .map_err(SslError::Ssl) + .map_init_err(|_| panic!()), + ) + .and_then(|io: TlsStream| { + let proto = if let Some(protos) = io.get_ref().1.get_alpn_protocol() { + if protos.windows(2).any(|window| window == b"h2") { + Protocol::Http2 + } else { + Protocol::Http1 + } + } else { + Protocol::Http1 + }; + let peer_addr = io.get_ref().0.peer_addr().ok(); + ok((io, proto, peer_addr)) + }) + .and_then(self.map_err(SslError::Service)) + } + } +} + impl ServiceFactory for HttpService where T: AsyncRead + AsyncWrite + Unpin, diff --git a/actix-http/src/test.rs b/actix-http/src/test.rs index ebb7bda37..b629ad784 100644 --- a/actix-http/src/test.rs +++ b/actix-http/src/test.rs @@ -1,4 +1,5 @@ //! Test Various helpers for Actix applications to use during testing. +use std::convert::TryFrom; use std::fmt::Write as FmtWrite; use std::io::{self, Read, Write}; use std::pin::Pin; @@ -8,7 +9,7 @@ use std::task::{Context, Poll}; use actix_codec::{AsyncRead, AsyncWrite}; use bytes::{Bytes, BytesMut}; use http::header::{self, HeaderName, HeaderValue}; -use http::{HttpTryFrom, Method, Uri, Version}; +use http::{Error as HttpError, Method, Uri, Version}; use percent_encoding::percent_encode; use crate::cookie::{Cookie, CookieJar, USERINFO}; @@ -82,7 +83,8 @@ impl TestRequest { /// Create TestRequest and set header pub fn with_header(key: K, value: V) -> TestRequest where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { TestRequest::default().header(key, value).take() @@ -118,7 +120,8 @@ impl TestRequest { /// Set a header pub fn header(&mut self, key: K, value: V) -> &mut Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { if let Ok(key) = HeaderName::try_from(key) { diff --git a/actix-http/src/ws/frame.rs b/actix-http/src/ws/frame.rs index 46e9f36db..0949b711f 100644 --- a/actix-http/src/ws/frame.rs +++ b/actix-http/src/ws/frame.rs @@ -180,11 +180,11 @@ impl Parser { } else if payload_len <= 65_535 { dst.reserve(p_len + 4 + if mask { 4 } else { 0 }); dst.put_slice(&[one, two | 126]); - dst.put_u16_be(payload_len as u16); + dst.put_u16(payload_len as u16); } else { dst.reserve(p_len + 10 + if mask { 4 } else { 0 }); dst.put_slice(&[one, two | 127]); - dst.put_u64_be(payload_len as u64); + dst.put_u64(payload_len as u64); }; if mask { diff --git a/actix-http/tests/test_rustls.rs b/actix-http/tests/test_rustls.rs index 4a649ca37..89719221d 100644 --- a/actix-http/tests/test_rustls.rs +++ b/actix-http/tests/test_rustls.rs @@ -1,13 +1,10 @@ #![cfg(feature = "rustls")] -use actix_codec::{AsyncRead, AsyncWrite}; use actix_http::error::PayloadError; use actix_http::http::header::{self, HeaderName, HeaderValue}; use actix_http::http::{Method, StatusCode, Version}; use actix_http::{body, error, Error, HttpService, Request, Response}; use actix_http_test::TestServer; -use actix_server::ssl::RustlsAcceptor; -use actix_server_config::ServerConfig; -use actix_service::{factory_fn_cfg, pipeline_factory, service_fn2, ServiceFactory}; +use actix_service::{factory_fn_cfg, service_fn2}; use bytes::{Bytes, BytesMut}; use futures::future::{self, err, ok}; @@ -31,7 +28,7 @@ where Ok(body) } -fn ssl_acceptor() -> io::Result> { +fn ssl_acceptor() -> RustlsServerConfig { // load ssl keys let mut config = RustlsServerConfig::new(NoClientAuth::new()); let cert_file = &mut BufReader::new(File::open("../tests/cert.pem").unwrap()); @@ -39,22 +36,45 @@ fn ssl_acceptor() -> io::Result let cert_chain = certs(cert_file).unwrap(); let mut keys = pkcs8_private_keys(key_file).unwrap(); config.set_single_cert(cert_chain, keys.remove(0)).unwrap(); + config +} - let protos = vec![b"h2".to_vec()]; - config.set_protocols(&protos); - Ok(RustlsAcceptor::new(config)) +#[actix_rt::test] +async fn test_h1() -> io::Result<()> { + let srv = TestServer::start(move || { + HttpService::build() + .h1(|_| future::ok::<_, Error>(Response::Ok().finish())) + .rustls(ssl_acceptor()) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + Ok(()) } #[actix_rt::test] async fn test_h2() -> io::Result<()> { - let rustls = ssl_acceptor()?; let srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|_| future::ok::<_, Error>(Response::Ok().finish())) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| future::ok::<_, Error>(Response::Ok().finish())) + .rustls(ssl_acceptor()) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert!(response.status().is_success()); + Ok(()) +} + +#[actix_rt::test] +async fn test_h1_1() -> io::Result<()> { + let srv = TestServer::start(move || { + HttpService::build() + .h1(|req: Request| { + assert!(req.peer_addr().is_some()); + assert_eq!(req.version(), Version::HTTP_11); + future::ok::<_, Error>(Response::Ok().finish()) + }) + .rustls(ssl_acceptor()) }); let response = srv.sget("/").send().await.unwrap(); @@ -64,18 +84,14 @@ async fn test_h2() -> io::Result<()> { #[actix_rt::test] async fn test_h2_1() -> io::Result<()> { - let rustls = ssl_acceptor()?; let srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .finish(|req: Request| { - assert!(req.peer_addr().is_some()); - assert_eq!(req.version(), Version::HTTP_2); - future::ok::<_, Error>(Response::Ok().finish()) - }) - .map_err(|_| ()), - ) + HttpService::build() + .finish(|req: Request| { + assert!(req.peer_addr().is_some()); + assert_eq!(req.version(), Version::HTTP_2); + future::ok::<_, Error>(Response::Ok().finish()) + }) + .rustls(ssl_acceptor()) }); let response = srv.sget("/").send().await.unwrap(); @@ -86,19 +102,15 @@ async fn test_h2_1() -> io::Result<()> { #[actix_rt::test] async fn test_h2_body1() -> io::Result<()> { let data = "HELLOWORLD".to_owned().repeat(64 * 1024); - let rustls = ssl_acceptor()?; let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|mut req: Request<_>| { - async move { - let body = load_body(req.take_payload()).await?; - Ok::<_, Error>(Response::Ok().body(body)) - } - }) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|mut req: Request<_>| { + async move { + let body = load_body(req.take_payload()).await?; + Ok::<_, Error>(Response::Ok().body(body)) + } + }) + .rustls(ssl_acceptor()) }); let response = srv.sget("/").send_body(data.clone()).await.unwrap(); @@ -111,31 +123,25 @@ async fn test_h2_body1() -> io::Result<()> { #[actix_rt::test] async fn test_h2_content_length() { - let rustls = ssl_acceptor().unwrap(); - let srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|req: Request| { - let indx: usize = req.uri().path()[1..].parse().unwrap(); - let statuses = [ - StatusCode::NO_CONTENT, - StatusCode::CONTINUE, - StatusCode::SWITCHING_PROTOCOLS, - StatusCode::PROCESSING, - StatusCode::OK, - StatusCode::NOT_FOUND, - ]; - future::ok::<_, ()>(Response::new(statuses[indx])) - }) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|req: Request| { + let indx: usize = req.uri().path()[1..].parse().unwrap(); + let statuses = [ + StatusCode::NO_CONTENT, + StatusCode::CONTINUE, + StatusCode::SWITCHING_PROTOCOLS, + StatusCode::PROCESSING, + StatusCode::OK, + StatusCode::NOT_FOUND, + ]; + future::ok::<_, ()>(Response::new(statuses[indx])) + }) + .rustls(ssl_acceptor()) }); let header = HeaderName::from_static("content-length"); let value = HeaderValue::from_static("0"); - { for i in 0..4 { let req = srv @@ -165,14 +171,9 @@ async fn test_h2_content_length() { async fn test_h2_headers() { let data = STR.repeat(10); let data2 = data.clone(); - let rustls = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { let data = data.clone(); - pipeline_factory(rustls - .clone() - .map_err(|e| println!("Rustls error: {}", e))) - .and_then( HttpService::build().h2(move |_| { let mut config = Response::Ok(); for idx in 0..90 { @@ -194,7 +195,8 @@ async fn test_h2_headers() { ); } future::ok::<_, ()>(config.body(data.clone())) - }).map_err(|_| ())) + }) + .rustls(ssl_acceptor()) }); let response = srv.sget("/").send().await.unwrap(); @@ -229,14 +231,10 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[actix_rt::test] async fn test_h2_body2() { - let rustls = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|_| future::ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| future::ok::<_, ()>(Response::Ok().body(STR))) + .rustls(ssl_acceptor()) }); let response = srv.sget("/").send().await.unwrap(); @@ -249,14 +247,10 @@ async fn test_h2_body2() { #[actix_rt::test] async fn test_h2_head_empty() { - let rustls = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) + HttpService::build() + .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) + .rustls(ssl_acceptor()) }); let response = srv.shead("/").send().await.unwrap(); @@ -278,18 +272,12 @@ async fn test_h2_head_empty() { #[actix_rt::test] async fn test_h2_head_binary() { - let rustls = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|_| { - ok::<_, ()>( - Response::Ok().content_length(STR.len() as u64).body(STR), - ) - }) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| { + ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) + }) + .rustls(ssl_acceptor()) }); let response = srv.shead("/").send().await.unwrap(); @@ -310,14 +298,10 @@ async fn test_h2_head_binary() { #[actix_rt::test] async fn test_h2_head_binary2() { - let rustls = ssl_acceptor().unwrap(); let srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) + .rustls(ssl_acceptor()) }); let response = srv.shead("/").send().await.unwrap(); @@ -334,20 +318,15 @@ async fn test_h2_head_binary2() { #[actix_rt::test] async fn test_h2_body_length() { - let rustls = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|_| { - let body = once(ok(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok() - .body(body::SizedStream::new(STR.len() as u64, body)), - ) - }) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| { + let body = once(ok(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok().body(body::SizedStream::new(STR.len() as u64, body)), + ) + }) + .rustls(ssl_acceptor()) }); let response = srv.sget("/").send().await.unwrap(); @@ -360,22 +339,17 @@ async fn test_h2_body_length() { #[actix_rt::test] async fn test_h2_body_chunked_explicit() { - let rustls = ssl_acceptor().unwrap(); let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|_| { - let body = - once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); - ok::<_, ()>( - Response::Ok() - .header(header::TRANSFER_ENCODING, "chunked") - .streaming(body), - ) - }) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| { + let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); + ok::<_, ()>( + Response::Ok() + .header(header::TRANSFER_ENCODING, "chunked") + .streaming(body), + ) + }) + .rustls(ssl_acceptor()) }); let response = srv.sget("/").send().await.unwrap(); @@ -391,24 +365,19 @@ async fn test_h2_body_chunked_explicit() { #[actix_rt::test] async fn test_h2_response_http_error_handling() { - let rustls = ssl_acceptor().unwrap(); - let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(factory_fn_cfg(|_: &ServerConfig| { - ok::<_, ()>(service_fn2(|_| { - let broken_header = Bytes::from_static(b"\0\0\0"); - ok::<_, ()>( - Response::Ok() - .header(http::header::CONTENT_TYPE, broken_header) - .body(STR), - ) - })) - })) - .map_err(|_| ()), - ) + HttpService::build() + .h2(factory_fn_cfg(|_: ()| { + ok::<_, ()>(service_fn2(|_| { + let broken_header = Bytes::from_static(b"\0\0\0"); + ok::<_, ()>( + Response::Ok() + .header(http::header::CONTENT_TYPE, broken_header) + .body(STR), + ) + })) + })) + .rustls(ssl_acceptor()) }); let response = srv.sget("/").send().await.unwrap(); @@ -421,15 +390,26 @@ async fn test_h2_response_http_error_handling() { #[actix_rt::test] async fn test_h2_service_error() { - let rustls = ssl_acceptor().unwrap(); - let mut srv = TestServer::start(move || { - pipeline_factory(rustls.clone().map_err(|e| println!("Rustls error: {}", e))) - .and_then( - HttpService::build() - .h2(|_| err::(error::ErrorBadRequest("error"))) - .map_err(|_| ()), - ) + HttpService::build() + .h2(|_| err::(error::ErrorBadRequest("error"))) + .rustls(ssl_acceptor()) + }); + + let response = srv.sget("/").send().await.unwrap(); + assert_eq!(response.status(), http::StatusCode::BAD_REQUEST); + + // read response + let bytes = srv.load_body(response).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(b"error")); +} + +#[actix_rt::test] +async fn test_h1_service_error() { + let mut srv = TestServer::start(move || { + HttpService::build() + .h1(|_| err::(error::ErrorBadRequest("error"))) + .rustls(ssl_acceptor()) }); let response = srv.sget("/").send().await.unwrap(); diff --git a/actix-http/tests/test_ws.rs b/actix-http/tests/test_ws.rs index 5ac5fcaaf..284a4218a 100644 --- a/actix-http/tests/test_ws.rs +++ b/actix-http/tests/test_ws.rs @@ -2,7 +2,7 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_http::{body, h1, ws, Error, HttpService, Request, Response}; use actix_http_test::TestServer; use actix_utils::framed::FramedTransport; -use bytes::{Bytes, BytesMut}; +use bytes::BytesMut; use futures::future; use futures::{SinkExt, StreamExt}; @@ -62,7 +62,7 @@ async fn test_simple() { let (item, mut framed) = framed.into_future().await; assert_eq!( item.unwrap().unwrap(), - ws::Frame::Binary(Some(Bytes::from_static(b"text").into())) + ws::Frame::Binary(Some(BytesMut::from(&b"text"[..]).into())) ); framed.send(ws::Message::Ping("text".into())).await.unwrap(); diff --git a/actix-identity/Cargo.toml b/actix-identity/Cargo.toml index f9c664672..eea854263 100644 --- a/actix-identity/Cargo.toml +++ b/actix-identity/Cargo.toml @@ -27,4 +27,4 @@ time = "0.1.42" [dev-dependencies] actix-rt = "1.0.0-alpha.2" actix-http = "0.3.0-alpha.2" -bytes = "0.4" \ No newline at end of file +bytes = "0.5.2" \ No newline at end of file diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index 2107af5a0..f1913c3f1 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -21,7 +21,7 @@ path = "src/lib.rs" actix-web = { version = "2.0.0-alpha.2", default-features = false } actix-service = "1.0.0-alpha.2" actix-utils = "1.0.0-alpha.2" -bytes = "0.4" +bytes = "0.5.2" derive_more = "0.99.2" httparse = "1.3" futures = "0.3.1" diff --git a/actix-multipart/src/server.rs b/actix-multipart/src/server.rs index c49896761..7d1bbca46 100644 --- a/actix-multipart/src/server.rs +++ b/actix-multipart/src/server.rs @@ -1,5 +1,6 @@ //! Multipart payload support use std::cell::{Cell, RefCell, RefMut}; +use std::convert::TryFrom; use std::marker::PhantomData; use std::pin::Pin; use std::rc::Rc; @@ -16,7 +17,6 @@ use actix_web::error::{ParseError, PayloadError}; use actix_web::http::header::{ self, ContentDisposition, HeaderMap, HeaderName, HeaderValue, }; -use actix_web::http::HttpTryFrom; use crate::error::MultipartError; @@ -582,7 +582,7 @@ impl InnerField { } } } else { - Poll::Ready(Some(Ok(payload.buf.take().freeze()))) + Poll::Ready(Some(Ok(payload.buf.split().freeze()))) }; } } @@ -792,7 +792,7 @@ impl PayloadBuffer { pub fn readline_or_eof(&mut self) -> Result, MultipartError> { match self.readline() { Err(MultipartError::Incomplete) if self.eof => { - Ok(Some(self.buf.take().freeze())) + Ok(Some(self.buf.split().freeze())) } line => line, } @@ -800,7 +800,7 @@ impl PayloadBuffer { /// Put unprocessed data back to the buffer pub fn unprocessed(&mut self, data: Bytes) { - let buf = BytesMut::from(data); + let buf = BytesMut::from(data.as_ref()); let buf = std::mem::replace(&mut self.buf, buf); self.buf.extend_from_slice(&buf); } @@ -893,8 +893,8 @@ mod tests { #[actix_rt::test] async fn test_multipart_no_end_crlf() { let (sender, payload) = create_stream(); - let (bytes, headers) = create_simple_request_with_header(); - let bytes_stripped = bytes.slice_to(bytes.len()); // strip crlf + let (mut bytes, headers) = create_simple_request_with_header(); + let bytes_stripped = bytes.split_to(bytes.len()); // strip crlf sender.send(Ok(bytes_stripped)).unwrap(); drop(sender); // eof diff --git a/actix-session/Cargo.toml b/actix-session/Cargo.toml index d396a787a..c33642d67 100644 --- a/actix-session/Cargo.toml +++ b/actix-session/Cargo.toml @@ -26,7 +26,7 @@ cookie-session = ["actix-web/secure-cookies"] [dependencies] actix-web = "2.0.0-alpha.2" actix-service = "1.0.0-alpha.2" -bytes = "0.4" +bytes = "0.5.2" derive_more = "0.99.2" futures = "0.3.1" serde = "1.0" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index e884aaddd..99ccd4bbb 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "awc" -version = "0.3.0-alpha.2" +version = "0.3.0-alpha.3" authors = ["Nikolay Kim "] description = "Actix http client." readme = "README.md" @@ -21,7 +21,7 @@ name = "awc" path = "src/lib.rs" [package.metadata.docs.rs] -features = ["openssl", "brotli", "flate2-zlib"] +features = ["openssl", "rustls", "brotli", "flate2-zlib"] [features] default = ["brotli", "flate2-zlib"] @@ -30,7 +30,7 @@ default = ["brotli", "flate2-zlib"] openssl = ["open-ssl", "actix-http/openssl"] # rustls -# rustls = ["rust-tls", "actix-http/rustls"] +rustls = ["rust-tls", "actix-http/rustls"] # brotli encoding, requires c compiler brotli = ["actix-http/brotli"] @@ -42,13 +42,13 @@ flate2-zlib = ["actix-http/flate2-zlib"] flate2-rust = ["actix-http/flate2-rust"] [dependencies] -actix-codec = "0.2.0-alpha.2" -actix-service = "1.0.0-alpha.2" -actix-http = "0.3.0-alpha.2" -actix-rt = "1.0.0-alpha.2" +actix-codec = "0.2.0-alpha.3" +actix-service = "1.0.0-alpha.3" +actix-http = "0.3.0-alpha.3" +actix-rt = "1.0.0-alpha.3" -base64 = "0.10.1" -bytes = "0.4" +base64 = "0.11" +bytes = "0.5.2" derive_more = "0.99.2" futures = "0.3.1" log =" 0.4" @@ -59,16 +59,16 @@ serde = "1.0" serde_json = "1.0" serde_urlencoded = "0.6.1" open-ssl = { version="0.10", package="openssl", optional = true } -# rust-tls = { version = "0.16.0", package="rustls", optional = true, features = ["dangerous_configuration"] } +rust-tls = { version = "0.16.0", package="rustls", optional = true, features = ["dangerous_configuration"] } [dev-dependencies] -actix-connect = { version = "1.0.0-alpha.2", features=["openssl"] } -actix-web = { version = "2.0.0-alpha.2", features=["openssl"] } -actix-http = { version = "0.3.0-alpha.2", features=["openssl"] } -actix-http-test = { version = "0.3.0-alpha.2", features=["openssl"] } -actix-utils = "1.0.0-alpha.2" -actix-server = { version = "1.0.0-alpha.2" } -#actix-tls = { version = "0.1.0-alpha.1", features=["openssl"] } +actix-connect = { version = "1.0.0-alpha.3", features=["openssl"] } +actix-web = { version = "2.0.0-alpha.3", features=["openssl"] } +actix-http = { version = "0.3.0-alpha.3", features=["openssl"] } +actix-http-test = { version = "0.3.0-alpha.3", features=["openssl"] } +actix-utils = "1.0.0-alpha.3" +actix-server = { version = "1.0.0-alpha.3" } +actix-tls = { version = "1.0.0-alpha.3", features=["openssl", "rustls"] } brotli2 = { version="0.3.2" } flate2 = { version="1.0.2" } env_logger = "0.6" diff --git a/awc/src/builder.rs b/awc/src/builder.rs index 463f40303..7bd0171ec 100644 --- a/awc/src/builder.rs +++ b/awc/src/builder.rs @@ -1,10 +1,11 @@ use std::cell::RefCell; +use std::convert::TryFrom; use std::fmt; use std::rc::Rc; use std::time::Duration; use actix_http::client::{Connect, ConnectError, Connection, Connector}; -use actix_http::http::{header, HeaderMap, HeaderName, HttpTryFrom}; +use actix_http::http::{header, Error as HttpError, HeaderMap, HeaderName}; use actix_service::Service; use crate::connect::ConnectorWrapper; @@ -97,8 +98,8 @@ impl ClientBuilder { /// get added to every request. pub fn header(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, - >::Error: fmt::Debug, + HeaderName: TryFrom, + >::Error: fmt::Debug + Into, V: header::IntoHeaderValue, V::Error: fmt::Debug, { diff --git a/awc/src/connect.rs b/awc/src/connect.rs index cc92fdbb6..44dbcd60a 100644 --- a/awc/src/connect.rs +++ b/awc/src/connect.rs @@ -1,7 +1,7 @@ use std::pin::Pin; use std::rc::Rc; use std::task::{Context, Poll}; -use std::{fmt, io, net}; +use std::{fmt, io, mem, net}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_http::body::Body; @@ -201,7 +201,10 @@ impl fmt::Debug for BoxedSocket { } impl AsyncRead for BoxedSocket { - unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { + unsafe fn prepare_uninitialized_buffer( + &self, + buf: &mut [mem::MaybeUninit], + ) -> bool { self.0.as_read().prepare_uninitialized_buffer(buf) } diff --git a/awc/src/error.rs b/awc/src/error.rs index 8816c4075..7fece74ee 100644 --- a/awc/src/error.rs +++ b/awc/src/error.rs @@ -3,13 +3,14 @@ pub use actix_http::client::{ ConnectError, FreezeRequestError, InvalidUrl, SendRequestError, }; pub use actix_http::error::PayloadError; +pub use actix_http::http::Error as HttpError; pub use actix_http::ws::HandshakeError as WsHandshakeError; pub use actix_http::ws::ProtocolError as WsProtocolError; use actix_http::ResponseError; use serde_json::error::Error as JsonError; -use actix_http::http::{header::HeaderValue, Error as HttpError, StatusCode}; +use actix_http::http::{header::HeaderValue, StatusCode}; use derive_more::{Display, From}; /// Websocket client error diff --git a/awc/src/frozen.rs b/awc/src/frozen.rs index 61ba87aad..748a15d3b 100644 --- a/awc/src/frozen.rs +++ b/awc/src/frozen.rs @@ -1,3 +1,4 @@ +use std::convert::TryFrom; use std::net; use std::rc::Rc; use std::time::Duration; @@ -8,9 +9,7 @@ use serde::Serialize; use actix_http::body::Body; use actix_http::http::header::IntoHeaderValue; -use actix_http::http::{ - Error as HttpError, HeaderMap, HeaderName, HttpTryFrom, Method, Uri, -}; +use actix_http::http::{Error as HttpError, HeaderMap, HeaderName, Method, Uri}; use actix_http::{Error, RequestHead}; use crate::sender::{RequestSender, SendClientRequest}; @@ -112,7 +111,8 @@ impl FrozenClientRequest { /// Create a `FrozenSendBuilder` with an extra header pub fn extra_header(&self, key: K, value: V) -> FrozenSendBuilder where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { self.extra_headers(HeaderMap::new()) @@ -139,7 +139,8 @@ impl FrozenSendBuilder { /// Insert a header, it overrides existing header in `FrozenClientRequest`. pub fn extra_header(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { match HeaderName::try_from(key) { diff --git a/awc/src/lib.rs b/awc/src/lib.rs index e995519ea..64784ed95 100644 --- a/awc/src/lib.rs +++ b/awc/src/lib.rs @@ -19,12 +19,13 @@ //! } //! ``` use std::cell::RefCell; +use std::convert::TryFrom; use std::rc::Rc; use std::time::Duration; pub use actix_http::{client::Connector, cookie, http}; -use actix_http::http::{HeaderMap, HttpTryFrom, Method, Uri}; +use actix_http::http::{Error as HttpError, HeaderMap, Method, Uri}; use actix_http::RequestHead; mod builder; @@ -102,7 +103,8 @@ impl Client { /// Construct HTTP request. pub fn request(&self, method: Method, url: U) -> ClientRequest where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { let mut req = ClientRequest::new(method, url, self.0.clone()); @@ -118,7 +120,8 @@ impl Client { /// copies all headers and the method. pub fn request_from(&self, url: U, head: &RequestHead) -> ClientRequest where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { let mut req = self.request(head.method.clone(), url); for (key, value) in head.headers.iter() { @@ -130,7 +133,8 @@ impl Client { /// Construct HTTP *GET* request. pub fn get(&self, url: U) -> ClientRequest where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { self.request(Method::GET, url) } @@ -138,7 +142,8 @@ impl Client { /// Construct HTTP *HEAD* request. pub fn head(&self, url: U) -> ClientRequest where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { self.request(Method::HEAD, url) } @@ -146,7 +151,8 @@ impl Client { /// Construct HTTP *PUT* request. pub fn put(&self, url: U) -> ClientRequest where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { self.request(Method::PUT, url) } @@ -154,7 +160,8 @@ impl Client { /// Construct HTTP *POST* request. pub fn post(&self, url: U) -> ClientRequest where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { self.request(Method::POST, url) } @@ -162,7 +169,8 @@ impl Client { /// Construct HTTP *PATCH* request. pub fn patch(&self, url: U) -> ClientRequest where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { self.request(Method::PATCH, url) } @@ -170,7 +178,8 @@ impl Client { /// Construct HTTP *DELETE* request. pub fn delete(&self, url: U) -> ClientRequest where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { self.request(Method::DELETE, url) } @@ -178,7 +187,8 @@ impl Client { /// Construct HTTP *OPTIONS* request. pub fn options(&self, url: U) -> ClientRequest where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { self.request(Method::OPTIONS, url) } @@ -186,7 +196,8 @@ impl Client { /// Construct WebSockets request. pub fn ws(&self, url: U) -> ws::WebsocketsRequest where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { let mut req = ws::WebsocketsRequest::new(url, self.0.clone()); for (key, value) in self.0.headers.iter() { diff --git a/awc/src/request.rs b/awc/src/request.rs index 3660f8086..5ca4973cd 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -1,10 +1,10 @@ +use std::convert::TryFrom; use std::fmt::Write as FmtWrite; -use std::io::Write; use std::rc::Rc; use std::time::Duration; use std::{fmt, net}; -use bytes::{BufMut, Bytes, BytesMut}; +use bytes::Bytes; use futures::Stream; use percent_encoding::percent_encode; use serde::Serialize; @@ -13,8 +13,8 @@ use actix_http::body::Body; use actix_http::cookie::{Cookie, CookieJar, USERINFO}; use actix_http::http::header::{self, Header, IntoHeaderValue}; use actix_http::http::{ - uri, ConnectionType, Error as HttpError, HeaderMap, HeaderName, HeaderValue, - HttpTryFrom, Method, Uri, Version, + uri, ConnectionType, Error as HttpError, HeaderMap, HeaderName, HeaderValue, Method, + Uri, Version, }; use actix_http::{Error, RequestHead}; @@ -67,7 +67,8 @@ impl ClientRequest { /// Create new client request builder. pub(crate) fn new(method: Method, uri: U, config: Rc) -> Self where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { ClientRequest { config, @@ -86,7 +87,8 @@ impl ClientRequest { #[inline] pub fn uri(mut self, uri: U) -> Self where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { match Uri::try_from(uri) { Ok(uri) => self.head.uri = uri, @@ -196,7 +198,8 @@ impl ClientRequest { /// ``` pub fn header(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { match HeaderName::try_from(key) { @@ -212,7 +215,8 @@ impl ClientRequest { /// Insert a header, replaces existing header. pub fn set_header(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { match HeaderName::try_from(key) { @@ -228,7 +232,8 @@ impl ClientRequest { /// Insert a header only if it is not yet set. pub fn set_header_if_none(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { match HeaderName::try_from(key) { @@ -264,7 +269,8 @@ impl ClientRequest { #[inline] pub fn content_type(mut self, value: V) -> Self where - HeaderValue: HttpTryFrom, + HeaderValue: TryFrom, + >::Error: Into, { match HeaderValue::try_from(value) { Ok(value) => self.head.headers.insert(header::CONTENT_TYPE, value), @@ -276,9 +282,7 @@ impl ClientRequest { /// Set content length #[inline] pub fn content_length(self, len: u64) -> Self { - let mut wrt = BytesMut::new().writer(); - let _ = write!(wrt, "{}", len); - self.header(header::CONTENT_LENGTH, wrt.get_mut().take().freeze()) + self.header(header::CONTENT_LENGTH, len) } /// Set HTTP basic authorization header @@ -513,9 +517,9 @@ impl ClientRequest { let uri = &self.head.uri; if uri.host().is_none() { return Err(InvalidUrl::MissingHost.into()); - } else if uri.scheme_part().is_none() { + } else if uri.scheme().is_none() { return Err(InvalidUrl::MissingScheme.into()); - } else if let Some(scheme) = uri.scheme_part() { + } else if let Some(scheme) = uri.scheme() { match scheme.as_str() { "http" | "ws" | "https" | "wss" => (), _ => return Err(InvalidUrl::UnknownScheme.into()), @@ -551,7 +555,7 @@ impl ClientRequest { let https = slf .head .uri - .scheme_part() + .scheme() .map(|s| s == &uri::Scheme::HTTPS) .unwrap_or(true); diff --git a/awc/src/response.rs b/awc/src/response.rs index 00ab4cee1..cb33e8a2b 100644 --- a/awc/src/response.rs +++ b/awc/src/response.rs @@ -348,7 +348,7 @@ where continue; } } - Poll::Ready(None) => Poll::Ready(Ok(this.buf.take().freeze())), + Poll::Ready(None) => Poll::Ready(Ok(this.buf.split().freeze())), Poll::Pending => Poll::Pending, }; } diff --git a/awc/src/test.rs b/awc/src/test.rs index 641ecaa88..a6cbd03e6 100644 --- a/awc/src/test.rs +++ b/awc/src/test.rs @@ -1,9 +1,10 @@ //! Test helpers for actix http client to use during testing. +use std::convert::TryFrom; use std::fmt::Write as FmtWrite; use actix_http::cookie::{Cookie, CookieJar, USERINFO}; use actix_http::http::header::{self, Header, HeaderValue, IntoHeaderValue}; -use actix_http::http::{HeaderName, HttpTryFrom, StatusCode, Version}; +use actix_http::http::{Error as HttpError, HeaderName, StatusCode, Version}; use actix_http::{h1, Payload, ResponseHead}; use bytes::Bytes; use percent_encoding::percent_encode; @@ -31,7 +32,8 @@ impl TestResponse { /// Create TestResponse and set header pub fn with_header(key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { Self::default().header(key, value) @@ -55,7 +57,8 @@ impl TestResponse { /// Append a header pub fn header(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { if let Ok(key) = HeaderName::try_from(key) { diff --git a/awc/src/ws.rs b/awc/src/ws.rs index 075c83562..e6f8e6968 100644 --- a/awc/src/ws.rs +++ b/awc/src/ws.rs @@ -1,4 +1,5 @@ //! Websockets client +use std::convert::TryFrom; use std::fmt::Write as FmtWrite; use std::net::SocketAddr; use std::rc::Rc; @@ -7,7 +8,7 @@ use std::{fmt, str}; use actix_codec::Framed; use actix_http::cookie::{Cookie, CookieJar}; use actix_http::{ws, Payload, RequestHead}; -use actix_rt::time::Timeout; +use actix_rt::time::timeout; use percent_encoding::percent_encode; use actix_http::cookie::USERINFO; @@ -19,7 +20,7 @@ use crate::http::header::{ self, HeaderName, HeaderValue, IntoHeaderValue, AUTHORIZATION, }; use crate::http::{ - ConnectionType, Error as HttpError, HttpTryFrom, Method, StatusCode, Uri, Version, + ConnectionType, Error as HttpError, Method, StatusCode, Uri, Version, }; use crate::response::ClientResponse; use crate::ClientConfig; @@ -41,7 +42,8 @@ impl WebsocketsRequest { /// Create new websocket connection pub(crate) fn new(uri: U, config: Rc) -> Self where - Uri: HttpTryFrom, + Uri: TryFrom, + >::Error: Into, { let mut err = None; let mut head = RequestHead::default(); @@ -102,9 +104,10 @@ impl WebsocketsRequest { } /// Set request Origin - pub fn origin(mut self, origin: V) -> Self + pub fn origin(mut self, origin: V) -> Self where - HeaderValue: HttpTryFrom, + HeaderValue: TryFrom, + HttpError: From, { match HeaderValue::try_from(origin) { Ok(value) => self.origin = Some(value), @@ -133,7 +136,8 @@ impl WebsocketsRequest { /// To override header use `set_header()` method. pub fn header(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { match HeaderName::try_from(key) { @@ -151,7 +155,8 @@ impl WebsocketsRequest { /// Insert a header, replaces existing header. pub fn set_header(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { match HeaderName::try_from(key) { @@ -169,7 +174,8 @@ impl WebsocketsRequest { /// Insert a header only if it is not yet set. pub fn set_header_if_none(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { match HeaderName::try_from(key) { @@ -220,9 +226,9 @@ impl WebsocketsRequest { let uri = &self.head.uri; if uri.host().is_none() { return Err(InvalidUrl::MissingHost.into()); - } else if uri.scheme_part().is_none() { + } else if uri.scheme().is_none() { return Err(InvalidUrl::MissingScheme.into()); - } else if let Some(scheme) = uri.scheme_part() { + } else if let Some(scheme) = uri.scheme() { match scheme.as_str() { "http" | "ws" | "https" | "wss" => (), _ => return Err(InvalidUrl::UnknownScheme.into()), @@ -295,8 +301,8 @@ impl WebsocketsRequest { .open_tunnel(head, self.addr); // set request timeout - let (head, framed) = if let Some(timeout) = self.config.timeout { - Timeout::new(fut, timeout) + let (head, framed) = if let Some(to) = self.config.timeout { + timeout(to, fut) .await .map_err(|_| SendRequestError::Timeout.into()) .and_then(|res| res)? diff --git a/awc/tests/test_rustls_client.rs b/awc/tests/test_rustls_client.rs index ac60d8e83..a6ced89d3 100644 --- a/awc/tests/test_rustls_client.rs +++ b/awc/tests/test_rustls_client.rs @@ -1,21 +1,17 @@ #![cfg(feature = "rustls")] -use rust_tls::ClientConfig; - -use std::io::Result; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; -use actix_codec::{AsyncRead, AsyncWrite}; use actix_http::HttpService; use actix_http_test::TestServer; -use actix_server::ssl::OpensslAcceptor; use actix_service::{pipeline_factory, ServiceFactory}; use actix_web::http::Version; use actix_web::{web, App, HttpResponse}; use futures::future::ok; use open_ssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslVerifyMode}; +use rust_tls::ClientConfig; -fn ssl_acceptor() -> Result> { +fn ssl_acceptor() -> SslAcceptor { // load ssl keys let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); builder.set_verify_callback(SslVerifyMode::NONE, |_, _| true); @@ -33,8 +29,8 @@ fn ssl_acceptor() -> Result> { Err(open_ssl::ssl::AlpnError::NOACK) } }); - builder.set_alpn_protos(b"\x02h2")?; - Ok(actix_server::ssl::OpensslAcceptor::new(builder.build())) + builder.set_alpn_protos(b"\x02h2").unwrap(); + builder.build() } mod danger { @@ -55,7 +51,6 @@ mod danger { // #[actix_rt::test] async fn _test_connection_reuse_h2() { - let openssl = ssl_acceptor().unwrap(); let num = Arc::new(AtomicUsize::new(0)); let num2 = num.clone(); @@ -65,15 +60,11 @@ async fn _test_connection_reuse_h2() { num2.fetch_add(1, Ordering::Relaxed); ok(io) }) - .and_then( - openssl - .clone() - .map_err(|e| println!("Openssl error: {}", e)), - ) .and_then( HttpService::build() .h2(App::new() .service(web::resource("/").route(web::to(|| HttpResponse::Ok())))) + .openssl(ssl_acceptor()) .map_err(|_| ()), ) }); diff --git a/awc/tests/test_ws.rs b/awc/tests/test_ws.rs index d90f55531..2f7ba2732 100644 --- a/awc/tests/test_ws.rs +++ b/awc/tests/test_ws.rs @@ -63,10 +63,7 @@ async fn test_simple() { .await .unwrap(); let item = framed.next().await.unwrap().unwrap(); - assert_eq!( - item, - ws::Frame::Binary(Some(Bytes::from_static(b"text").into())) - ); + assert_eq!(item, ws::Frame::Binary(Some(BytesMut::from(&b"text"[..])))); framed.send(ws::Message::Ping("text".into())).await.unwrap(); let item = framed.next().await.unwrap().unwrap(); diff --git a/src/guard.rs b/src/guard.rs index 3db525f9a..aaa99a9ec 100644 --- a/src/guard.rs +++ b/src/guard.rs @@ -24,9 +24,10 @@ //! ); //! } //! ``` - #![allow(non_snake_case)] -use actix_http::http::{self, header, uri::Uri, HttpTryFrom}; +use std::convert::TryFrom; + +use actix_http::http::{self, header, uri::Uri}; use actix_http::RequestHead; /// Trait defines resource guards. Guards are used for route selection. diff --git a/src/info.rs b/src/info.rs index a9c3e4eeb..c9a642b36 100644 --- a/src/info.rs +++ b/src/info.rs @@ -76,7 +76,7 @@ impl ConnectionInfo { } } if scheme.is_none() { - scheme = req.uri.scheme_part().map(|a| a.as_str()); + scheme = req.uri.scheme().map(|a| a.as_str()); if scheme.is_none() && cfg.secure() { scheme = Some("https") } @@ -98,7 +98,7 @@ impl ConnectionInfo { host = h.to_str().ok(); } if host.is_none() { - host = req.uri.authority_part().map(|a| a.as_str()); + host = req.uri.authority().map(|a| a.as_str()); if host.is_none() { host = Some(cfg.host()); } diff --git a/src/middleware/defaultheaders.rs b/src/middleware/defaultheaders.rs index 05a031065..14d035ab8 100644 --- a/src/middleware/defaultheaders.rs +++ b/src/middleware/defaultheaders.rs @@ -1,4 +1,5 @@ //! Middleware for setting default response headers +use std::convert::TryFrom; use std::rc::Rc; use std::task::{Context, Poll}; @@ -6,7 +7,7 @@ use actix_service::{Service, Transform}; use futures::future::{ok, FutureExt, LocalBoxFuture, Ready}; use crate::http::header::{HeaderName, HeaderValue, CONTENT_TYPE}; -use crate::http::{HeaderMap, HttpTryFrom}; +use crate::http::{Error as HttpError, HeaderMap}; use crate::service::{ServiceRequest, ServiceResponse}; use crate::Error; @@ -58,8 +59,10 @@ impl DefaultHeaders { #[inline] pub fn header(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, - HeaderValue: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, + HeaderValue: TryFrom, + >::Error: Into, { #[allow(clippy::match_wild_err_arm)] match HeaderName::try_from(key) { diff --git a/src/middleware/logger.rs b/src/middleware/logger.rs index a57ea2961..60c10b207 100644 --- a/src/middleware/logger.rs +++ b/src/middleware/logger.rs @@ -1,5 +1,6 @@ //! Request logging middleware use std::collections::HashSet; +use std::convert::TryFrom; use std::env; use std::fmt::{self, Display, Formatter}; use std::future::Future; @@ -17,7 +18,7 @@ use time; use crate::dev::{BodySize, MessageBody, ResponseBody}; use crate::error::{Error, Result}; -use crate::http::{HeaderName, HttpTryFrom, StatusCode}; +use crate::http::{HeaderName, StatusCode}; use crate::service::{ServiceRequest, ServiceResponse}; use crate::HttpResponse; diff --git a/src/middleware/normalize.rs b/src/middleware/normalize.rs index 2926eacc9..6bff068bc 100644 --- a/src/middleware/normalize.rs +++ b/src/middleware/normalize.rs @@ -1,7 +1,7 @@ //! `Middleware` to normalize request's URI use std::task::{Context, Poll}; -use actix_http::http::{HttpTryFrom, PathAndQuery, Uri}; +use actix_http::http::{PathAndQuery, Uri}; use actix_service::{Service, Transform}; use bytes::Bytes; use futures::future::{ok, Ready}; @@ -74,7 +74,6 @@ where fn call(&mut self, mut req: ServiceRequest) -> Self::Future { let head = req.head_mut(); - let path = head.uri.path(); let original_len = path.len(); let path = self.merge_slash.replace_all(path, "/"); @@ -86,9 +85,10 @@ where let path = if let Some(q) = pq.query() { Bytes::from(format!("{}?{}", path, q)) } else { - Bytes::from(path.as_ref()) + Bytes::copy_from_slice(path.as_bytes()) }; - parts.path_and_query = Some(PathAndQuery::try_from(path).unwrap()); + parts.path_and_query = Some(PathAndQuery::from_maybe_shared(path).unwrap()); + drop(head); let uri = Uri::from_parts(parts).unwrap(); req.match_info_mut().get_mut().update(&uri); diff --git a/src/responder.rs b/src/responder.rs index 7b30315f5..48eae09b6 100644 --- a/src/responder.rs +++ b/src/responder.rs @@ -1,3 +1,4 @@ +use std::convert::TryFrom; use std::future::Future; use std::marker::PhantomData; use std::pin::Pin; @@ -5,8 +6,7 @@ use std::task::{Context, Poll}; use actix_http::error::InternalError; use actix_http::http::{ - header::IntoHeaderValue, Error as HttpError, HeaderMap, HeaderName, HttpTryFrom, - StatusCode, + header::IntoHeaderValue, Error as HttpError, HeaderMap, HeaderName, StatusCode, }; use actix_http::{Error, Response, ResponseBuilder}; use bytes::{Bytes, BytesMut}; @@ -68,7 +68,8 @@ pub trait Responder { fn with_header(self, key: K, value: V) -> CustomResponder where Self: Sized, - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { CustomResponder::new(self).with_header(key, value) @@ -267,7 +268,8 @@ impl CustomResponder { /// ``` pub fn with_header(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { if self.headers.is_none() { diff --git a/src/server.rs b/src/server.rs index f3ec550cf..f5883c0d0 100644 --- a/src/server.rs +++ b/src/server.rs @@ -14,9 +14,9 @@ use parking_lot::Mutex; use net2::TcpBuilder; #[cfg(feature = "openssl")] -use open_ssl::ssl::{SslAcceptor, SslAcceptorBuilder}; +use actix_tls::openssl::{SslAcceptor, SslAcceptorBuilder}; #[cfg(feature = "rustls")] -use rust_tls::ServerConfig as RustlsServerConfig; +use actix_tls::rustls::ServerConfig as RustlsServerConfig; struct Socket { scheme: &'static str, @@ -315,15 +315,8 @@ where fn listen_rustls_inner( mut self, lst: net::TcpListener, - mut config: RustlsServerConfig, + config: RustlsServerConfig, ) -> io::Result { - use actix_server::ssl::{RustlsAcceptor, SslError}; - use actix_service::pipeline_factory; - - let protos = vec!["h2".to_string().into(), "http/1.1".to_string().into()]; - config.set_protocols(&protos); - - let acceptor = RustlsAcceptor::new(config); let factory = self.factory.clone(); let cfg = self.config.clone(); let addr = lst.local_addr().unwrap(); @@ -337,15 +330,12 @@ where lst, move || { let c = cfg.lock(); - pipeline_factory(acceptor.clone().map_err(SslError::Ssl)).and_then( - HttpService::build() - .keep_alive(c.keep_alive) - .client_timeout(c.client_timeout) - .client_disconnect(c.client_shutdown) - .finish(factory()) - .map_err(SslError::Service) - .map_init_err(|_| ()), - ) + HttpService::build() + .keep_alive(c.keep_alive) + .client_timeout(c.client_timeout) + .client_disconnect(c.client_shutdown) + .finish(factory()) + .rustls(config.clone()) }, )?; Ok(self) @@ -530,14 +520,13 @@ where /// use std::io; /// use actix_web::{web, App, HttpResponse, HttpServer}; /// - /// fn main() -> io::Result<()> { - /// let sys = actix_rt::System::new("example"); // <- create Actix system - /// + /// #[actix_rt::main] + /// async fn main() -> io::Result<()> { + /// # actix_rt::System::current().stop(); /// HttpServer::new(|| App::new().service(web::resource("/").to(|| HttpResponse::Ok()))) /// .bind("127.0.0.1:0")? - /// .start(); - /// # actix_rt::System::current().stop(); - /// sys.run() // <- Run actix system, this method starts all async processes + /// .start() + /// .await /// } /// ``` pub fn start(self) -> Server { diff --git a/src/test.rs b/src/test.rs index 82ba97fec..5e50f24e1 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,8 +1,9 @@ //! Various helpers for Actix applications to use during testing. +use std::convert::TryFrom; use std::rc::Rc; use actix_http::http::header::{ContentType, Header, HeaderName, IntoHeaderValue}; -use actix_http::http::{HttpTryFrom, Method, StatusCode, Uri, Version}; +use actix_http::http::{Error as HttpError, Method, StatusCode, Uri, Version}; use actix_http::test::TestRequest as HttpTestRequest; use actix_http::{cookie::Cookie, Extensions, Request}; use actix_router::{Path, ResourceDef, Url}; @@ -319,7 +320,8 @@ impl TestRequest { /// Create TestRequest and set header pub fn with_header(key: K, value: V) -> TestRequest where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { TestRequest::default().header(key, value) @@ -377,7 +379,8 @@ impl TestRequest { /// Set a header pub fn header(mut self, key: K, value: V) -> Self where - HeaderName: HttpTryFrom, + HeaderName: TryFrom, + >::Error: Into, V: IntoHeaderValue, { self.req.header(key, value); diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index b3a225f3d..0f6af8ff2 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http-test" -version = "0.3.0-alpha.2" +version = "0.3.0-alpha.3" authors = ["Nikolay Kim "] description = "Actix http test server" readme = "README.md" @@ -30,19 +30,19 @@ default = [] openssl = ["open-ssl", "awc/openssl", ] # "actix-tls/openssl"] [dependencies] -actix-service = "1.0.0-alpha.2" -actix-codec = "0.2.0-alpha.2" -actix-connect = "1.0.0-alpha.2" -actix-utils = "1.0.0-alpha.2" -actix-rt = "1.0.0-alpha.2" -actix-server = "1.0.0-alpha.2" -actix-testing = "1.0.0-alpha.2" -awc = "0.3.0-alpha.2" +actix-service = "1.0.0-alpha.3" +actix-codec = "0.2.0-alpha.3" +actix-connect = "1.0.0-alpha.3" +actix-utils = "1.0.0-alpha.3" +actix-rt = "1.0.0-alpha.3" +actix-server = "1.0.0-alpha.3" +actix-testing = "1.0.0-alpha.3" +awc = "0.3.0-alpha.3" base64 = "0.11" -bytes = "0.4" +bytes = "0.5.2" futures = "0.3.1" -http = "0.1.8" +http = "0.2.0" log = "0.4" env_logger = "0.6" net2 = "0.2" @@ -55,5 +55,5 @@ time = "0.1" open-ssl = { version="0.10", package="openssl", optional = true } [dev-dependencies] -actix-web = "2.0.0-alpha.2" -actix-http = "0.3.0-alpha.2" +actix-web = "2.0.0-alpha.3" +actix-http = "0.3.0-alpha.3" From fbead137f0b1a5288661ac4ebb7375d5be93d68f Mon Sep 17 00:00:00 2001 From: tglman Date: Fri, 6 Dec 2019 05:21:43 +0000 Subject: [PATCH 086/176] feat: add access to UserSession from RequestHead (#1164) * feat: add access to UserSession from RequestHead * add test case for session from RequestHead and changes entry for the new feature --- actix-session/CHANGES.md | 1 + actix-session/src/lib.rs | 24 +++++++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/actix-session/CHANGES.md b/actix-session/CHANGES.md index d85f6d5f1..784bfdf0d 100644 --- a/actix-session/CHANGES.md +++ b/actix-session/CHANGES.md @@ -6,6 +6,7 @@ at successful login to cycle a session (new key/cookie but keeps state). Use ``Session.purge()`` at logout to invalid a session cookie (and remove from redis cache, if applicable). +* Add access to the session from RequestHead for use of session from guard methods ## [0.1.1] - 2019-06-03 diff --git a/actix-session/src/lib.rs b/actix-session/src/lib.rs index 6f23ef913..771c4f67c 100644 --- a/actix-session/src/lib.rs +++ b/actix-session/src/lib.rs @@ -46,7 +46,9 @@ use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; -use actix_web::dev::{Extensions, Payload, ServiceRequest, ServiceResponse}; +use actix_web::dev::{ + Extensions, Payload, RequestHead, ServiceRequest, ServiceResponse, +}; use actix_web::{Error, FromRequest, HttpMessage, HttpRequest}; use futures::future::{ok, Ready}; use serde::de::DeserializeOwned; @@ -99,6 +101,12 @@ impl UserSession for ServiceRequest { } } +impl UserSession for RequestHead { + fn get_session(&mut self) -> Session { + Session::get_session(&mut *self.extensions_mut()) + } +} + #[derive(PartialEq, Clone, Debug)] pub enum SessionStatus { Changed, @@ -281,6 +289,20 @@ mod tests { assert_eq!(res, Some("value".to_string())); } + #[test] + fn get_session_from_request_head() { + let mut req = test::TestRequest::default().to_srv_request(); + + Session::set_session( + vec![("key".to_string(), "\"value\"".to_string())].into_iter(), + &mut req, + ); + + let session = req.head_mut().get_session(); + let res = session.get::("key").unwrap(); + assert_eq!(res, Some("value".to_string())); + } + #[test] fn purge_session() { let req = test::TestRequest::default().to_srv_request(); From 7dd676439c4c96f640b485e9128d51f7038be98a Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 6 Dec 2019 11:24:25 +0600 Subject: [PATCH 087/176] update changes for actix-session --- actix-session/CHANGES.md | 17 ++++++++++++----- actix-session/Cargo.toml | 8 ++++---- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/actix-session/CHANGES.md b/actix-session/CHANGES.md index 784bfdf0d..0c9dca564 100644 --- a/actix-session/CHANGES.md +++ b/actix-session/CHANGES.md @@ -1,12 +1,19 @@ # Changes +## [0.3.0-alpha.3] - 2019-12-xx + +* Add access to the session from RequestHead for use of session from guard methods + +* Migrate to `std::future` + +* Migrate to `actix-web` 2.0 + ## [0.2.0] - 2019-07-08 -* Enhanced ``actix-session`` to facilitate state changes. Use ``Session.renew()`` - at successful login to cycle a session (new key/cookie but keeps state). - Use ``Session.purge()`` at logout to invalid a session cookie (and remove - from redis cache, if applicable). -* Add access to the session from RequestHead for use of session from guard methods +* Enhanced ``actix-session`` to facilitate state changes. Use ``Session.renew()`` + at successful login to cycle a session (new key/cookie but keeps state). + Use ``Session.purge()`` at logout to invalid a session cookie (and remove + from redis cache, if applicable). ## [0.1.1] - 2019-06-03 diff --git a/actix-session/Cargo.toml b/actix-session/Cargo.toml index c33642d67..d8b3ecc9d 100644 --- a/actix-session/Cargo.toml +++ b/actix-session/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-session" -version = "0.3.0-alpha.2" +version = "0.3.0-alpha.3" authors = ["Nikolay Kim "] description = "Session for actix web framework." readme = "README.md" @@ -24,8 +24,8 @@ default = ["cookie-session"] cookie-session = ["actix-web/secure-cookies"] [dependencies] -actix-web = "2.0.0-alpha.2" -actix-service = "1.0.0-alpha.2" +actix-web = "2.0.0-alpha.3" +actix-service = "1.0.0-alpha.3" bytes = "0.5.2" derive_more = "0.99.2" futures = "0.3.1" @@ -34,4 +34,4 @@ serde_json = "1.0" time = "0.1.42" [dev-dependencies] -actix-rt = "1.0.0-alpha.2" +actix-rt = "1.0.0-alpha.3" From 98903028c7578a82e8dd8edb575ff06bf3578b81 Mon Sep 17 00:00:00 2001 From: krircc <718207002@qq.com> Date: Fri, 6 Dec 2019 14:22:29 +0800 Subject: [PATCH 088/176] Update README.md --- README.md | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index b7a1bf28f..728a548a8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,28 @@ -# Actix web [![Build Status](https://travis-ci.org/actix/actix-web.svg?branch=master)](https://travis-ci.org/actix/actix-web) [![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) [![crates.io](https://meritbadge.herokuapp.com/actix-web)](https://crates.io/crates/actix-web) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +

+

Actix web

+

Actix web is a small, pragmatic, and extremely fast rust web framework

+

+ +[![Build Status](https://travis-ci.org/actix/actix-web.svg?branch=master)](https://travis-ci.org/actix/actix-web) +[![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) +[![crates.io](https://meritbadge.herokuapp.com/actix-web)](https://crates.io/crates/actix-web) +[![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +[![Documentation](https://docs.rs/actix-web/badge.svg)](https://docs.rs/actix-web) +[![Download](https://img.shields.io/crates/d/actix-web.svg?style=flat-square)](https://crates.io/crates/actix-web) +[![Version](https://img.shields.io/badge/rustc-1.39+-lightgray.svg?style=flat-square)](https://blog.rust-lang.org/2019/11/07/Rust-1.39.0.html) +![License](https://img.shields.io/crates/l/actix-web.svg) + +

+ +

+ Website + | + Forum + | + Examples +

+
Actix web is a simple, pragmatic and extremely fast web framework for Rust. @@ -15,14 +39,6 @@ Actix web is a simple, pragmatic and extremely fast web framework for Rust. * Includes an asynchronous [HTTP client](https://actix.rs/actix-web/actix_web/client/index.html) * Supports [Actix actor framework](https://github.com/actix/actix) -## Documentation & community resources - -* [User Guide](https://actix.rs/docs/) -* [API Documentation (1.0)](https://docs.rs/actix-web/) -* [Chat on gitter](https://gitter.im/actix/actix) -* Cargo package: [actix-web](https://crates.io/crates/actix-web) -* Minimum supported Rust version: 1.39 or later - ## Example ```rust From 5243e8bacaa6d77d158e651c92235595b6534c87 Mon Sep 17 00:00:00 2001 From: krircc <718207002@qq.com> Date: Fri, 6 Dec 2019 14:23:28 +0800 Subject: [PATCH 089/176] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 728a548a8..6f3f2122d 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ Examples +
Actix web is a simple, pragmatic and extremely fast web framework for Rust. From 7b3354a9ad3e8a16b0d7d05b9b3e1b02d7834788 Mon Sep 17 00:00:00 2001 From: krircc <718207002@qq.com> Date: Fri, 6 Dec 2019 14:26:23 +0800 Subject: [PATCH 090/176] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6f3f2122d..9521d671f 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@

Website | - Forum + Forum | Examples

From 82110e0927e1279da6364eb4cb891a45f551442e Mon Sep 17 00:00:00 2001 From: krircc <718207002@qq.com> Date: Fri, 6 Dec 2019 14:29:10 +0800 Subject: [PATCH 091/176] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9521d671f..a7d3aa531 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

Actix web

-

Actix web is a small, pragmatic, and extremely fast rust web framework

+

Actix web is a small, pragmatic, and extremely fast rust web framework

[![Build Status](https://travis-ci.org/actix/actix-web.svg?branch=master)](https://travis-ci.org/actix/actix-web) From e32da08a2659c50766e1e922ee65c4431ca0c7bc Mon Sep 17 00:00:00 2001 From: krircc <718207002@qq.com> Date: Fri, 6 Dec 2019 14:34:14 +0800 Subject: [PATCH 092/176] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index a7d3aa531..66b275ec5 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,6 @@ [![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) [![crates.io](https://meritbadge.herokuapp.com/actix-web)](https://crates.io/crates/actix-web) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - [![Documentation](https://docs.rs/actix-web/badge.svg)](https://docs.rs/actix-web) [![Download](https://img.shields.io/crates/d/actix-web.svg?style=flat-square)](https://crates.io/crates/actix-web) [![Version](https://img.shields.io/badge/rustc-1.39+-lightgray.svg?style=flat-square)](https://blog.rust-lang.org/2019/11/07/Rust-1.39.0.html) From 439f02b6b1051f3154a150e003cc0262b57c4c82 Mon Sep 17 00:00:00 2001 From: krircc <718207002@qq.com> Date: Fri, 6 Dec 2019 14:59:11 +0800 Subject: [PATCH 093/176] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 66b275ec5..db090a3e4 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,8 @@ [![crates.io](https://meritbadge.herokuapp.com/actix-web)](https://crates.io/crates/actix-web) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Documentation](https://docs.rs/actix-web/badge.svg)](https://docs.rs/actix-web) -[![Download](https://img.shields.io/crates/d/actix-web.svg?style=flat-square)](https://crates.io/crates/actix-web) -[![Version](https://img.shields.io/badge/rustc-1.39+-lightgray.svg?style=flat-square)](https://blog.rust-lang.org/2019/11/07/Rust-1.39.0.html) +[![Download](https://img.shields.io/crates/d/actix-web.svg)](https://crates.io/crates/actix-web) +[![Version](https://img.shields.io/badge/rustc-1.39+-lightgray.svg)](https://blog.rust-lang.org/2019/11/07/Rust-1.39.0.html) ![License](https://img.shields.io/crates/l/actix-web.svg)

From ed2f3fe80d0d9a94bcdb3f971e87c4de75f9d9ee Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sat, 7 Dec 2019 12:28:26 +0600 Subject: [PATCH 094/176] use actix-net alpha.3 release --- Cargo.toml | 14 ++------------ src/server.rs | 3 +-- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ee3158a15..fa8041162 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,7 +93,7 @@ mime = "0.3" net2 = "0.2.33" parking_lot = "0.9" pin-project = "0.4.6" -regex = "1.0" +regex = "1.3" serde = { version = "1.0", features=["derive"] } serde_json = "1.0" serde_urlencoded = "0.6.1" @@ -106,7 +106,7 @@ rust-tls = { version = "0.16", package="rustls", optional = true } [dev-dependencies] # actix = "0.8.3" -actix-connect = "1.0.0-alpha.2" +actix-connect = "1.0.0-alpha.3" actix-http-test = "0.3.0-alpha.2" rand = "0.7" env_logger = "0.6" @@ -130,13 +130,3 @@ actix-session = { path = "actix-session" } actix-files = { path = "actix-files" } actix-multipart = { path = "actix-multipart" } awc = { path = "awc" } - -actix-codec = { git = "https://github.com/actix/actix-net.git" } -actix-connect = { git = "https://github.com/actix/actix-net.git" } -actix-rt = { git = "https://github.com/actix/actix-net.git" } -actix-server = { git = "https://github.com/actix/actix-net.git" } -actix-service = { git = "https://github.com/actix/actix-net.git" } -actix-testing = { git = "https://github.com/actix/actix-net.git" } -actix-tls = { git = "https://github.com/actix/actix-net.git" } -actix-utils = { git = "https://github.com/actix/actix-net.git" } -actix-router = { git = "https://github.com/actix/actix-net.git" } diff --git a/src/server.rs b/src/server.rs index f5883c0d0..57adec524 100644 --- a/src/server.rs +++ b/src/server.rs @@ -516,13 +516,12 @@ where /// This methods panics if no socket address can be bound or an `Actix` system is not yet /// configured. /// - /// ```rust + /// ```rust,no_run /// use std::io; /// use actix_web::{web, App, HttpResponse, HttpServer}; /// /// #[actix_rt::main] /// async fn main() -> io::Result<()> { - /// # actix_rt::System::current().stop(); /// HttpServer::new(|| App::new().service(web::resource("/").to(|| HttpResponse::Ok()))) /// .bind("127.0.0.1:0")? /// .start() From 1729a52f8b8013f84db9975feeeaad4ea971e57a Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sat, 7 Dec 2019 13:00:03 +0600 Subject: [PATCH 095/176] prepare alpha.3 release --- CHANGES.md | 2 +- Cargo.toml | 6 +++--- actix-cors/CHANGES.md | 2 +- actix-cors/Cargo.toml | 6 +++--- actix-files/CHANGES.md | 2 +- actix-files/Cargo.toml | 8 ++++---- actix-framed/Cargo.toml | 4 ++-- actix-http/CHANGES.md | 9 +++++++++ actix-http/Cargo.toml | 6 +++--- actix-identity/Cargo.toml | 10 +++++----- actix-multipart/Cargo.toml | 12 ++++++------ awc/CHANGES.md | 5 +++++ awc/Cargo.toml | 8 ++++---- test-server/CHANGES.md | 9 ++++++++- test-server/Cargo.toml | 6 +++--- 15 files changed, 58 insertions(+), 37 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 600dc8e15..cd7e0f7dc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## [2.0.0-alpha.3] - 2019-12-xx +## [2.0.0-alpha.3] - 2019-12-07 ### Changed diff --git a/Cargo.toml b/Cargo.toml index fa8041162..678586334 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,8 +80,8 @@ actix-threadpool = "0.3.0" actix-tls = { version = "1.0.0-alpha.3" } actix-web-codegen = "0.2.0-alpha.2" -actix-http = "0.3.0-alpha.2" -awc = { version = "0.3.0-alpha.2", optional = true } +actix-http = "1.0.0-alpha.3" +awc = { version = "1.0.0-alpha.3", optional = true } bytes = "0.5.2" derive_more = "0.99.2" @@ -107,7 +107,7 @@ rust-tls = { version = "0.16", package="rustls", optional = true } [dev-dependencies] # actix = "0.8.3" actix-connect = "1.0.0-alpha.3" -actix-http-test = "0.3.0-alpha.2" +actix-http-test = "1.0.0-alpha.3" rand = "0.7" env_logger = "0.6" serde_derive = "1.0" diff --git a/actix-cors/CHANGES.md b/actix-cors/CHANGES.md index 92e3b697b..e53abc790 100644 --- a/actix-cors/CHANGES.md +++ b/actix-cors/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## [0.2.0-alpha.3] - unreleased +## [0.2.0-alpha.3] - 2019-12-07 * Migrate to actix-web 2.0.0 diff --git a/actix-cors/Cargo.toml b/actix-cors/Cargo.toml index 976d0be7f..6f42109be 100644 --- a/actix-cors/Cargo.toml +++ b/actix-cors/Cargo.toml @@ -17,10 +17,10 @@ name = "actix_cors" path = "src/lib.rs" [dependencies] -actix-web = "2.0.0-alpha.1" -actix-service = "1.0.0-alpha.2" +actix-web = "2.0.0-alpha.3" +actix-service = "1.0.0-alpha.3" derive_more = "0.99.2" futures = "0.3.1" [dev-dependencies] -actix-rt = "1.0.0-alpha.2" +actix-rt = "1.0.0-alpha.3" diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index 29e774e0f..5a33d361d 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## [0.2.0-alpha.2] - 2019-12-03 +## [0.2.0-alpha.7] - 2019-12-07 * Migrate to `std::future` diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 261bf14e3..fe351c22d 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -18,8 +18,8 @@ name = "actix_files" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-alpha.2", default-features = false } -actix-http = "0.3.0-alpha.2" +actix-web = { version = "2.0.0-alpha.3", default-features = false } +actix-http = "1.0.0-alpha.3" actix-service = "1.0.0-alpha.3" bitflags = "1" bytes = "0.5.2" @@ -32,5 +32,5 @@ percent-encoding = "2.1" v_htmlescape = "0.4" [dev-dependencies] -actix-rt = "1.0.0-alpha.2" -actix-web = { version = "2.0.0-alpha.2", features=["openssl"] } +actix-rt = "1.0.0-alpha.3" +actix-web = { version = "2.0.0-alpha.3", features=["openssl"] } diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml index 0b80266aa..ec8392ba3 100644 --- a/actix-framed/Cargo.toml +++ b/actix-framed/Cargo.toml @@ -24,7 +24,7 @@ actix-codec = "0.2.0-alpha.3" actix-service = "1.0.0-alpha.3" actix-router = "0.2.0" actix-rt = "1.0.0-alpha.3" -actix-http = "0.3.0-alpha.3" +actix-http = "1.0.0-alpha.3" bytes = "0.5.2" futures = "0.3.1" @@ -34,5 +34,5 @@ log = "0.4" [dev-dependencies] actix-server = { version = "1.0.0-alpha.3" } actix-connect = { version = "1.0.0-alpha.3", features=["openssl"] } -actix-http-test = { version = "0.3.0-alpha.3", features=["openssl"] } +actix-http-test = { version = "1.0.0-alpha.3", features=["openssl"] } actix-utils = "1.0.0-alpha.3" diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 4cb5644c3..5b86a3977 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,5 +1,14 @@ # Changes +## [1.0.0-alpha.3] - 2019-12-07 + +### Changed + +* Migrate to tokio 0.2 + +* Migrate to `std::future` + + ## [0.2.11] - 2019-11-06 ### Added diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index aadef9e3c..4d89e55fb 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "0.3.0-alpha.3" +version = "1.0.0-alpha.3" authors = ["Nikolay Kim "] description = "Actix http primitives" readme = "README.md" @@ -65,7 +65,7 @@ either = "1.5.2" encoding_rs = "0.8" futures = "0.3.1" fxhash = "0.2.1" -h2 = "0.2.0" +h2 = "0.2.1" http = "0.2.0" httparse = "1.3" indexmap = "1.2" @@ -97,7 +97,7 @@ failure = { version = "0.1.5", optional = true } [dev-dependencies] actix-server = { version = "1.0.0-alpha.3" } actix-connect = { version = "1.0.0-alpha.3", features=["openssl"] } -actix-http-test = { version = "0.3.0-alpha.3", features=["openssl"] } +actix-http-test = { version = "1.0.0-alpha.3", features=["openssl"] } actix-tls = { version = "1.0.0-alpha.3", features=["openssl"] } env_logger = "0.6" serde_derive = "1.0" diff --git a/actix-identity/Cargo.toml b/actix-identity/Cargo.toml index eea854263..a9042dbf2 100644 --- a/actix-identity/Cargo.toml +++ b/actix-identity/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-identity" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.3" authors = ["Nikolay Kim "] description = "Identity service for actix web framework." readme = "README.md" @@ -17,14 +17,14 @@ name = "actix_identity" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-alpha.2", default-features = false, features = ["secure-cookies"] } -actix-service = "1.0.0-alpha.2" +actix-web = { version = "2.0.0-alpha.3", default-features = false, features = ["secure-cookies"] } +actix-service = "1.0.0-alpha.3" futures = "0.3.1" serde = "1.0" serde_json = "1.0" time = "0.1.42" [dev-dependencies] -actix-rt = "1.0.0-alpha.2" -actix-http = "0.3.0-alpha.2" +actix-rt = "1.0.0-alpha.3" +actix-http = "1.0.0-alpha.3" bytes = "0.5.2" \ No newline at end of file diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index f1913c3f1..ac1923155 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-multipart" -version = "0.2.0-alpha.2" +version = "0.2.0-alpha.3" authors = ["Nikolay Kim "] description = "Multipart support for actix web framework." readme = "README.md" @@ -18,9 +18,9 @@ name = "actix_multipart" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-alpha.2", default-features = false } -actix-service = "1.0.0-alpha.2" -actix-utils = "1.0.0-alpha.2" +actix-web = { version = "2.0.0-alpha.3", default-features = false } +actix-service = "1.0.0-alpha.3" +actix-utils = "1.0.0-alpha.3" bytes = "0.5.2" derive_more = "0.99.2" httparse = "1.3" @@ -31,5 +31,5 @@ time = "0.1" twoway = "0.2" [dev-dependencies] -actix-rt = "1.0.0-alpha.2" -actix-http = "0.3.0-alpha.2" \ No newline at end of file +actix-rt = "1.0.0-alpha.3" +actix-http = "1.0.0-alpha.3" \ No newline at end of file diff --git a/awc/CHANGES.md b/awc/CHANGES.md index 89423f80e..f4923db88 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,5 +1,10 @@ # Changes +## [1.0.0-alpha.3] + +* Migrate to `std::future` + + ## [0.2.8] - 2019-11-06 * Add support for setting query from Serialize type for client request. diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 99ccd4bbb..c4f3b7bf1 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "awc" -version = "0.3.0-alpha.3" +version = "1.0.0-alpha.3" authors = ["Nikolay Kim "] description = "Actix http client." readme = "README.md" @@ -44,7 +44,7 @@ flate2-rust = ["actix-http/flate2-rust"] [dependencies] actix-codec = "0.2.0-alpha.3" actix-service = "1.0.0-alpha.3" -actix-http = "0.3.0-alpha.3" +actix-http = "1.0.0-alpha.3" actix-rt = "1.0.0-alpha.3" base64 = "0.11" @@ -64,8 +64,8 @@ rust-tls = { version = "0.16.0", package="rustls", optional = true, features = [ [dev-dependencies] actix-connect = { version = "1.0.0-alpha.3", features=["openssl"] } actix-web = { version = "2.0.0-alpha.3", features=["openssl"] } -actix-http = { version = "0.3.0-alpha.3", features=["openssl"] } -actix-http-test = { version = "0.3.0-alpha.3", features=["openssl"] } +actix-http = { version = "1.0.0-alpha.3", features=["openssl"] } +actix-http-test = { version = "1.0.0-alpha.3", features=["openssl"] } actix-utils = "1.0.0-alpha.3" actix-server = { version = "1.0.0-alpha.3" } actix-tls = { version = "1.0.0-alpha.3", features=["openssl", "rustls"] } diff --git a/test-server/CHANGES.md b/test-server/CHANGES.md index 57068fe95..82fc1969e 100644 --- a/test-server/CHANGES.md +++ b/test-server/CHANGES.md @@ -1,6 +1,13 @@ # Changes -## [0.2.5] - 2019-0917 +## [1.0.0-alpha.3] - 2019-12-07 + +### Changed + +* Migrate to `std::future` + + +## [0.2.5] - 2019-09-17 ### Changed diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 0f6af8ff2..897a4bea0 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http-test" -version = "0.3.0-alpha.3" +version = "1.0.0-alpha.3" authors = ["Nikolay Kim "] description = "Actix http test server" readme = "README.md" @@ -37,7 +37,7 @@ actix-utils = "1.0.0-alpha.3" actix-rt = "1.0.0-alpha.3" actix-server = "1.0.0-alpha.3" actix-testing = "1.0.0-alpha.3" -awc = "0.3.0-alpha.3" +awc = "1.0.0-alpha.3" base64 = "0.11" bytes = "0.5.2" @@ -56,4 +56,4 @@ open-ssl = { version="0.10", package="openssl", optional = true } [dev-dependencies] actix-web = "2.0.0-alpha.3" -actix-http = "0.3.0-alpha.3" +actix-http = "1.0.0-alpha.3" From 91b3fcf85c694cfad047d27fc725f4ded11e731c Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sat, 7 Dec 2019 11:13:26 +0100 Subject: [PATCH 096/176] Fix dependency features. (#1196) --- Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 678586334..356e4941d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,13 +49,13 @@ default = ["brotli", "flate2-zlib", "client", "fail"] client = ["awc"] # brotli encoding, requires c compiler -brotli = ["actix-http/brotli"] +brotli = ["actix-http/brotli", "awc/brotli"] # miniz-sys backend for flate2 crate -flate2-zlib = ["actix-http/flate2-zlib"] +flate2-zlib = ["actix-http/flate2-zlib", "awc/flate2-zlib"] # rust backend for flate2 crate -flate2-rust = ["actix-http/flate2-rust"] +flate2-rust = ["actix-http/flate2-rust", "awc/flate2-rust"] # sessions feature, session require "ring" crate and c compiler secure-cookies = ["actix-http/secure-cookies"] @@ -81,7 +81,7 @@ actix-tls = { version = "1.0.0-alpha.3" } actix-web-codegen = "0.2.0-alpha.2" actix-http = "1.0.0-alpha.3" -awc = { version = "1.0.0-alpha.3", optional = true } +awc = { version = "1.0.0-alpha.3", default-features = false, optional = true } bytes = "0.5.2" derive_more = "0.99.2" From 4921243adda22d6217fd26e1a608b44a308abb7b Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sat, 7 Dec 2019 11:14:09 +0100 Subject: [PATCH 097/176] Fix rustls build. (#1195) --- actix-http/src/client/connector.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index 2710252e3..02e9df7d1 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -88,7 +88,7 @@ impl Connector<(), ()> { let mut config = ClientConfig::new(); config.set_protocols(&protos); config.root_store.add_server_trust_anchors( - &actix_connect::ssl::rustls::TLS_SERVER_ROOTS, + &actix_tls::rustls::TLS_SERVER_ROOTS, ); SslConnector::Rustls(Arc::new(config)) } From 8c3f58db9d80c117bec524ead43a1306da591b8a Mon Sep 17 00:00:00 2001 From: Vlad Frolov Date: Sat, 7 Dec 2019 16:08:06 +0200 Subject: [PATCH 098/176] Allow comma-separated websocket subprotocols without spaces (#1172) * Allow comma-separated websocket subprotocols without spaces * [CHANGES] Added an entry to CHANGES.md --- CHANGES.md | 3 +++ actix-web-actors/src/ws.rs | 44 +++++++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index cd7e0f7dc..fdb8c4b71 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,9 @@ * Migrate to tokio 0.2 +### Fixed + +* Allow comma-separated websocket subprotocols without spaces (#1172) ## [2.0.0-alpha.1] - 2019-11-22 diff --git a/actix-web-actors/src/ws.rs b/actix-web-actors/src/ws.rs index e25a7e6e4..0b026e35a 100644 --- a/actix-web-actors/src/ws.rs +++ b/actix-web-actors/src/ws.rs @@ -152,7 +152,8 @@ pub fn handshake_with_protocols( .and_then(|req_protocols| { let req_protocols = req_protocols.to_str().ok()?; req_protocols - .split(", ") + .split(',') + .map(|req_p| req_p.trim()) .find(|req_p| protocols.iter().any(|p| p == req_p)) }); @@ -736,5 +737,46 @@ mod tests { .headers() .get(&header::SEC_WEBSOCKET_PROTOCOL) ); + + let req = TestRequest::default() + .header( + header::UPGRADE, + header::HeaderValue::from_static("websocket"), + ) + .header( + header::CONNECTION, + header::HeaderValue::from_static("upgrade"), + ) + .header( + header::SEC_WEBSOCKET_VERSION, + header::HeaderValue::from_static("13"), + ) + .header( + header::SEC_WEBSOCKET_KEY, + header::HeaderValue::from_static("13"), + ) + .header( + header::SEC_WEBSOCKET_PROTOCOL, + header::HeaderValue::from_static("p1,p2,p3"), + ) + .to_http_request(); + + let protocols = vec!["p3", "p2"]; + + assert_eq!( + StatusCode::SWITCHING_PROTOCOLS, + handshake_with_protocols(&req, &protocols) + .unwrap() + .finish() + .status() + ); + assert_eq!( + Some(&header::HeaderValue::from_static("p2")), + handshake_with_protocols(&req, &protocols) + .unwrap() + .finish() + .headers() + .get(&header::SEC_WEBSOCKET_PROTOCOL) + ); } } From 6c226e47bdc80852cdae7bc371ac6d37b7701fe1 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sat, 7 Dec 2019 20:10:36 +0600 Subject: [PATCH 099/176] prepare actix-web-actors release --- CHANGES.md | 3 --- actix-web-actors/CHANGES.md | 4 ++++ actix-web-actors/Cargo.toml | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index fdb8c4b71..cd7e0f7dc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,9 +6,6 @@ * Migrate to tokio 0.2 -### Fixed - -* Allow comma-separated websocket subprotocols without spaces (#1172) ## [2.0.0-alpha.1] - 2019-11-22 diff --git a/actix-web-actors/CHANGES.md b/actix-web-actors/CHANGES.md index c1417c9c4..01e116baa 100644 --- a/actix-web-actors/CHANGES.md +++ b/actix-web-actors/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [1.0.4] - 2019-12-07 + +* Allow comma-separated websocket subprotocols without spaces (#1172) + ## [1.0.3] - 2019-11-14 * Update actix-web and actix-http dependencies diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index d5a6ce2c4..a74aef046 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web-actors" -version = "1.0.3" +version = "1.0.4" authors = ["Nikolay Kim "] description = "Actix actors support for actix web framework." readme = "README.md" From 0ba125444ade4dc67a7e1ac30c6674fd4973ccca Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sat, 7 Dec 2019 21:41:34 +0600 Subject: [PATCH 100/176] Add impl ResponseBuilder for Error --- actix-http/CHANGES.md | 4 ++++ actix-http/src/client/connector.rs | 6 +++--- actix-http/src/error.rs | 23 +++++++++++++++-------- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 5b86a3977..1a7e5ed5b 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,5 +1,9 @@ # Changes +### Added + +* Add impl ResponseBuilder for Error + ## [1.0.0-alpha.3] - 2019-12-07 ### Changed diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index 02e9df7d1..c78597d01 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -87,9 +87,9 @@ impl Connector<(), ()> { let protos = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; let mut config = ClientConfig::new(); config.set_protocols(&protos); - config.root_store.add_server_trust_anchors( - &actix_tls::rustls::TLS_SERVER_ROOTS, - ); + config + .root_store + .add_server_trust_anchors(&actix_tls::rustls::TLS_SERVER_ROOTS); SslConnector::Rustls(Arc::new(config)) } #[cfg(not(any(feature = "openssl", feature = "rustls")))] diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index c580f3846..ec56900fc 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -22,7 +22,7 @@ use serde_urlencoded::ser::Error as FormError; use crate::body::Body; pub use crate::cookie::ParseError as CookieParseError; use crate::helpers::Writer; -use crate::response::Response; +use crate::response::{Response, ResponseBuilder}; /// A specialized [`Result`](https://doc.rust-lang.org/std/result/enum.Result.html) /// for actix web operations @@ -157,6 +157,20 @@ impl From for Error { } } +/// Convert Response to a Error +impl From for Error { + fn from(res: Response) -> Error { + InternalError::from_response("", res).into() + } +} + +/// Convert ResponseBuilder to a Error +impl From for Error { + fn from(mut res: ResponseBuilder) -> Error { + InternalError::from_response("", res.finish()).into() + } +} + /// Return `GATEWAY_TIMEOUT` for `TimeoutError` impl ResponseError for TimeoutError { fn status_code(&self) -> StatusCode { @@ -555,13 +569,6 @@ where } } -/// Convert Response to a Error -impl From for Error { - fn from(res: Response) -> Error { - InternalError::from_response("", res).into() - } -} - /// Helper function that creates wrapper of any error and generate *BAD /// REQUEST* response. #[allow(non_snake_case)] From e5f3d88a4eb92d6618e55077623c4d1868ec3ad2 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sat, 7 Dec 2019 16:55:41 +0100 Subject: [PATCH 101/176] Switch brotli compressor to rust. (#1197) * Switch to a rustified version of brotli. * Some memory optimizations. * Make brotli not optional anymore. --- Cargo.toml | 9 ++---- actix-http/Cargo.toml | 7 ++--- actix-http/src/encoding/decoder.rs | 17 ++++------- actix-http/src/encoding/encoder.rs | 30 +++++++++--------- actix-http/src/encoding/mod.rs | 3 ++ awc/Cargo.toml | 9 ++---- awc/src/request.rs | 49 ++++++++++++------------------ awc/tests/test_client.rs | 3 +- src/lib.rs | 2 -- tests/test_server.rs | 6 ++-- 10 files changed, 53 insertions(+), 82 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 356e4941d..1674502d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] edition = "2018" [package.metadata.docs.rs] -features = ["openssl", "brotli", "flate2-zlib", "secure-cookies", "client"] +features = ["openssl", "flate2-zlib", "secure-cookies", "client"] [badges] travis-ci = { repository = "actix/actix-web", branch = "master" } @@ -43,14 +43,11 @@ members = [ ] [features] -default = ["brotli", "flate2-zlib", "client", "fail"] +default = ["flate2-zlib", "client", "fail"] # http client client = ["awc"] -# brotli encoding, requires c compiler -brotli = ["actix-http/brotli", "awc/brotli"] - # miniz-sys backend for flate2 crate flate2-zlib = ["actix-http/flate2-zlib", "awc/flate2-zlib"] @@ -111,7 +108,7 @@ actix-http-test = "1.0.0-alpha.3" rand = "0.7" env_logger = "0.6" serde_derive = "1.0" -brotli2 = "0.3.2" +brotli = "3.3.0" flate2 = "1.0.2" [profile.release] diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 4d89e55fb..0a8787e06 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -16,7 +16,7 @@ edition = "2018" workspace = ".." [package.metadata.docs.rs] -features = ["openssl", "rustls", "fail", "brotli", "flate2-zlib", "secure-cookies"] +features = ["openssl", "rustls", "fail", "flate2-zlib", "secure-cookies"] [lib] name = "actix_http" @@ -31,9 +31,6 @@ openssl = ["actix-tls/openssl", "actix-connect/openssl"] # rustls support rustls = ["actix-tls/rustls", "actix-connect/rustls"] -# brotli encoding, requires c compiler -brotli = ["brotli2"] - # miniz-sys backend for flate2 crate flate2-zlib = ["flate2/miniz-sys"] @@ -88,7 +85,7 @@ time = "0.1.42" ring = { version = "0.16.9", optional = true } # compression -brotli2 = { version="0.3.2", optional = true } +brotli = "3.3.0" flate2 = { version="1.0.7", optional = true, default-features = false } # optional deps diff --git a/actix-http/src/encoding/decoder.rs b/actix-http/src/encoding/decoder.rs index dca774838..90199b4f7 100644 --- a/actix-http/src/encoding/decoder.rs +++ b/actix-http/src/encoding/decoder.rs @@ -4,8 +4,7 @@ use std::pin::Pin; use std::task::{Context, Poll}; use actix_threadpool::{run, CpuFuture}; -#[cfg(feature = "brotli")] -use brotli2::write::BrotliDecoder; +use brotli::DecompressorWriter; use bytes::Bytes; #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] use flate2::write::{GzDecoder, ZlibDecoder}; @@ -32,9 +31,8 @@ where #[inline] pub fn new(stream: S, encoding: ContentEncoding) -> Decoder { let decoder = match encoding { - #[cfg(feature = "brotli")] ContentEncoding::Br => Some(ContentDecoder::Br(Box::new( - BrotliDecoder::new(Writer::new()), + DecompressorWriter::new(Writer::new(), 0), ))), #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] ContentEncoding::Deflate => Some(ContentDecoder::Deflate(Box::new( @@ -144,18 +142,16 @@ enum ContentDecoder { Deflate(Box>), #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] Gzip(Box>), - #[cfg(feature = "brotli")] - Br(Box>), + Br(Box>), } impl ContentDecoder { #[allow(unreachable_patterns)] fn feed_eof(&mut self) -> io::Result> { match self { - #[cfg(feature = "brotli")] - ContentDecoder::Br(ref mut decoder) => match decoder.finish() { - Ok(mut writer) => { - let b = writer.take(); + ContentDecoder::Br(ref mut decoder) => match decoder.flush() { + Ok(()) => { + let b = decoder.get_mut().take(); if !b.is_empty() { Ok(Some(b)) } else { @@ -195,7 +191,6 @@ impl ContentDecoder { #[allow(unreachable_patterns)] fn feed_data(&mut self, data: Bytes) -> io::Result> { match self { - #[cfg(feature = "brotli")] ContentDecoder::Br(ref mut decoder) => match decoder.write_all(&data) { Ok(_) => { decoder.flush()?; diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index d1b64bfb8..d06762787 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -5,8 +5,7 @@ use std::pin::Pin; use std::task::{Context, Poll}; use actix_threadpool::{run, CpuFuture}; -#[cfg(feature = "brotli")] -use brotli2::write::BrotliEncoder; +use brotli::CompressorWriter; use bytes::Bytes; #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] use flate2::write::{GzEncoder, ZlibEncoder}; @@ -177,8 +176,7 @@ enum ContentEncoder { Deflate(ZlibEncoder), #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] Gzip(GzEncoder), - #[cfg(feature = "brotli")] - Br(BrotliEncoder), + Br(CompressorWriter), } impl ContentEncoder { @@ -194,10 +192,12 @@ impl ContentEncoder { Writer::new(), flate2::Compression::fast(), ))), - #[cfg(feature = "brotli")] - ContentEncoding::Br => { - Some(ContentEncoder::Br(BrotliEncoder::new(Writer::new(), 3))) - } + ContentEncoding::Br => Some(ContentEncoder::Br(CompressorWriter::new( + Writer::new(), + 0, + 3, + 0, + ))), _ => None, } } @@ -205,8 +205,11 @@ impl ContentEncoder { #[inline] pub(crate) fn take(&mut self) -> Bytes { match *self { - #[cfg(feature = "brotli")] - ContentEncoder::Br(ref mut encoder) => encoder.get_mut().take(), + ContentEncoder::Br(ref mut encoder) => { + let mut encoder_new = CompressorWriter::new(Writer::new(), 0, 3, 0); + std::mem::swap(encoder, &mut encoder_new); + encoder_new.into_inner().freeze() + } #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] ContentEncoder::Deflate(ref mut encoder) => encoder.get_mut().take(), #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] @@ -216,11 +219,7 @@ impl ContentEncoder { fn finish(self) -> Result { match self { - #[cfg(feature = "brotli")] - ContentEncoder::Br(encoder) => match encoder.finish() { - Ok(writer) => Ok(writer.buf.freeze()), - Err(err) => Err(err), - }, + ContentEncoder::Br(encoder) => Ok(encoder.into_inner().buf.freeze()), #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] ContentEncoder::Gzip(encoder) => match encoder.finish() { Ok(writer) => Ok(writer.buf.freeze()), @@ -236,7 +235,6 @@ impl ContentEncoder { fn write(&mut self, data: &[u8]) -> Result<(), io::Error> { match *self { - #[cfg(feature = "brotli")] ContentEncoder::Br(ref mut encoder) => match encoder.write_all(data) { Ok(_) => Ok(()), Err(err) => { diff --git a/actix-http/src/encoding/mod.rs b/actix-http/src/encoding/mod.rs index 9f65f800c..48cf83252 100644 --- a/actix-http/src/encoding/mod.rs +++ b/actix-http/src/encoding/mod.rs @@ -22,6 +22,9 @@ impl Writer { fn take(&mut self) -> Bytes { self.buf.split().freeze() } + fn freeze(self) -> Bytes { + self.buf.freeze() + } } impl io::Write for Writer { diff --git a/awc/Cargo.toml b/awc/Cargo.toml index c4f3b7bf1..9bb72ca97 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -21,10 +21,10 @@ name = "awc" path = "src/lib.rs" [package.metadata.docs.rs] -features = ["openssl", "rustls", "brotli", "flate2-zlib"] +features = ["openssl", "rustls", "flate2-zlib"] [features] -default = ["brotli", "flate2-zlib"] +default = ["flate2-zlib"] # openssl openssl = ["open-ssl", "actix-http/openssl"] @@ -32,9 +32,6 @@ openssl = ["open-ssl", "actix-http/openssl"] # rustls rustls = ["rust-tls", "actix-http/rustls"] -# brotli encoding, requires c compiler -brotli = ["actix-http/brotli"] - # miniz-sys backend for flate2 crate flate2-zlib = ["actix-http/flate2-zlib"] @@ -69,7 +66,7 @@ actix-http-test = { version = "1.0.0-alpha.3", features=["openssl"] } actix-utils = "1.0.0-alpha.3" actix-server = { version = "1.0.0-alpha.3" } actix-tls = { version = "1.0.0-alpha.3", features=["openssl", "rustls"] } -brotli2 = { version="0.3.2" } +brotli = "3.3.0" flate2 = { version="1.0.2" } env_logger = "0.6" webpki = { version = "0.21" } diff --git a/awc/src/request.rs b/awc/src/request.rs index 5ca4973cd..b9d728b7e 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -23,13 +23,10 @@ use crate::frozen::FrozenClientRequest; use crate::sender::{PrepForSendingError, RequestSender, SendClientRequest}; use crate::ClientConfig; -#[cfg(any(feature = "brotli", feature = "flate2-zlib", feature = "flate2-rust"))] +#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] const HTTPS_ENCODING: &str = "br, gzip, deflate"; -#[cfg(all( - any(feature = "flate2-zlib", feature = "flate2-rust"), - not(feature = "brotli") -))] -const HTTPS_ENCODING: &str = "gzip, deflate"; +#[cfg(not(any(feature = "flate2-zlib", feature = "flate2-rust")))] +const HTTPS_ENCODING: &str = "br"; /// An HTTP Client request builder /// @@ -544,31 +541,23 @@ impl ClientRequest { let mut slf = self; - // enable br only for https - #[cfg(any( - feature = "brotli", - feature = "flate2-zlib", - feature = "flate2-rust" - ))] - { - if slf.response_decompress { - let https = slf - .head - .uri - .scheme() - .map(|s| s == &uri::Scheme::HTTPS) - .unwrap_or(true); + if slf.response_decompress { + let https = slf + .head + .uri + .scheme() + .map(|s| s == &uri::Scheme::HTTPS) + .unwrap_or(true); - if https { - slf = slf.set_header_if_none(header::ACCEPT_ENCODING, HTTPS_ENCODING) - } else { - #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] - { - slf = slf - .set_header_if_none(header::ACCEPT_ENCODING, "gzip, deflate") - } - }; - } + if https { + slf = slf.set_header_if_none(header::ACCEPT_ENCODING, HTTPS_ENCODING) + } else { + #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] + { + slf = + slf.set_header_if_none(header::ACCEPT_ENCODING, "gzip, deflate") + } + }; } Ok(slf) diff --git a/awc/tests/test_client.rs b/awc/tests/test_client.rs index 6bd39973f..3d2ed2354 100644 --- a/awc/tests/test_client.rs +++ b/awc/tests/test_client.rs @@ -4,7 +4,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::time::Duration; -use brotli2::write::BrotliEncoder; +use brotli::write::BrotliEncoder; use bytes::Bytes; use flate2::read::GzDecoder; use flate2::write::GzEncoder; @@ -568,7 +568,6 @@ async fn test_client_brotli_encoding() { // assert_eq!(bytes, Bytes::from(data)); // } -// #[cfg(feature = "brotli")] // #[actix_rt::test] // async fn test_client_deflate_encoding() { // let srv = test::TestServer::start(|app| { diff --git a/src/lib.rs b/src/lib.rs index b7fd8d155..04149fbfd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,8 +72,6 @@ //! * `rustls` - enables ssl support via `rustls` crate, supports `http/2` //! * `secure-cookies` - enables secure cookies support, includes `ring` crate as //! dependency -//! * `brotli` - enables `brotli` compression support, requires `c` -//! compiler (default enabled) //! * `flate2-zlib` - enables `gzip`, `deflate` compression support, requires //! `c` compiler (default enabled) //! * `flate2-rust` - experimental rust based implementation for diff --git a/tests/test_server.rs b/tests/test_server.rs index 7cfda04ad..a83526954 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -6,7 +6,7 @@ use actix_http::http::header::{ }; use actix_http::{Error, HttpService, Response}; use actix_http_test::TestServer; -use brotli2::write::{BrotliDecoder, BrotliEncoder}; +use brotli::write::{BrotliDecoder, BrotliEncoder}; use bytes::Bytes; use flate2::read::GzDecoder; use flate2::write::{GzEncoder, ZlibDecoder, ZlibEncoder}; @@ -296,7 +296,6 @@ async fn test_body_chunked_implicit() { } #[actix_rt::test] -#[cfg(feature = "brotli")] async fn test_body_br_streaming() { let srv = TestServer::start(move || { HttpService::build() @@ -411,7 +410,6 @@ async fn test_body_deflate() { } #[actix_rt::test] -#[cfg(any(feature = "brotli"))] async fn test_body_brotli() { let srv = TestServer::start(move || { HttpService::build() @@ -717,7 +715,7 @@ async fn test_brotli_encoding_large() { assert_eq!(bytes, Bytes::from(data)); } -// #[cfg(all(feature = "brotli", feature = "ssl"))] +// #[cfg(feature = "ssl")] // #[actix_rt::test] // async fn test_brotli_encoding_large_ssl() { // use actix::{Actor, System}; From 7ec5ca88a1cd48f967173a7be8b2a4019083c543 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sat, 7 Dec 2019 22:01:55 +0600 Subject: [PATCH 102/176] update changes --- actix-http/CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 1a7e5ed5b..3df706dc9 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -4,6 +4,10 @@ * Add impl ResponseBuilder for Error +### Changed + +* Use rust based brotli compression library + ## [1.0.0-alpha.3] - 2019-12-07 ### Changed From 8df33f7a81fa98c77e0dc25dfd5288cff41557f7 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sun, 8 Dec 2019 00:06:04 +0600 Subject: [PATCH 103/176] remove HttpServer::run() as it is not useful with async/await --- CHANGES.md | 4 ++++ src/server.rs | 26 -------------------------- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index cd7e0f7dc..e70452821 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Changes +### Deleted + +* Delete HttpServer::run(), it is not useful witht async/await + ## [2.0.0-alpha.3] - 2019-12-07 ### Changed diff --git a/src/server.rs b/src/server.rs index 57adec524..03abad160 100644 --- a/src/server.rs +++ b/src/server.rs @@ -531,32 +531,6 @@ where pub fn start(self) -> Server { self.builder.start() } - - /// Spawn new thread and start listening for incoming connections. - /// - /// This method spawns new thread and starts new actix system. Other than - /// that it is similar to `start()` method. This method blocks. - /// - /// This methods panics if no socket addresses get bound. - /// - /// ```rust - /// use std::io; - /// use actix_web::{web, App, HttpResponse, HttpServer}; - /// - /// fn main() -> io::Result<()> { - /// # std::thread::spawn(|| { - /// HttpServer::new(|| App::new().service(web::resource("/").to(|| HttpResponse::Ok()))) - /// .bind("127.0.0.1:0")? - /// .run() - /// # }); - /// # Ok(()) - /// } - /// ``` - pub fn run(self) -> io::Result<()> { - let sys = System::new("http-server"); - self.start(); - sys.run() - } } fn create_tcp_listener( From 6c9f9fff735023005a99bb3d17d3359bb46339c0 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sun, 8 Dec 2019 00:46:51 +0600 Subject: [PATCH 104/176] clippy warnings --- actix-http/src/body.rs | 33 ++++++++++--------- actix-http/src/client/connection.rs | 4 +-- actix-http/src/client/connector.rs | 14 ++++---- actix-http/src/client/h1proto.rs | 7 ++-- actix-http/src/client/pool.rs | 10 +++--- actix-http/src/cloneable.rs | 2 +- actix-http/src/cookie/draft.rs | 2 +- actix-http/src/cookie/jar.rs | 8 ++--- actix-http/src/cookie/mod.rs | 10 +++--- actix-http/src/cookie/parse.rs | 2 +- actix-http/src/cookie/secure/private.rs | 4 +-- actix-http/src/cookie/secure/signed.rs | 2 +- actix-http/src/encoding/decoder.rs | 2 +- actix-http/src/encoding/encoder.rs | 14 ++++---- actix-http/src/error.rs | 8 ++--- actix-http/src/extensions.rs | 2 +- actix-http/src/h1/codec.rs | 2 +- actix-http/src/h1/decoder.rs | 8 +++-- actix-http/src/h1/dispatcher.rs | 18 +++++----- actix-http/src/h1/expect.rs | 2 +- actix-http/src/h1/payload.rs | 8 ++--- actix-http/src/h1/service.rs | 8 ++--- actix-http/src/h1/upgrade.rs | 2 +- actix-http/src/h1/utils.rs | 2 +- actix-http/src/h2/dispatcher.rs | 4 +-- actix-http/src/h2/mod.rs | 5 ++- actix-http/src/h2/service.rs | 6 ++-- actix-http/src/header/common/cache_control.rs | 4 +-- .../src/header/common/content_disposition.rs | 6 ++-- actix-http/src/header/common/content_range.rs | 2 +- actix-http/src/header/common/if_range.rs | 2 +- actix-http/src/header/common/mod.rs | 8 ++--- actix-http/src/header/map.rs | 6 ++-- actix-http/src/header/mod.rs | 8 ++--- actix-http/src/header/shared/charset.rs | 2 +- actix-http/src/header/shared/encoding.rs | 2 +- actix-http/src/header/shared/entity.rs | 2 +- actix-http/src/header/shared/httpdate.rs | 2 +- actix-http/src/header/shared/quality_item.rs | 2 +- actix-http/src/httpmessage.rs | 10 +++--- actix-http/src/lib.rs | 1 + actix-http/src/message.rs | 8 ++--- actix-http/src/payload.rs | 5 ++- actix-http/src/request.rs | 6 ++-- actix-http/src/response.rs | 20 +++++------ actix-http/src/service.rs | 8 ++--- actix-http/src/ws/mask.rs | 4 +-- actix-http/src/ws/proto.rs | 2 +- actix-http/src/ws/transport.rs | 2 +- src/app_service.rs | 8 ++--- src/extract.rs | 2 +- src/handler.rs | 8 ++--- src/lib.rs | 3 +- src/middleware/compress.rs | 4 +-- src/middleware/condition.rs | 2 +- src/middleware/defaultheaders.rs | 2 +- src/middleware/errhandlers.rs | 2 +- src/middleware/logger.rs | 20 +++++------ src/middleware/normalize.rs | 3 +- src/request.rs | 12 +++---- src/resource.rs | 4 +-- src/responder.rs | 6 ++-- src/route.rs | 6 ++-- src/scope.rs | 4 +-- src/server.rs | 1 - src/service.rs | 10 +++--- src/test.rs | 2 +- src/types/form.rs | 6 ++-- src/types/json.rs | 6 ++-- src/types/path.rs | 4 +-- src/types/payload.rs | 4 +-- src/types/query.rs | 4 +-- src/types/readlines.rs | 5 ++- 73 files changed, 222 insertions(+), 207 deletions(-) diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs index ff7235626..ecb12fc23 100644 --- a/actix-http/src/body.rs +++ b/actix-http/src/body.rs @@ -35,7 +35,7 @@ impl BodySize { pub trait MessageBody { fn size(&self) -> BodySize; - fn poll_next(&mut self, cx: &mut Context) -> Poll>>; + fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll>>; } impl MessageBody for () { @@ -43,7 +43,7 @@ impl MessageBody for () { BodySize::Empty } - fn poll_next(&mut self, _: &mut Context) -> Poll>> { + fn poll_next(&mut self, _: &mut Context<'_>) -> Poll>> { Poll::Ready(None) } } @@ -53,7 +53,7 @@ impl MessageBody for Box { self.as_ref().size() } - fn poll_next(&mut self, cx: &mut Context) -> Poll>> { + fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll>> { self.as_mut().poll_next(cx) } } @@ -97,7 +97,7 @@ impl MessageBody for ResponseBody { } } - fn poll_next(&mut self, cx: &mut Context) -> Poll>> { + fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll>> { match self { ResponseBody::Body(ref mut body) => body.poll_next(cx), ResponseBody::Other(ref mut body) => body.poll_next(cx), @@ -109,7 +109,10 @@ impl Stream for ResponseBody { type Item = Result; #[project] - fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { #[project] match self.project() { ResponseBody::Body(ref mut body) => body.poll_next(cx), @@ -152,7 +155,7 @@ impl MessageBody for Body { } } - fn poll_next(&mut self, cx: &mut Context) -> Poll>> { + fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll>> { match self { Body::None => Poll::Ready(None), Body::Empty => Poll::Ready(None), @@ -190,7 +193,7 @@ impl PartialEq for Body { } impl fmt::Debug for Body { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Body::None => write!(f, "Body::None"), Body::Empty => write!(f, "Body::Empty"), @@ -272,7 +275,7 @@ impl MessageBody for Bytes { BodySize::Sized(self.len()) } - fn poll_next(&mut self, _: &mut Context) -> Poll>> { + fn poll_next(&mut self, _: &mut Context<'_>) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { @@ -286,7 +289,7 @@ impl MessageBody for BytesMut { BodySize::Sized(self.len()) } - fn poll_next(&mut self, _: &mut Context) -> Poll>> { + fn poll_next(&mut self, _: &mut Context<'_>) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { @@ -300,7 +303,7 @@ impl MessageBody for &'static str { BodySize::Sized(self.len()) } - fn poll_next(&mut self, _: &mut Context) -> Poll>> { + fn poll_next(&mut self, _: &mut Context<'_>) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { @@ -316,7 +319,7 @@ impl MessageBody for &'static [u8] { BodySize::Sized(self.len()) } - fn poll_next(&mut self, _: &mut Context) -> Poll>> { + fn poll_next(&mut self, _: &mut Context<'_>) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { @@ -330,7 +333,7 @@ impl MessageBody for Vec { BodySize::Sized(self.len()) } - fn poll_next(&mut self, _: &mut Context) -> Poll>> { + fn poll_next(&mut self, _: &mut Context<'_>) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { @@ -344,7 +347,7 @@ impl MessageBody for String { BodySize::Sized(self.len()) } - fn poll_next(&mut self, _: &mut Context) -> Poll>> { + fn poll_next(&mut self, _: &mut Context<'_>) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { @@ -386,7 +389,7 @@ where BodySize::Stream } - fn poll_next(&mut self, cx: &mut Context) -> Poll>> { + fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll>> { unsafe { Pin::new_unchecked(self) } .project() .stream @@ -421,7 +424,7 @@ where BodySize::Sized64(self.size) } - fn poll_next(&mut self, cx: &mut Context) -> Poll>> { + fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll>> { unsafe { Pin::new_unchecked(self) } .project() .stream diff --git a/actix-http/src/client/connection.rs b/actix-http/src/client/connection.rs index 9b590690f..566769c5e 100644 --- a/actix-http/src/client/connection.rs +++ b/actix-http/src/client/connection.rs @@ -63,7 +63,7 @@ impl fmt::Debug for IoConnection where T: fmt::Debug, { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.io { Some(ConnectionType::H1(ref io)) => write!(f, "H1Connection({:?})", io), Some(ConnectionType::H2(_)) => write!(f, "H2Connection"), @@ -247,7 +247,7 @@ where #[project] fn poll_write( self: Pin<&mut Self>, - cx: &mut Context, + cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { #[project] diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index c78597d01..a06afa7b0 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -62,7 +62,7 @@ trait Io: AsyncRead + AsyncWrite + Unpin {} impl Io for T {} impl Connector<(), ()> { - #[allow(clippy::new_ret_no_self)] + #[allow(clippy::new_ret_no_self, clippy::let_unit_value)] pub fn new() -> Connector< impl Service< Request = TcpConnect, @@ -378,7 +378,7 @@ mod connect_impl { Ready, ConnectError>>, >; - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.tcp_pool.poll_ready(cx) } @@ -451,7 +451,7 @@ mod connect_impl { InnerConnectorResponseB, >; - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.tcp_pool.poll_ready(cx) } @@ -490,10 +490,10 @@ mod connect_impl { { type Output = Result, ConnectError>; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Poll::Ready( ready!(Pin::new(&mut self.get_mut().fut).poll(cx)) - .map(|res| EitherConnection::A(res)), + .map(EitherConnection::A), ) } } @@ -519,10 +519,10 @@ mod connect_impl { { type Output = Result, ConnectError>; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Poll::Ready( ready!(Pin::new(&mut self.get_mut().fut).poll(cx)) - .map(|res| EitherConnection::B(res)), + .map(EitherConnection::B), ) } } diff --git a/actix-http/src/client/h1proto.rs b/actix-http/src/client/h1proto.rs index 048d3795c..db4dede71 100644 --- a/actix-http/src/client/h1proto.rs +++ b/actix-http/src/client/h1proto.rs @@ -234,7 +234,7 @@ impl AsyncWrite for H1Connection fn poll_shutdown( mut self: Pin<&mut Self>, - cx: &mut Context, + cx: &mut Context<'_>, ) -> Poll> { Pin::new(self.io.as_mut().unwrap()).poll_shutdown(cx) } @@ -255,7 +255,10 @@ impl PlStream { impl Stream for PlStream { type Item = Result; - fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { let this = self.get_mut(); match this.framed.as_mut().unwrap().next_item(cx)? { diff --git a/actix-http/src/client/pool.rs b/actix-http/src/client/pool.rs index ef5bd5965..0346c0614 100644 --- a/actix-http/src/client/pool.rs +++ b/actix-http/src/client/pool.rs @@ -93,7 +93,7 @@ where type Error = ConnectError; type Future = LocalBoxFuture<'static, Result, ConnectError>>; - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.0.poll_ready(cx) } @@ -308,7 +308,7 @@ where (rx, token) } - fn acquire(&mut self, key: &Key, cx: &mut Context) -> Acquire { + fn acquire(&mut self, key: &Key, cx: &mut Context<'_>) -> Acquire { // check limits if self.limit > 0 && self.acquired >= self.limit { return Acquire::NotAvailable; @@ -409,7 +409,7 @@ where { type Output = (); - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { let this = self.get_mut(); match Pin::new(&mut this.timeout).poll(cx) { @@ -438,7 +438,7 @@ where { type Output = (); - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = unsafe { self.get_unchecked_mut() }; let mut inner = this.inner.as_ref().borrow_mut(); @@ -545,7 +545,7 @@ where { type Output = (); - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = unsafe { self.get_unchecked_mut() }; if let Some(ref mut h2) = this.h2 { diff --git a/actix-http/src/cloneable.rs b/actix-http/src/cloneable.rs index 18869c66d..90d198b9c 100644 --- a/actix-http/src/cloneable.rs +++ b/actix-http/src/cloneable.rs @@ -32,7 +32,7 @@ where type Error = T::Error; type Future = T::Future; - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { unsafe { &mut *self.0.as_ref().get() }.poll_ready(cx) } diff --git a/actix-http/src/cookie/draft.rs b/actix-http/src/cookie/draft.rs index 362133946..a2b039121 100644 --- a/actix-http/src/cookie/draft.rs +++ b/actix-http/src/cookie/draft.rs @@ -88,7 +88,7 @@ impl SameSite { } impl fmt::Display for SameSite { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { SameSite::Strict => write!(f, "Strict"), SameSite::Lax => write!(f, "Lax"), diff --git a/actix-http/src/cookie/jar.rs b/actix-http/src/cookie/jar.rs index cc67536c6..91af49320 100644 --- a/actix-http/src/cookie/jar.rs +++ b/actix-http/src/cookie/jar.rs @@ -307,7 +307,7 @@ impl CookieJar { /// // Delta contains two new cookies ("new", "yac") and a removal ("name"). /// assert_eq!(jar.delta().count(), 3); /// ``` - pub fn delta(&self) -> Delta { + pub fn delta(&self) -> Delta<'_> { Delta { iter: self.delta_cookies.iter(), } @@ -343,7 +343,7 @@ impl CookieJar { /// } /// } /// ``` - pub fn iter(&self) -> Iter { + pub fn iter(&self) -> Iter<'_> { Iter { delta_cookies: self .delta_cookies @@ -386,7 +386,7 @@ impl CookieJar { /// assert!(jar.get("private").is_some()); /// ``` #[cfg(feature = "secure-cookies")] - pub fn private(&mut self, key: &Key) -> PrivateJar { + pub fn private(&mut self, key: &Key) -> PrivateJar<'_> { PrivateJar::new(self, key) } @@ -424,7 +424,7 @@ impl CookieJar { /// assert!(jar.get("signed").is_some()); /// ``` #[cfg(feature = "secure-cookies")] - pub fn signed(&mut self, key: &Key) -> SignedJar { + pub fn signed(&mut self, key: &Key) -> SignedJar<'_> { SignedJar::new(self, key) } } diff --git a/actix-http/src/cookie/mod.rs b/actix-http/src/cookie/mod.rs index db8211427..cd150337d 100644 --- a/actix-http/src/cookie/mod.rs +++ b/actix-http/src/cookie/mod.rs @@ -110,7 +110,7 @@ impl CookieStr { /// # Panics /// /// Panics if `self` is an indexed string and `string` is None. - fn to_str<'s>(&'s self, string: Option<&'s Cow>) -> &'s str { + fn to_str<'s>(&'s self, string: Option<&'s Cow<'_, str>>) -> &'s str { match *self { CookieStr::Indexed(i, j) => { let s = string.expect( @@ -742,7 +742,7 @@ impl<'c> Cookie<'c> { self.set_expires(time::now() + twenty_years); } - fn fmt_parameters(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt_parameters(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(true) = self.http_only() { write!(f, "; HttpOnly")?; } @@ -924,10 +924,10 @@ impl<'c> Cookie<'c> { /// let mut c = Cookie::new("my name", "this; value?"); /// assert_eq!(&c.encoded().to_string(), "my%20name=this%3B%20value%3F"); /// ``` -pub struct EncodedCookie<'a, 'c: 'a>(&'a Cookie<'c>); +pub struct EncodedCookie<'a, 'c>(&'a Cookie<'c>); impl<'a, 'c: 'a> fmt::Display for EncodedCookie<'a, 'c> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Percent-encode the name and value. let name = percent_encode(self.0.name().as_bytes(), USERINFO); let value = percent_encode(self.0.value().as_bytes(), USERINFO); @@ -952,7 +952,7 @@ impl<'c> fmt::Display for Cookie<'c> { /// /// assert_eq!(&cookie.to_string(), "foo=bar; Path=/"); /// ``` - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}={}", self.name(), self.value())?; self.fmt_parameters(f) } diff --git a/actix-http/src/cookie/parse.rs b/actix-http/src/cookie/parse.rs index 42a2c1fcf..8c9d4b644 100644 --- a/actix-http/src/cookie/parse.rs +++ b/actix-http/src/cookie/parse.rs @@ -40,7 +40,7 @@ impl ParseError { } impl fmt::Display for ParseError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.as_str()) } } diff --git a/actix-http/src/cookie/secure/private.rs b/actix-http/src/cookie/secure/private.rs index 6c16e94e8..f05e23800 100644 --- a/actix-http/src/cookie/secure/private.rs +++ b/actix-http/src/cookie/secure/private.rs @@ -10,7 +10,7 @@ use crate::cookie::{Cookie, CookieJar}; // Keep these in sync, and keep the key len synced with the `private` docs as // well as the `KEYS_INFO` const in secure::Key. -static ALGO: &'static Algorithm = &AES_256_GCM; +static ALGO: &Algorithm = &AES_256_GCM; const NONCE_LEN: usize = 12; pub const KEY_LEN: usize = 32; @@ -159,7 +159,7 @@ Please change it as soon as possible." /// Encrypts the cookie's value with /// authenticated encryption assuring confidentiality, integrity, and authenticity. - fn encrypt_cookie(&self, cookie: &mut Cookie) { + fn encrypt_cookie(&self, cookie: &mut Cookie<'_>) { let name = cookie.name().as_bytes(); let value = cookie.value().as_bytes(); let data = encrypt_name_value(name, value, &self.key); diff --git a/actix-http/src/cookie/secure/signed.rs b/actix-http/src/cookie/secure/signed.rs index 3fcd2cd84..64e8d5dda 100644 --- a/actix-http/src/cookie/secure/signed.rs +++ b/actix-http/src/cookie/secure/signed.rs @@ -129,7 +129,7 @@ impl<'a> SignedJar<'a> { } /// Signs the cookie's value assuring integrity and authenticity. - fn sign_cookie(&self, cookie: &mut Cookie) { + fn sign_cookie(&self, cookie: &mut Cookie<'_>) { let digest = sign(&self.key, cookie.value().as_bytes()); let mut new_value = base64::encode(digest.as_ref()); new_value.push_str(cookie.value()); diff --git a/actix-http/src/encoding/decoder.rs b/actix-http/src/encoding/decoder.rs index 90199b4f7..35be2d13c 100644 --- a/actix-http/src/encoding/decoder.rs +++ b/actix-http/src/encoding/decoder.rs @@ -78,7 +78,7 @@ where fn poll_next( mut self: Pin<&mut Self>, - cx: &mut Context, + cx: &mut Context<'_>, ) -> Poll> { loop { if let Some(ref mut fut) = self.fut { diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index d06762787..f8f996ff1 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -95,7 +95,7 @@ impl MessageBody for Encoder { } } - fn poll_next(&mut self, cx: &mut Context) -> Poll>> { + fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll>> { loop { if self.eof { return Poll::Ready(None); @@ -176,7 +176,7 @@ enum ContentEncoder { Deflate(ZlibEncoder), #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] Gzip(GzEncoder), - Br(CompressorWriter), + Br(Box>), } impl ContentEncoder { @@ -192,11 +192,8 @@ impl ContentEncoder { Writer::new(), flate2::Compression::fast(), ))), - ContentEncoding::Br => Some(ContentEncoder::Br(CompressorWriter::new( - Writer::new(), - 0, - 3, - 0, + ContentEncoding::Br => Some(ContentEncoder::Br(Box::new( + CompressorWriter::new(Writer::new(), 0, 3, 0), ))), _ => None, } @@ -206,7 +203,8 @@ impl ContentEncoder { pub(crate) fn take(&mut self) -> Bytes { match *self { ContentEncoder::Br(ref mut encoder) => { - let mut encoder_new = CompressorWriter::new(Writer::new(), 0, 3, 0); + let mut encoder_new = + Box::new(CompressorWriter::new(Writer::new(), 0, 3, 0)); std::mem::swap(encoder, &mut encoder_new); encoder_new.into_inner().freeze() } diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index ec56900fc..1dca55902 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -102,13 +102,13 @@ impl dyn ResponseError + 'static { } impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.cause, f) } } impl fmt::Debug for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "{:?}", &self.cause) } } @@ -515,7 +515,7 @@ impl fmt::Debug for InternalError where T: fmt::Debug + 'static, { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.cause, f) } } @@ -524,7 +524,7 @@ impl fmt::Display for InternalError where T: fmt::Display + 'static, { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.cause, f) } } diff --git a/actix-http/src/extensions.rs b/actix-http/src/extensions.rs index 066e0a3ca..d85ca184d 100644 --- a/actix-http/src/extensions.rs +++ b/actix-http/src/extensions.rs @@ -65,7 +65,7 @@ impl Extensions { } impl fmt::Debug for Extensions { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Extensions").finish() } } diff --git a/actix-http/src/h1/codec.rs b/actix-http/src/h1/codec.rs index 22c7ed232..726d1c97f 100644 --- a/actix-http/src/h1/codec.rs +++ b/actix-http/src/h1/codec.rs @@ -49,7 +49,7 @@ impl Default for Codec { } impl fmt::Debug for Codec { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "h1::Codec({:?})", self.flags) } } diff --git a/actix-http/src/h1/decoder.rs b/actix-http/src/h1/decoder.rs index 32ec64ba3..87e2a1ec8 100644 --- a/actix-http/src/h1/decoder.rs +++ b/actix-http/src/h1/decoder.rs @@ -185,6 +185,7 @@ impl MessageType for Request { &mut self.head_mut().headers } + #[allow(clippy::uninit_assumed_init)] fn decode(src: &mut BytesMut) -> Result, ParseError> { // Unsafe: we read only this data only after httparse parses headers into. // performance bump for pipeline benchmarks. @@ -192,7 +193,7 @@ impl MessageType for Request { unsafe { MaybeUninit::uninit().assume_init() }; let (len, method, uri, ver, h_len) = { - let mut parsed: [httparse::Header; MAX_HEADERS] = + let mut parsed: [httparse::Header<'_>; MAX_HEADERS] = unsafe { MaybeUninit::uninit().assume_init() }; let mut req = httparse::Request::new(&mut parsed); @@ -260,6 +261,7 @@ impl MessageType for ResponseHead { &mut self.headers } + #[allow(clippy::uninit_assumed_init)] fn decode(src: &mut BytesMut) -> Result, ParseError> { // Unsafe: we read only this data only after httparse parses headers into. // performance bump for pipeline benchmarks. @@ -267,7 +269,7 @@ impl MessageType for ResponseHead { unsafe { MaybeUninit::uninit().assume_init() }; let (len, ver, status, h_len) = { - let mut parsed: [httparse::Header; MAX_HEADERS] = + let mut parsed: [httparse::Header<'_>; MAX_HEADERS] = unsafe { MaybeUninit::uninit().assume_init() }; let mut res = httparse::Response::new(&mut parsed); @@ -326,7 +328,7 @@ pub(crate) struct HeaderIndex { impl HeaderIndex { pub(crate) fn record( bytes: &[u8], - headers: &[httparse::Header], + headers: &[httparse::Header<'_>], indices: &mut [HeaderIndex], ) { let bytes_ptr = bytes.as_ptr() as usize; diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 0e2a58346..7276d5a38 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -264,7 +264,7 @@ where U: Service), Response = ()>, U::Error: fmt::Display, { - fn can_read(&self, cx: &mut Context) -> bool { + fn can_read(&self, cx: &mut Context<'_>) -> bool { if self .flags .intersects(Flags::READ_DISCONNECT | Flags::UPGRADE) @@ -290,7 +290,7 @@ where /// /// true - got whouldblock /// false - didnt get whouldblock - fn poll_flush(&mut self, cx: &mut Context) -> Result { + fn poll_flush(&mut self, cx: &mut Context<'_>) -> Result { if self.write_buf.is_empty() { return Ok(false); } @@ -355,7 +355,7 @@ where fn poll_response( &mut self, - cx: &mut Context, + cx: &mut Context<'_>, ) -> Result { loop { let state = match self.state { @@ -459,7 +459,7 @@ where fn handle_request( &mut self, req: Request, - cx: &mut Context, + cx: &mut Context<'_>, ) -> Result, DispatchError> { // Handle `EXPECT: 100-Continue` header let req = if req.head().expect() { @@ -500,7 +500,7 @@ where /// Process one incoming requests pub(self) fn poll_request( &mut self, - cx: &mut Context, + cx: &mut Context<'_>, ) -> Result { // limit a mount of non processed requests if self.messages.len() >= MAX_PIPELINED_MESSAGES || !self.can_read(cx) { @@ -604,7 +604,7 @@ where } /// keep-alive timer - fn poll_keepalive(&mut self, cx: &mut Context) -> Result<(), DispatchError> { + fn poll_keepalive(&mut self, cx: &mut Context<'_>) -> Result<(), DispatchError> { if self.ka_timer.is_none() { // shutdown timeout if self.flags.contains(Flags::SHUTDOWN) { @@ -710,7 +710,7 @@ where type Output = Result<(), DispatchError>; #[inline] - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match self.as_mut().inner { DispatcherState::Normal(ref mut inner) => { inner.poll_keepalive(cx)?; @@ -832,7 +832,7 @@ where } fn read_available( - cx: &mut Context, + cx: &mut Context<'_>, io: &mut T, buf: &mut BytesMut, ) -> Result, io::Error> @@ -874,7 +874,7 @@ where } fn read( - cx: &mut Context, + cx: &mut Context<'_>, io: &mut T, buf: &mut BytesMut, ) -> Poll> diff --git a/actix-http/src/h1/expect.rs b/actix-http/src/h1/expect.rs index 109491bac..187999358 100644 --- a/actix-http/src/h1/expect.rs +++ b/actix-http/src/h1/expect.rs @@ -28,7 +28,7 @@ impl Service for ExpectHandler { type Error = Error; type Future = Ready>; - fn poll_ready(&mut self, _: &mut Context) -> Poll> { + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } diff --git a/actix-http/src/h1/payload.rs b/actix-http/src/h1/payload.rs index 46f2f9728..abf42dc89 100644 --- a/actix-http/src/h1/payload.rs +++ b/actix-http/src/h1/payload.rs @@ -82,7 +82,7 @@ impl Payload { #[inline] pub fn readany( &mut self, - cx: &mut Context, + cx: &mut Context<'_>, ) -> Poll>> { self.inner.borrow_mut().readany(cx) } @@ -93,7 +93,7 @@ impl Stream for Payload { fn poll_next( self: Pin<&mut Self>, - cx: &mut Context, + cx: &mut Context<'_>, ) -> Poll>> { self.inner.borrow_mut().readany(cx) } @@ -127,7 +127,7 @@ impl PayloadSender { } #[inline] - pub fn need_read(&self, cx: &mut Context) -> PayloadStatus { + pub fn need_read(&self, cx: &mut Context<'_>) -> PayloadStatus { // we check need_read only if Payload (other side) is alive, // otherwise always return true (consume payload) if let Some(shared) = self.inner.upgrade() { @@ -194,7 +194,7 @@ impl Inner { fn readany( &mut self, - cx: &mut Context, + cx: &mut Context<'_>, ) -> Poll>> { if let Some(data) = self.items.pop_front() { self.len -= data.len(); diff --git a/actix-http/src/h1/service.rs b/actix-http/src/h1/service.rs index d6494b5cb..6d5123843 100644 --- a/actix-http/src/h1/service.rs +++ b/actix-http/src/h1/service.rs @@ -324,7 +324,7 @@ where { type Output = Result, ()>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.as_mut().project(); if let Some(fut) = this.fut_ex.as_pin_mut() { @@ -419,7 +419,7 @@ where type Error = DispatchError; type Future = Dispatcher; - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { let ready = self .expect .poll_ready(cx) @@ -523,7 +523,7 @@ where type Error = ParseError; type Future = OneRequestServiceResponse; - fn poll_ready(&mut self, _: &mut Context) -> Poll> { + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } @@ -548,7 +548,7 @@ where { type Output = Result<(Request, Framed), ParseError>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match self.framed.as_mut().unwrap().next_item(cx) { Poll::Ready(Some(Ok(req))) => match req { Message::Item(req) => { diff --git a/actix-http/src/h1/upgrade.rs b/actix-http/src/h1/upgrade.rs index e3ce66521..d02d4f075 100644 --- a/actix-http/src/h1/upgrade.rs +++ b/actix-http/src/h1/upgrade.rs @@ -31,7 +31,7 @@ impl Service for UpgradeHandler { type Error = Error; type Future = Ready>; - fn poll_ready(&mut self, _: &mut Context) -> Poll> { + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } diff --git a/actix-http/src/h1/utils.rs b/actix-http/src/h1/utils.rs index 7af0b124e..9ba4aa053 100644 --- a/actix-http/src/h1/utils.rs +++ b/actix-http/src/h1/utils.rs @@ -39,7 +39,7 @@ where { type Output = Result, Error>; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.get_mut(); loop { diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index 707ac7b9d..b827762cb 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -106,7 +106,7 @@ where type Output = Result<(), DispatchError>; #[inline] - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.get_mut(); loop { @@ -252,7 +252,7 @@ where { type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.as_mut().project(); match this.state { diff --git a/actix-http/src/h2/mod.rs b/actix-http/src/h2/mod.rs index 28dd75383..21080c69a 100644 --- a/actix-http/src/h2/mod.rs +++ b/actix-http/src/h2/mod.rs @@ -29,7 +29,10 @@ impl Payload { impl Stream for Payload { type Item = Result; - fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { let this = self.get_mut(); match Pin::new(&mut this.pl).poll_data(cx) { diff --git a/actix-http/src/h2/service.rs b/actix-http/src/h2/service.rs index 1bacc5c95..cdef71c57 100644 --- a/actix-http/src/h2/service.rs +++ b/actix-http/src/h2/service.rs @@ -235,7 +235,7 @@ where { type Output = Result, S::InitError>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.as_mut().project(); Poll::Ready(ready!(this.fut.poll(cx)).map(|service| { @@ -293,7 +293,7 @@ where type Error = DispatchError; type Future = H2ServiceHandlerResponse; - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.srv.poll_ready(cx).map_err(|e| { let e = e.into(); error!("Service readiness error: {:?}", e); @@ -358,7 +358,7 @@ where { type Output = Result<(), DispatchError>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match self.state { State::Incoming(ref mut disp) => Pin::new(disp).poll(cx), State::Handshake( diff --git a/actix-http/src/header/common/cache_control.rs b/actix-http/src/header/common/cache_control.rs index a3253b85b..ec94ce4a9 100644 --- a/actix-http/src/header/common/cache_control.rs +++ b/actix-http/src/header/common/cache_control.rs @@ -74,7 +74,7 @@ impl Header for CacheControl { } impl fmt::Display for CacheControl { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt_comma_delimited(f, &self[..]) } } @@ -126,7 +126,7 @@ pub enum CacheDirective { } impl fmt::Display for CacheDirective { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use self::CacheDirective::*; fmt::Display::fmt( match *self { diff --git a/actix-http/src/header/common/content_disposition.rs b/actix-http/src/header/common/content_disposition.rs index d09024f3f..d0d5af765 100644 --- a/actix-http/src/header/common/content_disposition.rs +++ b/actix-http/src/header/common/content_disposition.rs @@ -486,7 +486,7 @@ impl Header for ContentDisposition { } impl fmt::Display for DispositionType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { DispositionType::Inline => write!(f, "inline"), DispositionType::Attachment => write!(f, "attachment"), @@ -497,7 +497,7 @@ impl fmt::Display for DispositionType { } impl fmt::Display for DispositionParam { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // All ASCII control characters (0-30, 127) including horizontal tab, double quote, and // backslash should be escaped in quoted-string (i.e. "foobar"). // Ref: RFC6266 S4.1 -> RFC2616 S3.6 @@ -555,7 +555,7 @@ impl fmt::Display for DispositionParam { } impl fmt::Display for ContentDisposition { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.disposition)?; self.parameters .iter() diff --git a/actix-http/src/header/common/content_range.rs b/actix-http/src/header/common/content_range.rs index a3e4d49ba..9a604c641 100644 --- a/actix-http/src/header/common/content_range.rs +++ b/actix-http/src/header/common/content_range.rs @@ -166,7 +166,7 @@ impl FromStr for ContentRangeSpec { } impl Display for ContentRangeSpec { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { ContentRangeSpec::Bytes { range, diff --git a/actix-http/src/header/common/if_range.rs b/actix-http/src/header/common/if_range.rs index ea5d69a13..b14ad0391 100644 --- a/actix-http/src/header/common/if_range.rs +++ b/actix-http/src/header/common/if_range.rs @@ -87,7 +87,7 @@ impl Header for IfRange { } impl Display for IfRange { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { IfRange::EntityTag(ref x) => Display::fmt(x, f), IfRange::Date(ref x) => Display::fmt(x, f), diff --git a/actix-http/src/header/common/mod.rs b/actix-http/src/header/common/mod.rs index 814050b13..08950ea8b 100644 --- a/actix-http/src/header/common/mod.rs +++ b/actix-http/src/header/common/mod.rs @@ -159,7 +159,7 @@ macro_rules! header { } impl std::fmt::Display for $id { #[inline] - fn fmt(&self, f: &mut std::fmt::Formatter) -> ::std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> ::std::fmt::Result { $crate::http::header::fmt_comma_delimited(f, &self.0[..]) } } @@ -195,7 +195,7 @@ macro_rules! header { } impl std::fmt::Display for $id { #[inline] - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { $crate::http::header::fmt_comma_delimited(f, &self.0[..]) } } @@ -231,7 +231,7 @@ macro_rules! header { } impl std::fmt::Display for $id { #[inline] - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(&self.0, f) } } @@ -276,7 +276,7 @@ macro_rules! header { } impl std::fmt::Display for $id { #[inline] - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match *self { $id::Any => f.write_str("*"), $id::Items(ref fields) => $crate::http::header::fmt_comma_delimited( diff --git a/actix-http/src/header/map.rs b/actix-http/src/header/map.rs index dc49d53f3..132087b9e 100644 --- a/actix-http/src/header/map.rs +++ b/actix-http/src/header/map.rs @@ -143,7 +143,7 @@ impl HeaderMap { /// Returns `None` if there are no values associated with the key. /// /// [`GetAll`]: struct.GetAll.html - pub fn get_all(&self, name: N) -> GetAll { + pub fn get_all(&self, name: N) -> GetAll<'_> { GetAll { idx: 0, item: self.get2(name), @@ -187,7 +187,7 @@ impl HeaderMap { /// The iteration order is arbitrary, but consistent across platforms for /// the same crate version. Each key will be yielded once per associated /// value. So, if a key has 3 associated values, it will be yielded 3 times. - pub fn iter(&self) -> Iter { + pub fn iter(&self) -> Iter<'_> { Iter::new(self.inner.iter()) } @@ -196,7 +196,7 @@ impl HeaderMap { /// The iteration order is arbitrary, but consistent across platforms for /// the same crate version. Each key will be yielded only once even if it /// has multiple associated values. - pub fn keys(&self) -> Keys { + pub fn keys(&self) -> Keys<'_> { Keys(self.inner.keys()) } diff --git a/actix-http/src/header/mod.rs b/actix-http/src/header/mod.rs index 6fd3c1b96..0db26ceb0 100644 --- a/actix-http/src/header/mod.rs +++ b/actix-http/src/header/mod.rs @@ -217,7 +217,7 @@ impl fmt::Write for Writer { } #[inline] - fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result { + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result { fmt::write(self, args) } } @@ -259,7 +259,7 @@ pub fn from_one_raw_str(val: Option<&HeaderValue>) -> Result(f: &mut fmt::Formatter, parts: &[T]) -> fmt::Result +pub fn fmt_comma_delimited(f: &mut fmt::Formatter<'_>, parts: &[T]) -> fmt::Result where T: fmt::Display, { @@ -361,7 +361,7 @@ pub fn parse_extended_value( } impl fmt::Display for ExtendedValue { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let encoded_value = percent_encoding::percent_encode(&self.value[..], HTTP_VALUE); if let Some(ref lang) = self.language_tag { @@ -376,7 +376,7 @@ impl fmt::Display for ExtendedValue { /// [https://tools.ietf.org/html/rfc5987#section-3.2][url] /// /// [url]: https://tools.ietf.org/html/rfc5987#section-3.2 -pub fn http_percent_encode(f: &mut fmt::Formatter, bytes: &[u8]) -> fmt::Result { +pub fn http_percent_encode(f: &mut fmt::Formatter<'_>, bytes: &[u8]) -> fmt::Result { let encoded = percent_encoding::percent_encode(bytes, HTTP_VALUE); fmt::Display::fmt(&encoded, f) } diff --git a/actix-http/src/header/shared/charset.rs b/actix-http/src/header/shared/charset.rs index ec3fe3854..6ddfa03ea 100644 --- a/actix-http/src/header/shared/charset.rs +++ b/actix-http/src/header/shared/charset.rs @@ -98,7 +98,7 @@ impl Charset { } impl Display for Charset { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.label()) } } diff --git a/actix-http/src/header/shared/encoding.rs b/actix-http/src/header/shared/encoding.rs index af7404828..aa49dea45 100644 --- a/actix-http/src/header/shared/encoding.rs +++ b/actix-http/src/header/shared/encoding.rs @@ -27,7 +27,7 @@ pub enum Encoding { } impl fmt::Display for Encoding { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(match *self { Chunked => "chunked", Brotli => "br", diff --git a/actix-http/src/header/shared/entity.rs b/actix-http/src/header/shared/entity.rs index 7ef51a7d6..3525a19c6 100644 --- a/actix-http/src/header/shared/entity.rs +++ b/actix-http/src/header/shared/entity.rs @@ -113,7 +113,7 @@ impl EntityTag { } impl Display for EntityTag { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.weak { write!(f, "W/\"{}\"", self.tag) } else { diff --git a/actix-http/src/header/shared/httpdate.rs b/actix-http/src/header/shared/httpdate.rs index c8d26ef54..28d6a25ec 100644 --- a/actix-http/src/header/shared/httpdate.rs +++ b/actix-http/src/header/shared/httpdate.rs @@ -28,7 +28,7 @@ impl FromStr for HttpDate { } impl Display for HttpDate { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.0.to_utc().rfc822(), f) } } diff --git a/actix-http/src/header/shared/quality_item.rs b/actix-http/src/header/shared/quality_item.rs index fc3930c5e..98230dec1 100644 --- a/actix-http/src/header/shared/quality_item.rs +++ b/actix-http/src/header/shared/quality_item.rs @@ -53,7 +53,7 @@ impl cmp::PartialOrd for QualityItem { } impl fmt::Display for QualityItem { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.item, f)?; match self.quality.0 { 1000 => Ok(()), diff --git a/actix-http/src/httpmessage.rs b/actix-http/src/httpmessage.rs index 05d668c10..e1c4136b0 100644 --- a/actix-http/src/httpmessage.rs +++ b/actix-http/src/httpmessage.rs @@ -25,10 +25,10 @@ pub trait HttpMessage: Sized { fn take_payload(&mut self) -> Payload; /// Request's extensions container - fn extensions(&self) -> Ref; + fn extensions(&self) -> Ref<'_, Extensions>; /// Mutable reference to a the request's extensions container - fn extensions_mut(&self) -> RefMut; + fn extensions_mut(&self) -> RefMut<'_, Extensions>; #[doc(hidden)] /// Get a header @@ -105,7 +105,7 @@ pub trait HttpMessage: Sized { /// Load request cookies. #[inline] - fn cookies(&self) -> Result>>, CookieParseError> { + fn cookies(&self) -> Result>>, CookieParseError> { if self.extensions().get::().is_none() { let mut cookies = Vec::new(); for hdr in self.headers().get_all(header::COOKIE) { @@ -153,12 +153,12 @@ where } /// Request's extensions container - fn extensions(&self) -> Ref { + fn extensions(&self) -> Ref<'_, Extensions> { (**self).extensions() } /// Mutable reference to a the request's extensions container - fn extensions_mut(&self) -> RefMut { + fn extensions_mut(&self) -> RefMut<'_, Extensions> { (**self).extensions_mut() } } diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs index 190d5fbdc..1da074ad1 100644 --- a/actix-http/src/lib.rs +++ b/actix-http/src/lib.rs @@ -1,4 +1,5 @@ //! Basic http primitives for actix-net framework. +#![deny(rust_2018_idioms, warnings)] #![allow( clippy::type_complexity, clippy::too_many_arguments, diff --git a/actix-http/src/message.rs b/actix-http/src/message.rs index 5994ed39e..d005ad04a 100644 --- a/actix-http/src/message.rs +++ b/actix-http/src/message.rs @@ -78,13 +78,13 @@ impl Head for RequestHead { impl RequestHead { /// Message extensions #[inline] - pub fn extensions(&self) -> Ref { + pub fn extensions(&self) -> Ref<'_, Extensions> { self.extensions.borrow() } /// Mutable reference to a the message's extensions #[inline] - pub fn extensions_mut(&self) -> RefMut { + pub fn extensions_mut(&self) -> RefMut<'_, Extensions> { self.extensions.borrow_mut() } @@ -237,13 +237,13 @@ impl ResponseHead { /// Message extensions #[inline] - pub fn extensions(&self) -> Ref { + pub fn extensions(&self) -> Ref<'_, Extensions> { self.extensions.borrow() } /// Mutable reference to a the message's extensions #[inline] - pub fn extensions_mut(&self) -> RefMut { + pub fn extensions_mut(&self) -> RefMut<'_, Extensions> { self.extensions.borrow_mut() } diff --git a/actix-http/src/payload.rs b/actix-http/src/payload.rs index b3ec04d11..9f7f2a31f 100644 --- a/actix-http/src/payload.rs +++ b/actix-http/src/payload.rs @@ -56,7 +56,10 @@ where type Item = Result; #[inline] - fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { match self.get_mut() { Payload::None => Poll::Ready(None), Payload::H1(ref mut pl) => pl.readany(cx), diff --git a/actix-http/src/request.rs b/actix-http/src/request.rs index 371e2ccd8..64e302441 100644 --- a/actix-http/src/request.rs +++ b/actix-http/src/request.rs @@ -25,13 +25,13 @@ impl

HttpMessage for Request

{ /// Request extensions #[inline] - fn extensions(&self) -> Ref { + fn extensions(&self) -> Ref<'_, Extensions> { self.head.extensions() } /// Mutable reference to a the request's extensions #[inline] - fn extensions_mut(&self) -> RefMut { + fn extensions_mut(&self) -> RefMut<'_, Extensions> { self.head.extensions_mut() } @@ -165,7 +165,7 @@ impl

Request

{ } impl

fmt::Debug for Request

{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!( f, "\nRequest {:?} {}:{}", diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index e7f145d39..eee0abc73 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -130,7 +130,7 @@ impl Response { /// Get an iterator for the cookies set by this response #[inline] - pub fn cookies(&self) -> CookieIter { + pub fn cookies(&self) -> CookieIter<'_> { CookieIter { iter: self.head.headers.get_all(header::SET_COOKIE), } @@ -138,7 +138,7 @@ impl Response { /// Add a cookie to this response #[inline] - pub fn add_cookie(&mut self, cookie: &Cookie) -> Result<(), HttpError> { + pub fn add_cookie(&mut self, cookie: &Cookie<'_>) -> Result<(), HttpError> { let h = &mut self.head.headers; HeaderValue::from_str(&cookie.to_string()) .map(|c| { @@ -186,13 +186,13 @@ impl Response { /// Responses extensions #[inline] - pub fn extensions(&self) -> Ref { + pub fn extensions(&self) -> Ref<'_, Extensions> { self.head.extensions.borrow() } /// Mutable reference to a the response's extensions #[inline] - pub fn extensions_mut(&mut self) -> RefMut { + pub fn extensions_mut(&mut self) -> RefMut<'_, Extensions> { self.head.extensions.borrow_mut() } @@ -265,7 +265,7 @@ impl Response { } impl fmt::Debug for Response { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let res = writeln!( f, "\nResponse {:?} {}{}", @@ -285,7 +285,7 @@ impl fmt::Debug for Response { impl Future for Response { type Output = Result; - fn poll(mut self: Pin<&mut Self>, _: &mut Context) -> Poll { + fn poll(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { Poll::Ready(Ok(Response { head: self.head.take(), body: self.body.take_body(), @@ -588,14 +588,14 @@ impl ResponseBuilder { /// Responses extensions #[inline] - pub fn extensions(&self) -> Ref { + pub fn extensions(&self) -> Ref<'_, Extensions> { let head = self.head.as_ref().expect("cannot reuse response builder"); head.extensions.borrow() } /// Mutable reference to a the response's extensions #[inline] - pub fn extensions_mut(&mut self) -> RefMut { + pub fn extensions_mut(&mut self) -> RefMut<'_, Extensions> { let head = self.head.as_ref().expect("cannot reuse response builder"); head.extensions.borrow_mut() } @@ -765,13 +765,13 @@ impl<'a> From<&'a ResponseHead> for ResponseBuilder { impl Future for ResponseBuilder { type Output = Result; - fn poll(mut self: Pin<&mut Self>, _: &mut Context) -> Poll { + fn poll(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { Poll::Ready(Ok(self.finish())) } } impl fmt::Debug for ResponseBuilder { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let head = self.head.as_ref().unwrap(); let res = writeln!( diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index 3624060bf..cb7e541ee 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -403,7 +403,7 @@ where type Output = Result, ()>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.as_mut().project(); if let Some(fut) = this.fut_ex.as_pin_mut() { @@ -499,7 +499,7 @@ where type Error = DispatchError; type Future = HttpServiceHandlerResponse; - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { let ready = self .expect .poll_ready(cx) @@ -619,7 +619,7 @@ where { type Output = Result<(), DispatchError>; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { self.project().state.poll(cx) } } @@ -639,7 +639,7 @@ where #[project] fn poll( mut self: Pin<&mut Self>, - cx: &mut Context, + cx: &mut Context<'_>, ) -> Poll> { #[project] match self.as_mut().project() { diff --git a/actix-http/src/ws/mask.rs b/actix-http/src/ws/mask.rs index 9f7304039..7eb5d148f 100644 --- a/actix-http/src/ws/mask.rs +++ b/actix-http/src/ws/mask.rs @@ -51,7 +51,7 @@ pub(crate) fn apply_mask(buf: &mut [u8], mask_u32: u32) { // inefficient, it could be done better. The compiler does not understand that // a `ShortSlice` must be smaller than a u64. #[allow(clippy::needless_pass_by_value)] -fn xor_short(buf: ShortSlice, mask: u64) { +fn xor_short(buf: ShortSlice<'_>, mask: u64) { // Unsafe: we know that a `ShortSlice` fits in a u64 unsafe { let (ptr, len) = (buf.0.as_mut_ptr(), buf.0.len()); @@ -77,7 +77,7 @@ unsafe fn cast_slice(buf: &mut [u8]) -> &mut [u64] { #[inline] // Splits a slice into three parts: an unaligned short head and tail, plus an aligned // u64 mid section. -fn align_buf(buf: &mut [u8]) -> (ShortSlice, &mut [u64], ShortSlice) { +fn align_buf(buf: &mut [u8]) -> (ShortSlice<'_>, &mut [u64], ShortSlice<'_>) { let start_ptr = buf.as_ptr() as usize; let end_ptr = start_ptr + buf.len(); diff --git a/actix-http/src/ws/proto.rs b/actix-http/src/ws/proto.rs index e14651a56..df928cdbb 100644 --- a/actix-http/src/ws/proto.rs +++ b/actix-http/src/ws/proto.rs @@ -24,7 +24,7 @@ pub enum OpCode { } impl fmt::Display for OpCode { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Continue => write!(f, "CONTINUE"), Text => write!(f, "TEXT"), diff --git a/actix-http/src/ws/transport.rs b/actix-http/src/ws/transport.rs index 58ba3160f..101e8f65d 100644 --- a/actix-http/src/ws/transport.rs +++ b/actix-http/src/ws/transport.rs @@ -45,7 +45,7 @@ where { type Output = Result<(), FramedTransportError>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::new(&mut self.inner).poll(cx) } } diff --git a/src/app_service.rs b/src/app_service.rs index 6a91fa079..b6e4388a1 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -154,7 +154,7 @@ where { type Output = Result, ()>; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); // async data factories @@ -220,7 +220,7 @@ where type Error = T::Error; type Future = T::Future; - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.service.poll_ready(cx) } @@ -310,7 +310,7 @@ enum CreateAppRoutingItem { impl Future for AppRoutingFactoryResponse { type Output = Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut done = true; if let Some(ref mut fut) = self.default_fut { @@ -381,7 +381,7 @@ impl Service for AppRouting { type Error = Error; type Future = BoxResponse; - fn poll_ready(&mut self, _: &mut Context) -> Poll> { + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { if self.ready.is_none() { Poll::Ready(Ok(())) } else { diff --git a/src/extract.rs b/src/extract.rs index d43402c73..bc3027c1a 100644 --- a/src/extract.rs +++ b/src/extract.rs @@ -220,7 +220,7 @@ macro_rules! tuple_from_req ({$fut_type:ident, $(($n:tt, $T:ident)),+} => { { type Output = Result<($($T,)+), Error>; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); let mut ready = true; diff --git a/src/handler.rs b/src/handler.rs index d1b070d88..33cd2408d 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -85,7 +85,7 @@ where type Error = Infallible; type Future = HandlerServiceResponse; - fn poll_ready(&mut self, _: &mut Context) -> Poll> { + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } @@ -119,7 +119,7 @@ where { type Output = Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.as_mut().project(); if let Some(fut) = this.fut2.as_pin_mut() { @@ -203,7 +203,7 @@ where type Error = (Error, ServiceRequest); type Future = ExtractResponse; - fn poll_ready(&mut self, _: &mut Context) -> Poll> { + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } @@ -240,7 +240,7 @@ where { type Output = Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.as_mut().project(); if let Some(fut) = this.fut_s.as_pin_mut() { diff --git a/src/lib.rs b/src/lib.rs index 04149fbfd..1c5698f1d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ -#![allow(clippy::borrow_interior_mutable_const)] +#![deny(rust_2018_idioms, warnings)] +#![allow(clippy::type_complexity, clippy::borrow_interior_mutable_const)] //! Actix web is a small, pragmatic, and extremely fast web framework //! for Rust. //! diff --git a/src/middleware/compress.rs b/src/middleware/compress.rs index a697deaec..9551ac1e0 100644 --- a/src/middleware/compress.rs +++ b/src/middleware/compress.rs @@ -106,7 +106,7 @@ where type Error = Error; type Future = CompressResponse; - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.service.poll_ready(cx) } @@ -150,7 +150,7 @@ where { type Output = Result>, Error>; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); match futures::ready!(this.fut.poll(cx)) { diff --git a/src/middleware/condition.rs b/src/middleware/condition.rs index 2ede81783..68d06837e 100644 --- a/src/middleware/condition.rs +++ b/src/middleware/condition.rs @@ -76,7 +76,7 @@ where type Error = E::Error; type Future = Either; - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { use ConditionMiddleware::*; match self { Enable(service) => service.poll_ready(cx), diff --git a/src/middleware/defaultheaders.rs b/src/middleware/defaultheaders.rs index 14d035ab8..464be1ace 100644 --- a/src/middleware/defaultheaders.rs +++ b/src/middleware/defaultheaders.rs @@ -124,7 +124,7 @@ where type Error = Error; type Future = LocalBoxFuture<'static, Result>; - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.service.poll_ready(cx) } diff --git a/src/middleware/errhandlers.rs b/src/middleware/errhandlers.rs index 7a8684936..ed1e4c999 100644 --- a/src/middleware/errhandlers.rs +++ b/src/middleware/errhandlers.rs @@ -119,7 +119,7 @@ where type Error = Error; type Future = LocalBoxFuture<'static, Result>; - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.service.poll_ready(cx) } diff --git a/src/middleware/logger.rs b/src/middleware/logger.rs index 60c10b207..97fa7463f 100644 --- a/src/middleware/logger.rs +++ b/src/middleware/logger.rs @@ -154,7 +154,7 @@ where type Error = Error; type Future = LoggerResponse; - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.service.poll_ready(cx) } @@ -204,7 +204,7 @@ where { type Output = Result>, Error>; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); let res = match futures::ready!(this.fut.poll(cx)) { @@ -248,7 +248,7 @@ pub struct StreamLog { impl Drop for StreamLog { fn drop(&mut self) { if let Some(ref format) = self.format { - let render = |fmt: &mut Formatter| { + let render = |fmt: &mut Formatter<'_>| { for unit in &format.0 { unit.render(fmt, self.size, self.time)?; } @@ -264,7 +264,7 @@ impl MessageBody for StreamLog { self.body.size() } - fn poll_next(&mut self, cx: &mut Context) -> Poll>> { + fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll>> { match self.body.poll_next(cx) { Poll::Ready(Some(Ok(chunk))) => { self.size += chunk.len(); @@ -364,7 +364,7 @@ pub enum FormatText { impl FormatText { fn render( &self, - fmt: &mut Formatter, + fmt: &mut Formatter<'_>, size: usize, entry_time: time::Tm, ) -> Result<(), fmt::Error> { @@ -464,11 +464,11 @@ impl FormatText { } pub(crate) struct FormatDisplay<'a>( - &'a dyn Fn(&mut Formatter) -> Result<(), fmt::Error>, + &'a dyn Fn(&mut Formatter<'_>) -> Result<(), fmt::Error>, ); impl<'a> fmt::Display for FormatDisplay<'a> { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), fmt::Error> { (self.0)(fmt) } } @@ -523,7 +523,7 @@ mod tests { unit.render_response(&resp); } - let render = |fmt: &mut Formatter| { + let render = |fmt: &mut Formatter<'_>| { for unit in &format.0 { unit.render(fmt, 1024, now)?; } @@ -555,7 +555,7 @@ mod tests { } let entry_time = time::now(); - let render = |fmt: &mut Formatter| { + let render = |fmt: &mut Formatter<'_>| { for unit in &format.0 { unit.render(fmt, 1024, entry_time)?; } @@ -582,7 +582,7 @@ mod tests { unit.render_response(&resp); } - let render = |fmt: &mut Formatter| { + let render = |fmt: &mut Formatter<'_>| { for unit in &format.0 { unit.render(fmt, 1024, now)?; } diff --git a/src/middleware/normalize.rs b/src/middleware/normalize.rs index 6bff068bc..f6b834bfe 100644 --- a/src/middleware/normalize.rs +++ b/src/middleware/normalize.rs @@ -68,7 +68,7 @@ where type Error = Error; type Future = S::Future; - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.service.poll_ready(cx) } @@ -88,7 +88,6 @@ where Bytes::copy_from_slice(path.as_bytes()) }; parts.path_and_query = Some(PathAndQuery::from_maybe_shared(path).unwrap()); - drop(head); let uri = Uri::from_parts(parts).unwrap(); req.match_info_mut().get_mut().update(&uri); diff --git a/src/request.rs b/src/request.rs index 84f0503c0..46b8fe387 100644 --- a/src/request.rs +++ b/src/request.rs @@ -125,13 +125,13 @@ impl HttpRequest { /// Request extensions #[inline] - pub fn extensions(&self) -> Ref { + pub fn extensions(&self) -> Ref<'_, Extensions> { self.head().extensions() } /// Mutable reference to a the request's extensions #[inline] - pub fn extensions_mut(&self) -> RefMut { + pub fn extensions_mut(&self) -> RefMut<'_, Extensions> { self.head().extensions_mut() } @@ -197,7 +197,7 @@ impl HttpRequest { /// This method panics if request's extensions container is already /// borrowed. #[inline] - pub fn connection_info(&self) -> Ref { + pub fn connection_info(&self) -> Ref<'_, ConnectionInfo> { ConnectionInfo::get(self.head(), &*self.app_config()) } @@ -239,13 +239,13 @@ impl HttpMessage for HttpRequest { /// Request extensions #[inline] - fn extensions(&self) -> Ref { + fn extensions(&self) -> Ref<'_, Extensions> { self.0.head.extensions() } /// Mutable reference to a the request's extensions #[inline] - fn extensions_mut(&self) -> RefMut { + fn extensions_mut(&self) -> RefMut<'_, Extensions> { self.0.head.extensions_mut() } @@ -299,7 +299,7 @@ impl FromRequest for HttpRequest { } impl fmt::Debug for HttpRequest { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!( f, "\nHttpRequest {:?} {}:{}", diff --git a/src/resource.rs b/src/resource.rs index 7ee0506a3..41d663d3d 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -470,7 +470,7 @@ pub struct CreateResourceService { impl Future for CreateResourceService { type Output = Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut done = true; if let Some(ref mut fut) = self.default_fut { @@ -530,7 +530,7 @@ impl Service for ResourceService { LocalBoxFuture<'static, Result>, >; - fn poll_ready(&mut self, _: &mut Context) -> Poll> { + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } diff --git a/src/responder.rs b/src/responder.rs index 48eae09b6..7189eecf1 100644 --- a/src/responder.rs +++ b/src/responder.rs @@ -313,7 +313,7 @@ pub struct CustomResponderFut { impl Future for CustomResponderFut { type Output = Result; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); let mut res = match ready!(this.fut.poll(cx)) { @@ -397,7 +397,7 @@ where type Output = Result; #[project] - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { #[project] match self.project() { EitherResponder::A(fut) => { @@ -446,7 +446,7 @@ where { type Output = Result; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Poll::Ready(ready!(self.project().fut.poll(cx)).map_err(|e| e.into())) } } diff --git a/src/route.rs b/src/route.rs index 2c643099b..f7e391746 100644 --- a/src/route.rs +++ b/src/route.rs @@ -92,7 +92,7 @@ pub struct CreateRouteService { impl Future for CreateRouteService { type Output = Result; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); match this.fut.poll(cx)? { @@ -127,7 +127,7 @@ impl Service for RouteService { type Error = Error; type Future = LocalBoxFuture<'static, Result>; - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.service.poll_ready(cx) } @@ -313,7 +313,7 @@ where type Error = Error; type Future = LocalBoxFuture<'static, Result>; - fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.service.poll_ready(cx).map_err(|(e, _)| e) } diff --git a/src/scope.rs b/src/scope.rs index d6b88577b..26ace6892 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -534,7 +534,7 @@ enum CreateScopeServiceItem { impl Future for ScopeFactoryResponse { type Output = Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut done = true; if let Some(ref mut fut) = self.default_fut { @@ -606,7 +606,7 @@ impl Service for ScopeService { type Error = Error; type Future = Either>>; - fn poll_ready(&mut self, _: &mut Context) -> Poll> { + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } diff --git a/src/server.rs b/src/server.rs index 03abad160..a4569ce3d 100644 --- a/src/server.rs +++ b/src/server.rs @@ -5,7 +5,6 @@ use std::{fmt, io, net}; use actix_http::{ body::MessageBody, Error, HttpService, KeepAlive, Protocol, Request, Response, }; -use actix_rt::System; use actix_server::{Server, ServerBuilder}; use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory}; use futures::future::ok; diff --git a/src/service.rs b/src/service.rs index b392e6e8b..b58fb5b4e 100644 --- a/src/service.rs +++ b/src/service.rs @@ -181,7 +181,7 @@ impl ServiceRequest { /// Get *ConnectionInfo* for the current request. #[inline] - pub fn connection_info(&self) -> Ref { + pub fn connection_info(&self) -> Ref<'_, ConnectionInfo> { ConnectionInfo::get(self.head(), &*self.app_config()) } @@ -253,13 +253,13 @@ impl HttpMessage for ServiceRequest { /// Request extensions #[inline] - fn extensions(&self) -> Ref { + fn extensions(&self) -> Ref<'_, Extensions> { self.0.extensions() } /// Mutable reference to a the request's extensions #[inline] - fn extensions_mut(&self) -> RefMut { + fn extensions_mut(&self) -> RefMut<'_, Extensions> { self.0.extensions_mut() } @@ -270,7 +270,7 @@ impl HttpMessage for ServiceRequest { } impl fmt::Debug for ServiceRequest { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!( f, "\nServiceRequest {:?} {}:{}", @@ -404,7 +404,7 @@ impl Into> for ServiceResponse { } impl fmt::Debug for ServiceResponse { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let res = writeln!( f, "\nServiceResponse {:?} {}{}", diff --git a/src/test.rs b/src/test.rs index 5e50f24e1..419ea2d36 100644 --- a/src/test.rs +++ b/src/test.rs @@ -388,7 +388,7 @@ impl TestRequest { } /// Set cookie for this request - pub fn cookie(mut self, cookie: Cookie) -> Self { + pub fn cookie(mut self, cookie: Cookie<'_>) -> Self { self.req.cookie(cookie); self } diff --git a/src/types/form.rs b/src/types/form.rs index e1bd52375..977f88d0b 100644 --- a/src/types/form.rs +++ b/src/types/form.rs @@ -141,13 +141,13 @@ where } impl fmt::Debug for Form { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } impl fmt::Display for Form { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } @@ -308,7 +308,7 @@ where { type Output = Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { if let Some(ref mut fut) = self.fut { return Pin::new(fut).poll(cx); } diff --git a/src/types/json.rs b/src/types/json.rs index 028092d1a..8112d04f2 100644 --- a/src/types/json.rs +++ b/src/types/json.rs @@ -106,7 +106,7 @@ impl fmt::Debug for Json where T: fmt::Debug, { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Json: {:?}", self.0) } } @@ -115,7 +115,7 @@ impl fmt::Display for Json where T: fmt::Display, { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.0, f) } } @@ -356,7 +356,7 @@ where { type Output = Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { if let Some(ref mut fut) = self.fut { return Pin::new(fut).poll(cx); } diff --git a/src/types/path.rs b/src/types/path.rs index 404759300..d1a5f1fb9 100644 --- a/src/types/path.rs +++ b/src/types/path.rs @@ -97,13 +97,13 @@ impl From for Path { } impl fmt::Debug for Path { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) } } impl fmt::Display for Path { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) } } diff --git a/src/types/payload.rs b/src/types/payload.rs index 2969e385a..8e52a3b6c 100644 --- a/src/types/payload.rs +++ b/src/types/payload.rs @@ -59,7 +59,7 @@ impl Stream for Payload { #[inline] fn poll_next( mut self: Pin<&mut Self>, - cx: &mut Context, + cx: &mut Context<'_>, ) -> Poll> { Pin::new(&mut self.0).poll_next(cx) } @@ -351,7 +351,7 @@ impl HttpMessageBody { impl Future for HttpMessageBody { type Output = Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { if let Some(ref mut fut) = self.fut { return Pin::new(fut).poll(cx); } diff --git a/src/types/query.rs b/src/types/query.rs index b1f4572fa..9d62f31c6 100644 --- a/src/types/query.rs +++ b/src/types/query.rs @@ -84,13 +84,13 @@ impl ops::DerefMut for Query { } impl fmt::Debug for Query { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } impl fmt::Display for Query { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } diff --git a/src/types/readlines.rs b/src/types/readlines.rs index 123f8102b..82853381b 100644 --- a/src/types/readlines.rs +++ b/src/types/readlines.rs @@ -68,7 +68,10 @@ where { type Item = Result; - fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { let this = self.get_mut(); if let Some(err) = this.err.take() { From 4a8a9ef4050ce76ba94afedc6810f05953af22db Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sun, 8 Dec 2019 12:31:16 +0600 Subject: [PATCH 105/176] update tests and clippy warnings --- actix-http/src/cookie/builder.rs | 10 --- actix-http/src/cookie/jar.rs | 4 -- actix-http/src/cookie/mod.rs | 6 -- actix-http/src/error.rs | 2 - actix-http/src/response.rs | 3 - actix-http/src/test.rs | 16 ++--- actix-session/src/lib.rs | 11 ++-- actix-web-codegen/Cargo.toml | 4 +- awc/src/connect.rs | 2 +- awc/src/lib.rs | 35 +++++----- awc/src/request.rs | 2 +- awc/src/response.rs | 19 +++--- awc/src/sender.rs | 4 +- awc/src/ws.rs | 4 +- awc/tests/test_client.rs | 63 +++++++++--------- src/app.rs | 14 ++-- src/extract.rs | 1 + src/guard.rs | 13 ++-- src/lib.rs | 19 +++--- src/middleware/compress.rs | 3 +- src/web.rs | 108 +++++++++++++------------------ tests/test_server.rs | 10 +-- 22 files changed, 152 insertions(+), 201 deletions(-) diff --git a/actix-http/src/cookie/builder.rs b/actix-http/src/cookie/builder.rs index efeddbb62..f99d02b02 100644 --- a/actix-http/src/cookie/builder.rs +++ b/actix-http/src/cookie/builder.rs @@ -18,7 +18,6 @@ use super::{Cookie, SameSite}; /// ```rust /// use actix_http::cookie::Cookie; /// -/// # fn main() { /// let cookie: Cookie = Cookie::build("name", "value") /// .domain("www.rust-lang.org") /// .path("/") @@ -26,7 +25,6 @@ use super::{Cookie, SameSite}; /// .http_only(true) /// .max_age(84600) /// .finish(); -/// # } /// ``` #[derive(Debug, Clone)] pub struct CookieBuilder { @@ -65,13 +63,11 @@ impl CookieBuilder { /// ```rust /// use actix_http::cookie::Cookie; /// - /// # fn main() { /// let c = Cookie::build("foo", "bar") /// .expires(time::now()) /// .finish(); /// /// assert!(c.expires().is_some()); - /// # } /// ``` #[inline] pub fn expires(mut self, when: Tm) -> CookieBuilder { @@ -86,13 +82,11 @@ impl CookieBuilder { /// ```rust /// use actix_http::cookie::Cookie; /// - /// # fn main() { /// let c = Cookie::build("foo", "bar") /// .max_age(1800) /// .finish(); /// /// assert_eq!(c.max_age(), Some(time::Duration::seconds(30 * 60))); - /// # } /// ``` #[inline] pub fn max_age(self, seconds: i64) -> CookieBuilder { @@ -106,13 +100,11 @@ impl CookieBuilder { /// ```rust /// use actix_http::cookie::Cookie; /// - /// # fn main() { /// let c = Cookie::build("foo", "bar") /// .max_age_time(time::Duration::minutes(30)) /// .finish(); /// /// assert_eq!(c.max_age(), Some(time::Duration::seconds(30 * 60))); - /// # } /// ``` #[inline] pub fn max_age_time(mut self, value: Duration) -> CookieBuilder { @@ -222,14 +214,12 @@ impl CookieBuilder { /// use actix_http::cookie::Cookie; /// use chrono::Duration; /// - /// # fn main() { /// let c = Cookie::build("foo", "bar") /// .permanent() /// .finish(); /// /// assert_eq!(c.max_age(), Some(Duration::days(365 * 20))); /// # assert!(c.expires().is_some()); - /// # } /// ``` #[inline] pub fn permanent(mut self) -> CookieBuilder { diff --git a/actix-http/src/cookie/jar.rs b/actix-http/src/cookie/jar.rs index 91af49320..dc2de4dfe 100644 --- a/actix-http/src/cookie/jar.rs +++ b/actix-http/src/cookie/jar.rs @@ -190,7 +190,6 @@ impl CookieJar { /// use actix_http::cookie::{CookieJar, Cookie}; /// use chrono::Duration; /// - /// # fn main() { /// let mut jar = CookieJar::new(); /// /// // Assume this cookie originally had a path of "/" and domain of "a.b". @@ -204,7 +203,6 @@ impl CookieJar { /// assert_eq!(delta.len(), 1); /// assert_eq!(delta[0].name(), "name"); /// assert_eq!(delta[0].max_age(), Some(Duration::seconds(0))); - /// # } /// ``` /// /// Removing a new cookie does not result in a _removal_ cookie: @@ -243,7 +241,6 @@ impl CookieJar { /// use actix_http::cookie::{CookieJar, Cookie}; /// use chrono::Duration; /// - /// # fn main() { /// let mut jar = CookieJar::new(); /// /// // Add an original cookie and a new cookie. @@ -261,7 +258,6 @@ impl CookieJar { /// jar.force_remove(Cookie::new("key", "value")); /// assert_eq!(jar.delta().count(), 0); /// assert_eq!(jar.iter().count(), 0); - /// # } /// ``` pub fn force_remove<'a>(&mut self, cookie: Cookie<'a>) { self.original_cookies.remove(cookie.name()); diff --git a/actix-http/src/cookie/mod.rs b/actix-http/src/cookie/mod.rs index cd150337d..13fd5cf4e 100644 --- a/actix-http/src/cookie/mod.rs +++ b/actix-http/src/cookie/mod.rs @@ -647,13 +647,11 @@ impl<'c> Cookie<'c> { /// use actix_http::cookie::Cookie; /// use chrono::Duration; /// - /// # fn main() { /// let mut c = Cookie::new("name", "value"); /// assert_eq!(c.max_age(), None); /// /// c.set_max_age(Duration::hours(10)); /// assert_eq!(c.max_age(), Some(Duration::hours(10))); - /// # } /// ``` #[inline] pub fn set_max_age(&mut self, value: Duration) { @@ -701,7 +699,6 @@ impl<'c> Cookie<'c> { /// ```rust /// use actix_http::cookie::Cookie; /// - /// # fn main() { /// let mut c = Cookie::new("name", "value"); /// assert_eq!(c.expires(), None); /// @@ -710,7 +707,6 @@ impl<'c> Cookie<'c> { /// /// c.set_expires(now); /// assert!(c.expires().is_some()) - /// # } /// ``` #[inline] pub fn set_expires(&mut self, time: Tm) { @@ -726,7 +722,6 @@ impl<'c> Cookie<'c> { /// use actix_http::cookie::Cookie; /// use chrono::Duration; /// - /// # fn main() { /// let mut c = Cookie::new("foo", "bar"); /// assert!(c.expires().is_none()); /// assert!(c.max_age().is_none()); @@ -734,7 +729,6 @@ impl<'c> Cookie<'c> { /// c.make_permanent(); /// assert!(c.expires().is_some()); /// assert_eq!(c.max_age(), Some(Duration::days(365 * 20))); - /// # } /// ``` pub fn make_permanent(&mut self) { let twenty_years = Duration::days(365 * 20); diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index 1dca55902..ec6039170 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -474,14 +474,12 @@ impl ResponseError for ContentTypeError { /// default. /// /// ```rust -/// # extern crate actix_http; /// # use std::io; /// # use actix_http::*; /// /// fn index(req: Request) -> Result<&'static str> { /// Err(error::ErrorBadRequest(io::Error::new(io::ErrorKind::Other, "error"))) /// } -/// # fn main() {} /// ``` pub struct InternalError { cause: T, diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index eee0abc73..be62151be 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -354,7 +354,6 @@ impl ResponseBuilder { /// )) /// .finish()) /// } - /// fn main() {} /// ``` #[doc(hidden)] pub fn set(&mut self, hdr: H) -> &mut Self { @@ -380,7 +379,6 @@ impl ResponseBuilder { /// .header(http::header::CONTENT_TYPE, "application/json") /// .finish() /// } - /// fn main() {} /// ``` pub fn header(&mut self, key: K, value: V) -> &mut Self where @@ -413,7 +411,6 @@ impl ResponseBuilder { /// .set_header(http::header::CONTENT_TYPE, "application/json") /// .finish() /// } - /// fn main() {} /// ``` pub fn set_header(&mut self, key: K, value: V) -> &mut Self where diff --git a/actix-http/src/test.rs b/actix-http/src/test.rs index b629ad784..061ba610f 100644 --- a/actix-http/src/test.rs +++ b/actix-http/src/test.rs @@ -21,8 +21,6 @@ use crate::Request; /// Test `Request` builder /// /// ```rust,ignore -/// # extern crate http; -/// # extern crate actix_web; /// # use http::{header, StatusCode}; /// # use actix_web::*; /// use actix_web::test::TestRequest; @@ -35,15 +33,13 @@ use crate::Request; /// } /// } /// -/// fn main() { -/// let resp = TestRequest::with_header("content-type", "text/plain") -/// .run(&index) -/// .unwrap(); -/// assert_eq!(resp.status(), StatusCode::OK); +/// let resp = TestRequest::with_header("content-type", "text/plain") +/// .run(&index) +/// .unwrap(); +/// assert_eq!(resp.status(), StatusCode::OK); /// -/// let resp = TestRequest::default().run(&index).unwrap(); -/// assert_eq!(resp.status(), StatusCode::BAD_REQUEST); -/// } +/// let resp = TestRequest::default().run(&index).unwrap(); +/// assert_eq!(resp.status(), StatusCode::BAD_REQUEST); /// ``` pub struct TestRequest(Option); diff --git a/actix-session/src/lib.rs b/actix-session/src/lib.rs index 771c4f67c..ef44e5213 100644 --- a/actix-session/src/lib.rs +++ b/actix-session/src/lib.rs @@ -12,7 +12,7 @@ //! [*Session*](struct.Session.html) extractor must be used. Session //! extractor allows us to get or set session data. //! -//! ```rust +//! ```rust,no_run //! use actix_web::{web, App, HttpServer, HttpResponse, Error}; //! use actix_session::{Session, CookieSession}; //! @@ -28,8 +28,8 @@ //! Ok("Welcome!") //! } //! -//! fn main() -> std::io::Result<()> { -//! # std::thread::spawn(|| +//! #[actix_rt::main] +//! async fn main() -> std::io::Result<()> { //! HttpServer::new( //! || App::new().wrap( //! CookieSession::signed(&[0; 32]) // <- create cookie based session middleware @@ -37,9 +37,8 @@ //! ) //! .service(web::resource("/").to(|| HttpResponse::Ok()))) //! .bind("127.0.0.1:59880")? -//! .run() -//! # ); -//! # Ok(()) +//! .start() +//! .await //! } //! ``` use std::cell::RefCell; diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 6a8a8a792..3a1d617f7 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -19,6 +19,6 @@ proc-macro2 = "^1" [dev-dependencies] actix-rt = { version = "1.0.0-alpha.2" } actix-web = { version = "2.0.0-alpha.2" } -actix-http = { version = "0.3.0-alpha.2", features=["openssl"] } -actix-http-test = { version = "0.3.0-alpha.2", features=["openssl"] } +actix-http = { version = "1.0.0-alpha.3", features=["openssl"] } +actix-http-test = { version = "1.0.0-alpha.3", features=["openssl"] } futures = { version = "0.3.1" } diff --git a/awc/src/connect.rs b/awc/src/connect.rs index 44dbcd60a..59a909df5 100644 --- a/awc/src/connect.rs +++ b/awc/src/connect.rs @@ -195,7 +195,7 @@ impl AsyncSocket for Socket { pub struct BoxedSocket(Box); impl fmt::Debug for BoxedSocket { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "BoxedSocket") } } diff --git a/awc/src/lib.rs b/awc/src/lib.rs index 64784ed95..8944fe229 100644 --- a/awc/src/lib.rs +++ b/awc/src/lib.rs @@ -1,4 +1,9 @@ -#![allow(clippy::borrow_interior_mutable_const)] +#![deny(rust_2018_idioms, warnings)] +#![allow( + clippy::type_complexity, + clippy::borrow_interior_mutable_const, + clippy::needless_doctest_main +)] //! An HTTP Client //! //! ```rust @@ -11,9 +16,9 @@ //! let mut client = Client::default(); //! //! let response = client.get("http://www.rust-lang.org") // <- Create request builder -//! .header("User-Agent", "Actix-web") -//! .send() // <- Send http request -//! .await; +//! .header("User-Agent", "Actix-web") +//! .send() // <- Send http request +//! .await; //! //! println!("Response: {:?}", response); //! } @@ -50,22 +55,18 @@ use self::connect::{Connect, ConnectorWrapper}; /// An HTTP Client /// /// ```rust -/// use actix_rt::System; /// use awc::Client; /// -/// fn main() { -/// System::new("test").block_on(async { -/// let mut client = Client::default(); +/// #[actix_rt::main] +/// async fn main() { +/// let mut client = Client::default(); /// -/// client.get("http://www.rust-lang.org") // <- Create request builder -/// .header("User-Agent", "Actix-web") -/// .send() // <- Send http request -/// .await -/// .and_then(|response| { // <- server http response -/// println!("Response: {:?}", response); -/// Ok(()) -/// }) -/// }); +/// let res = client.get("http://www.rust-lang.org") // <- Create request builder +/// .header("User-Agent", "Actix-web") +/// .send() // <- Send http request +/// .await; // <- send request and wait for response +/// +/// println!("Response: {:?}", res); /// } /// ``` #[derive(Clone)] diff --git a/awc/src/request.rs b/awc/src/request.rs index b9d728b7e..e8434aea9 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -565,7 +565,7 @@ impl ClientRequest { } impl fmt::Debug for ClientRequest { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!( f, "\nClientRequest {:?} {}:{}", diff --git a/awc/src/response.rs b/awc/src/response.rs index cb33e8a2b..c1cbf9e25 100644 --- a/awc/src/response.rs +++ b/awc/src/response.rs @@ -29,11 +29,11 @@ impl HttpMessage for ClientResponse { &self.head.headers } - fn extensions(&self) -> Ref { + fn extensions(&self) -> Ref<'_, Extensions> { self.head.extensions() } - fn extensions_mut(&self) -> RefMut { + fn extensions_mut(&self) -> RefMut<'_, Extensions> { self.head.extensions_mut() } @@ -43,7 +43,7 @@ impl HttpMessage for ClientResponse { /// Load request cookies. #[inline] - fn cookies(&self) -> Result>>, CookieParseError> { + fn cookies(&self) -> Result>>, CookieParseError> { struct Cookies(Vec>); if self.extensions().get::().is_none() { @@ -131,13 +131,16 @@ where { type Item = Result; - fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { Pin::new(&mut self.get_mut().payload).poll_next(cx) } } impl fmt::Debug for ClientResponse { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "\nClientResponse {:?} {}", self.version(), self.status(),)?; writeln!(f, " headers:")?; for (key, val) in self.headers().iter() { @@ -203,7 +206,7 @@ where { type Output = Result; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.get_mut(); if let Some(err) = this.err.take() { @@ -295,7 +298,7 @@ where { type Output = Result; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { if let Some(err) = self.err.take() { return Poll::Ready(Err(err)); } @@ -335,7 +338,7 @@ where { type Output = Result; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.get_mut(); loop { diff --git a/awc/src/sender.rs b/awc/src/sender.rs index 9cf158c0d..f6142ab2a 100644 --- a/awc/src/sender.rs +++ b/awc/src/sender.rs @@ -62,7 +62,7 @@ impl SendClientRequest { response_decompress: bool, timeout: Option, ) -> SendClientRequest { - let delay = timeout.map(|t| delay_for(t)); + let delay = timeout.map(delay_for); SendClientRequest::Fut(send, delay, response_decompress) } } @@ -71,7 +71,7 @@ impl Future for SendClientRequest { type Output = Result>>, SendRequestError>; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.get_mut(); match this { diff --git a/awc/src/ws.rs b/awc/src/ws.rs index e6f8e6968..89ca50b59 100644 --- a/awc/src/ws.rs +++ b/awc/src/ws.rs @@ -304,7 +304,7 @@ impl WebsocketsRequest { let (head, framed) = if let Some(to) = self.config.timeout { timeout(to, fut) .await - .map_err(|_| SendRequestError::Timeout.into()) + .map_err(|_| SendRequestError::Timeout) .and_then(|res| res)? } else { fut.await? @@ -379,7 +379,7 @@ impl WebsocketsRequest { } impl fmt::Debug for WebsocketsRequest { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!( f, "\nWebsocketsRequest {}:{}", diff --git a/awc/tests/test_client.rs b/awc/tests/test_client.rs index 3d2ed2354..a797e0725 100644 --- a/awc/tests/test_client.rs +++ b/awc/tests/test_client.rs @@ -4,7 +4,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::time::Duration; -use brotli::write::BrotliEncoder; +use brotli::CompressorWriter; use bytes::Bytes; use flate2::read::GzDecoder; use flate2::write::GzEncoder; @@ -514,9 +514,9 @@ async fn test_client_brotli_encoding() { let srv = TestServer::start(|| { HttpService::new(App::new().service(web::resource("/").route(web::to( |data: Bytes| { - let mut e = BrotliEncoder::new(Vec::new(), 5); + let mut e = CompressorWriter::new(Vec::new(), 0, 5, 0); e.write_all(&data).unwrap(); - let data = e.finish().unwrap(); + let data = e.into_inner(); HttpResponse::Ok() .header("content-encoding", "br") .body(data) @@ -534,39 +534,36 @@ async fn test_client_brotli_encoding() { assert_eq!(bytes, Bytes::from_static(STR.as_ref())); } -// #[actix_rt::test] -// async fn test_client_brotli_encoding_large_random() { -// let data = rand::thread_rng() -// .sample_iter(&rand::distributions::Alphanumeric) -// .take(70_000) -// .collect::(); +#[actix_rt::test] +async fn test_client_brotli_encoding_large_random() { + let data = rand::thread_rng() + .sample_iter(&rand::distributions::Alphanumeric) + .take(70_000) + .collect::(); -// let srv = test::TestServer::start(|app| { -// app.handler(|req: &HttpRequest| { -// req.body() -// .and_then(move |bytes: Bytes| { -// Ok(HttpResponse::Ok() -// .content_encoding(http::ContentEncoding::Gzip) -// .body(bytes)) -// }) -// .responder() -// }) -// }); + let srv = TestServer::start(|| { + HttpService::new(App::new().service(web::resource("/").route(web::to( + |data: Bytes| { + let mut e = CompressorWriter::new(Vec::new(), 0, 5, 0); + e.write_all(&data).unwrap(); + let data = e.into_inner(); + HttpResponse::Ok() + .header("content-encoding", "br") + .body(data) + }, + )))) + .tcp() + }); -// // client request -// let request = srv -// .client(http::Method::POST, "/") -// .content_encoding(http::ContentEncoding::Br) -// .body(data.clone()) -// .unwrap(); -// let response = request.send().await.unwrap(); -// assert!(response.status().is_success()); + // client request + let mut response = srv.post("/").send_body(data.clone()).await.unwrap(); + assert!(response.status().is_success()); -// // read response -// let bytes = response.body().await.unwrap(); -// assert_eq!(bytes.len(), data.len()); -// assert_eq!(bytes, Bytes::from(data)); -// } + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes.len(), data.len()); + assert_eq!(bytes, Bytes::from(data)); +} // #[actix_rt::test] // async fn test_client_deflate_encoding() { diff --git a/src/app.rs b/src/app.rs index d67817d21..ce4f1f013 100644 --- a/src/app.rs +++ b/src/app.rs @@ -38,7 +38,7 @@ pub struct App { data_factories: Vec, config: AppConfigInner, external: Vec, - _t: PhantomData<(B)>, + _t: PhantomData, } impl App { @@ -93,13 +93,11 @@ where /// HttpResponse::Ok() /// } /// - /// fn main() { - /// let app = App::new() - /// .data(MyData{ counter: Cell::new(0) }) - /// .service( - /// web::resource("/index.html").route( - /// web::get().to(index))); - /// } + /// let app = App::new() + /// .data(MyData{ counter: Cell::new(0) }) + /// .service( + /// web::resource("/index.html").route( + /// web::get().to(index))); /// ``` pub fn data(mut self, data: U) -> Self { self.data.push(Box::new(Data::new(data))); diff --git a/src/extract.rs b/src/extract.rs index bc3027c1a..c189bbf97 100644 --- a/src/extract.rs +++ b/src/extract.rs @@ -195,6 +195,7 @@ macro_rules! tuple_from_req ({$fut_type:ident, $(($n:tt, $T:ident)),+} => { /// FromRequest implementation for tuple #[doc(hidden)] + #[allow(unused_parens)] impl<$($T: FromRequest + 'static),+> FromRequest for ($($T,)+) { type Error = Error; diff --git a/src/guard.rs b/src/guard.rs index aaa99a9ec..e6303e9c7 100644 --- a/src/guard.rs +++ b/src/guard.rs @@ -259,16 +259,15 @@ impl Guard for HeaderGuard { /// Return predicate that matches if request contains specified Host name. /// -/// ```rust,ignore -/// # extern crate actix_web; -/// use actix_web::{guard::Host, App, HttpResponse}; +/// ```rust +/// use actix_web::{web, guard::Host, App, HttpResponse}; /// /// fn main() { -/// App::new().resource("/index.html", |r| { -/// r.route() +/// App::new().service( +/// web::resource("/index.html") /// .guard(Host("www.rust-lang.org")) -/// .f(|_| HttpResponse::MethodNotAllowed()) -/// }); +/// .to(|| HttpResponse::MethodNotAllowed()) +/// ); /// } /// ``` pub fn Host>(host: H) -> HostGuard { diff --git a/src/lib.rs b/src/lib.rs index 1c5698f1d..524c6378c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,25 +1,27 @@ #![deny(rust_2018_idioms, warnings)] -#![allow(clippy::type_complexity, clippy::borrow_interior_mutable_const)] +#![allow( + clippy::needless_doctest_main, + clippy::type_complexity, + clippy::borrow_interior_mutable_const +)] //! Actix web is a small, pragmatic, and extremely fast web framework //! for Rust. //! -//! ```rust +//! ```rust,no_run //! use actix_web::{web, App, Responder, HttpServer}; -//! # use std::thread; //! //! async fn index(info: web::Path<(String, u32)>) -> impl Responder { //! format!("Hello {}! id:{}", info.0, info.1) //! } //! -//! fn main() -> std::io::Result<()> { -//! # thread::spawn(|| { +//! #[actix_rt::main] +//! async fn main() -> std::io::Result<()> { //! HttpServer::new(|| App::new().service( //! web::resource("/{name}/{id}/index.html").to(index)) //! ) //! .bind("127.0.0.1:8080")? -//! .run() -//! # }); -//! # Ok(()) +//! .start() +//! .await //! } //! ``` //! @@ -170,7 +172,6 @@ pub mod client { //! An HTTP Client //! //! ```rust - //! use actix_rt::System; //! use actix_web::client::Client; //! //! #[actix_rt::main] diff --git a/src/middleware/compress.rs b/src/middleware/compress.rs index 9551ac1e0..0826606ba 100644 --- a/src/middleware/compress.rs +++ b/src/middleware/compress.rs @@ -140,7 +140,7 @@ where #[pin] fut: S::Future, encoding: ContentEncoding, - _t: PhantomData<(B)>, + _t: PhantomData, } impl Future for CompressResponse @@ -178,6 +178,7 @@ struct AcceptEncoding { impl Eq for AcceptEncoding {} impl Ord for AcceptEncoding { + #[allow(clippy::comparison_chain)] fn cmp(&self, other: &AcceptEncoding) -> cmp::Ordering { if self.quality > other.quality { cmp::Ordering::Less diff --git a/src/web.rs b/src/web.rs index 2a66a132d..51094c32e 100644 --- a/src/web.rs +++ b/src/web.rs @@ -44,13 +44,11 @@ pub use crate::types::*; /// # extern crate actix_web; /// use actix_web::{web, App, HttpResponse}; /// -/// fn main() { -/// let app = App::new().service( -/// web::resource("/users/{userid}/{friend}") -/// .route(web::get().to(|| HttpResponse::Ok())) -/// .route(web::head().to(|| HttpResponse::MethodNotAllowed())) -/// ); -/// } +/// let app = App::new().service( +/// web::resource("/users/{userid}/{friend}") +/// .route(web::get().to(|| HttpResponse::Ok())) +/// .route(web::head().to(|| HttpResponse::MethodNotAllowed())) +/// ); /// ``` pub fn resource(path: &str) -> Resource { Resource::new(path) @@ -64,14 +62,12 @@ pub fn resource(path: &str) -> Resource { /// ```rust /// use actix_web::{web, App, HttpResponse}; /// -/// fn main() { -/// let app = App::new().service( -/// web::scope("/{project_id}") -/// .service(web::resource("/path1").to(|| HttpResponse::Ok())) -/// .service(web::resource("/path2").to(|| HttpResponse::Ok())) -/// .service(web::resource("/path3").to(|| HttpResponse::MethodNotAllowed())) -/// ); -/// } +/// let app = App::new().service( +/// web::scope("/{project_id}") +/// .service(web::resource("/path1").to(|| HttpResponse::Ok())) +/// .service(web::resource("/path2").to(|| HttpResponse::Ok())) +/// .service(web::resource("/path3").to(|| HttpResponse::MethodNotAllowed())) +/// ); /// ``` /// /// In the above example, three routes get added: @@ -93,12 +89,10 @@ pub fn route() -> Route { /// ```rust /// use actix_web::{web, App, HttpResponse}; /// -/// fn main() { -/// let app = App::new().service( -/// web::resource("/{project_id}") -/// .route(web::get().to(|| HttpResponse::Ok())) -/// ); -/// } +/// let app = App::new().service( +/// web::resource("/{project_id}") +/// .route(web::get().to(|| HttpResponse::Ok())) +/// ); /// ``` /// /// In the above example, one `GET` route get added: @@ -113,12 +107,10 @@ pub fn get() -> Route { /// ```rust /// use actix_web::{web, App, HttpResponse}; /// -/// fn main() { -/// let app = App::new().service( -/// web::resource("/{project_id}") -/// .route(web::post().to(|| HttpResponse::Ok())) -/// ); -/// } +/// let app = App::new().service( +/// web::resource("/{project_id}") +/// .route(web::post().to(|| HttpResponse::Ok())) +/// ); /// ``` /// /// In the above example, one `POST` route get added: @@ -133,12 +125,10 @@ pub fn post() -> Route { /// ```rust /// use actix_web::{web, App, HttpResponse}; /// -/// fn main() { -/// let app = App::new().service( -/// web::resource("/{project_id}") -/// .route(web::put().to(|| HttpResponse::Ok())) -/// ); -/// } +/// let app = App::new().service( +/// web::resource("/{project_id}") +/// .route(web::put().to(|| HttpResponse::Ok())) +/// ); /// ``` /// /// In the above example, one `PUT` route get added: @@ -153,12 +143,10 @@ pub fn put() -> Route { /// ```rust /// use actix_web::{web, App, HttpResponse}; /// -/// fn main() { -/// let app = App::new().service( -/// web::resource("/{project_id}") -/// .route(web::patch().to(|| HttpResponse::Ok())) -/// ); -/// } +/// let app = App::new().service( +/// web::resource("/{project_id}") +/// .route(web::patch().to(|| HttpResponse::Ok())) +/// ); /// ``` /// /// In the above example, one `PATCH` route get added: @@ -173,12 +161,10 @@ pub fn patch() -> Route { /// ```rust /// use actix_web::{web, App, HttpResponse}; /// -/// fn main() { -/// let app = App::new().service( -/// web::resource("/{project_id}") -/// .route(web::delete().to(|| HttpResponse::Ok())) -/// ); -/// } +/// let app = App::new().service( +/// web::resource("/{project_id}") +/// .route(web::delete().to(|| HttpResponse::Ok())) +/// ); /// ``` /// /// In the above example, one `DELETE` route get added: @@ -193,12 +179,10 @@ pub fn delete() -> Route { /// ```rust /// use actix_web::{web, App, HttpResponse}; /// -/// fn main() { -/// let app = App::new().service( -/// web::resource("/{project_id}") -/// .route(web::head().to(|| HttpResponse::Ok())) -/// ); -/// } +/// let app = App::new().service( +/// web::resource("/{project_id}") +/// .route(web::head().to(|| HttpResponse::Ok())) +/// ); /// ``` /// /// In the above example, one `HEAD` route get added: @@ -213,12 +197,10 @@ pub fn head() -> Route { /// ```rust /// use actix_web::{web, http, App, HttpResponse}; /// -/// fn main() { -/// let app = App::new().service( -/// web::resource("/{project_id}") -/// .route(web::method(http::Method::GET).to(|| HttpResponse::Ok())) -/// ); -/// } +/// let app = App::new().service( +/// web::resource("/{project_id}") +/// .route(web::method(http::Method::GET).to(|| HttpResponse::Ok())) +/// ); /// ``` /// /// In the above example, one `GET` route get added: @@ -261,13 +243,11 @@ where /// Ok(req.into_response(HttpResponse::Ok().finish())) /// } /// -/// fn main() { -/// let app = App::new().service( -/// web::service("/users/*") -/// .guard(guard::Header("content-type", "text/plain")) -/// .finish(my_service) -/// ); -/// } +/// let app = App::new().service( +/// web::service("/users/*") +/// .guard(guard::Header("content-type", "text/plain")) +/// .finish(my_service) +/// ); /// ``` pub fn service(path: &str) -> WebService { WebService::new(path) diff --git a/tests/test_server.rs b/tests/test_server.rs index a83526954..505b6cc0c 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -6,7 +6,7 @@ use actix_http::http::header::{ }; use actix_http::{Error, HttpService, Response}; use actix_http_test::TestServer; -use brotli::write::{BrotliDecoder, BrotliEncoder}; +use brotli::DecompressorWriter; use bytes::Bytes; use flate2::read::GzDecoder; use flate2::write::{GzEncoder, ZlibDecoder, ZlibEncoder}; @@ -322,9 +322,9 @@ async fn test_body_br_streaming() { let bytes = response.body().await.unwrap(); // decode br - let mut e = BrotliDecoder::new(Vec::with_capacity(2048)); + let mut e = DecompressorWriter::new(Vec::with_capacity(2048), 0); e.write_all(bytes.as_ref()).unwrap(); - let dec = e.finish().unwrap(); + let dec = e.into_inner().unwrap(); assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); } @@ -433,9 +433,9 @@ async fn test_body_brotli() { let bytes = response.body().await.unwrap(); // decode brotli - let mut e = BrotliDecoder::new(Vec::with_capacity(2048)); + let mut e = DecompressorWriter::new(Vec::with_capacity(2048), 0); e.write_all(bytes.as_ref()).unwrap(); - let dec = e.finish().unwrap(); + let dec = e.into_inner().unwrap(); assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); } From 1f3ffe38e886c7bcde10604976861e30f3ac4e6c Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sun, 8 Dec 2019 19:25:24 +0600 Subject: [PATCH 106/176] update actix-service dep --- Cargo.toml | 3 +++ actix-cors/src/lib.rs | 4 ++-- actix-http/src/h2/service.rs | 16 ++++++++-------- actix-http/tests/test_openssl.rs | 4 ++-- actix-http/tests/test_rustls.rs | 6 +++--- actix-http/tests/test_server.rs | 12 ++++++------ actix-http/tests/test_ws.rs | 2 +- src/app_service.rs | 4 ++-- 8 files changed, 27 insertions(+), 24 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1674502d8..d848861ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -127,3 +127,6 @@ actix-session = { path = "actix-session" } actix-files = { path = "actix-files" } actix-multipart = { path = "actix-multipart" } awc = { path = "awc" } + +actix-service = { git = "https://github.com/actix/actix-net.git" } +actix-server = { git = "https://github.com/actix/actix-net.git" } \ No newline at end of file diff --git a/actix-cors/src/lib.rs b/actix-cors/src/lib.rs index 71d98f896..ddb20d2b5 100644 --- a/actix-cors/src/lib.rs +++ b/actix-cors/src/lib.rs @@ -822,7 +822,7 @@ where #[cfg(test)] mod tests { - use actix_service::{service_fn2, Transform}; + use actix_service::{fn_service, Transform}; use actix_web::test::{self, TestRequest}; use super::*; @@ -1083,7 +1083,7 @@ mod tests { .expose_headers(exposed_headers.clone()) .allowed_header(header::CONTENT_TYPE) .finish() - .new_transform(service_fn2(|req: ServiceRequest| { + .new_transform(fn_service(|req: ServiceRequest| { ok(req.into_response( HttpResponse::Ok().header(header::VARY, "Accept").finish(), )) diff --git a/actix-http/src/h2/service.rs b/actix-http/src/h2/service.rs index cdef71c57..da2499345 100644 --- a/actix-http/src/h2/service.rs +++ b/actix-http/src/h2/service.rs @@ -8,7 +8,7 @@ use std::{io, net, rc}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_rt::net::TcpStream; use actix_service::{ - factory_fn, pipeline_factory, service_fn2, IntoServiceFactory, Service, + fn_factory, fn_service, pipeline_factory, IntoServiceFactory, Service, ServiceFactory, }; use bytes::Bytes; @@ -87,9 +87,9 @@ where Error = DispatchError, InitError = S::InitError, > { - pipeline_factory(factory_fn(|| { + pipeline_factory(fn_factory(|| { async { - Ok::<_, S::InitError>(service_fn2(|io: TcpStream| { + Ok::<_, S::InitError>(fn_service(|io: TcpStream| { let peer_addr = io.peer_addr().ok(); ok::<_, DispatchError>((io, peer_addr)) })) @@ -101,7 +101,7 @@ where #[cfg(feature = "openssl")] mod openssl { - use actix_service::{factory_fn, service_fn2}; + use actix_service::{fn_factory, fn_service}; use actix_tls::openssl::{Acceptor, SslAcceptor, SslStream}; use actix_tls::{openssl::HandshakeError, SslError}; @@ -131,8 +131,8 @@ mod openssl { .map_err(SslError::Ssl) .map_init_err(|_| panic!()), ) - .and_then(factory_fn(|| { - ok::<_, S::InitError>(service_fn2(|io: SslStream| { + .and_then(fn_factory(|| { + ok::<_, S::InitError>(fn_service(|io: SslStream| { let peer_addr = io.get_ref().peer_addr().ok(); ok((io, peer_addr)) })) @@ -176,8 +176,8 @@ mod rustls { .map_err(SslError::Ssl) .map_init_err(|_| panic!()), ) - .and_then(factory_fn(|| { - ok::<_, S::InitError>(service_fn2(|io: TlsStream| { + .and_then(fn_factory(|| { + ok::<_, S::InitError>(fn_service(|io: TlsStream| { let peer_addr = io.get_ref().0.peer_addr().ok(); ok((io, peer_addr)) })) diff --git a/actix-http/tests/test_openssl.rs b/actix-http/tests/test_openssl.rs index 35e234af9..5d466ee30 100644 --- a/actix-http/tests/test_openssl.rs +++ b/actix-http/tests/test_openssl.rs @@ -2,7 +2,7 @@ use std::io; use actix_http_test::TestServer; -use actix_service::{service_fn, ServiceFactory}; +use actix_service::{fn_service, ServiceFactory}; use bytes::{Bytes, BytesMut}; use futures::future::{err, ok, ready}; @@ -361,7 +361,7 @@ async fn test_h2_body_chunked_explicit() { async fn test_h2_response_http_error_handling() { let mut srv = TestServer::start(move || { HttpService::build() - .h2(service_fn(|_| { + .h2(fn_service(|_| { let broken_header = Bytes::from_static(b"\0\0\0"); ok::<_, ()>( Response::Ok() diff --git a/actix-http/tests/test_rustls.rs b/actix-http/tests/test_rustls.rs index 89719221d..b5c5cf3b1 100644 --- a/actix-http/tests/test_rustls.rs +++ b/actix-http/tests/test_rustls.rs @@ -4,7 +4,7 @@ use actix_http::http::header::{self, HeaderName, HeaderValue}; use actix_http::http::{Method, StatusCode, Version}; use actix_http::{body, error, Error, HttpService, Request, Response}; use actix_http_test::TestServer; -use actix_service::{factory_fn_cfg, service_fn2}; +use actix_service::{fn_factory_with_config, fn_service}; use bytes::{Bytes, BytesMut}; use futures::future::{self, err, ok}; @@ -367,8 +367,8 @@ async fn test_h2_body_chunked_explicit() { async fn test_h2_response_http_error_handling() { let mut srv = TestServer::start(move || { HttpService::build() - .h2(factory_fn_cfg(|_: ()| { - ok::<_, ()>(service_fn2(|_| { + .h2(fn_factory_with_config(|_: ()| { + ok::<_, ()>(fn_service(|_| { let broken_header = Bytes::from_static(b"\0\0\0"); ok::<_, ()>( Response::Ok() diff --git a/actix-http/tests/test_server.rs b/actix-http/tests/test_server.rs index 850d30a3f..fc51a103d 100644 --- a/actix-http/tests/test_server.rs +++ b/actix-http/tests/test_server.rs @@ -4,7 +4,7 @@ use std::{net, thread}; use actix_http_test::TestServer; use actix_rt::time::delay_for; -use actix_service::service_fn; +use actix_service::fn_service; use bytes::Bytes; use futures::future::{self, err, ok, ready, FutureExt}; use futures::stream::{once, StreamExt}; @@ -56,7 +56,7 @@ async fn test_h1_2() { async fn test_expect_continue() { let srv = TestServer::start(|| { HttpService::build() - .expect(service_fn(|req: Request| { + .expect(fn_service(|req: Request| { if req.head().uri.query() == Some("yes=") { ok(req) } else { @@ -84,7 +84,7 @@ async fn test_expect_continue() { async fn test_expect_continue_h1() { let srv = TestServer::start(|| { HttpService::build() - .expect(service_fn(|req: Request| { + .expect(fn_service(|req: Request| { delay_for(Duration::from_millis(20)).then(move |_| { if req.head().uri.query() == Some("yes=") { ok(req) @@ -93,7 +93,7 @@ async fn test_expect_continue_h1() { } }) })) - .h1(service_fn(|_| future::ok::<_, ()>(Response::Ok().finish()))) + .h1(fn_service(|_| future::ok::<_, ()>(Response::Ok().finish()))) .tcp() }); @@ -117,7 +117,7 @@ async fn test_chunked_payload() { let srv = TestServer::start(|| { HttpService::build() - .h1(service_fn(|mut request: Request| { + .h1(fn_service(|mut request: Request| { request .take_payload() .map(|res| match res { @@ -602,7 +602,7 @@ async fn test_h1_body_chunked_implicit() { async fn test_h1_response_http_error_handling() { let mut srv = TestServer::start(|| { HttpService::build() - .h1(service_fn(|_| { + .h1(fn_service(|_| { let broken_header = Bytes::from_static(b"\0\0\0"); ok::<_, ()>( Response::Ok() diff --git a/actix-http/tests/test_ws.rs b/actix-http/tests/test_ws.rs index 284a4218a..f0e0ff63c 100644 --- a/actix-http/tests/test_ws.rs +++ b/actix-http/tests/test_ws.rs @@ -38,7 +38,7 @@ async fn service(msg: ws::Frame) -> Result { async fn test_simple() { let mut srv = TestServer::start(|| { HttpService::build() - .upgrade(actix_service::service_fn(ws_service)) + .upgrade(actix_service::fn_service(ws_service)) .finish(|_| future::ok::<_, ()>(Response::NotFound())) .tcp() }); diff --git a/src/app_service.rs b/src/app_service.rs index b6e4388a1..bb42a3dd9 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -8,7 +8,7 @@ use std::task::{Context, Poll}; use actix_http::{Extensions, Request, Response}; use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url}; use actix_service::boxed::{self, BoxService, BoxServiceFactory}; -use actix_service::{service_fn, Service, ServiceFactory}; +use actix_service::{fn_service, Service, ServiceFactory}; use futures::future::{ok, FutureExt, LocalBoxFuture}; use crate::config::{AppConfig, AppService}; @@ -69,7 +69,7 @@ where fn new_service(&self, _: ()) -> Self::Future { // update resource default service let default = self.default.clone().unwrap_or_else(|| { - Rc::new(boxed::factory(service_fn(|req: ServiceRequest| { + Rc::new(boxed::factory(fn_service(|req: ServiceRequest| { ok(req.into_response(Response::NotFound().finish())) }))) }); From 3b2e78db473f599907f0da1d633a2f5387c1ec68 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sun, 8 Dec 2019 19:27:06 +0600 Subject: [PATCH 107/176] add link to chat --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index db090a3e4..ed333dc80 100644 --- a/README.md +++ b/README.md @@ -5,19 +5,19 @@ [![Build Status](https://travis-ci.org/actix/actix-web.svg?branch=master)](https://travis-ci.org/actix/actix-web) [![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) -[![crates.io](https://meritbadge.herokuapp.com/actix-web)](https://crates.io/crates/actix-web) +[![crates.io](https://meritbadge.herokuapp.com/actix-web)](https://crates.io/crates/actix-web) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Documentation](https://docs.rs/actix-web/badge.svg)](https://docs.rs/actix-web) [![Download](https://img.shields.io/crates/d/actix-web.svg)](https://crates.io/crates/actix-web) [![Version](https://img.shields.io/badge/rustc-1.39+-lightgray.svg)](https://blog.rust-lang.org/2019/11/07/Rust-1.39.0.html) ![License](https://img.shields.io/crates/l/actix-web.svg) - +

- +

Website | - Forum + Chat | Examples

From b92eafb8391c0433a53e35ca8f7987b5453eff5e Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sun, 8 Dec 2019 20:15:51 +0600 Subject: [PATCH 108/176] prepare actix-http release --- Cargo.toml | 7 ++----- actix-http/CHANGES.md | 2 ++ actix-http/Cargo.toml | 6 +++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d848861ef..d2c61a4dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,11 +67,11 @@ rustls = ["rust-tls", "actix-tls/rustls", "awc/rustls"] [dependencies] actix-codec = "0.2.0-alpha.3" -actix-service = "1.0.0-alpha.3" +actix-service = "1.0.0-alpha.4" actix-utils = "1.0.0-alpha.3" actix-router = "0.2.0" actix-rt = "1.0.0-alpha.3" -actix-server = "1.0.0-alpha.3" +actix-server = "1.0.0-alpha.4" actix-testing = "1.0.0-alpha.3" actix-threadpool = "0.3.0" actix-tls = { version = "1.0.0-alpha.3" } @@ -127,6 +127,3 @@ actix-session = { path = "actix-session" } actix-files = { path = "actix-files" } actix-multipart = { path = "actix-multipart" } awc = { path = "awc" } - -actix-service = { git = "https://github.com/actix/actix-net.git" } -actix-server = { git = "https://github.com/actix/actix-net.git" } \ No newline at end of file diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 3df706dc9..4349417ca 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,5 +1,7 @@ # Changes +## [1.0.0-alpha.4] - 2019-12-08 + ### Added * Add impl ResponseBuilder for Error diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 0a8787e06..f42604bed 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.4" authors = ["Nikolay Kim "] description = "Actix http primitives" readme = "README.md" @@ -44,7 +44,7 @@ fail = ["failure"] secure-cookies = ["ring"] [dependencies] -actix-service = "1.0.0-alpha.3" +actix-service = "1.0.0-alpha.4" actix-codec = "0.2.0-alpha.3" actix-connect = "1.0.0-alpha.3" actix-utils = "1.0.0-alpha.3" @@ -92,7 +92,7 @@ flate2 = { version="1.0.7", optional = true, default-features = false } failure = { version = "0.1.5", optional = true } [dev-dependencies] -actix-server = { version = "1.0.0-alpha.3" } +actix-server = { version = "1.0.0-alpha.4" } actix-connect = { version = "1.0.0-alpha.3", features=["openssl"] } actix-http-test = { version = "1.0.0-alpha.3", features=["openssl"] } actix-tls = { version = "1.0.0-alpha.3", features=["openssl"] } From 42258ee2896db29cd8012d489f3d41c47b04faa9 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sun, 8 Dec 2019 20:22:39 +0600 Subject: [PATCH 109/176] deps --- CHANGES.md | 2 ++ Cargo.toml | 4 ++-- awc/Cargo.toml | 10 +++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e70452821..c1ae04b96 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,7 @@ # Changes +## [2.0.0-alpha.4] - 2019-12-08 + ### Deleted * Delete HttpServer::run(), it is not useful witht async/await diff --git a/Cargo.toml b/Cargo.toml index d2c61a4dd..93d8ef1ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "2.0.0-alpha.3" +version = "2.0.0-alpha.4" authors = ["Nikolay Kim "] description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." readme = "README.md" @@ -77,7 +77,7 @@ actix-threadpool = "0.3.0" actix-tls = { version = "1.0.0-alpha.3" } actix-web-codegen = "0.2.0-alpha.2" -actix-http = "1.0.0-alpha.3" +actix-http = "1.0.0-alpha.4" awc = { version = "1.0.0-alpha.3", default-features = false, optional = true } bytes = "0.5.2" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 9bb72ca97..8c98efcb2 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "awc" -version = "1.0.0-alpha.3" +version = "1.0.0-alpha.4" authors = ["Nikolay Kim "] description = "Actix http client." readme = "README.md" @@ -40,8 +40,8 @@ flate2-rust = ["actix-http/flate2-rust"] [dependencies] actix-codec = "0.2.0-alpha.3" -actix-service = "1.0.0-alpha.3" -actix-http = "1.0.0-alpha.3" +actix-service = "1.0.0-alpha.4" +actix-http = "1.0.0-alpha.4" actix-rt = "1.0.0-alpha.3" base64 = "0.11" @@ -61,10 +61,10 @@ rust-tls = { version = "0.16.0", package="rustls", optional = true, features = [ [dev-dependencies] actix-connect = { version = "1.0.0-alpha.3", features=["openssl"] } actix-web = { version = "2.0.0-alpha.3", features=["openssl"] } -actix-http = { version = "1.0.0-alpha.3", features=["openssl"] } +actix-http = { version = "1.0.0-alpha.4", features=["openssl"] } actix-http-test = { version = "1.0.0-alpha.3", features=["openssl"] } actix-utils = "1.0.0-alpha.3" -actix-server = { version = "1.0.0-alpha.3" } +actix-server = { version = "1.0.0-alpha.4" } actix-tls = { version = "1.0.0-alpha.3", features=["openssl", "rustls"] } brotli = "3.3.0" flate2 = { version="1.0.2" } From a3ce371312c12fb70d855d85a5ede2b98504ffa1 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Mon, 9 Dec 2019 07:01:22 +0600 Subject: [PATCH 110/176] ws ping and pong uses bytes #1049 --- Cargo.toml | 2 +- actix-http/CHANGES.md | 5 +++++ actix-http/src/ws/codec.rs | 20 ++++++++++---------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 93d8ef1ac..65f8df2b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,7 +78,7 @@ actix-tls = { version = "1.0.0-alpha.3" } actix-web-codegen = "0.2.0-alpha.2" actix-http = "1.0.0-alpha.4" -awc = { version = "1.0.0-alpha.3", default-features = false, optional = true } +awc = { version = "1.0.0-alpha.4", default-features = false, optional = true } bytes = "0.5.2" derive_more = "0.99.2" diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 4349417ca..b15a0c657 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,5 +1,10 @@ # Changes + +### Changed + +* Websockets: Ping and Pong should have binary data #1049 + ## [1.0.0-alpha.4] - 2019-12-08 ### Added diff --git a/actix-http/src/ws/codec.rs b/actix-http/src/ws/codec.rs index 9891bfa6e..fee8a632b 100644 --- a/actix-http/src/ws/codec.rs +++ b/actix-http/src/ws/codec.rs @@ -13,9 +13,9 @@ pub enum Message { /// Binary message Binary(Bytes), /// Ping message - Ping(String), + Ping(Bytes), /// Pong message - Pong(String), + Pong(Bytes), /// Close message with optional reason Close(Option), /// No-op. Useful for actix-net services @@ -30,9 +30,9 @@ pub enum Frame { /// Binary frame Binary(Option), /// Ping message - Ping(String), + Ping(Bytes), /// Pong message - Pong(String), + Pong(Bytes), /// Close message with optional reason Close(Option), } @@ -119,17 +119,17 @@ impl Decoder for Codec { } } OpCode::Ping => { - if let Some(ref pl) = payload { - Ok(Some(Frame::Ping(String::from_utf8_lossy(pl).into()))) + if let Some(pl) = payload { + Ok(Some(Frame::Ping(pl.freeze()))) } else { - Ok(Some(Frame::Ping(String::new()))) + Ok(Some(Frame::Ping(Bytes::new()))) } } OpCode::Pong => { - if let Some(ref pl) = payload { - Ok(Some(Frame::Pong(String::from_utf8_lossy(pl).into()))) + if let Some(pl) = payload { + Ok(Some(Frame::Pong(pl.freeze()))) } else { - Ok(Some(Frame::Pong(String::new()))) + Ok(Some(Frame::Pong(Bytes::new()))) } } OpCode::Binary => Ok(Some(Frame::Binary(payload))), From e4382e4fc126a495baca0e583fd95d7aaee10c0a Mon Sep 17 00:00:00 2001 From: Sameer Dhar Date: Sun, 8 Dec 2019 23:02:43 -0500 Subject: [PATCH 111/176] Fix broken docs (#1204) Fixed un escaped brackets in lib.rs, and reflowed links to ConnectionInfo in app, config, and server.rs --- actix-http/src/ws/proto.rs | 2 +- src/app.rs | 6 +++--- src/config.rs | 6 +++--- src/server.rs | 8 +++++--- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/actix-http/src/ws/proto.rs b/actix-http/src/ws/proto.rs index df928cdbb..ad42b7a6b 100644 --- a/actix-http/src/ws/proto.rs +++ b/actix-http/src/ws/proto.rs @@ -95,7 +95,7 @@ pub enum CloseCode { Abnormal, /// Indicates that an endpoint is terminating the connection /// because it has received data within a message that was not - /// consistent with the type of the message (e.g., non-UTF-8 [RFC3629] + /// consistent with the type of the message (e.g., non-UTF-8 \[RFC3629\] /// data within a text message). Invalid, /// Indicates that an endpoint is terminating the connection diff --git a/src/app.rs b/src/app.rs index ce4f1f013..e0f56a1ac 100644 --- a/src/app.rs +++ b/src/app.rs @@ -227,9 +227,9 @@ where /// Set server host name. /// - /// Host name is used by application router as a hostname for url - /// generation. Check [ConnectionInfo](./dev/struct.ConnectionInfo. - /// html#method.host) documentation for more information. + /// Host name is used by application router as a hostname for url generation. + /// Check [ConnectionInfo](./dev/struct.ConnectionInfo.html#method.host) + /// documentation for more information. /// /// By default host name is set to a "localhost" value. pub fn hostname(mut self, val: &str) -> Self { diff --git a/src/config.rs b/src/config.rs index 57ba10079..d57791e1a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -133,9 +133,9 @@ impl AppConfig { /// Set server host name. /// - /// Host name is used by application router as a hostname for url - /// generation. Check [ConnectionInfo](./dev/struct.ConnectionInfo. - /// html#method.host) documentation for more information. + /// Host name is used by application router as a hostname for url generation. + /// Check [ConnectionInfo](./struct.ConnectionInfo.html#method.host) + /// documentation for more information. /// /// By default host name is set to a "localhost" value. pub fn host(&self) -> &str { diff --git a/src/server.rs b/src/server.rs index a4569ce3d..8ee94420e 100644 --- a/src/server.rs +++ b/src/server.rs @@ -180,9 +180,11 @@ where /// Set server host name. /// - /// Host name is used by application router as a hostname for url - /// generation. Check [ConnectionInfo](./dev/struct.ConnectionInfo. - /// html#method.host) documentation for more information. + /// Host name is used by application router as a hostname for url generation. + /// Check [ConnectionInfo](./dev/struct.ConnectionInfo.html#method.host) + /// documentation for more information. + /// + /// By default host name is set to a "localhost" value. pub fn server_hostname>(mut self, val: T) -> Self { self.host = Some(val.as_ref().to_owned()); self From 0c1f5f9edc581878da5a6c1d4b40a90ed22d1b99 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Mon, 9 Dec 2019 17:40:15 +0600 Subject: [PATCH 112/176] Check Upgrade service readiness before calling it --- actix-http/CHANGES.md | 5 ++++ actix-http/src/h1/dispatcher.rs | 42 +++++++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index b15a0c657..fc1eea560 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,5 +1,10 @@ # Changes +## [1.0.0-alpha.5] - 2019-12-xx + +### Fixed + +* Check `Upgrade` service readiness before calling it ### Changed diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 7276d5a38..3bcf1137b 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -66,6 +66,7 @@ where U::Error: fmt::Display, { Normal(InnerDispatcher), + UpgradeReadiness(InnerDispatcher, Request), Upgrade(U::Future), None, } @@ -761,16 +762,8 @@ where if let DispatcherState::Normal(inner) = std::mem::replace(&mut self.inner, DispatcherState::None) { - let mut parts = FramedParts::with_read_buf( - inner.io, - inner.codec, - inner.read_buf, - ); - parts.write_buf = inner.write_buf; - let framed = Framed::from_parts(parts); - self.inner = DispatcherState::Upgrade( - inner.upgrade.unwrap().call((req, framed)), - ); + self.inner = + DispatcherState::UpgradeReadiness(inner, req); return self.poll(cx); } else { panic!() @@ -820,6 +813,35 @@ where } } } + DispatcherState::UpgradeReadiness(ref mut inner, _) => { + let upgrade = inner.upgrade.as_mut().unwrap(); + match upgrade.poll_ready(cx) { + Poll::Ready(Ok(_)) => { + if let DispatcherState::UpgradeReadiness(inner, req) = + std::mem::replace(&mut self.inner, DispatcherState::None) + { + let mut parts = FramedParts::with_read_buf( + inner.io, + inner.codec, + inner.read_buf, + ); + parts.write_buf = inner.write_buf; + let framed = Framed::from_parts(parts); + self.inner = DispatcherState::Upgrade( + inner.upgrade.unwrap().call((req, framed)), + ); + self.poll(cx) + } else { + panic!() + } + } + Poll::Pending => Poll::Pending, + Poll::Ready(Err(e)) => { + error!("Upgrade handler readiness check error: {}", e); + Poll::Ready(Err(DispatchError::Upgrade)) + } + } + } DispatcherState::Upgrade(ref mut fut) => { unsafe { Pin::new_unchecked(fut) }.poll(cx).map_err(|e| { error!("Upgrade handler error: {}", e); From 5132257b0dee34fb52de039626ea3ffb0964d524 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Mon, 9 Dec 2019 21:44:26 +0600 Subject: [PATCH 113/176] Fix buffer remaining capacity calcualtion --- actix-http/CHANGES.md | 4 +++- actix-http/Cargo.toml | 2 +- actix-http/src/h1/dispatcher.rs | 13 ++++++++----- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index fc1eea560..26a670e9a 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,11 +1,13 @@ # Changes -## [1.0.0-alpha.5] - 2019-12-xx +## [1.0.0-alpha.5] - 2019-12-09 ### Fixed * Check `Upgrade` service readiness before calling it +* Fix buffer remaining capacity calcualtion + ### Changed * Websockets: Ping and Pong should have binary data #1049 diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index f42604bed..c3b228e70 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "1.0.0-alpha.4" +version = "1.0.0-alpha.5" authors = ["Nikolay Kim "] description = "Actix http primitives" readme = "README.md" diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 3bcf1137b..1147465be 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -8,7 +8,7 @@ use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed, FramedParts}; use actix_rt::time::{delay_until, Delay, Instant}; use actix_service::Service; use bitflags::bitflags; -use bytes::{BufMut, BytesMut}; +use bytes::BytesMut; use log::{error, trace}; use crate::body::{Body, BodySize, MessageBody, ResponseBody}; @@ -751,8 +751,10 @@ where }; loop { - if inner.write_buf.remaining_mut() < LW_BUFFER_SIZE { - inner.write_buf.reserve(HW_BUFFER_SIZE); + let remaining = + inner.write_buf.capacity() - inner.write_buf.len(); + if remaining < LW_BUFFER_SIZE { + inner.write_buf.reserve(HW_BUFFER_SIZE - remaining); } let result = inner.poll_response(cx)?; let drain = result == PollResponse::DrainWriteBuf; @@ -863,8 +865,9 @@ where { let mut read_some = false; loop { - if buf.remaining_mut() < LW_BUFFER_SIZE { - buf.reserve(HW_BUFFER_SIZE); + let remaining = buf.capacity() - buf.len(); + if remaining < LW_BUFFER_SIZE { + buf.reserve(HW_BUFFER_SIZE - remaining); } match read(cx, io, buf) { From ef3a33b9d6e67ace0dcd5a1d7889d1844f1de466 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Tue, 10 Dec 2019 09:00:51 +0600 Subject: [PATCH 114/176] use std mutext instead of parking_lot --- Cargo.toml | 3 +-- src/server.rs | 19 +++++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 65f8df2b5..f259aa0fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,7 +88,6 @@ fxhash = "0.2.1" log = "0.4" mime = "0.3" net2 = "0.2.33" -parking_lot = "0.9" pin-project = "0.4.6" regex = "1.3" serde = { version = "1.0", features=["derive"] } @@ -126,4 +125,4 @@ actix-identity = { path = "actix-identity" } actix-session = { path = "actix-session" } actix-files = { path = "actix-files" } actix-multipart = { path = "actix-multipart" } -awc = { path = "awc" } +awc = { path = "awc" } \ No newline at end of file diff --git a/src/server.rs b/src/server.rs index 8ee94420e..5212e0867 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,5 +1,5 @@ use std::marker::PhantomData; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use std::{fmt, io, net}; use actix_http::{ @@ -8,7 +8,6 @@ use actix_http::{ use actix_server::{Server, ServerBuilder}; use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory}; use futures::future::ok; -use parking_lot::Mutex; use net2::TcpBuilder; @@ -147,7 +146,7 @@ where /// /// By default keep alive is set to a 5 seconds. pub fn keep_alive>(self, val: T) -> Self { - self.config.lock().keep_alive = val.into(); + self.config.lock().unwrap().keep_alive = val.into(); self } @@ -161,7 +160,7 @@ where /// /// By default client timeout is set to 5000 milliseconds. pub fn client_timeout(self, val: u64) -> Self { - self.config.lock().client_timeout = val; + self.config.lock().unwrap().client_timeout = val; self } @@ -174,7 +173,7 @@ where /// /// By default client timeout is set to 5000 milliseconds. pub fn client_shutdown(self, val: u64) -> Self { - self.config.lock().client_shutdown = val; + self.config.lock().unwrap().client_shutdown = val; self } @@ -246,7 +245,7 @@ where format!("actix-web-service-{}", addr), lst, move || { - let c = cfg.lock(); + let c = cfg.lock().unwrap(); HttpService::build() .keep_alive(c.keep_alive) .client_timeout(c.client_timeout) @@ -288,7 +287,7 @@ where format!("actix-web-service-{}", addr), lst, move || { - let c = cfg.lock(); + let c = cfg.lock().unwrap(); HttpService::build() .keep_alive(c.keep_alive) .client_timeout(c.client_timeout) @@ -330,7 +329,7 @@ where format!("actix-web-service-{}", addr), lst, move || { - let c = cfg.lock(); + let c = cfg.lock().unwrap(); HttpService::build() .keep_alive(c.keep_alive) .client_timeout(c.client_timeout) @@ -448,7 +447,7 @@ where let addr = format!("actix-web-service-{:?}", lst.local_addr()?); self.builder = self.builder.listen_uds(addr, lst, move || { - let c = cfg.lock(); + let c = cfg.lock().unwrap(); pipeline_factory(|io: UnixStream| ok((io, Protocol::Http1, None))).and_then( HttpService::build() .keep_alive(c.keep_alive) @@ -483,7 +482,7 @@ where format!("actix-web-service-{:?}", addr.as_ref()), addr, move || { - let c = cfg.lock(); + let c = cfg.lock().unwrap(); pipeline_factory(|io: UnixStream| ok((io, Protocol::Http1, None))) .and_then( HttpService::build() From 131c89709977335c1150be99e367780fe619398e Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 11 Dec 2019 19:20:20 +0600 Subject: [PATCH 115/176] upgrade to actix-net release --- Cargo.toml | 26 +++++++++---------- actix-cors/Cargo.toml | 4 +-- actix-files/Cargo.toml | 4 +-- actix-framed/Cargo.toml | 12 ++++----- actix-framed/tests/test_server.rs | 4 +-- actix-http/Cargo.toml | 20 +++++++------- .../src/ws/{transport.rs => dispatcher.rs} | 20 +++++++------- actix-http/src/ws/mod.rs | 4 +-- actix-http/tests/test_ws.rs | 4 +-- actix-identity/Cargo.toml | 4 +-- actix-multipart/Cargo.toml | 6 ++--- actix-session/Cargo.toml | 4 +-- awc/Cargo.toml | 14 +++++----- awc/tests/test_ws.rs | 2 +- src/server.rs | 4 +-- test-server/Cargo.toml | 14 +++++----- 16 files changed, 71 insertions(+), 75 deletions(-) rename actix-http/src/ws/{transport.rs => dispatcher.rs} (67%) diff --git a/Cargo.toml b/Cargo.toml index f259aa0fb..9e2b78152 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,21 +60,21 @@ secure-cookies = ["actix-http/secure-cookies"] fail = ["actix-http/fail"] # openssl -openssl = ["open-ssl", "actix-tls/openssl", "awc/openssl"] +openssl = ["actix-tls/openssl", "awc/openssl"] # rustls -rustls = ["rust-tls", "actix-tls/rustls", "awc/rustls"] +rustls = ["actix-tls/rustls", "awc/rustls"] [dependencies] -actix-codec = "0.2.0-alpha.3" -actix-service = "1.0.0-alpha.4" -actix-utils = "1.0.0-alpha.3" +actix-codec = "0.2.0" +actix-service = "1.0.0" +actix-utils = "1.0.1" actix-router = "0.2.0" -actix-rt = "1.0.0-alpha.3" -actix-server = "1.0.0-alpha.4" -actix-testing = "1.0.0-alpha.3" +actix-rt = "1.0.0" +actix-server = "1.0.0" +actix-testing = "1.0.0" actix-threadpool = "0.3.0" -actix-tls = { version = "1.0.0-alpha.3" } +actix-tls = "1.0.0" actix-web-codegen = "0.2.0-alpha.2" actix-http = "1.0.0-alpha.4" @@ -96,19 +96,17 @@ serde_urlencoded = "0.6.1" time = "0.1.42" url = "2.1" -# ssl support -open-ssl = { version="0.10", package="openssl", optional = true } -rust-tls = { version = "0.16", package="rustls", optional = true } - [dev-dependencies] # actix = "0.8.3" -actix-connect = "1.0.0-alpha.3" +actix-connect = "1.0.0" actix-http-test = "1.0.0-alpha.3" rand = "0.7" env_logger = "0.6" serde_derive = "1.0" brotli = "3.3.0" flate2 = "1.0.2" +open-ssl = { version="0.10", package = "openssl" } +rust_tls = { version = "0.16.0", package = "rustls" } [profile.release] lto = true diff --git a/actix-cors/Cargo.toml b/actix-cors/Cargo.toml index 6f42109be..935166ebc 100644 --- a/actix-cors/Cargo.toml +++ b/actix-cors/Cargo.toml @@ -18,9 +18,9 @@ path = "src/lib.rs" [dependencies] actix-web = "2.0.0-alpha.3" -actix-service = "1.0.0-alpha.3" +actix-service = "1.0.0" derive_more = "0.99.2" futures = "0.3.1" [dev-dependencies] -actix-rt = "1.0.0-alpha.3" +actix-rt = "1.0.0" diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index fe351c22d..eb5f65ea0 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -20,7 +20,7 @@ path = "src/lib.rs" [dependencies] actix-web = { version = "2.0.0-alpha.3", default-features = false } actix-http = "1.0.0-alpha.3" -actix-service = "1.0.0-alpha.3" +actix-service = "1.0.0" bitflags = "1" bytes = "0.5.2" futures = "0.3.1" @@ -32,5 +32,5 @@ percent-encoding = "2.1" v_htmlescape = "0.4" [dev-dependencies] -actix-rt = "1.0.0-alpha.3" +actix-rt = "1.0.0" actix-web = { version = "2.0.0-alpha.3", features=["openssl"] } diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml index ec8392ba3..b7e041765 100644 --- a/actix-framed/Cargo.toml +++ b/actix-framed/Cargo.toml @@ -20,10 +20,10 @@ name = "actix_framed" path = "src/lib.rs" [dependencies] -actix-codec = "0.2.0-alpha.3" -actix-service = "1.0.0-alpha.3" +actix-codec = "0.2.0" +actix-service = "1.0.0" actix-router = "0.2.0" -actix-rt = "1.0.0-alpha.3" +actix-rt = "1.0.0" actix-http = "1.0.0-alpha.3" bytes = "0.5.2" @@ -32,7 +32,7 @@ pin-project = "0.4.6" log = "0.4" [dev-dependencies] -actix-server = { version = "1.0.0-alpha.3" } -actix-connect = { version = "1.0.0-alpha.3", features=["openssl"] } +actix-server = "1.0.0" +actix-connect = { version = "1.0.0", features=["openssl"] } actix-http-test = { version = "1.0.0-alpha.3", features=["openssl"] } -actix-utils = "1.0.0-alpha.3" +actix-utils = "1.0.0" diff --git a/actix-framed/tests/test_server.rs b/actix-framed/tests/test_server.rs index 40e698c70..0e265d894 100644 --- a/actix-framed/tests/test_server.rs +++ b/actix-framed/tests/test_server.rs @@ -2,7 +2,7 @@ use actix_codec::{AsyncRead, AsyncWrite}; use actix_http::{body, http::StatusCode, ws, Error, HttpService, Response}; use actix_http_test::TestServer; use actix_service::{pipeline_factory, IntoServiceFactory, ServiceFactory}; -use actix_utils::framed::FramedTransport; +use actix_utils::framed::Dispatcher; use bytes::BytesMut; use futures::{future, SinkExt, StreamExt}; @@ -18,7 +18,7 @@ async fn ws_service( .send((res, body::BodySize::None).into()) .await .unwrap(); - FramedTransport::new(framed.into_framed(ws::Codec::new()), service) + Dispatcher::new(framed.into_framed(ws::Codec::new()), service) .await .unwrap(); diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index c3b228e70..8b9a1cc31 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "1.0.0-alpha.5" +version = "1.0.0-alpha.6" authors = ["Nikolay Kim "] description = "Actix http primitives" readme = "README.md" @@ -44,13 +44,13 @@ fail = ["failure"] secure-cookies = ["ring"] [dependencies] -actix-service = "1.0.0-alpha.4" -actix-codec = "0.2.0-alpha.3" -actix-connect = "1.0.0-alpha.3" -actix-utils = "1.0.0-alpha.3" -actix-rt = "1.0.0-alpha.3" +actix-service = "1.0.0" +actix-codec = "0.2.0" +actix-connect = "1.0.0" +actix-utils = "1.0.1" +actix-rt = "1.0.0" actix-threadpool = "0.3.0" -actix-tls = { version = "1.0.0-alpha.3", optional = true } +actix-tls = { version = "1.0.0", optional = true } base64 = "0.11" bitflags = "1.0" @@ -92,10 +92,10 @@ flate2 = { version="1.0.7", optional = true, default-features = false } failure = { version = "0.1.5", optional = true } [dev-dependencies] -actix-server = { version = "1.0.0-alpha.4" } -actix-connect = { version = "1.0.0-alpha.3", features=["openssl"] } +actix-server = "1.0.0" +actix-connect = { version = "1.0.0", features=["openssl"] } actix-http-test = { version = "1.0.0-alpha.3", features=["openssl"] } -actix-tls = { version = "1.0.0-alpha.3", features=["openssl"] } +actix-tls = { version = "1.0.0", features=["openssl"] } env_logger = "0.6" serde_derive = "1.0" open-ssl = { version="0.10", package = "openssl" } diff --git a/actix-http/src/ws/transport.rs b/actix-http/src/ws/dispatcher.rs similarity index 67% rename from actix-http/src/ws/transport.rs rename to actix-http/src/ws/dispatcher.rs index 101e8f65d..7a6b11b18 100644 --- a/actix-http/src/ws/transport.rs +++ b/actix-http/src/ws/dispatcher.rs @@ -4,19 +4,19 @@ use std::task::{Context, Poll}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_service::{IntoService, Service}; -use actix_utils::framed::{FramedTransport, FramedTransportError}; +use actix_utils::framed; use super::{Codec, Frame, Message}; -pub struct Transport +pub struct Dispatcher where S: Service + 'static, T: AsyncRead + AsyncWrite, { - inner: FramedTransport, + inner: framed::Dispatcher, } -impl Transport +impl Dispatcher where T: AsyncRead + AsyncWrite, S: Service, @@ -24,26 +24,26 @@ where S::Error: 'static, { pub fn new>(io: T, service: F) -> Self { - Transport { - inner: FramedTransport::new(Framed::new(io, Codec::new()), service), + Dispatcher { + inner: framed::Dispatcher::new(Framed::new(io, Codec::new()), service), } } pub fn with>(framed: Framed, service: F) -> Self { - Transport { - inner: FramedTransport::new(framed, service), + Dispatcher { + inner: framed::Dispatcher::new(framed, service), } } } -impl Future for Transport +impl Future for Dispatcher where T: AsyncRead + AsyncWrite, S: Service, S::Future: 'static, S::Error: 'static, { - type Output = Result<(), FramedTransportError>; + type Output = Result<(), framed::DispatcherError>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::new(&mut self.inner).poll(cx) diff --git a/actix-http/src/ws/mod.rs b/actix-http/src/ws/mod.rs index 891d5110d..bc48c8e4e 100644 --- a/actix-http/src/ws/mod.rs +++ b/actix-http/src/ws/mod.rs @@ -13,15 +13,15 @@ use crate::message::RequestHead; use crate::response::{Response, ResponseBuilder}; mod codec; +mod dispatcher; mod frame; mod mask; mod proto; -mod transport; pub use self::codec::{Codec, Frame, Message}; +pub use self::dispatcher::Dispatcher; pub use self::frame::Parser; pub use self::proto::{hash_key, CloseCode, CloseReason, OpCode}; -pub use self::transport::Transport; /// Websocket protocol errors #[derive(Debug, Display, From)] diff --git a/actix-http/tests/test_ws.rs b/actix-http/tests/test_ws.rs index f0e0ff63c..6295ae283 100644 --- a/actix-http/tests/test_ws.rs +++ b/actix-http/tests/test_ws.rs @@ -1,7 +1,7 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_http::{body, h1, ws, Error, HttpService, Request, Response}; use actix_http_test::TestServer; -use actix_utils::framed::FramedTransport; +use actix_utils::framed::Dispatcher; use bytes::BytesMut; use futures::future; use futures::{SinkExt, StreamExt}; @@ -16,7 +16,7 @@ async fn ws_service( .await .unwrap(); - FramedTransport::new(framed.into_framed(ws::Codec::new()), service) + Dispatcher::new(framed.into_framed(ws::Codec::new()), service) .await .map_err(|_| panic!()) } diff --git a/actix-identity/Cargo.toml b/actix-identity/Cargo.toml index a9042dbf2..156b105d4 100644 --- a/actix-identity/Cargo.toml +++ b/actix-identity/Cargo.toml @@ -18,13 +18,13 @@ path = "src/lib.rs" [dependencies] actix-web = { version = "2.0.0-alpha.3", default-features = false, features = ["secure-cookies"] } -actix-service = "1.0.0-alpha.3" +actix-service = "1.0.0" futures = "0.3.1" serde = "1.0" serde_json = "1.0" time = "0.1.42" [dev-dependencies] -actix-rt = "1.0.0-alpha.3" +actix-rt = "1.0.0" actix-http = "1.0.0-alpha.3" bytes = "0.5.2" \ No newline at end of file diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index ac1923155..82b98bc09 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -19,8 +19,8 @@ path = "src/lib.rs" [dependencies] actix-web = { version = "2.0.0-alpha.3", default-features = false } -actix-service = "1.0.0-alpha.3" -actix-utils = "1.0.0-alpha.3" +actix-service = "1.0.0" +actix-utils = "1.0.0" bytes = "0.5.2" derive_more = "0.99.2" httparse = "1.3" @@ -31,5 +31,5 @@ time = "0.1" twoway = "0.2" [dev-dependencies] -actix-rt = "1.0.0-alpha.3" +actix-rt = "1.0.0" actix-http = "1.0.0-alpha.3" \ No newline at end of file diff --git a/actix-session/Cargo.toml b/actix-session/Cargo.toml index d8b3ecc9d..ef8f40e7e 100644 --- a/actix-session/Cargo.toml +++ b/actix-session/Cargo.toml @@ -25,7 +25,7 @@ cookie-session = ["actix-web/secure-cookies"] [dependencies] actix-web = "2.0.0-alpha.3" -actix-service = "1.0.0-alpha.3" +actix-service = "1.0.0" bytes = "0.5.2" derive_more = "0.99.2" futures = "0.3.1" @@ -34,4 +34,4 @@ serde_json = "1.0" time = "0.1.42" [dev-dependencies] -actix-rt = "1.0.0-alpha.3" +actix-rt = "1.0.0" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 8c98efcb2..49cdb7828 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -39,10 +39,10 @@ flate2-zlib = ["actix-http/flate2-zlib"] flate2-rust = ["actix-http/flate2-rust"] [dependencies] -actix-codec = "0.2.0-alpha.3" -actix-service = "1.0.0-alpha.4" +actix-codec = "0.2.0" +actix-service = "1.0.0" actix-http = "1.0.0-alpha.4" -actix-rt = "1.0.0-alpha.3" +actix-rt = "1.0.0" base64 = "0.11" bytes = "0.5.2" @@ -59,13 +59,13 @@ open-ssl = { version="0.10", package="openssl", optional = true } rust-tls = { version = "0.16.0", package="rustls", optional = true, features = ["dangerous_configuration"] } [dev-dependencies] -actix-connect = { version = "1.0.0-alpha.3", features=["openssl"] } +actix-connect = { version = "1.0.0", features=["openssl"] } actix-web = { version = "2.0.0-alpha.3", features=["openssl"] } actix-http = { version = "1.0.0-alpha.4", features=["openssl"] } actix-http-test = { version = "1.0.0-alpha.3", features=["openssl"] } -actix-utils = "1.0.0-alpha.3" -actix-server = { version = "1.0.0-alpha.4" } -actix-tls = { version = "1.0.0-alpha.3", features=["openssl", "rustls"] } +actix-utils = "1.0.0" +actix-server = "1.0.0" +actix-tls = { version = "1.0.0", features=["openssl", "rustls"] } brotli = "3.3.0" flate2 = { version="1.0.2" } env_logger = "0.6" diff --git a/awc/tests/test_ws.rs b/awc/tests/test_ws.rs index 2f7ba2732..52dbb129c 100644 --- a/awc/tests/test_ws.rs +++ b/awc/tests/test_ws.rs @@ -42,7 +42,7 @@ async fn test_simple() { // start websocket service let framed = framed.into_framed(ws::Codec::new()); - ws::Transport::with(framed, ws_service).await + ws::Dispatcher::with(framed, ws_service).await } }) .finish(|_| ok::<_, Error>(Response::NotFound())) diff --git a/src/server.rs b/src/server.rs index 5212e0867..d6835ffa8 100644 --- a/src/server.rs +++ b/src/server.rs @@ -12,7 +12,7 @@ use futures::future::ok; use net2::TcpBuilder; #[cfg(feature = "openssl")] -use actix_tls::openssl::{SslAcceptor, SslAcceptorBuilder}; +use actix_tls::openssl::{AlpnError, SslAcceptor, SslAcceptorBuilder}; #[cfg(feature = "rustls")] use actix_tls::rustls::ServerConfig as RustlsServerConfig; @@ -549,8 +549,6 @@ fn create_tcp_listener( #[cfg(feature = "openssl")] /// Configure `SslAcceptorBuilder` with custom server flags. fn openssl_acceptor(mut builder: SslAcceptorBuilder) -> io::Result { - use open_ssl::ssl::AlpnError; - builder.set_alpn_select_callback(|_, protos| { const H2: &[u8] = b"\x02h2"; const H11: &[u8] = b"\x08http/1.1"; diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 897a4bea0..274404018 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -30,13 +30,13 @@ default = [] openssl = ["open-ssl", "awc/openssl", ] # "actix-tls/openssl"] [dependencies] -actix-service = "1.0.0-alpha.3" -actix-codec = "0.2.0-alpha.3" -actix-connect = "1.0.0-alpha.3" -actix-utils = "1.0.0-alpha.3" -actix-rt = "1.0.0-alpha.3" -actix-server = "1.0.0-alpha.3" -actix-testing = "1.0.0-alpha.3" +actix-service = "1.0.0" +actix-codec = "0.2.0" +actix-connect = "1.0.0" +actix-utils = "1.0.0" +actix-rt = "1.0.0" +actix-server = "1.0.0" +actix-testing = "1.0.0" awc = "1.0.0-alpha.3" base64 = "0.11" From a612b74aeb17bfe5ef822b3c626012a1fbb32eb1 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 12 Dec 2019 02:03:44 +0100 Subject: [PATCH 116/176] actix-multipart: Fix multipart boundary reading (#1205) * actix-multipart: Fix multipart boundary reading If we're not ready to read the first line after the multipart field (which should be a "\r\n" line) then return Pending instead of Ready(None) so that we will get called again to read that line. Without this I was getting MultipartError::Boundary from read_boundary() because it got the "\r\n" line instead of the boundary. Also tweaks the test_stream test to test partial reads. This is a forward port of #1189 from 1.0 * actix-multipart: Update changes for boundary fix --- actix-multipart/CHANGES.md | 4 ++ actix-multipart/src/server.rs | 72 +++++++++++++++++++++++++---------- 2 files changed, 56 insertions(+), 20 deletions(-) diff --git a/actix-multipart/CHANGES.md b/actix-multipart/CHANGES.md index ae6f5bf14..c0792b84c 100644 --- a/actix-multipart/CHANGES.md +++ b/actix-multipart/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [2.0.0-alpha.4] - 2019-12-xx + +* Multipart handling now handles Pending during read of boundary #1205 + ## [0.2.0-alpha.2] - 2019-12-03 * Migrate to `std::future` diff --git a/actix-multipart/src/server.rs b/actix-multipart/src/server.rs index 7d1bbca46..dca94d7a2 100644 --- a/actix-multipart/src/server.rs +++ b/actix-multipart/src/server.rs @@ -610,7 +610,7 @@ impl InnerField { } match payload.readline() { - Ok(None) => Poll::Ready(None), + Ok(None) => Poll::Pending, Ok(Some(line)) => { if line.as_ref() != b"\r\n" { log::warn!("multipart field did not read all the data or it is malformed"); @@ -867,6 +867,42 @@ mod tests { (tx, rx.map(|res| res.map_err(|_| panic!()))) } + // Stream that returns from a Bytes, one char at a time and Pending every other poll() + struct SlowStream { + bytes: Bytes, + pos: usize, + ready: bool, + } + + impl SlowStream { + fn new(bytes: Bytes) -> SlowStream { + return SlowStream { + bytes: bytes, + pos: 0, + ready: false, + } + } + } + + impl Stream for SlowStream { + type Item = Result; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let this = self.get_mut(); + if !this.ready { + this.ready = true; + cx.waker().wake_by_ref(); + return Poll::Pending; + } + if this.pos == this.bytes.len() { + return Poll::Ready(None); + } + let res = Poll::Ready(Some(Ok(this.bytes.slice(this.pos..(this.pos + 1))))); + this.pos += 1; + this.ready = false; + res + } + } fn create_simple_request_with_header() -> (Bytes, HeaderMap) { let bytes = Bytes::from( @@ -969,12 +1005,22 @@ mod tests { } } + // Loops, collecting all bytes until end-of-field + async fn get_whole_field(field: &mut Field) -> BytesMut { + let mut b = BytesMut::new(); + loop { + match field.next().await { + Some(Ok(chunk)) => b.extend_from_slice(&chunk), + None => return b, + _ => unreachable!(), + } + } + } + #[actix_rt::test] async fn test_stream() { - let (sender, payload) = create_stream(); let (bytes, headers) = create_simple_request_with_header(); - - sender.send(Ok(bytes)).unwrap(); + let payload = SlowStream::new(bytes); let mut multipart = Multipart::new(&headers, payload); match multipart.next().await.unwrap() { @@ -986,14 +1032,7 @@ mod tests { assert_eq!(field.content_type().type_(), mime::TEXT); assert_eq!(field.content_type().subtype(), mime::PLAIN); - match field.next().await.unwrap() { - Ok(chunk) => assert_eq!(chunk, "test"), - _ => unreachable!(), - } - match field.next().await { - None => (), - _ => unreachable!(), - } + assert_eq!(get_whole_field(&mut field).await, "test"); } _ => unreachable!(), } @@ -1003,14 +1042,7 @@ mod tests { assert_eq!(field.content_type().type_(), mime::TEXT); assert_eq!(field.content_type().subtype(), mime::PLAIN); - match field.next().await { - Some(Ok(chunk)) => assert_eq!(chunk, "data"), - _ => unreachable!(), - } - match field.next().await { - None => (), - _ => unreachable!(), - } + assert_eq!(get_whole_field(&mut field).await, "data"); } _ => unreachable!(), } From a43a005f59902a88c31645e3aeedc8112da72671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Gonz=C3=A1lez?= Date: Wed, 11 Dec 2019 19:04:53 -0600 Subject: [PATCH 117/176] Log path if it is not a directory (#1208) --- actix-files/src/lib.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index c33367d71..c45caf375 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -274,10 +274,14 @@ impl Files { /// By default pool with 5x threads of available cpus is used. /// Pool size can be changed by setting ACTIX_CPU_POOL environment variable. pub fn new>(path: &str, dir: T) -> Files { - let dir = dir.into().canonicalize().unwrap_or_else(|_| PathBuf::new()); - if !dir.is_dir() { - log::error!("Specified path is not a directory: {:?}", dir); - } + let orig_dir = dir.into(); + let dir = match orig_dir.canonicalize() { + Ok(canon_dir) => canon_dir, + Err(_) => { + log::error!("Specified path is not a directory: {:?}", orig_dir); + PathBuf::new() + } + }; Files { path: path.to_string(), From 1b8d74793728573c719ac887df8135dfb8618d9d Mon Sep 17 00:00:00 2001 From: 0x1793d1 <2362128+0x1793d1@users.noreply.github.com> Date: Thu, 12 Dec 2019 02:05:39 +0100 Subject: [PATCH 118/176] Fix extra line feed (#1209) --- actix-http/src/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index ec6039170..8ec21c004 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -109,7 +109,7 @@ impl fmt::Display for Error { impl fmt::Debug for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "{:?}", &self.cause) + write!(f, "{:?}", &self.cause) } } From 4a1695f719ffb5b906ecba4f22b8d25d8173c60e Mon Sep 17 00:00:00 2001 From: Jonathan Speiser Date: Wed, 11 Dec 2019 20:06:22 -0500 Subject: [PATCH 119/176] fixes missing import in example (#1210) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ed333dc80..579e87e87 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Actix web is a simple, pragmatic and extremely fast web framework for Rust. ## Example ```rust -use actix_web::{get, App, HttpServer, Responder}; +use actix_web::{get, web, App, HttpServer, Responder}; #[get("/{id}/{name}/index.html")] async fn index(info: web::Path<(u32, String)>) -> impl Responder { From b4b3350b3e4b857daec140ff60981aaa1d4388ab Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 12 Dec 2019 14:06:54 +0600 Subject: [PATCH 120/176] Add websockets continuation frame support --- actix-http/CHANGES.md | 6 + actix-http/Cargo.toml | 15 ++- actix-http/src/ws/codec.rs | 220 +++++++++++++++++++++++++++------- actix-http/src/ws/frame.rs | 6 +- actix-http/src/ws/mod.rs | 17 +-- actix-http/tests/test_ws.rs | 66 +++++++++- actix-multipart/src/server.rs | 7 +- 7 files changed, 269 insertions(+), 68 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 26a670e9a..052b3eff3 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,5 +1,11 @@ # Changes +## [1.0.0] - 2019-12-xx + +### Added + +* Add websockets continuation frame support + ## [1.0.0-alpha.5] - 2019-12-09 ### Fixed diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 8b9a1cc31..79e8777d6 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "1.0.0-alpha.6" +version = "1.0.0" authors = ["Nikolay Kim "] description = "Actix http primitives" readme = "README.md" @@ -13,7 +13,6 @@ categories = ["network-programming", "asynchronous", "web-programming::websocket"] license = "MIT/Apache-2.0" edition = "2018" -workspace = ".." [package.metadata.docs.rs] features = ["openssl", "rustls", "fail", "flate2-zlib", "secure-cookies"] @@ -47,26 +46,26 @@ secure-cookies = ["ring"] actix-service = "1.0.0" actix-codec = "0.2.0" actix-connect = "1.0.0" -actix-utils = "1.0.1" +actix-utils = "1.0.3" actix-rt = "1.0.0" -actix-threadpool = "0.3.0" +actix-threadpool = "0.3.1" actix-tls = { version = "1.0.0", optional = true } base64 = "0.11" -bitflags = "1.0" +bitflags = "1.2" bytes = "0.5.2" copyless = "0.1.4" chrono = "0.4.6" derive_more = "0.99.2" -either = "1.5.2" +either = "1.5.3" encoding_rs = "0.8" futures = "0.3.1" fxhash = "0.2.1" h2 = "0.2.1" http = "0.2.0" httparse = "1.3" -indexmap = "1.2" -lazy_static = "1.0" +indexmap = "1.3" +lazy_static = "1.4" language-tags = "0.2" log = "0.4" mime = "0.3" diff --git a/actix-http/src/ws/codec.rs b/actix-http/src/ws/codec.rs index fee8a632b..a37208a2b 100644 --- a/actix-http/src/ws/codec.rs +++ b/actix-http/src/ws/codec.rs @@ -12,6 +12,8 @@ pub enum Message { Text(String), /// Binary message Binary(Bytes), + /// Continuation + Continuation(Item), /// Ping message Ping(Bytes), /// Pong message @@ -26,9 +28,11 @@ pub enum Message { #[derive(Debug, PartialEq)] pub enum Frame { /// Text frame, codec does not verify utf8 encoding - Text(Option), + Text(Bytes), /// Binary frame - Binary(Option), + Binary(Bytes), + /// Continuation + Continuation(Item), /// Ping message Ping(Bytes), /// Pong message @@ -37,11 +41,28 @@ pub enum Frame { Close(Option), } +/// `WebSocket` continuation item +#[derive(Debug, PartialEq)] +pub enum Item { + FirstText(Bytes), + FirstBinary(Bytes), + Continue(Bytes), + Last(Bytes), +} + #[derive(Debug, Copy, Clone)] /// WebSockets protocol codec pub struct Codec { + flags: Flags, max_size: usize, - server: bool, +} + +bitflags::bitflags! { + struct Flags: u8 { + const SERVER = 0b0000_0001; + const CONTINUATION = 0b0000_0010; + const W_CONTINUATION = 0b0000_0100; + } } impl Codec { @@ -49,7 +70,7 @@ impl Codec { pub fn new() -> Codec { Codec { max_size: 65_536, - server: true, + flags: Flags::SERVER, } } @@ -65,7 +86,7 @@ impl Codec { /// /// By default decoder works in server mode. pub fn client_mode(mut self) -> Self { - self.server = false; + self.flags.remove(Flags::SERVER); self } } @@ -76,19 +97,94 @@ impl Encoder for Codec { fn encode(&mut self, item: Message, dst: &mut BytesMut) -> Result<(), Self::Error> { match item { - Message::Text(txt) => { - Parser::write_message(dst, txt, OpCode::Text, true, !self.server) + Message::Text(txt) => Parser::write_message( + dst, + txt, + OpCode::Text, + true, + !self.flags.contains(Flags::SERVER), + ), + Message::Binary(bin) => Parser::write_message( + dst, + bin, + OpCode::Binary, + true, + !self.flags.contains(Flags::SERVER), + ), + Message::Ping(txt) => Parser::write_message( + dst, + txt, + OpCode::Ping, + true, + !self.flags.contains(Flags::SERVER), + ), + Message::Pong(txt) => Parser::write_message( + dst, + txt, + OpCode::Pong, + true, + !self.flags.contains(Flags::SERVER), + ), + Message::Close(reason) => { + Parser::write_close(dst, reason, !self.flags.contains(Flags::SERVER)) } - Message::Binary(bin) => { - Parser::write_message(dst, bin, OpCode::Binary, true, !self.server) - } - Message::Ping(txt) => { - Parser::write_message(dst, txt, OpCode::Ping, true, !self.server) - } - Message::Pong(txt) => { - Parser::write_message(dst, txt, OpCode::Pong, true, !self.server) - } - Message::Close(reason) => Parser::write_close(dst, reason, !self.server), + Message::Continuation(cont) => match cont { + Item::FirstText(data) => { + if self.flags.contains(Flags::W_CONTINUATION) { + return Err(ProtocolError::ContinuationStarted); + } else { + self.flags.insert(Flags::W_CONTINUATION); + Parser::write_message( + dst, + &data[..], + OpCode::Binary, + false, + !self.flags.contains(Flags::SERVER), + ) + } + } + Item::FirstBinary(data) => { + if self.flags.contains(Flags::W_CONTINUATION) { + return Err(ProtocolError::ContinuationStarted); + } else { + self.flags.insert(Flags::W_CONTINUATION); + Parser::write_message( + dst, + &data[..], + OpCode::Text, + false, + !self.flags.contains(Flags::SERVER), + ) + } + } + Item::Continue(data) => { + if self.flags.contains(Flags::W_CONTINUATION) { + Parser::write_message( + dst, + &data[..], + OpCode::Continue, + false, + !self.flags.contains(Flags::SERVER), + ) + } else { + return Err(ProtocolError::ContinuationNotStarted); + } + } + Item::Last(data) => { + if self.flags.contains(Flags::W_CONTINUATION) { + self.flags.remove(Flags::W_CONTINUATION); + Parser::write_message( + dst, + &data[..], + OpCode::Continue, + true, + !self.flags.contains(Flags::SERVER), + ) + } else { + return Err(ProtocolError::ContinuationNotStarted); + } + } + }, Message::Nop => (), } Ok(()) @@ -100,15 +196,64 @@ impl Decoder for Codec { type Error = ProtocolError; fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { - match Parser::parse(src, self.server, self.max_size) { + match Parser::parse(src, self.flags.contains(Flags::SERVER), self.max_size) { Ok(Some((finished, opcode, payload))) => { // continuation is not supported if !finished { - return Err(ProtocolError::NoContinuation); + return match opcode { + OpCode::Continue => { + if self.flags.contains(Flags::CONTINUATION) { + Ok(Some(Frame::Continuation(Item::Continue( + payload + .map(|pl| pl.freeze()) + .unwrap_or_else(Bytes::new), + )))) + } else { + Err(ProtocolError::ContinuationNotStarted) + } + } + OpCode::Binary => { + if !self.flags.contains(Flags::CONTINUATION) { + self.flags.insert(Flags::CONTINUATION); + Ok(Some(Frame::Continuation(Item::FirstBinary( + payload + .map(|pl| pl.freeze()) + .unwrap_or_else(Bytes::new), + )))) + } else { + Err(ProtocolError::ContinuationStarted) + } + } + OpCode::Text => { + if !self.flags.contains(Flags::CONTINUATION) { + self.flags.insert(Flags::CONTINUATION); + Ok(Some(Frame::Continuation(Item::FirstText( + payload + .map(|pl| pl.freeze()) + .unwrap_or_else(Bytes::new), + )))) + } else { + Err(ProtocolError::ContinuationStarted) + } + } + _ => { + error!("Unfinished fragment {:?}", opcode); + Err(ProtocolError::ContinuationFragment(opcode)) + } + }; } match opcode { - OpCode::Continue => Err(ProtocolError::NoContinuation), + OpCode::Continue => { + if self.flags.contains(Flags::CONTINUATION) { + self.flags.remove(Flags::CONTINUATION); + Ok(Some(Frame::Continuation(Item::Last( + payload.map(|pl| pl.freeze()).unwrap_or_else(Bytes::new), + )))) + } else { + Err(ProtocolError::ContinuationNotStarted) + } + } OpCode::Bad => Err(ProtocolError::BadOpCode), OpCode::Close => { if let Some(ref pl) = payload { @@ -118,29 +263,18 @@ impl Decoder for Codec { Ok(Some(Frame::Close(None))) } } - OpCode::Ping => { - if let Some(pl) = payload { - Ok(Some(Frame::Ping(pl.freeze()))) - } else { - Ok(Some(Frame::Ping(Bytes::new()))) - } - } - OpCode::Pong => { - if let Some(pl) = payload { - Ok(Some(Frame::Pong(pl.freeze()))) - } else { - Ok(Some(Frame::Pong(Bytes::new()))) - } - } - OpCode::Binary => Ok(Some(Frame::Binary(payload))), - OpCode::Text => { - Ok(Some(Frame::Text(payload))) - //let tmp = Vec::from(payload.as_ref()); - //match String::from_utf8(tmp) { - // Ok(s) => Ok(Some(Message::Text(s))), - // Err(_) => Err(ProtocolError::BadEncoding), - //} - } + OpCode::Ping => Ok(Some(Frame::Ping( + payload.map(|pl| pl.freeze()).unwrap_or_else(Bytes::new), + ))), + OpCode::Pong => Ok(Some(Frame::Pong( + payload.map(|pl| pl.freeze()).unwrap_or_else(Bytes::new), + ))), + OpCode::Binary => Ok(Some(Frame::Binary( + payload.map(|pl| pl.freeze()).unwrap_or_else(Bytes::new), + ))), + OpCode::Text => Ok(Some(Frame::Text( + payload.map(|pl| pl.freeze()).unwrap_or_else(Bytes::new), + ))), } } Ok(None) => Ok(None), diff --git a/actix-http/src/ws/frame.rs b/actix-http/src/ws/frame.rs index 0949b711f..a280ff9c7 100644 --- a/actix-http/src/ws/frame.rs +++ b/actix-http/src/ws/frame.rs @@ -1,6 +1,6 @@ use std::convert::TryFrom; -use bytes::{BufMut, Bytes, BytesMut}; +use bytes::{BufMut, BytesMut}; use log::debug; use rand; @@ -154,14 +154,14 @@ impl Parser { } /// Generate binary representation - pub fn write_message>( + pub fn write_message>( dst: &mut BytesMut, pl: B, op: OpCode, fin: bool, mask: bool, ) { - let payload = pl.into(); + let payload = pl.as_ref(); let one: u8 = if fin { 0x80 | Into::::into(op) } else { diff --git a/actix-http/src/ws/mod.rs b/actix-http/src/ws/mod.rs index bc48c8e4e..ffa397979 100644 --- a/actix-http/src/ws/mod.rs +++ b/actix-http/src/ws/mod.rs @@ -18,7 +18,7 @@ mod frame; mod mask; mod proto; -pub use self::codec::{Codec, Frame, Message}; +pub use self::codec::{Codec, Frame, Item, Message}; pub use self::dispatcher::Dispatcher; pub use self::frame::Parser; pub use self::proto::{hash_key, CloseCode, CloseReason, OpCode}; @@ -44,12 +44,15 @@ pub enum ProtocolError { /// A payload reached size limit. #[display(fmt = "A payload reached size limit.")] Overflow, - /// Continuation is not supported - #[display(fmt = "Continuation is not supported.")] - NoContinuation, - /// Bad utf-8 encoding - #[display(fmt = "Bad utf-8 encoding.")] - BadEncoding, + /// Continuation is not started + #[display(fmt = "Continuation is not started.")] + ContinuationNotStarted, + /// Received new continuation but it is already started + #[display(fmt = "Received new continuation but it is already started")] + ContinuationStarted, + /// Unknown continuation fragment + #[display(fmt = "Unknown continuation fragment.")] + ContinuationFragment(OpCode), /// Io error #[display(fmt = "io error: {}", _0)] Io(io::Error), diff --git a/actix-http/tests/test_ws.rs b/actix-http/tests/test_ws.rs index 6295ae283..5d70d24ae 100644 --- a/actix-http/tests/test_ws.rs +++ b/actix-http/tests/test_ws.rs @@ -2,7 +2,7 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_http::{body, h1, ws, Error, HttpService, Request, Response}; use actix_http_test::TestServer; use actix_utils::framed::Dispatcher; -use bytes::BytesMut; +use bytes::Bytes; use futures::future; use futures::{SinkExt, StreamExt}; @@ -25,9 +25,10 @@ async fn service(msg: ws::Frame) -> Result { let msg = match msg { ws::Frame::Ping(msg) => ws::Message::Pong(msg), ws::Frame::Text(text) => { - ws::Message::Text(String::from_utf8_lossy(&text.unwrap()).to_string()) + ws::Message::Text(String::from_utf8_lossy(&text).to_string()) } - ws::Frame::Binary(bin) => ws::Message::Binary(bin.unwrap().freeze()), + ws::Frame::Binary(bin) => ws::Message::Binary(bin), + ws::Frame::Continuation(item) => ws::Message::Continuation(item), ws::Frame::Close(reason) => ws::Message::Close(reason), _ => panic!(), }; @@ -52,7 +53,7 @@ async fn test_simple() { let (item, mut framed) = framed.into_future().await; assert_eq!( item.unwrap().unwrap(), - ws::Frame::Text(Some(BytesMut::from("text"))) + ws::Frame::Text(Bytes::from_static(b"text")) ); framed @@ -62,7 +63,7 @@ async fn test_simple() { let (item, mut framed) = framed.into_future().await; assert_eq!( item.unwrap().unwrap(), - ws::Frame::Binary(Some(BytesMut::from(&b"text"[..]).into())) + ws::Frame::Binary(Bytes::from_static(&b"text"[..])) ); framed.send(ws::Message::Ping("text".into())).await.unwrap(); @@ -72,6 +73,61 @@ async fn test_simple() { ws::Frame::Pong("text".to_string().into()) ); + framed + .send(ws::Message::Continuation(ws::Item::FirstText( + "text".into(), + ))) + .await + .unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Continuation(ws::Item::FirstText(Bytes::from_static(b"text"))) + ); + + assert!(framed + .send(ws::Message::Continuation(ws::Item::FirstText( + "text".into() + ))) + .await + .is_err()); + assert!(framed + .send(ws::Message::Continuation(ws::Item::FirstBinary( + "text".into() + ))) + .await + .is_err()); + + framed + .send(ws::Message::Continuation(ws::Item::Continue("text".into()))) + .await + .unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Continuation(ws::Item::Continue(Bytes::from_static(b"text"))) + ); + + framed + .send(ws::Message::Continuation(ws::Item::Last("text".into()))) + .await + .unwrap(); + let (item, mut framed) = framed.into_future().await; + assert_eq!( + item.unwrap().unwrap(), + ws::Frame::Continuation(ws::Item::Last(Bytes::from_static(b"text"))) + ); + + assert!(framed + .send(ws::Message::Continuation(ws::Item::Continue("text".into()))) + .await + .is_err()); + + assert!(framed + .send(ws::Message::Continuation(ws::Item::Last("text".into()))) + .await + .is_err()); + framed .send(ws::Message::Close(Some(ws::CloseCode::Normal.into()))) .await diff --git a/actix-multipart/src/server.rs b/actix-multipart/src/server.rs index dca94d7a2..2555cb7a3 100644 --- a/actix-multipart/src/server.rs +++ b/actix-multipart/src/server.rs @@ -880,14 +880,17 @@ mod tests { bytes: bytes, pos: 0, ready: false, - } + }; } } impl Stream for SlowStream { type Item = Result; - fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll> { let this = self.get_mut(); if !this.ready { this.ready = true; From fa07415721fa09738b4e33cfd6fd94e75f60bf3a Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 12 Dec 2019 15:08:08 +0600 Subject: [PATCH 121/176] Replace flate2-xxx features with compress --- Cargo.toml | 19 ++++++++----------- actix-framed/tests/test_server.rs | 14 +++++++------- actix-http/CHANGES.md | 4 ++++ actix-http/Cargo.toml | 13 +++++-------- actix-http/src/encoding/decoder.rs | 13 ------------- actix-http/src/encoding/encoder.rs | 11 ----------- actix-http/src/h1/encoder.rs | 1 + actix-http/src/lib.rs | 3 ++- awc/Cargo.toml | 23 +++++++++-------------- awc/tests/test_ws.rs | 21 ++++++--------------- src/lib.rs | 6 +----- test-server/Cargo.toml | 10 +++++----- tests/test_server.rs | 20 +------------------- 13 files changed, 49 insertions(+), 109 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9e2b78152..f75e4a316 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] edition = "2018" [package.metadata.docs.rs] -features = ["openssl", "flate2-zlib", "secure-cookies", "client"] +features = ["openssl", "compress", "secure-cookies", "client"] [badges] travis-ci = { repository = "actix/actix-web", branch = "master" } @@ -43,16 +43,13 @@ members = [ ] [features] -default = ["flate2-zlib", "client", "fail"] +default = ["compress", "client", "fail"] # http client client = ["awc"] -# miniz-sys backend for flate2 crate -flate2-zlib = ["actix-http/flate2-zlib", "awc/flate2-zlib"] - -# rust backend for flate2 crate -flate2-rust = ["actix-http/flate2-rust", "awc/flate2-rust"] +# content-encoding support +compress = ["actix-http/compress", "awc/compress"] # sessions feature, session require "ring" crate and c compiler secure-cookies = ["actix-http/secure-cookies"] @@ -68,7 +65,7 @@ rustls = ["actix-tls/rustls", "awc/rustls"] [dependencies] actix-codec = "0.2.0" actix-service = "1.0.0" -actix-utils = "1.0.1" +actix-utils = "1.0.3" actix-router = "0.2.0" actix-rt = "1.0.0" actix-server = "1.0.0" @@ -77,8 +74,8 @@ actix-threadpool = "0.3.0" actix-tls = "1.0.0" actix-web-codegen = "0.2.0-alpha.2" -actix-http = "1.0.0-alpha.4" -awc = { version = "1.0.0-alpha.4", default-features = false, optional = true } +actix-http = "1.0.0" +awc = { version = "1.0.0", default-features = false, optional = true } bytes = "0.5.2" derive_more = "0.99.2" @@ -104,7 +101,7 @@ rand = "0.7" env_logger = "0.6" serde_derive = "1.0" brotli = "3.3.0" -flate2 = "1.0.2" +flate2 = "1.0.13" open-ssl = { version="0.10", package = "openssl" } rust_tls = { version = "0.16.0", package = "rustls" } diff --git a/actix-framed/tests/test_server.rs b/actix-framed/tests/test_server.rs index 0e265d894..f6b068630 100644 --- a/actix-framed/tests/test_server.rs +++ b/actix-framed/tests/test_server.rs @@ -3,7 +3,7 @@ use actix_http::{body, http::StatusCode, ws, Error, HttpService, Response}; use actix_http_test::TestServer; use actix_service::{pipeline_factory, IntoServiceFactory, ServiceFactory}; use actix_utils::framed::Dispatcher; -use bytes::BytesMut; +use bytes::Bytes; use futures::{future, SinkExt, StreamExt}; use actix_framed::{FramedApp, FramedRequest, FramedRoute, SendError, VerifyWebSockets}; @@ -29,9 +29,9 @@ async fn service(msg: ws::Frame) -> Result { let msg = match msg { ws::Frame::Ping(msg) => ws::Message::Pong(msg), ws::Frame::Text(text) => { - ws::Message::Text(String::from_utf8_lossy(&text.unwrap()).to_string()) + ws::Message::Text(String::from_utf8_lossy(&text).to_string()) } - ws::Frame::Binary(bin) => ws::Message::Binary(bin.unwrap().freeze()), + ws::Frame::Binary(bin) => ws::Message::Binary(bin), ws::Frame::Close(reason) => ws::Message::Close(reason), _ => panic!(), }; @@ -60,7 +60,7 @@ async fn test_simple() { let (item, mut framed) = framed.into_future().await; assert_eq!( item.unwrap().unwrap(), - ws::Frame::Text(Some(BytesMut::from("text"))) + ws::Frame::Text(Bytes::from_static(b"text")) ); framed @@ -70,7 +70,7 @@ async fn test_simple() { let (item, mut framed) = framed.into_future().await; assert_eq!( item.unwrap().unwrap(), - ws::Frame::Binary(Some(BytesMut::from(&b"text"[..]))) + ws::Frame::Binary(Bytes::from_static(b"text")) ); framed.send(ws::Message::Ping("text".into())).await.unwrap(); @@ -126,7 +126,7 @@ async fn test_service() { let (item, mut framed) = framed.into_future().await; assert_eq!( item.unwrap().unwrap(), - ws::Frame::Text(Some(BytesMut::from("text"))) + ws::Frame::Text(Bytes::from_static(b"text")) ); framed @@ -136,7 +136,7 @@ async fn test_service() { let (item, mut framed) = framed.into_future().await; assert_eq!( item.unwrap().unwrap(), - ws::Frame::Binary(Some(BytesMut::from(&b"text"[..]))) + ws::Frame::Binary(Bytes::from_static(b"text")) ); framed.send(ws::Message::Ping("text".into())).await.unwrap(); diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 052b3eff3..587abaf77 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -6,6 +6,10 @@ * Add websockets continuation frame support +### Changed + +* Replace `flate2-xxx` features with `compress` + ## [1.0.0-alpha.5] - 2019-12-09 ### Fixed diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 79e8777d6..14c3c44d5 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -15,7 +15,7 @@ license = "MIT/Apache-2.0" edition = "2018" [package.metadata.docs.rs] -features = ["openssl", "rustls", "fail", "flate2-zlib", "secure-cookies"] +features = ["openssl", "rustls", "fail", "compress", "secure-cookies"] [lib] name = "actix_http" @@ -30,11 +30,8 @@ openssl = ["actix-tls/openssl", "actix-connect/openssl"] # rustls support rustls = ["actix-tls/rustls", "actix-connect/rustls"] -# miniz-sys backend for flate2 crate -flate2-zlib = ["flate2/miniz-sys"] - -# rust backend for flate2 crate -flate2-rust = ["flate2/rust_backend"] +# enable compressison support +compress = ["flate2", "brotli"] # failure integration. actix does not use failure anymore fail = ["failure"] @@ -84,8 +81,8 @@ time = "0.1.42" ring = { version = "0.16.9", optional = true } # compression -brotli = "3.3.0" -flate2 = { version="1.0.7", optional = true, default-features = false } +brotli = { version = "3.3.0", optional = true } +flate2 = { version = "1.0.13", optional = true } # optional deps failure = { version = "0.1.5", optional = true } diff --git a/actix-http/src/encoding/decoder.rs b/actix-http/src/encoding/decoder.rs index 35be2d13c..10635b3b3 100644 --- a/actix-http/src/encoding/decoder.rs +++ b/actix-http/src/encoding/decoder.rs @@ -6,7 +6,6 @@ use std::task::{Context, Poll}; use actix_threadpool::{run, CpuFuture}; use brotli::DecompressorWriter; use bytes::Bytes; -#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] use flate2::write::{GzDecoder, ZlibDecoder}; use futures::{ready, Stream}; @@ -34,11 +33,9 @@ where ContentEncoding::Br => Some(ContentDecoder::Br(Box::new( DecompressorWriter::new(Writer::new(), 0), ))), - #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] ContentEncoding::Deflate => Some(ContentDecoder::Deflate(Box::new( ZlibDecoder::new(Writer::new()), ))), - #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] ContentEncoding::Gzip => Some(ContentDecoder::Gzip(Box::new( GzDecoder::new(Writer::new()), ))), @@ -138,15 +135,12 @@ where } enum ContentDecoder { - #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] Deflate(Box>), - #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] Gzip(Box>), Br(Box>), } impl ContentDecoder { - #[allow(unreachable_patterns)] fn feed_eof(&mut self) -> io::Result> { match self { ContentDecoder::Br(ref mut decoder) => match decoder.flush() { @@ -160,7 +154,6 @@ impl ContentDecoder { } Err(e) => Err(e), }, - #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] ContentDecoder::Gzip(ref mut decoder) => match decoder.try_finish() { Ok(_) => { let b = decoder.get_mut().take(); @@ -172,7 +165,6 @@ impl ContentDecoder { } Err(e) => Err(e), }, - #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] ContentDecoder::Deflate(ref mut decoder) => match decoder.try_finish() { Ok(_) => { let b = decoder.get_mut().take(); @@ -184,11 +176,9 @@ impl ContentDecoder { } Err(e) => Err(e), }, - _ => Ok(None), } } - #[allow(unreachable_patterns)] fn feed_data(&mut self, data: Bytes) -> io::Result> { match self { ContentDecoder::Br(ref mut decoder) => match decoder.write_all(&data) { @@ -203,7 +193,6 @@ impl ContentDecoder { } Err(e) => Err(e), }, - #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] ContentDecoder::Gzip(ref mut decoder) => match decoder.write_all(&data) { Ok(_) => { decoder.flush()?; @@ -216,7 +205,6 @@ impl ContentDecoder { } Err(e) => Err(e), }, - #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] ContentDecoder::Deflate(ref mut decoder) => match decoder.write_all(&data) { Ok(_) => { decoder.flush()?; @@ -229,7 +217,6 @@ impl ContentDecoder { } Err(e) => Err(e), }, - _ => Ok(Some(data)), } } } diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index f8f996ff1..c58e1f434 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -7,7 +7,6 @@ use std::task::{Context, Poll}; use actix_threadpool::{run, CpuFuture}; use brotli::CompressorWriter; use bytes::Bytes; -#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] use flate2::write::{GzEncoder, ZlibEncoder}; use crate::body::{Body, BodySize, MessageBody, ResponseBody}; @@ -172,9 +171,7 @@ fn update_head(encoding: ContentEncoding, head: &mut ResponseHead) { } enum ContentEncoder { - #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] Deflate(ZlibEncoder), - #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] Gzip(GzEncoder), Br(Box>), } @@ -182,12 +179,10 @@ enum ContentEncoder { impl ContentEncoder { fn encoder(encoding: ContentEncoding) -> Option { match encoding { - #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] ContentEncoding::Deflate => Some(ContentEncoder::Deflate(ZlibEncoder::new( Writer::new(), flate2::Compression::fast(), ))), - #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] ContentEncoding::Gzip => Some(ContentEncoder::Gzip(GzEncoder::new( Writer::new(), flate2::Compression::fast(), @@ -208,9 +203,7 @@ impl ContentEncoder { std::mem::swap(encoder, &mut encoder_new); encoder_new.into_inner().freeze() } - #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] ContentEncoder::Deflate(ref mut encoder) => encoder.get_mut().take(), - #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] ContentEncoder::Gzip(ref mut encoder) => encoder.get_mut().take(), } } @@ -218,12 +211,10 @@ impl ContentEncoder { fn finish(self) -> Result { match self { ContentEncoder::Br(encoder) => Ok(encoder.into_inner().buf.freeze()), - #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] ContentEncoder::Gzip(encoder) => match encoder.finish() { Ok(writer) => Ok(writer.buf.freeze()), Err(err) => Err(err), }, - #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] ContentEncoder::Deflate(encoder) => match encoder.finish() { Ok(writer) => Ok(writer.buf.freeze()), Err(err) => Err(err), @@ -240,7 +231,6 @@ impl ContentEncoder { Err(err) } }, - #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] ContentEncoder::Gzip(ref mut encoder) => match encoder.write_all(data) { Ok(_) => Ok(()), Err(err) => { @@ -248,7 +238,6 @@ impl ContentEncoder { Err(err) } }, - #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] ContentEncoder::Deflate(ref mut encoder) => match encoder.write_all(data) { Ok(_) => Ok(()), Err(err) => { diff --git a/actix-http/src/h1/encoder.rs b/actix-http/src/h1/encoder.rs index e83ce90cf..e474d4f81 100644 --- a/actix-http/src/h1/encoder.rs +++ b/actix-http/src/h1/encoder.rs @@ -108,6 +108,7 @@ pub(crate) trait MessageType: Sized { } else { dst.put_slice(b"\r\ncontent-length: "); } + #[allow(clippy::write_with_newline)] write!(dst.writer(), "{}\r\n", len)?; } BodySize::None => dst.put_slice(b"\r\n"), diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs index 1da074ad1..b682e5aa5 100644 --- a/actix-http/src/lib.rs +++ b/actix-http/src/lib.rs @@ -5,7 +5,7 @@ clippy::too_many_arguments, clippy::new_without_default, clippy::borrow_interior_mutable_const, - clippy::write_with_newline +// clippy::write_with_newline )] #[macro_use] @@ -16,6 +16,7 @@ mod builder; pub mod client; mod cloneable; mod config; +#[cfg(feature = "compress")] pub mod encoding; mod extensions; mod header; diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 49cdb7828..a183b9fee 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "awc" -version = "1.0.0-alpha.4" +version = "1.0.0" authors = ["Nikolay Kim "] description = "Actix http client." readme = "README.md" @@ -12,19 +12,17 @@ categories = ["network-programming", "asynchronous", "web-programming::http-client", "web-programming::websocket"] license = "MIT/Apache-2.0" -exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] edition = "2018" -workspace = ".." [lib] name = "awc" path = "src/lib.rs" [package.metadata.docs.rs] -features = ["openssl", "rustls", "flate2-zlib"] +features = ["openssl", "rustls", "compress"] [features] -default = ["flate2-zlib"] +default = ["compress"] # openssl openssl = ["open-ssl", "actix-http/openssl"] @@ -32,16 +30,13 @@ openssl = ["open-ssl", "actix-http/openssl"] # rustls rustls = ["rust-tls", "actix-http/rustls"] -# miniz-sys backend for flate2 crate -flate2-zlib = ["actix-http/flate2-zlib"] - -# rust backend for flate2 crate -flate2-rust = ["actix-http/flate2-rust"] +# content-encoding support +compress = ["actix-http/compress"] [dependencies] actix-codec = "0.2.0" actix-service = "1.0.0" -actix-http = "1.0.0-alpha.4" +actix-http = "1.0.0" actix-rt = "1.0.0" base64 = "0.11" @@ -61,12 +56,12 @@ rust-tls = { version = "0.16.0", package="rustls", optional = true, features = [ [dev-dependencies] actix-connect = { version = "1.0.0", features=["openssl"] } actix-web = { version = "2.0.0-alpha.3", features=["openssl"] } -actix-http = { version = "1.0.0-alpha.4", features=["openssl"] } +actix-http = { version = "1.0.0", features=["openssl"] } actix-http-test = { version = "1.0.0-alpha.3", features=["openssl"] } actix-utils = "1.0.0" actix-server = "1.0.0" actix-tls = { version = "1.0.0", features=["openssl", "rustls"] } brotli = "3.3.0" -flate2 = { version="1.0.2" } +flate2 = "1.0.13" env_logger = "0.6" -webpki = { version = "0.21" } +webpki = "0.21" diff --git a/awc/tests/test_ws.rs b/awc/tests/test_ws.rs index 52dbb129c..6f1dcded5 100644 --- a/awc/tests/test_ws.rs +++ b/awc/tests/test_ws.rs @@ -3,26 +3,17 @@ use std::io; use actix_codec::Framed; use actix_http::{body::BodySize, h1, ws, Error, HttpService, Request, Response}; use actix_http_test::TestServer; -use bytes::{Bytes, BytesMut}; +use bytes::Bytes; use futures::future::ok; use futures::{SinkExt, StreamExt}; async fn ws_service(req: ws::Frame) -> Result { match req { ws::Frame::Ping(msg) => Ok(ws::Message::Pong(msg)), - ws::Frame::Text(text) => { - let text = if let Some(pl) = text { - String::from_utf8(Vec::from(pl.as_ref())).unwrap() - } else { - String::new() - }; - Ok(ws::Message::Text(text)) - } - ws::Frame::Binary(bin) => Ok(ws::Message::Binary( - bin.map(|e| e.freeze()) - .unwrap_or_else(|| Bytes::from("")) - .into(), + ws::Frame::Text(text) => Ok(ws::Message::Text( + String::from_utf8(Vec::from(text.as_ref())).unwrap(), )), + ws::Frame::Binary(bin) => Ok(ws::Message::Binary(bin)), ws::Frame::Close(reason) => Ok(ws::Message::Close(reason)), _ => Ok(ws::Message::Close(None)), } @@ -56,14 +47,14 @@ async fn test_simple() { .await .unwrap(); let item = framed.next().await.unwrap().unwrap(); - assert_eq!(item, ws::Frame::Text(Some(BytesMut::from("text")))); + assert_eq!(item, ws::Frame::Text(Bytes::from_static(b"text"))); framed .send(ws::Message::Binary("text".into())) .await .unwrap(); let item = framed.next().await.unwrap().unwrap(); - assert_eq!(item, ws::Frame::Binary(Some(BytesMut::from(&b"text"[..])))); + assert_eq!(item, ws::Frame::Binary(Bytes::from_static(b"text"))); framed.send(ws::Message::Ping("text".into())).await.unwrap(); let item = framed.next().await.unwrap().unwrap(); diff --git a/src/lib.rs b/src/lib.rs index 524c6378c..8d46cd801 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,15 +71,11 @@ //! ## Package feature //! //! * `client` - enables http client (default enabled) +//! * `compress` - enables content encoding compression support (default enabled) //! * `openssl` - enables ssl support via `openssl` crate, supports `http/2` //! * `rustls` - enables ssl support via `rustls` crate, supports `http/2` //! * `secure-cookies` - enables secure cookies support, includes `ring` crate as //! dependency -//! * `flate2-zlib` - enables `gzip`, `deflate` compression support, requires -//! `c` compiler (default enabled) -//! * `flate2-rust` - experimental rust based implementation for -//! `gzip`, `deflate` compression. -//! #![allow(clippy::type_complexity, clippy::new_without_default)] mod app; diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 274404018..434262de3 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -27,17 +27,17 @@ path = "src/lib.rs" default = [] # openssl -openssl = ["open-ssl", "awc/openssl", ] # "actix-tls/openssl"] +openssl = ["open-ssl", "awc/openssl"] [dependencies] actix-service = "1.0.0" actix-codec = "0.2.0" actix-connect = "1.0.0" -actix-utils = "1.0.0" +actix-utils = "1.0.3" actix-rt = "1.0.0" actix-server = "1.0.0" actix-testing = "1.0.0" -awc = "1.0.0-alpha.3" +awc = "1.0.0" base64 = "0.11" bytes = "0.5.2" @@ -55,5 +55,5 @@ time = "0.1" open-ssl = { version="0.10", package="openssl", optional = true } [dev-dependencies] -actix-web = "2.0.0-alpha.3" -actix-http = "1.0.0-alpha.3" +actix-web = "2.0.0-alpha.4" +actix-http = "1.0.0" diff --git a/tests/test_server.rs b/tests/test_server.rs index 505b6cc0c..4f9e2c7ee 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -56,7 +56,6 @@ async fn test_body() { assert_eq!(bytes, Bytes::from_static(STR.as_ref())); } -#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[actix_rt::test] async fn test_body_gzip() { let srv = TestServer::start(|| { @@ -86,7 +85,6 @@ async fn test_body_gzip() { assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); } -#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[actix_rt::test] async fn test_body_gzip2() { let srv = TestServer::start(|| { @@ -118,7 +116,6 @@ async fn test_body_gzip2() { assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); } -#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[actix_rt::test] async fn test_body_encoding_override() { let srv = TestServer::start(|| { @@ -179,7 +176,6 @@ async fn test_body_encoding_override() { assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); } -#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[actix_rt::test] async fn test_body_gzip_large() { let data = STR.repeat(10); @@ -216,7 +212,6 @@ async fn test_body_gzip_large() { assert_eq!(Bytes::from(dec), Bytes::from(data)); } -#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[actix_rt::test] async fn test_body_gzip_large_random() { let data = rand::thread_rng() @@ -257,7 +252,6 @@ async fn test_body_gzip_large_random() { assert_eq!(Bytes::from(dec), Bytes::from(data)); } -#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] #[actix_rt::test] async fn test_body_chunked_implicit() { let srv = TestServer::start(move || { @@ -378,7 +372,6 @@ async fn test_no_chunking() { } #[actix_rt::test] -#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] async fn test_body_deflate() { let srv = TestServer::start(move || { HttpService::build() @@ -440,7 +433,6 @@ async fn test_body_brotli() { } #[actix_rt::test] -#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] async fn test_encoding() { let srv = TestServer::start(move || { HttpService::build() @@ -469,7 +461,6 @@ async fn test_encoding() { } #[actix_rt::test] -#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] async fn test_gzip_encoding() { let srv = TestServer::start(move || { HttpService::build() @@ -498,7 +489,6 @@ async fn test_gzip_encoding() { } #[actix_rt::test] -#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] async fn test_gzip_encoding_large() { let data = STR.repeat(10); let srv = TestServer::start(move || { @@ -528,7 +518,6 @@ async fn test_gzip_encoding_large() { } #[actix_rt::test] -#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] async fn test_reading_gzip_encoding_large_random() { let data = rand::thread_rng() .sample_iter(&Alphanumeric) @@ -563,7 +552,6 @@ async fn test_reading_gzip_encoding_large_random() { } #[actix_rt::test] -#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] async fn test_reading_deflate_encoding() { let srv = TestServer::start(move || { HttpService::build() @@ -592,7 +580,6 @@ async fn test_reading_deflate_encoding() { } #[actix_rt::test] -#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] async fn test_reading_deflate_encoding_large() { let data = STR.repeat(10); let srv = TestServer::start(move || { @@ -622,7 +609,6 @@ async fn test_reading_deflate_encoding_large() { } #[actix_rt::test] -#[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] async fn test_reading_deflate_encoding_large_random() { let data = rand::thread_rng() .sample_iter(&Alphanumeric) @@ -771,11 +757,7 @@ async fn test_brotli_encoding_large() { // assert_eq!(bytes, Bytes::from(data)); // } -#[cfg(all( - feature = "rustls", - feature = "openssl", - any(feature = "flate2-zlib", feature = "flate2-rust") -))] +#[cfg(all(feature = "rustls", feature = "openssl"))] #[actix_rt::test] async fn test_reading_deflate_encoding_large_random_ssl() { use open_ssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; From db1d6b7963ba5a4ddc2e54f10bfe04a42e225e7d Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 12 Dec 2019 22:28:47 +0600 Subject: [PATCH 122/176] refactor test server impl --- Cargo.toml | 20 +- src/lib.rs | 5 - src/test.rs | 409 ++++++++++++++++++++++++++++++- tests/test_server.rs | 558 ++++++++++++++++--------------------------- 4 files changed, 624 insertions(+), 368 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f75e4a316..0159a21dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,10 +43,7 @@ members = [ ] [features] -default = ["compress", "client", "fail"] - -# http client -client = ["awc"] +default = ["compress", "fail"] # content-encoding support compress = ["actix-http/compress", "awc/compress"] @@ -57,10 +54,10 @@ secure-cookies = ["actix-http/secure-cookies"] fail = ["actix-http/fail"] # openssl -openssl = ["actix-tls/openssl", "awc/openssl"] +openssl = ["actix-tls/openssl", "awc/openssl", "open-ssl"] # rustls -rustls = ["actix-tls/rustls", "awc/rustls"] +rustls = ["actix-tls/rustls", "awc/rustls", "rust-tls"] [dependencies] actix-codec = "0.2.0" @@ -70,12 +67,13 @@ actix-router = "0.2.0" actix-rt = "1.0.0" actix-server = "1.0.0" actix-testing = "1.0.0" +actix-macros = "0.1.0" actix-threadpool = "0.3.0" actix-tls = "1.0.0" actix-web-codegen = "0.2.0-alpha.2" actix-http = "1.0.0" -awc = { version = "1.0.0", default-features = false, optional = true } +awc = { version = "1.0.0", default-features = false } bytes = "0.5.2" derive_more = "0.99.2" @@ -92,18 +90,16 @@ serde_json = "1.0" serde_urlencoded = "0.6.1" time = "0.1.42" url = "2.1" +open-ssl = { version="0.10", package = "openssl", optional = true } +rust-tls = { version = "0.16.0", package = "rustls", optional = true } [dev-dependencies] # actix = "0.8.3" -actix-connect = "1.0.0" -actix-http-test = "1.0.0-alpha.3" rand = "0.7" env_logger = "0.6" serde_derive = "1.0" brotli = "3.3.0" flate2 = "1.0.13" -open-ssl = { version="0.10", package = "openssl" } -rust_tls = { version = "0.16.0", package = "rustls" } [profile.release] lto = true @@ -120,4 +116,4 @@ actix-identity = { path = "actix-identity" } actix-session = { path = "actix-session" } actix-files = { path = "actix-files" } actix-multipart = { path = "actix-multipart" } -awc = { path = "awc" } \ No newline at end of file +awc = { path = "awc" } diff --git a/src/lib.rs b/src/lib.rs index 8d46cd801..7a1dbec0f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -100,10 +100,6 @@ pub mod test; mod types; pub mod web; -#[allow(unused_imports)] -#[macro_use] -extern crate actix_web_codegen; - #[doc(hidden)] pub use actix_web_codegen::*; @@ -163,7 +159,6 @@ pub mod dev { } } -#[cfg(feature = "client")] pub mod client { //! An HTTP Client //! diff --git a/src/test.rs b/src/test.rs index 419ea2d36..6d546702a 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,16 +1,24 @@ //! Various helpers for Actix applications to use during testing. use std::convert::TryFrom; use std::rc::Rc; +use std::sync::mpsc; +use std::{fmt, net, thread, time}; +use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_http::http::header::{ContentType, Header, HeaderName, IntoHeaderValue}; use actix_http::http::{Error as HttpError, Method, StatusCode, Uri, Version}; use actix_http::test::TestRequest as HttpTestRequest; -use actix_http::{cookie::Cookie, Extensions, Request}; +use actix_http::{cookie::Cookie, ws, Extensions, HttpService, Request}; use actix_router::{Path, ResourceDef, Url}; +use actix_rt::System; +use actix_server::Server; use actix_service::{IntoService, IntoServiceFactory, Service, ServiceFactory}; +use awc::error::PayloadError; +use awc::{Client, ClientRequest, ClientResponse, Connector}; use bytes::{Bytes, BytesMut}; use futures::future::ok; use futures::stream::{Stream, StreamExt}; +use net2::TcpBuilder; use serde::de::DeserializeOwned; use serde::Serialize; use serde_json; @@ -500,6 +508,405 @@ impl TestRequest { } } +/// Start test server with default configuration +/// +/// Test server is very simple server that simplify process of writing +/// integration tests cases for actix web applications. +/// +/// # Examples +/// +/// ```rust +/// use actix_web::{web, test, App, HttpResponse, Error}; +/// +/// async fn my_handler() -> Result { +/// Ok(HttpResponse::Ok().into()) +/// } +/// +/// #[actix_rt::test] +/// async fn test_example() { +/// let mut srv = test::start( +/// || App::new().service( +/// web::resource("/").to(my_handler)) +/// ); +/// +/// let req = srv.get("/"); +/// let response = req.send().await.unwrap(); +/// assert!(response.status().is_success()); +/// } +/// ``` +pub fn start(factory: F) -> TestServer +where + F: Fn() -> I + Send + Clone + 'static, + I: IntoServiceFactory, + S: ServiceFactory + 'static, + S::Error: Into + 'static, + S::InitError: fmt::Debug, + S::Response: Into> + 'static, + ::Future: 'static, + B: MessageBody + 'static, +{ + start_with(TestServerConfig::default(), factory) +} + +/// Start test server with custom configuration +/// +/// Test server could be configured in different ways, for details check +/// `TestServerConfig` docs. +/// +/// # Examples +/// +/// ```rust +/// use actix_web::{web, test, App, HttpResponse, Error}; +/// +/// async fn my_handler() -> Result { +/// Ok(HttpResponse::Ok().into()) +/// } +/// +/// #[actix_rt::test] +/// async fn test_example() { +/// let mut srv = test::start_with(test::config().h1(), || +/// App::new().service(web::resource("/").to(my_handler)) +/// ); +/// +/// let req = srv.get("/"); +/// let response = req.send().await.unwrap(); +/// assert!(response.status().is_success()); +/// } +/// ``` +pub fn start_with(cfg: TestServerConfig, factory: F) -> TestServer +where + F: Fn() -> I + Send + Clone + 'static, + I: IntoServiceFactory, + S: ServiceFactory + 'static, + S::Error: Into + 'static, + S::InitError: fmt::Debug, + S::Response: Into> + 'static, + ::Future: 'static, + B: MessageBody + 'static, +{ + let (tx, rx) = mpsc::channel(); + + let ssl = match cfg.stream { + StreamType::Tcp => false, + #[cfg(feature = "openssl")] + StreamType::Openssl(_) => true, + #[cfg(feature = "rustls")] + StreamType::Rustls(_) => true, + }; + + // run server in separate thread + thread::spawn(move || { + let sys = System::new("actix-test-server"); + let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap(); + let local_addr = tcp.local_addr().unwrap(); + let factory = factory.clone(); + let cfg = cfg.clone(); + let ctimeout = cfg.client_timeout; + let builder = Server::build().workers(1).disable_signals(); + + match cfg.stream { + StreamType::Tcp => match cfg.tp { + HttpVer::Http1 => builder.listen("test", tcp, move || { + HttpService::build() + .client_timeout(ctimeout) + .h1(factory()) + .tcp() + }), + HttpVer::Http2 => builder.listen("test", tcp, move || { + HttpService::build() + .client_timeout(ctimeout) + .h2(factory()) + .tcp() + }), + HttpVer::Both => builder.listen("test", tcp, move || { + HttpService::build() + .client_timeout(ctimeout) + .finish(factory()) + .tcp() + }), + }, + #[cfg(feature = "openssl")] + StreamType::Openssl(acceptor) => match cfg.tp { + HttpVer::Http1 => builder.listen("test", tcp, move || { + HttpService::build() + .client_timeout(ctimeout) + .h1(factory()) + .openssl(acceptor.clone()) + }), + HttpVer::Http2 => builder.listen("test", tcp, move || { + HttpService::build() + .client_timeout(ctimeout) + .h2(factory()) + .openssl(acceptor.clone()) + }), + HttpVer::Both => builder.listen("test", tcp, move || { + HttpService::build() + .client_timeout(ctimeout) + .finish(factory()) + .openssl(acceptor.clone()) + }), + }, + #[cfg(feature = "rustls")] + StreamType::Rustls(config) => match cfg.tp { + HttpVer::Http1 => builder.listen("test", tcp, move || { + HttpService::build() + .client_timeout(ctimeout) + .h1(factory()) + .rustls(config.clone()) + }), + HttpVer::Http2 => builder.listen("test", tcp, move || { + HttpService::build() + .client_timeout(ctimeout) + .h2(factory()) + .rustls(config.clone()) + }), + HttpVer::Both => builder.listen("test", tcp, move || { + HttpService::build() + .client_timeout(ctimeout) + .finish(factory()) + .rustls(config.clone()) + }), + }, + } + .unwrap() + .start(); + + tx.send((System::current(), local_addr)).unwrap(); + sys.run() + }); + + let (system, addr) = rx.recv().unwrap(); + + let client = { + let connector = { + #[cfg(feature = "openssl")] + { + use open_ssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; + + let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); + builder.set_verify(SslVerifyMode::NONE); + let _ = builder + .set_alpn_protos(b"\x02h2\x08http/1.1") + .map_err(|e| log::error!("Can not set alpn protocol: {:?}", e)); + Connector::new() + .conn_lifetime(time::Duration::from_secs(0)) + .timeout(time::Duration::from_millis(3000)) + .ssl(builder.build()) + .finish() + } + #[cfg(not(feature = "openssl"))] + { + Connector::new() + .conn_lifetime(time::Duration::from_secs(0)) + .timeout(time::Duration::from_millis(3000)) + .finish() + } + }; + + Client::build().connector(connector).finish() + }; + + TestServer { + ssl, + addr, + client, + system, + } +} + +#[derive(Clone)] +pub struct TestServerConfig { + tp: HttpVer, + stream: StreamType, + client_timeout: u64, +} + +#[derive(Clone)] +enum HttpVer { + Http1, + Http2, + Both, +} + +#[derive(Clone)] +enum StreamType { + Tcp, + #[cfg(feature = "openssl")] + Openssl(open_ssl::ssl::SslAcceptor), + #[cfg(feature = "rustls")] + Rustls(rust_tls::ServerConfig), +} + +impl Default for TestServerConfig { + fn default() -> Self { + TestServerConfig::new() + } +} + +/// Create default test server config +pub fn config() -> TestServerConfig { + TestServerConfig::new() +} + +impl TestServerConfig { + /// Create default server configuration + pub(crate) fn new() -> TestServerConfig { + TestServerConfig { + tp: HttpVer::Both, + stream: StreamType::Tcp, + client_timeout: 5000, + } + } + + /// Start http/1.1 server only + pub fn h1(mut self) -> Self { + self.tp = HttpVer::Http1; + self + } + + /// Start http/2 server only + pub fn h2(mut self) -> Self { + self.tp = HttpVer::Http2; + self + } + + /// Start openssl server + #[cfg(feature = "openssl")] + pub fn openssl(mut self, acceptor: open_ssl::ssl::SslAcceptor) -> Self { + self.stream = StreamType::Openssl(acceptor); + self + } + + /// Start rustls server + #[cfg(feature = "rustls")] + pub fn rustls(mut self, config: rust_tls::ServerConfig) -> Self { + self.stream = StreamType::Rustls(config); + self + } + + /// Set server client timeout in milliseconds for first request. + pub fn client_timeout(mut self, val: u64) -> Self { + self.client_timeout = val; + self + } +} + +/// Get first available unused address +pub fn unused_addr() -> net::SocketAddr { + let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); + let socket = TcpBuilder::new_v4().unwrap(); + socket.bind(&addr).unwrap(); + socket.reuse_address(true).unwrap(); + let tcp = socket.to_tcp_listener().unwrap(); + tcp.local_addr().unwrap() +} + +/// Test server controller +pub struct TestServer { + addr: net::SocketAddr, + client: awc::Client, + system: actix_rt::System, + ssl: bool, +} + +impl TestServer { + /// Construct test server url + pub fn addr(&self) -> net::SocketAddr { + self.addr + } + + /// Construct test server url + pub fn url(&self, uri: &str) -> String { + let scheme = if self.ssl { "https" } else { "http" }; + + if uri.starts_with('/') { + format!("{}://localhost:{}{}", scheme, self.addr.port(), uri) + } else { + format!("{}://localhost:{}/{}", scheme, self.addr.port(), uri) + } + } + + /// Create `GET` request + pub fn get>(&self, path: S) -> ClientRequest { + self.client.get(self.url(path.as_ref()).as_str()) + } + + /// Create `POST` request + pub fn post>(&self, path: S) -> ClientRequest { + self.client.post(self.url(path.as_ref()).as_str()) + } + + /// Create `HEAD` request + pub fn head>(&self, path: S) -> ClientRequest { + self.client.head(self.url(path.as_ref()).as_str()) + } + + /// Create `PUT` request + pub fn put>(&self, path: S) -> ClientRequest { + self.client.put(self.url(path.as_ref()).as_str()) + } + + /// Create `PATCH` request + pub fn patch>(&self, path: S) -> ClientRequest { + self.client.patch(self.url(path.as_ref()).as_str()) + } + + /// Create `DELETE` request + pub fn delete>(&self, path: S) -> ClientRequest { + self.client.delete(self.url(path.as_ref()).as_str()) + } + + /// Create `OPTIONS` request + pub fn options>(&self, path: S) -> ClientRequest { + self.client.options(self.url(path.as_ref()).as_str()) + } + + /// Connect to test http server + pub fn request>(&self, method: Method, path: S) -> ClientRequest { + self.client.request(method, path.as_ref()) + } + + pub async fn load_body( + &mut self, + mut response: ClientResponse, + ) -> Result + where + S: Stream> + Unpin + 'static, + { + response.body().limit(10_485_760).await + } + + /// Connect to websocket server at a given path + pub async fn ws_at( + &mut self, + path: &str, + ) -> Result, awc::error::WsClientError> + { + let url = self.url(path); + let connect = self.client.ws(url).connect(); + connect.await.map(|(_, framed)| framed) + } + + /// Connect to a websocket server + pub async fn ws( + &mut self, + ) -> Result, awc::error::WsClientError> + { + self.ws_at("/").await + } + + /// Stop http server + fn stop(&mut self) { + self.system.stop(); + } +} + +impl Drop for TestServer { + fn drop(&mut self) { + self.stop() + } +} + #[cfg(test)] mod tests { use actix_http::httpmessage::HttpMessage; diff --git a/tests/test_server.rs b/tests/test_server.rs index 4f9e2c7ee..ca4aff638 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -4,9 +4,7 @@ use actix_http::http::header::{ ContentEncoding, ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_LENGTH, TRANSFER_ENCODING, }; -use actix_http::{Error, HttpService, Response}; -use actix_http_test::TestServer; -use brotli::DecompressorWriter; +use brotli::{CompressorWriter, DecompressorWriter}; use bytes::Bytes; use flate2::read::GzDecoder; use flate2::write::{GzEncoder, ZlibDecoder, ZlibEncoder}; @@ -15,7 +13,7 @@ use futures::{future::ok, stream::once}; use rand::{distributions::Alphanumeric, Rng}; use actix_web::middleware::{BodyEncoding, Compress}; -use actix_web::{dev, http, web, App, HttpResponse, HttpServer}; +use actix_web::{dev, http, test, web, App, Error, HttpResponse}; const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World \ @@ -41,11 +39,9 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[actix_rt::test] async fn test_body() { - let srv = TestServer::start(|| { - HttpService::build() - .h1(App::new() - .service(web::resource("/").route(web::to(|| Response::Ok().body(STR))))) - .tcp() + let srv = test::start(|| { + App::new() + .service(web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR)))) }); let mut response = srv.get("/").send().await.unwrap(); @@ -58,12 +54,10 @@ async fn test_body() { #[actix_rt::test] async fn test_body_gzip() { - let srv = TestServer::start(|| { - HttpService::build() - .h1(App::new() - .wrap(Compress::new(ContentEncoding::Gzip)) - .service(web::resource("/").route(web::to(|| Response::Ok().body(STR))))) - .tcp() + let srv = test::start_with(test::config().h1(), || { + App::new() + .wrap(Compress::new(ContentEncoding::Gzip)) + .service(web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR)))) }); let mut response = srv @@ -87,14 +81,12 @@ async fn test_body_gzip() { #[actix_rt::test] async fn test_body_gzip2() { - let srv = TestServer::start(|| { - HttpService::build() - .h1(App::new() - .wrap(Compress::new(ContentEncoding::Gzip)) - .service(web::resource("/").route(web::to(|| { - Response::Ok().body(STR).into_body::() - })))) - .tcp() + let srv = test::start_with(test::config().h1(), || { + App::new() + .wrap(Compress::new(ContentEncoding::Gzip)) + .service(web::resource("/").route(web::to(|| { + HttpResponse::Ok().body(STR).into_body::() + }))) }); let mut response = srv @@ -118,23 +110,23 @@ async fn test_body_gzip2() { #[actix_rt::test] async fn test_body_encoding_override() { - let srv = TestServer::start(|| { - HttpService::build() - .h1(App::new() - .wrap(Compress::new(ContentEncoding::Gzip)) - .service(web::resource("/").route(web::to(|| { - Response::Ok().encoding(ContentEncoding::Deflate).body(STR) - }))) - .service(web::resource("/raw").route(web::to(|| { - let body = actix_web::dev::Body::Bytes(STR.into()); - let mut response = - Response::with_body(actix_web::http::StatusCode::OK, body); + let srv = test::start_with(test::config().h1(), || { + App::new() + .wrap(Compress::new(ContentEncoding::Gzip)) + .service(web::resource("/").route(web::to(|| { + HttpResponse::Ok() + .encoding(ContentEncoding::Deflate) + .body(STR) + }))) + .service(web::resource("/raw").route(web::to(|| { + let body = actix_web::dev::Body::Bytes(STR.into()); + let mut response = + HttpResponse::with_body(actix_web::http::StatusCode::OK, body); - response.encoding(ContentEncoding::Deflate); + response.encoding(ContentEncoding::Deflate); - response - })))) - .tcp() + response + }))) }); // Builder @@ -181,16 +173,14 @@ async fn test_body_gzip_large() { let data = STR.repeat(10); let srv_data = data.clone(); - let srv = TestServer::start(move || { + let srv = test::start_with(test::config().h1(), move || { let data = srv_data.clone(); - HttpService::build() - .h1(App::new() - .wrap(Compress::new(ContentEncoding::Gzip)) - .service( - web::resource("/") - .route(web::to(move || Response::Ok().body(data.clone()))), - )) - .tcp() + App::new() + .wrap(Compress::new(ContentEncoding::Gzip)) + .service( + web::resource("/") + .route(web::to(move || HttpResponse::Ok().body(data.clone()))), + ) }); let mut response = srv @@ -220,16 +210,14 @@ async fn test_body_gzip_large_random() { .collect::(); let srv_data = data.clone(); - let srv = TestServer::start(move || { + let srv = test::start_with(test::config().h1(), move || { let data = srv_data.clone(); - HttpService::build() - .h1(App::new() - .wrap(Compress::new(ContentEncoding::Gzip)) - .service( - web::resource("/") - .route(web::to(move || Response::Ok().body(data.clone()))), - )) - .tcp() + App::new() + .wrap(Compress::new(ContentEncoding::Gzip)) + .service( + web::resource("/") + .route(web::to(move || HttpResponse::Ok().body(data.clone()))), + ) }); let mut response = srv @@ -254,16 +242,13 @@ async fn test_body_gzip_large_random() { #[actix_rt::test] async fn test_body_chunked_implicit() { - let srv = TestServer::start(move || { - HttpService::build() - .h1(App::new() - .wrap(Compress::new(ContentEncoding::Gzip)) - .service(web::resource("/").route(web::get().to(move || { - Response::Ok().streaming(once(ok::<_, Error>(Bytes::from_static( - STR.as_ref(), - )))) - })))) - .tcp() + let srv = test::start_with(test::config().h1(), || { + App::new() + .wrap(Compress::new(ContentEncoding::Gzip)) + .service(web::resource("/").route(web::get().to(move || { + HttpResponse::Ok() + .streaming(once(ok::<_, Error>(Bytes::from_static(STR.as_ref())))) + }))) }); let mut response = srv @@ -291,16 +276,13 @@ async fn test_body_chunked_implicit() { #[actix_rt::test] async fn test_body_br_streaming() { - let srv = TestServer::start(move || { - HttpService::build() - .h1(App::new().wrap(Compress::new(ContentEncoding::Br)).service( - web::resource("/").route(web::to(move || { - Response::Ok().streaming(once(ok::<_, Error>(Bytes::from_static( - STR.as_ref(), - )))) - })), - )) - .tcp() + let srv = test::start_with(test::config().h1(), || { + App::new().wrap(Compress::new(ContentEncoding::Br)).service( + web::resource("/").route(web::to(move || { + HttpResponse::Ok() + .streaming(once(ok::<_, Error>(Bytes::from_static(STR.as_ref())))) + })), + ) }); let mut response = srv @@ -324,12 +306,10 @@ async fn test_body_br_streaming() { #[actix_rt::test] async fn test_head_binary() { - let srv = TestServer::start(move || { - HttpService::build() - .h1(App::new().service(web::resource("/").route( - web::head().to(move || Response::Ok().content_length(100).body(STR)), - ))) - .tcp() + let srv = test::start_with(test::config().h1(), || { + App::new().service(web::resource("/").route( + web::head().to(move || HttpResponse::Ok().content_length(100).body(STR)), + )) }); let mut response = srv.head("/").send().await.unwrap(); @@ -347,19 +327,13 @@ async fn test_head_binary() { #[actix_rt::test] async fn test_no_chunking() { - let srv = TestServer::start(move || { - HttpService::build() - .h1( - App::new().service(web::resource("/").route(web::to(move || { - Response::Ok() - .no_chunking() - .content_length(STR.len() as u64) - .streaming(once(ok::<_, Error>(Bytes::from_static( - STR.as_ref(), - )))) - }))), - ) - .tcp() + let srv = test::start_with(test::config().h1(), || { + App::new().service(web::resource("/").route(web::to(move || { + HttpResponse::Ok() + .no_chunking() + .content_length(STR.len() as u64) + .streaming(once(ok::<_, Error>(Bytes::from_static(STR.as_ref())))) + }))) }); let mut response = srv.get("/").send().await.unwrap(); @@ -373,14 +347,12 @@ async fn test_no_chunking() { #[actix_rt::test] async fn test_body_deflate() { - let srv = TestServer::start(move || { - HttpService::build() - .h1(App::new() - .wrap(Compress::new(ContentEncoding::Deflate)) - .service( - web::resource("/").route(web::to(move || Response::Ok().body(STR))), - )) - .tcp() + let srv = test::start_with(test::config().h1(), || { + App::new() + .wrap(Compress::new(ContentEncoding::Deflate)) + .service( + web::resource("/").route(web::to(move || HttpResponse::Ok().body(STR))), + ) }); // client request @@ -404,12 +376,10 @@ async fn test_body_deflate() { #[actix_rt::test] async fn test_body_brotli() { - let srv = TestServer::start(move || { - HttpService::build() - .h1(App::new().wrap(Compress::new(ContentEncoding::Br)).service( - web::resource("/").route(web::to(move || Response::Ok().body(STR))), - )) - .tcp() + let srv = test::start_with(test::config().h1(), || { + App::new().wrap(Compress::new(ContentEncoding::Br)).service( + web::resource("/").route(web::to(move || HttpResponse::Ok().body(STR))), + ) }); // client request @@ -434,13 +404,11 @@ async fn test_body_brotli() { #[actix_rt::test] async fn test_encoding() { - let srv = TestServer::start(move || { - HttpService::build() - .h1(App::new().wrap(Compress::default()).service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - )) - .tcp() + let srv = test::start_with(test::config().h1(), || { + App::new().wrap(Compress::default()).service( + web::resource("/") + .route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))), + ) }); // client request @@ -462,13 +430,11 @@ async fn test_encoding() { #[actix_rt::test] async fn test_gzip_encoding() { - let srv = TestServer::start(move || { - HttpService::build() - .h1(App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - )) - .tcp() + let srv = test::start_with(test::config().h1(), || { + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))), + ) }); // client request @@ -491,13 +457,11 @@ async fn test_gzip_encoding() { #[actix_rt::test] async fn test_gzip_encoding_large() { let data = STR.repeat(10); - let srv = TestServer::start(move || { - HttpService::build() - .h1(App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - )) - .tcp() + let srv = test::start_with(test::config().h1(), || { + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))), + ) }); // client request @@ -524,13 +488,11 @@ async fn test_reading_gzip_encoding_large_random() { .take(60_000) .collect::(); - let srv = TestServer::start(move || { - HttpService::build() - .h1(App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - )) - .tcp() + let srv = test::start_with(test::config().h1(), || { + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))), + ) }); // client request @@ -553,13 +515,11 @@ async fn test_reading_gzip_encoding_large_random() { #[actix_rt::test] async fn test_reading_deflate_encoding() { - let srv = TestServer::start(move || { - HttpService::build() - .h1(App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - )) - .tcp() + let srv = test::start_with(test::config().h1(), || { + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))), + ) }); let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); @@ -582,13 +542,11 @@ async fn test_reading_deflate_encoding() { #[actix_rt::test] async fn test_reading_deflate_encoding_large() { let data = STR.repeat(10); - let srv = TestServer::start(move || { - HttpService::build() - .h1(App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - )) - .tcp() + let srv = test::start_with(test::config().h1(), || { + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))), + ) }); let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); @@ -615,13 +573,11 @@ async fn test_reading_deflate_encoding_large_random() { .take(160_000) .collect::(); - let srv = TestServer::start(move || { - HttpService::build() - .h1(App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - )) - .tcp() + let srv = test::start_with(test::config().h1(), || { + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))), + ) }); let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); @@ -643,20 +599,17 @@ async fn test_reading_deflate_encoding_large_random() { } #[actix_rt::test] -#[cfg(feature = "brotli")] async fn test_brotli_encoding() { - let srv = TestServer::start(move || { - HttpService::build() - .h1(App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - )) - .tcp() + let srv = test::start_with(test::config().h1(), || { + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))), + ) }); - let mut e = BrotliEncoder::new(Vec::new(), 5); + let mut e = CompressorWriter::new(Vec::new(), 0, 3, 0); e.write_all(STR.as_ref()).unwrap(); - let enc = e.finish().unwrap(); + let enc = e.into_inner(); // client request let request = srv @@ -671,22 +624,19 @@ async fn test_brotli_encoding() { assert_eq!(bytes, Bytes::from_static(STR.as_ref())); } -#[cfg(feature = "brotli")] #[actix_rt::test] async fn test_brotli_encoding_large() { let data = STR.repeat(10); - let srv = TestServer::start(move || { - HttpService::build() - .h1(App::new().service( - web::resource("/") - .route(web::to(move |body: Bytes| Response::Ok().body(body))), - )) - .tcp() + let srv = test::start_with(test::config().h1(), || { + App::new().service( + web::resource("/") + .route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))), + ) }); - let mut e = BrotliEncoder::new(Vec::new(), 5); + let mut e = CompressorWriter::new(Vec::new(), 0, 3, 0); e.write_all(data.as_ref()).unwrap(); - let enc = e.finish().unwrap(); + let enc = e.into_inner(); // client request let request = srv @@ -701,124 +651,75 @@ async fn test_brotli_encoding_large() { assert_eq!(bytes, Bytes::from(data)); } -// #[cfg(feature = "ssl")] -// #[actix_rt::test] -// async fn test_brotli_encoding_large_ssl() { -// use actix::{Actor, System}; -// use openssl::ssl::{ -// SslAcceptor, SslConnector, SslFiletype, SslMethod, SslVerifyMode, -// }; -// // load ssl keys -// let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); -// builder -// .set_private_key_file("tests/key.pem", SslFiletype::PEM) -// .unwrap(); -// builder -// .set_certificate_chain_file("tests/cert.pem") -// .unwrap(); +#[cfg(feature = "openssl")] +#[actix_rt::test] +async fn test_brotli_encoding_large_openssl() { + // load ssl keys + use open_ssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; + let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); + builder + .set_private_key_file("tests/key.pem", SslFiletype::PEM) + .unwrap(); + builder + .set_certificate_chain_file("tests/cert.pem") + .unwrap(); -// let data = STR.repeat(10); -// let srv = test::TestServer::build().ssl(builder).start(|app| { -// app.handler(|req: &HttpRequest| { -// req.body() -// .and_then(|bytes: Bytes| { -// Ok(HttpResponse::Ok() -// .content_encoding(http::ContentEncoding::Identity) -// .body(bytes)) -// }) -// .responder() -// }) -// }); -// let mut rt = System::new("test"); + let data = STR.repeat(10); + let srv = test::start_with(test::config().openssl(builder.build()), move || { + App::new().service(web::resource("/").route(web::to(|bytes: Bytes| { + HttpResponse::Ok() + .encoding(http::ContentEncoding::Identity) + .body(bytes) + }))) + }); -// // client connector -// let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); -// builder.set_verify(SslVerifyMode::NONE); -// let conn = client::ClientConnector::with_connector(builder.build()).start(); + // body + let mut e = CompressorWriter::new(Vec::new(), 0, 3, 0); + e.write_all(data.as_ref()).unwrap(); + let enc = e.into_inner(); -// // body -// let mut e = BrotliEncoder::new(Vec::new(), 5); -// e.write_all(data.as_ref()).unwrap(); -// let enc = e.finish().unwrap(); + // client request + let mut response = srv + .post("/") + .header(http::header::CONTENT_ENCODING, "br") + .send_body(enc) + .await + .unwrap(); + assert!(response.status().is_success()); -// // client request -// let request = client::ClientRequest::build() -// .uri(srv.url("/")) -// .method(http::Method::POST) -// .header(http::header::CONTENT_ENCODING, "br") -// .with_connector(conn) -// .body(enc) -// .unwrap(); -// let response = rt.block_on(request.send()).unwrap(); -// assert!(response.status().is_success()); - -// // read response -// let bytes = rt.block_on(response.body()).unwrap(); -// assert_eq!(bytes, Bytes::from(data)); -// } + // read response + let bytes = response.body().await.unwrap(); + assert_eq!(bytes, Bytes::from(data)); +} #[cfg(all(feature = "rustls", feature = "openssl"))] #[actix_rt::test] -async fn test_reading_deflate_encoding_large_random_ssl() { - use open_ssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; +async fn test_reading_deflate_encoding_large_random_rustls() { use rust_tls::internal::pemfile::{certs, pkcs8_private_keys}; use rust_tls::{NoClientAuth, ServerConfig}; use std::fs::File; use std::io::BufReader; - use std::sync::mpsc; - - let addr = TestServer::unused_addr(); - let (tx, rx) = mpsc::channel(); let data = rand::thread_rng() .sample_iter(&Alphanumeric) .take(160_000) .collect::(); - std::thread::spawn(move || { - let sys = actix_rt::System::new("test"); + // load ssl keys + let mut config = ServerConfig::new(NoClientAuth::new()); + let cert_file = &mut BufReader::new(File::open("tests/cert.pem").unwrap()); + let key_file = &mut BufReader::new(File::open("tests/key.pem").unwrap()); + let cert_chain = certs(cert_file).unwrap(); + let mut keys = pkcs8_private_keys(key_file).unwrap(); + config.set_single_cert(cert_chain, keys.remove(0)).unwrap(); - // load ssl keys - let mut config = ServerConfig::new(NoClientAuth::new()); - let cert_file = &mut BufReader::new(File::open("tests/cert.pem").unwrap()); - let key_file = &mut BufReader::new(File::open("tests/key.pem").unwrap()); - let cert_chain = certs(cert_file).unwrap(); - let mut keys = pkcs8_private_keys(key_file).unwrap(); - config.set_single_cert(cert_chain, keys.remove(0)).unwrap(); - - let srv = HttpServer::new(|| { - App::new().service(web::resource("/").route(web::to(|bytes: Bytes| { - async move { - Ok::<_, Error>( - HttpResponse::Ok() - .encoding(http::ContentEncoding::Identity) - .body(bytes), - ) - } - }))) - }) - .bind_rustls(addr, config) - .unwrap() - .start(); - - let _ = tx.send((srv, actix_rt::System::current())); - let _ = sys.run(); + let srv = test::start_with(test::config().rustls(config), || { + App::new().service(web::resource("/").route(web::to(|bytes: Bytes| { + HttpResponse::Ok() + .encoding(http::ContentEncoding::Identity) + .body(bytes) + }))) }); - let (srv, _sys) = rx.recv().unwrap(); - let client = { - let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); - builder.set_verify(SslVerifyMode::NONE); - let _ = builder.set_alpn_protos(b"\x02h2\x08http/1.1").unwrap(); - - awc::Client::build() - .connector( - awc::Connector::new() - .timeout(std::time::Duration::from_millis(500)) - .ssl(builder.build()) - .finish(), - ) - .finish() - }; // encode data let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); @@ -826,8 +727,8 @@ async fn test_reading_deflate_encoding_large_random_ssl() { let enc = e.finish().unwrap(); // client request - let req = client - .post(format!("https://localhost:{}/", addr.port())) + let req = srv + .post("/") .header(http::header::CONTENT_ENCODING, "deflate") .send_body(enc); @@ -838,14 +739,11 @@ async fn test_reading_deflate_encoding_large_random_ssl() { let bytes = response.body().await.unwrap(); assert_eq!(bytes.len(), data.len()); assert_eq!(bytes, Bytes::from(data)); - - // stop - let _ = srv.stop(false); } // #[cfg(all(feature = "tls", feature = "ssl"))] // #[test] -// fn test_reading_deflate_encoding_large_random_tls() { +// fn test_reading_deflate_encoding_large_random_nativetls() { // use native_tls::{Identity, TlsAcceptor}; // use openssl::ssl::{ // SslAcceptor, SslConnector, SslFiletype, SslMethod, SslVerifyMode, @@ -986,55 +884,31 @@ async fn test_reading_deflate_encoding_large_random_ssl() { // } // } -// #[test] -// fn test_slow_request() { -// use actix::System; +#[actix_rt::test] +async fn test_slow_request() { + use std::net; + + let srv = test::start_with(test::config().client_timeout(200), || { + App::new().service(web::resource("/").route(web::to(|| HttpResponse::Ok()))) + }); + + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); + assert!(data.starts_with("HTTP/1.1 408 Request Timeout")); + + let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n"); + let mut data = String::new(); + let _ = stream.read_to_string(&mut data); + assert!(data.starts_with("HTTP/1.1 408 Request Timeout")); +} + +// #[cfg(feature = "openssl")] +// #[actix_rt::test] +// async fn test_ssl_handshake_timeout() { +// use open_ssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; // use std::net; -// use std::sync::mpsc; -// let (tx, rx) = mpsc::channel(); - -// let addr = test::TestServer::unused_addr(); -// thread::spawn(move || { -// System::run(move || { -// let srv = server::new(|| { -// vec![App::new().resource("/", |r| { -// r.method(http::Method::GET).f(|_| HttpResponse::Ok()) -// })] -// }); - -// let srv = srv.bind(addr).unwrap(); -// srv.client_timeout(200).start(); -// let _ = tx.send(System::current()); -// }); -// }); -// let sys = rx.recv().unwrap(); - -// thread::sleep(time::Duration::from_millis(200)); - -// let mut stream = net::TcpStream::connect(addr).unwrap(); -// let mut data = String::new(); -// let _ = stream.read_to_string(&mut data); -// assert!(data.starts_with("HTTP/1.1 408 Request Timeout")); - -// let mut stream = net::TcpStream::connect(addr).unwrap(); -// let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\n"); -// let mut data = String::new(); -// let _ = stream.read_to_string(&mut data); -// assert!(data.starts_with("HTTP/1.1 408 Request Timeout")); - -// sys.stop(); -// } - -// #[test] -// #[cfg(feature = "ssl")] -// fn test_ssl_handshake_timeout() { -// use actix::System; -// use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; -// use std::net; -// use std::sync::mpsc; - -// let (tx, rx) = mpsc::channel(); -// let addr = test::TestServer::unused_addr(); // // load ssl keys // let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); @@ -1045,28 +919,12 @@ async fn test_reading_deflate_encoding_large_random_ssl() { // .set_certificate_chain_file("tests/cert.pem") // .unwrap(); -// thread::spawn(move || { -// System::run(move || { -// let srv = server::new(|| { -// App::new().resource("/", |r| { -// r.method(http::Method::GET).f(|_| HttpResponse::Ok()) -// }) -// }); - -// srv.bind_ssl(addr, builder) -// .unwrap() -// .workers(1) -// .client_timeout(200) -// .start(); -// let _ = tx.send(System::current()); -// }); +// let srv = test::start_with(test::config().openssl(builder.build()), || { +// App::new().service(web::resource("/").route(web::to(|| HttpResponse::Ok()))) // }); -// let sys = rx.recv().unwrap(); -// let mut stream = net::TcpStream::connect(addr).unwrap(); +// let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); // let mut data = String::new(); // let _ = stream.read_to_string(&mut data); // assert!(data.is_empty()); - -// let _ = sys.stop(); // } From 4937c9f9c23d700cdc826db0d43faa5a66544c45 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 12 Dec 2019 23:08:38 +0600 Subject: [PATCH 123/176] refactor http-test server --- Cargo.toml | 4 +- actix-framed/tests/test_server.rs | 6 +- actix-http/Cargo.toml | 4 +- actix-http/src/error.rs | 10 +- actix-http/tests/test_client.rs | 8 +- actix-http/tests/test_openssl.rs | 30 ++--- actix-http/tests/test_rustls.rs | 34 ++--- actix-http/tests/test_server.rs | 52 ++++---- actix-http/tests/test_ws.rs | 4 +- actix-web-codegen/Cargo.toml | 8 +- actix-web-codegen/tests/test_macro.rs | 45 +++---- awc/tests/test_client.rs | 183 +++++++++++--------------- awc/tests/test_rustls_client.rs | 4 +- awc/tests/test_ssl_client.rs | 4 +- awc/tests/test_ws.rs | 4 +- test-server/Cargo.toml | 2 +- test-server/src/lib.rs | 146 ++++++++++---------- 17 files changed, 252 insertions(+), 296 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0159a21dd..b5b5577f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,7 +43,7 @@ members = [ ] [features] -default = ["compress", "fail"] +default = ["compress", "failure"] # content-encoding support compress = ["actix-http/compress", "awc/compress"] @@ -51,7 +51,7 @@ compress = ["actix-http/compress", "awc/compress"] # sessions feature, session require "ring" crate and c compiler secure-cookies = ["actix-http/secure-cookies"] -fail = ["actix-http/fail"] +failure = ["actix-http/failure"] # openssl openssl = ["actix-tls/openssl", "awc/openssl", "open-ssl"] diff --git a/actix-framed/tests/test_server.rs b/actix-framed/tests/test_server.rs index f6b068630..7d6fc08a6 100644 --- a/actix-framed/tests/test_server.rs +++ b/actix-framed/tests/test_server.rs @@ -1,6 +1,6 @@ use actix_codec::{AsyncRead, AsyncWrite}; use actix_http::{body, http::StatusCode, ws, Error, HttpService, Response}; -use actix_http_test::TestServer; +use actix_http_test::test_server; use actix_service::{pipeline_factory, IntoServiceFactory, ServiceFactory}; use actix_utils::framed::Dispatcher; use bytes::Bytes; @@ -40,7 +40,7 @@ async fn service(msg: ws::Frame) -> Result { #[actix_rt::test] async fn test_simple() { - let mut srv = TestServer::start(|| { + let mut srv = test_server(|| { HttpService::build() .upgrade( FramedApp::new().service(FramedRoute::get("/index.html").to(ws_service)), @@ -94,7 +94,7 @@ async fn test_simple() { #[actix_rt::test] async fn test_service() { - let mut srv = TestServer::start(|| { + let mut srv = test_server(|| { pipeline_factory(actix_http::h1::OneRequest::new().map_err(|_| ())).and_then( pipeline_factory( pipeline_factory(VerifyWebSockets::default()) diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 14c3c44d5..63e3977a5 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -34,7 +34,7 @@ rustls = ["actix-tls/rustls", "actix-connect/rustls"] compress = ["flate2", "brotli"] # failure integration. actix does not use failure anymore -fail = ["failure"] +failure = ["fail-ure"] # support for secure cookies secure-cookies = ["ring"] @@ -85,7 +85,7 @@ brotli = { version = "3.3.0", optional = true } flate2 = { version = "1.0.13", optional = true } # optional deps -failure = { version = "0.1.5", optional = true } +fail-ure = { version = "0.1.5", package="failure", optional = true } [dev-dependencies] actix-server = "1.0.0" diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index 8ec21c004..512b14ca7 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -957,13 +957,9 @@ where InternalError::new(err, StatusCode::NETWORK_AUTHENTICATION_REQUIRED).into() } -#[cfg(feature = "fail")] -mod failure_integration { - use super::*; - - /// Compatibility for `failure::Error` - impl ResponseError for failure::Error {} -} +#[cfg(feature = "failure")] +/// Compatibility for `failure::Error` +impl ResponseError for fail_ure::Error {} #[cfg(test)] mod tests { diff --git a/actix-http/tests/test_client.rs b/actix-http/tests/test_client.rs index 711ee7afd..9da3b04a2 100644 --- a/actix-http/tests/test_client.rs +++ b/actix-http/tests/test_client.rs @@ -3,7 +3,7 @@ use bytes::Bytes; use futures::future::{self, ok}; use actix_http::{http, HttpService, Request, Response}; -use actix_http_test::TestServer; +use actix_http_test::test_server; const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World \ @@ -29,7 +29,7 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[actix_rt::test] async fn test_h1_v2() { - let srv = TestServer::start(move || { + let srv = test_server(move || { HttpService::build() .finish(|_| future::ok::<_, ()>(Response::Ok().body(STR))) .tcp() @@ -56,7 +56,7 @@ async fn test_h1_v2() { #[actix_rt::test] async fn test_connection_close() { - let srv = TestServer::start(move || { + let srv = test_server(move || { HttpService::build() .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) .tcp() @@ -69,7 +69,7 @@ async fn test_connection_close() { #[actix_rt::test] async fn test_with_query_parameter() { - let srv = TestServer::start(move || { + let srv = test_server(move || { HttpService::build() .finish(|req: Request| { if req.uri().query().unwrap().contains("qp=") { diff --git a/actix-http/tests/test_openssl.rs b/actix-http/tests/test_openssl.rs index 5d466ee30..b25f05272 100644 --- a/actix-http/tests/test_openssl.rs +++ b/actix-http/tests/test_openssl.rs @@ -1,7 +1,7 @@ #![cfg(feature = "openssl")] use std::io; -use actix_http_test::TestServer; +use actix_http_test::test_server; use actix_service::{fn_service, ServiceFactory}; use bytes::{Bytes, BytesMut}; @@ -62,7 +62,7 @@ fn ssl_acceptor() -> SslAcceptor { #[actix_rt::test] async fn test_h2() -> io::Result<()> { - let srv = TestServer::start(move || { + let srv = test_server(move || { HttpService::build() .h2(|_| ok::<_, Error>(Response::Ok().finish())) .openssl(ssl_acceptor()) @@ -76,7 +76,7 @@ async fn test_h2() -> io::Result<()> { #[actix_rt::test] async fn test_h2_1() -> io::Result<()> { - let srv = TestServer::start(move || { + let srv = test_server(move || { HttpService::build() .finish(|req: Request| { assert!(req.peer_addr().is_some()); @@ -95,7 +95,7 @@ async fn test_h2_1() -> io::Result<()> { #[actix_rt::test] async fn test_h2_body() -> io::Result<()> { let data = "HELLOWORLD".to_owned().repeat(64 * 1024); - let mut srv = TestServer::start(move || { + let mut srv = test_server(move || { HttpService::build() .h2(|mut req: Request<_>| { async move { @@ -117,7 +117,7 @@ async fn test_h2_body() -> io::Result<()> { #[actix_rt::test] async fn test_h2_content_length() { - let srv = TestServer::start(move || { + let srv = test_server(move || { HttpService::build() .h2(|req: Request| { let indx: usize = req.uri().path()[1..].parse().unwrap(); @@ -168,7 +168,7 @@ async fn test_h2_headers() { let data = STR.repeat(10); let data2 = data.clone(); - let mut srv = TestServer::start(move || { + let mut srv = test_server(move || { let data = data.clone(); HttpService::build().h2(move |_| { let mut builder = Response::Ok(); @@ -228,7 +228,7 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[actix_rt::test] async fn test_h2_body2() { - let mut srv = TestServer::start(move || { + let mut srv = test_server(move || { HttpService::build() .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) .openssl(ssl_acceptor()) @@ -245,7 +245,7 @@ async fn test_h2_body2() { #[actix_rt::test] async fn test_h2_head_empty() { - let mut srv = TestServer::start(move || { + let mut srv = test_server(move || { HttpService::build() .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) .openssl(ssl_acceptor()) @@ -268,7 +268,7 @@ async fn test_h2_head_empty() { #[actix_rt::test] async fn test_h2_head_binary() { - let mut srv = TestServer::start(move || { + let mut srv = test_server(move || { HttpService::build() .h2(|_| { ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) @@ -292,7 +292,7 @@ async fn test_h2_head_binary() { #[actix_rt::test] async fn test_h2_head_binary2() { - let srv = TestServer::start(move || { + let srv = test_server(move || { HttpService::build() .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) .openssl(ssl_acceptor()) @@ -310,7 +310,7 @@ async fn test_h2_head_binary2() { #[actix_rt::test] async fn test_h2_body_length() { - let mut srv = TestServer::start(move || { + let mut srv = test_server(move || { HttpService::build() .h2(|_| { let body = once(ok(Bytes::from_static(STR.as_ref()))); @@ -332,7 +332,7 @@ async fn test_h2_body_length() { #[actix_rt::test] async fn test_h2_body_chunked_explicit() { - let mut srv = TestServer::start(move || { + let mut srv = test_server(move || { HttpService::build() .h2(|_| { let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); @@ -359,7 +359,7 @@ async fn test_h2_body_chunked_explicit() { #[actix_rt::test] async fn test_h2_response_http_error_handling() { - let mut srv = TestServer::start(move || { + let mut srv = test_server(move || { HttpService::build() .h2(fn_service(|_| { let broken_header = Bytes::from_static(b"\0\0\0"); @@ -383,7 +383,7 @@ async fn test_h2_response_http_error_handling() { #[actix_rt::test] async fn test_h2_service_error() { - let mut srv = TestServer::start(move || { + let mut srv = test_server(move || { HttpService::build() .h2(|_| err::(ErrorBadRequest("error"))) .openssl(ssl_acceptor()) @@ -400,7 +400,7 @@ async fn test_h2_service_error() { #[actix_rt::test] async fn test_h2_on_connect() { - let srv = TestServer::start(move || { + let srv = test_server(move || { HttpService::build() .on_connect(|_| 10usize) .h2(|req: Request| { diff --git a/actix-http/tests/test_rustls.rs b/actix-http/tests/test_rustls.rs index b5c5cf3b1..bc0c91cc3 100644 --- a/actix-http/tests/test_rustls.rs +++ b/actix-http/tests/test_rustls.rs @@ -3,7 +3,7 @@ use actix_http::error::PayloadError; use actix_http::http::header::{self, HeaderName, HeaderValue}; use actix_http::http::{Method, StatusCode, Version}; use actix_http::{body, error, Error, HttpService, Request, Response}; -use actix_http_test::TestServer; +use actix_http_test::test_server; use actix_service::{fn_factory_with_config, fn_service}; use bytes::{Bytes, BytesMut}; @@ -41,7 +41,7 @@ fn ssl_acceptor() -> RustlsServerConfig { #[actix_rt::test] async fn test_h1() -> io::Result<()> { - let srv = TestServer::start(move || { + let srv = test_server(move || { HttpService::build() .h1(|_| future::ok::<_, Error>(Response::Ok().finish())) .rustls(ssl_acceptor()) @@ -54,7 +54,7 @@ async fn test_h1() -> io::Result<()> { #[actix_rt::test] async fn test_h2() -> io::Result<()> { - let srv = TestServer::start(move || { + let srv = test_server(move || { HttpService::build() .h2(|_| future::ok::<_, Error>(Response::Ok().finish())) .rustls(ssl_acceptor()) @@ -67,7 +67,7 @@ async fn test_h2() -> io::Result<()> { #[actix_rt::test] async fn test_h1_1() -> io::Result<()> { - let srv = TestServer::start(move || { + let srv = test_server(move || { HttpService::build() .h1(|req: Request| { assert!(req.peer_addr().is_some()); @@ -84,7 +84,7 @@ async fn test_h1_1() -> io::Result<()> { #[actix_rt::test] async fn test_h2_1() -> io::Result<()> { - let srv = TestServer::start(move || { + let srv = test_server(move || { HttpService::build() .finish(|req: Request| { assert!(req.peer_addr().is_some()); @@ -102,7 +102,7 @@ async fn test_h2_1() -> io::Result<()> { #[actix_rt::test] async fn test_h2_body1() -> io::Result<()> { let data = "HELLOWORLD".to_owned().repeat(64 * 1024); - let mut srv = TestServer::start(move || { + let mut srv = test_server(move || { HttpService::build() .h2(|mut req: Request<_>| { async move { @@ -123,7 +123,7 @@ async fn test_h2_body1() -> io::Result<()> { #[actix_rt::test] async fn test_h2_content_length() { - let srv = TestServer::start(move || { + let srv = test_server(move || { HttpService::build() .h2(|req: Request| { let indx: usize = req.uri().path()[1..].parse().unwrap(); @@ -172,7 +172,7 @@ async fn test_h2_headers() { let data = STR.repeat(10); let data2 = data.clone(); - let mut srv = TestServer::start(move || { + let mut srv = test_server(move || { let data = data.clone(); HttpService::build().h2(move |_| { let mut config = Response::Ok(); @@ -231,7 +231,7 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[actix_rt::test] async fn test_h2_body2() { - let mut srv = TestServer::start(move || { + let mut srv = test_server(move || { HttpService::build() .h2(|_| future::ok::<_, ()>(Response::Ok().body(STR))) .rustls(ssl_acceptor()) @@ -247,7 +247,7 @@ async fn test_h2_body2() { #[actix_rt::test] async fn test_h2_head_empty() { - let mut srv = TestServer::start(move || { + let mut srv = test_server(move || { HttpService::build() .finish(|_| ok::<_, ()>(Response::Ok().body(STR))) .rustls(ssl_acceptor()) @@ -272,7 +272,7 @@ async fn test_h2_head_empty() { #[actix_rt::test] async fn test_h2_head_binary() { - let mut srv = TestServer::start(move || { + let mut srv = test_server(move || { HttpService::build() .h2(|_| { ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) @@ -298,7 +298,7 @@ async fn test_h2_head_binary() { #[actix_rt::test] async fn test_h2_head_binary2() { - let srv = TestServer::start(move || { + let srv = test_server(move || { HttpService::build() .h2(|_| ok::<_, ()>(Response::Ok().body(STR))) .rustls(ssl_acceptor()) @@ -318,7 +318,7 @@ async fn test_h2_head_binary2() { #[actix_rt::test] async fn test_h2_body_length() { - let mut srv = TestServer::start(move || { + let mut srv = test_server(move || { HttpService::build() .h2(|_| { let body = once(ok(Bytes::from_static(STR.as_ref()))); @@ -339,7 +339,7 @@ async fn test_h2_body_length() { #[actix_rt::test] async fn test_h2_body_chunked_explicit() { - let mut srv = TestServer::start(move || { + let mut srv = test_server(move || { HttpService::build() .h2(|_| { let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); @@ -365,7 +365,7 @@ async fn test_h2_body_chunked_explicit() { #[actix_rt::test] async fn test_h2_response_http_error_handling() { - let mut srv = TestServer::start(move || { + let mut srv = test_server(move || { HttpService::build() .h2(fn_factory_with_config(|_: ()| { ok::<_, ()>(fn_service(|_| { @@ -390,7 +390,7 @@ async fn test_h2_response_http_error_handling() { #[actix_rt::test] async fn test_h2_service_error() { - let mut srv = TestServer::start(move || { + let mut srv = test_server(move || { HttpService::build() .h2(|_| err::(error::ErrorBadRequest("error"))) .rustls(ssl_acceptor()) @@ -406,7 +406,7 @@ async fn test_h2_service_error() { #[actix_rt::test] async fn test_h1_service_error() { - let mut srv = TestServer::start(move || { + let mut srv = test_server(move || { HttpService::build() .h1(|_| err::(error::ErrorBadRequest("error"))) .rustls(ssl_acceptor()) diff --git a/actix-http/tests/test_server.rs b/actix-http/tests/test_server.rs index fc51a103d..a84692f9d 100644 --- a/actix-http/tests/test_server.rs +++ b/actix-http/tests/test_server.rs @@ -2,7 +2,7 @@ use std::io::{Read, Write}; use std::time::Duration; use std::{net, thread}; -use actix_http_test::TestServer; +use actix_http_test::test_server; use actix_rt::time::delay_for; use actix_service::fn_service; use bytes::Bytes; @@ -17,7 +17,7 @@ use actix_http::{ #[actix_rt::test] async fn test_h1() { - let srv = TestServer::start(|| { + let srv = test_server(|| { HttpService::build() .keep_alive(KeepAlive::Disabled) .client_timeout(1000) @@ -35,7 +35,7 @@ async fn test_h1() { #[actix_rt::test] async fn test_h1_2() { - let srv = TestServer::start(|| { + let srv = test_server(|| { HttpService::build() .keep_alive(KeepAlive::Disabled) .client_timeout(1000) @@ -54,7 +54,7 @@ async fn test_h1_2() { #[actix_rt::test] async fn test_expect_continue() { - let srv = TestServer::start(|| { + let srv = test_server(|| { HttpService::build() .expect(fn_service(|req: Request| { if req.head().uri.query() == Some("yes=") { @@ -82,7 +82,7 @@ async fn test_expect_continue() { #[actix_rt::test] async fn test_expect_continue_h1() { - let srv = TestServer::start(|| { + let srv = test_server(|| { HttpService::build() .expect(fn_service(|req: Request| { delay_for(Duration::from_millis(20)).then(move |_| { @@ -115,7 +115,7 @@ async fn test_chunked_payload() { let chunk_sizes = vec![32768, 32, 32768]; let total_size: usize = chunk_sizes.iter().sum(); - let srv = TestServer::start(|| { + let srv = test_server(|| { HttpService::build() .h1(fn_service(|mut request: Request| { request @@ -167,7 +167,7 @@ async fn test_chunked_payload() { #[actix_rt::test] async fn test_slow_request() { - let srv = TestServer::start(|| { + let srv = test_server(|| { HttpService::build() .client_timeout(100) .finish(|_| future::ok::<_, ()>(Response::Ok().finish())) @@ -183,7 +183,7 @@ async fn test_slow_request() { #[actix_rt::test] async fn test_http1_malformed_request() { - let srv = TestServer::start(|| { + let srv = test_server(|| { HttpService::build() .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) .tcp() @@ -198,7 +198,7 @@ async fn test_http1_malformed_request() { #[actix_rt::test] async fn test_http1_keepalive() { - let srv = TestServer::start(|| { + let srv = test_server(|| { HttpService::build() .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) .tcp() @@ -218,7 +218,7 @@ async fn test_http1_keepalive() { #[actix_rt::test] async fn test_http1_keepalive_timeout() { - let srv = TestServer::start(|| { + let srv = test_server(|| { HttpService::build() .keep_alive(1) .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) @@ -239,7 +239,7 @@ async fn test_http1_keepalive_timeout() { #[actix_rt::test] async fn test_http1_keepalive_close() { - let srv = TestServer::start(|| { + let srv = test_server(|| { HttpService::build() .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) .tcp() @@ -259,7 +259,7 @@ async fn test_http1_keepalive_close() { #[actix_rt::test] async fn test_http10_keepalive_default_close() { - let srv = TestServer::start(|| { + let srv = test_server(|| { HttpService::build() .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) .tcp() @@ -278,7 +278,7 @@ async fn test_http10_keepalive_default_close() { #[actix_rt::test] async fn test_http10_keepalive() { - let srv = TestServer::start(|| { + let srv = test_server(|| { HttpService::build() .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) .tcp() @@ -304,7 +304,7 @@ async fn test_http10_keepalive() { #[actix_rt::test] async fn test_http1_keepalive_disabled() { - let srv = TestServer::start(|| { + let srv = test_server(|| { HttpService::build() .keep_alive(KeepAlive::Disabled) .h1(|_| future::ok::<_, ()>(Response::Ok().finish())) @@ -329,7 +329,7 @@ async fn test_content_length() { StatusCode, }; - let srv = TestServer::start(|| { + let srv = test_server(|| { HttpService::build() .h1(|req: Request| { let indx: usize = req.uri().path()[1..].parse().unwrap(); @@ -373,7 +373,7 @@ async fn test_h1_headers() { let data = STR.repeat(10); let data2 = data.clone(); - let mut srv = TestServer::start(move || { + let mut srv = test_server(move || { let data = data.clone(); HttpService::build().h1(move |_| { let mut builder = Response::Ok(); @@ -431,7 +431,7 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[actix_rt::test] async fn test_h1_body() { - let mut srv = TestServer::start(|| { + let mut srv = test_server(|| { HttpService::build() .h1(|_| ok::<_, ()>(Response::Ok().body(STR))) .tcp() @@ -447,7 +447,7 @@ async fn test_h1_body() { #[actix_rt::test] async fn test_h1_head_empty() { - let mut srv = TestServer::start(|| { + let mut srv = test_server(|| { HttpService::build() .h1(|_| ok::<_, ()>(Response::Ok().body(STR))) .tcp() @@ -471,7 +471,7 @@ async fn test_h1_head_empty() { #[actix_rt::test] async fn test_h1_head_binary() { - let mut srv = TestServer::start(|| { + let mut srv = test_server(|| { HttpService::build() .h1(|_| { ok::<_, ()>(Response::Ok().content_length(STR.len() as u64).body(STR)) @@ -497,7 +497,7 @@ async fn test_h1_head_binary() { #[actix_rt::test] async fn test_h1_head_binary2() { - let srv = TestServer::start(|| { + let srv = test_server(|| { HttpService::build() .h1(|_| ok::<_, ()>(Response::Ok().body(STR))) .tcp() @@ -517,7 +517,7 @@ async fn test_h1_head_binary2() { #[actix_rt::test] async fn test_h1_body_length() { - let mut srv = TestServer::start(|| { + let mut srv = test_server(|| { HttpService::build() .h1(|_| { let body = once(ok(Bytes::from_static(STR.as_ref()))); @@ -538,7 +538,7 @@ async fn test_h1_body_length() { #[actix_rt::test] async fn test_h1_body_chunked_explicit() { - let mut srv = TestServer::start(|| { + let mut srv = test_server(|| { HttpService::build() .h1(|_| { let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); @@ -572,7 +572,7 @@ async fn test_h1_body_chunked_explicit() { #[actix_rt::test] async fn test_h1_body_chunked_implicit() { - let mut srv = TestServer::start(|| { + let mut srv = test_server(|| { HttpService::build() .h1(|_| { let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); @@ -600,7 +600,7 @@ async fn test_h1_body_chunked_implicit() { #[actix_rt::test] async fn test_h1_response_http_error_handling() { - let mut srv = TestServer::start(|| { + let mut srv = test_server(|| { HttpService::build() .h1(fn_service(|_| { let broken_header = Bytes::from_static(b"\0\0\0"); @@ -623,7 +623,7 @@ async fn test_h1_response_http_error_handling() { #[actix_rt::test] async fn test_h1_service_error() { - let mut srv = TestServer::start(|| { + let mut srv = test_server(|| { HttpService::build() .h1(|_| future::err::(error::ErrorBadRequest("error"))) .tcp() @@ -639,7 +639,7 @@ async fn test_h1_service_error() { #[actix_rt::test] async fn test_h1_on_connect() { - let srv = TestServer::start(|| { + let srv = test_server(|| { HttpService::build() .on_connect(|_| 10usize) .h1(|req: Request| { diff --git a/actix-http/tests/test_ws.rs b/actix-http/tests/test_ws.rs index 5d70d24ae..2c1d6cdc1 100644 --- a/actix-http/tests/test_ws.rs +++ b/actix-http/tests/test_ws.rs @@ -1,6 +1,6 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_http::{body, h1, ws, Error, HttpService, Request, Response}; -use actix_http_test::TestServer; +use actix_http_test::test_server; use actix_utils::framed::Dispatcher; use bytes::Bytes; use futures::future; @@ -37,7 +37,7 @@ async fn service(msg: ws::Frame) -> Result { #[actix_rt::test] async fn test_simple() { - let mut srv = TestServer::start(|| { + let mut srv = test_server(|| { HttpService::build() .upgrade(actix_service::fn_service(ws_service)) .finish(|_| future::ok::<_, ()>(Response::NotFound())) diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 3a1d617f7..71facfe9d 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web-codegen" -version = "0.2.0-alpha.2" +version = "0.2.0" description = "Actix web proc macros" readme = "README.md" authors = ["Nikolay Kim "] @@ -17,8 +17,6 @@ syn = { version = "^1", features = ["full", "parsing"] } proc-macro2 = "^1" [dev-dependencies] -actix-rt = { version = "1.0.0-alpha.2" } -actix-web = { version = "2.0.0-alpha.2" } -actix-http = { version = "1.0.0-alpha.3", features=["openssl"] } -actix-http-test = { version = "1.0.0-alpha.3", features=["openssl"] } +actix-rt = { version = "1.0.0" } +actix-web = { version = "2.0.0-alpha.4" } futures = { version = "0.3.1" } diff --git a/actix-web-codegen/tests/test_macro.rs b/actix-web-codegen/tests/test_macro.rs index c4f2d7e89..4ac1a8023 100644 --- a/actix-web-codegen/tests/test_macro.rs +++ b/actix-web-codegen/tests/test_macro.rs @@ -1,11 +1,9 @@ -use actix_http::HttpService; -use actix_http_test::TestServer; -use actix_web::{http, web::Path, App, HttpResponse, Responder}; +use actix_web::{http, test, web::Path, App, HttpResponse, Responder}; use actix_web_codegen::{connect, delete, get, head, options, patch, post, put, trace}; use futures::{future, Future}; #[get("/test")] -async fn test() -> impl Responder { +async fn test_handler() -> impl Responder { HttpResponse::Ok() } @@ -71,14 +69,11 @@ async fn get_param_test(_: Path) -> impl Responder { #[actix_rt::test] async fn test_params() { - let srv = TestServer::start(|| { - HttpService::new( - App::new() - .service(get_param_test) - .service(put_param_test) - .service(delete_param_test), - ) - .tcp() + let srv = test::start(|| { + App::new() + .service(get_param_test) + .service(put_param_test) + .service(delete_param_test) }); let request = srv.request(http::Method::GET, srv.url("/test/it")); @@ -96,19 +91,16 @@ async fn test_params() { #[actix_rt::test] async fn test_body() { - let srv = TestServer::start(|| { - HttpService::new( - App::new() - .service(post_test) - .service(put_test) - .service(head_test) - .service(connect_test) - .service(options_test) - .service(trace_test) - .service(patch_test) - .service(test), - ) - .tcp() + let srv = test::start(|| { + App::new() + .service(post_test) + .service(put_test) + .service(head_test) + .service(connect_test) + .service(options_test) + .service(trace_test) + .service(patch_test) + .service(test_handler) }); let request = srv.request(http::Method::GET, srv.url("/test")); let response = request.send().await.unwrap(); @@ -151,8 +143,7 @@ async fn test_body() { #[actix_rt::test] async fn test_auto_async() { - let srv = - TestServer::start(|| HttpService::new(App::new().service(auto_async)).tcp()); + let srv = test::start(|| App::new().service(auto_async)); let request = srv.request(http::Method::GET, srv.url("/test")); let response = request.send().await.unwrap(); diff --git a/awc/tests/test_client.rs b/awc/tests/test_client.rs index a797e0725..73a4696de 100644 --- a/awc/tests/test_client.rs +++ b/awc/tests/test_client.rs @@ -13,11 +13,13 @@ use futures::future::ok; use rand::Rng; use actix_http::HttpService; -use actix_http_test::TestServer; +use actix_http_test::test_server; use actix_service::pipeline_factory; use actix_web::http::Cookie; use actix_web::middleware::{BodyEncoding, Compress}; -use actix_web::{http::header, web, App, Error, HttpMessage, HttpRequest, HttpResponse}; +use actix_web::{ + http::header, test, web, App, Error, HttpMessage, HttpRequest, HttpResponse, +}; use awc::error::SendRequestError; const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ @@ -44,13 +46,10 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ #[actix_rt::test] async fn test_simple() { - let srv = - TestServer::start(|| { - HttpService::new(App::new().service( - web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))), - )) - .tcp() - }); + let srv = test::start(|| { + App::new() + .service(web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR)))) + }); let request = srv.get("/").header("x-test", "111").send(); let mut response = request.await.unwrap(); @@ -74,11 +73,10 @@ async fn test_simple() { #[actix_rt::test] async fn test_json() { - let srv = TestServer::start(|| { - HttpService::new(App::new().service( + let srv = test::start(|| { + App::new().service( web::resource("/").route(web::to(|_: web::Json| HttpResponse::Ok())), - )) - .tcp() + ) }); let request = srv @@ -91,11 +89,10 @@ async fn test_json() { #[actix_rt::test] async fn test_form() { - let srv = TestServer::start(|| { - HttpService::new(App::new().service(web::resource("/").route(web::to( + let srv = test::start(|| { + App::new().service(web::resource("/").route(web::to( |_: web::Form>| HttpResponse::Ok(), - )))) - .tcp() + ))) }); let mut data = HashMap::new(); @@ -108,14 +105,13 @@ async fn test_form() { #[actix_rt::test] async fn test_timeout() { - let srv = TestServer::start(|| { - HttpService::new(App::new().service(web::resource("/").route(web::to(|| { + let srv = test::start(|| { + App::new().service(web::resource("/").route(web::to(|| { async { actix_rt::time::delay_for(Duration::from_millis(200)).await; Ok::<_, Error>(HttpResponse::Ok().body(STR)) } - })))) - .tcp() + }))) }); let connector = awc::Connector::new() @@ -139,14 +135,13 @@ async fn test_timeout() { #[actix_rt::test] async fn test_timeout_override() { - let srv = TestServer::start(|| { - HttpService::new(App::new().service(web::resource("/").route(web::to(|| { + let srv = test::start(|| { + App::new().service(web::resource("/").route(web::to(|| { async { actix_rt::time::delay_for(Duration::from_millis(200)).await; Ok::<_, Error>(HttpResponse::Ok().body(STR)) } - })))) - .tcp() + }))) }); let client = awc::Client::build() @@ -167,7 +162,7 @@ async fn test_connection_reuse() { let num = Arc::new(AtomicUsize::new(0)); let num2 = num.clone(); - let srv = TestServer::start(move || { + let srv = test_server(move || { let num2 = num2.clone(); pipeline_factory(move |io| { num2.fetch_add(1, Ordering::Relaxed); @@ -203,7 +198,7 @@ async fn test_connection_force_close() { let num = Arc::new(AtomicUsize::new(0)); let num2 = num.clone(); - let srv = TestServer::start(move || { + let srv = test_server(move || { let num2 = num2.clone(); pipeline_factory(move |io| { num2.fetch_add(1, Ordering::Relaxed); @@ -239,7 +234,7 @@ async fn test_connection_server_close() { let num = Arc::new(AtomicUsize::new(0)); let num2 = num.clone(); - let srv = TestServer::start(move || { + let srv = test_server(move || { let num2 = num2.clone(); pipeline_factory(move |io| { num2.fetch_add(1, Ordering::Relaxed); @@ -277,7 +272,7 @@ async fn test_connection_wait_queue() { let num = Arc::new(AtomicUsize::new(0)); let num2 = num.clone(); - let srv = TestServer::start(move || { + let srv = test_server(move || { let num2 = num2.clone(); pipeline_factory(move |io| { num2.fetch_add(1, Ordering::Relaxed); @@ -321,7 +316,7 @@ async fn test_connection_wait_queue_force_close() { let num = Arc::new(AtomicUsize::new(0)); let num2 = num.clone(); - let srv = TestServer::start(move || { + let srv = test_server(move || { let num2 = num2.clone(); pipeline_factory(move |io| { num2.fetch_add(1, Ordering::Relaxed); @@ -365,17 +360,14 @@ async fn test_connection_wait_queue_force_close() { #[actix_rt::test] async fn test_with_query_parameter() { - let srv = TestServer::start(|| { - HttpService::new(App::new().service(web::resource("/").to( - |req: HttpRequest| { - if req.query_string().contains("qp") { - HttpResponse::Ok() - } else { - HttpResponse::BadRequest() - } - }, - ))) - .tcp() + let srv = test::start(|| { + App::new().service(web::resource("/").to(|req: HttpRequest| { + if req.query_string().contains("qp") { + HttpResponse::Ok() + } else { + HttpResponse::BadRequest() + } + })) }); let res = awc::Client::new() @@ -388,15 +380,14 @@ async fn test_with_query_parameter() { #[actix_rt::test] async fn test_no_decompress() { - let srv = TestServer::start(|| { - HttpService::new(App::new().wrap(Compress::default()).service( - web::resource("/").route(web::to(|| { + let srv = test::start(|| { + App::new() + .wrap(Compress::default()) + .service(web::resource("/").route(web::to(|| { let mut res = HttpResponse::Ok().body(STR); res.encoding(header::ContentEncoding::Gzip); res - })), - )) - .tcp() + }))) }); let mut res = awc::Client::new() @@ -433,8 +424,8 @@ async fn test_no_decompress() { #[actix_rt::test] async fn test_client_gzip_encoding() { - let srv = TestServer::start(|| { - HttpService::new(App::new().service(web::resource("/").route(web::to(|| { + let srv = test::start(|| { + App::new().service(web::resource("/").route(web::to(|| { let mut e = GzEncoder::new(Vec::new(), Compression::default()); e.write_all(STR.as_ref()).unwrap(); let data = e.finish().unwrap(); @@ -442,8 +433,7 @@ async fn test_client_gzip_encoding() { HttpResponse::Ok() .header("content-encoding", "gzip") .body(data) - })))) - .tcp() + }))) }); // client request @@ -457,8 +447,8 @@ async fn test_client_gzip_encoding() { #[actix_rt::test] async fn test_client_gzip_encoding_large() { - let srv = TestServer::start(|| { - HttpService::new(App::new().service(web::resource("/").route(web::to(|| { + let srv = test::start(|| { + App::new().service(web::resource("/").route(web::to(|| { let mut e = GzEncoder::new(Vec::new(), Compression::default()); e.write_all(STR.repeat(10).as_ref()).unwrap(); let data = e.finish().unwrap(); @@ -466,8 +456,7 @@ async fn test_client_gzip_encoding_large() { HttpResponse::Ok() .header("content-encoding", "gzip") .body(data) - })))) - .tcp() + }))) }); // client request @@ -486,18 +475,15 @@ async fn test_client_gzip_encoding_large_random() { .take(100_000) .collect::(); - let srv = TestServer::start(|| { - HttpService::new(App::new().service(web::resource("/").route(web::to( - |data: Bytes| { - let mut e = GzEncoder::new(Vec::new(), Compression::default()); - e.write_all(&data).unwrap(); - let data = e.finish().unwrap(); - HttpResponse::Ok() - .header("content-encoding", "gzip") - .body(data) - }, - )))) - .tcp() + let srv = test::start(|| { + App::new().service(web::resource("/").route(web::to(|data: Bytes| { + let mut e = GzEncoder::new(Vec::new(), Compression::default()); + e.write_all(&data).unwrap(); + let data = e.finish().unwrap(); + HttpResponse::Ok() + .header("content-encoding", "gzip") + .body(data) + }))) }); // client request @@ -511,18 +497,15 @@ async fn test_client_gzip_encoding_large_random() { #[actix_rt::test] async fn test_client_brotli_encoding() { - let srv = TestServer::start(|| { - HttpService::new(App::new().service(web::resource("/").route(web::to( - |data: Bytes| { - let mut e = CompressorWriter::new(Vec::new(), 0, 5, 0); - e.write_all(&data).unwrap(); - let data = e.into_inner(); - HttpResponse::Ok() - .header("content-encoding", "br") - .body(data) - }, - )))) - .tcp() + let srv = test::start(|| { + App::new().service(web::resource("/").route(web::to(|data: Bytes| { + let mut e = CompressorWriter::new(Vec::new(), 0, 5, 0); + e.write_all(&data).unwrap(); + let data = e.into_inner(); + HttpResponse::Ok() + .header("content-encoding", "br") + .body(data) + }))) }); // client request @@ -541,18 +524,15 @@ async fn test_client_brotli_encoding_large_random() { .take(70_000) .collect::(); - let srv = TestServer::start(|| { - HttpService::new(App::new().service(web::resource("/").route(web::to( - |data: Bytes| { - let mut e = CompressorWriter::new(Vec::new(), 0, 5, 0); - e.write_all(&data).unwrap(); - let data = e.into_inner(); - HttpResponse::Ok() - .header("content-encoding", "br") - .body(data) - }, - )))) - .tcp() + let srv = test::start(|| { + App::new().service(web::resource("/").route(web::to(|data: Bytes| { + let mut e = CompressorWriter::new(Vec::new(), 0, 5, 0); + e.write_all(&data).unwrap(); + let data = e.into_inner(); + HttpResponse::Ok() + .header("content-encoding", "br") + .body(data) + }))) }); // client request @@ -688,11 +668,11 @@ async fn test_client_cookie_handling() { let cookie1b = cookie1.clone(); let cookie2b = cookie2.clone(); - let srv = TestServer::start(move || { + let srv = test::start(move || { let cookie1 = cookie1b.clone(); let cookie2 = cookie2b.clone(); - HttpService::new(App::new().route( + App::new().route( "/", web::to(move |req: HttpRequest| { let cookie1 = cookie1.clone(); @@ -730,8 +710,7 @@ async fn test_client_cookie_handling() { } } }), - )) - .tcp() + ) }); let request = srv.get("/").cookie(cookie1.clone()).cookie(cookie2.clone()); @@ -775,8 +754,8 @@ async fn test_client_cookie_handling() { #[actix_rt::test] async fn client_basic_auth() { - let srv = TestServer::start(|| { - HttpService::new(App::new().route( + let srv = test::start(|| { + App::new().route( "/", web::to(|req: HttpRequest| { if req @@ -792,8 +771,7 @@ async fn client_basic_auth() { HttpResponse::BadRequest() } }), - )) - .tcp() + ) }); // set authorization header to Basic @@ -804,8 +782,8 @@ async fn client_basic_auth() { #[actix_rt::test] async fn client_bearer_auth() { - let srv = TestServer::start(|| { - HttpService::new(App::new().route( + let srv = test::start(|| { + App::new().route( "/", web::to(|req: HttpRequest| { if req @@ -821,8 +799,7 @@ async fn client_bearer_auth() { HttpResponse::BadRequest() } }), - )) - .tcp() + ) }); // set authorization header to Bearer diff --git a/awc/tests/test_rustls_client.rs b/awc/tests/test_rustls_client.rs index a6ced89d3..46db518aa 100644 --- a/awc/tests/test_rustls_client.rs +++ b/awc/tests/test_rustls_client.rs @@ -3,7 +3,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use actix_http::HttpService; -use actix_http_test::TestServer; +use actix_http_test::test_server; use actix_service::{pipeline_factory, ServiceFactory}; use actix_web::http::Version; use actix_web::{web, App, HttpResponse}; @@ -54,7 +54,7 @@ async fn _test_connection_reuse_h2() { let num = Arc::new(AtomicUsize::new(0)); let num2 = num.clone(); - let srv = TestServer::start(move || { + let srv = test_server(move || { let num2 = num2.clone(); pipeline_factory(move |io| { num2.fetch_add(1, Ordering::Relaxed); diff --git a/awc/tests/test_ssl_client.rs b/awc/tests/test_ssl_client.rs index a9a7fa2fb..d36e303fa 100644 --- a/awc/tests/test_ssl_client.rs +++ b/awc/tests/test_ssl_client.rs @@ -3,7 +3,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use actix_http::HttpService; -use actix_http_test::TestServer; +use actix_http_test::test_server; use actix_service::{pipeline_factory, ServiceFactory}; use actix_web::http::Version; use actix_web::{web, App, HttpResponse}; @@ -36,7 +36,7 @@ async fn test_connection_reuse_h2() { let num = Arc::new(AtomicUsize::new(0)); let num2 = num.clone(); - let srv = TestServer::start(move || { + let srv = test_server(move || { let num2 = num2.clone(); pipeline_factory(move |io| { num2.fetch_add(1, Ordering::Relaxed); diff --git a/awc/tests/test_ws.rs b/awc/tests/test_ws.rs index 6f1dcded5..ee937e43e 100644 --- a/awc/tests/test_ws.rs +++ b/awc/tests/test_ws.rs @@ -2,7 +2,7 @@ use std::io; use actix_codec::Framed; use actix_http::{body::BodySize, h1, ws, Error, HttpService, Request, Response}; -use actix_http_test::TestServer; +use actix_http_test::test_server; use bytes::Bytes; use futures::future::ok; use futures::{SinkExt, StreamExt}; @@ -21,7 +21,7 @@ async fn ws_service(req: ws::Frame) -> Result { #[actix_rt::test] async fn test_simple() { - let mut srv = TestServer::start(|| { + let mut srv = test_server(|| { HttpService::build() .upgrade(|(req, mut framed): (Request, Framed<_, _>)| { async move { diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 434262de3..cc60259e6 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http-test" -version = "1.0.0-alpha.3" +version = "1.0.0" authors = ["Nikolay Kim "] description = "Actix http test server" readme = "README.md" diff --git a/test-server/src/lib.rs b/test-server/src/lib.rs index a28811486..ff564c3e1 100644 --- a/test-server/src/lib.rs +++ b/test-server/src/lib.rs @@ -13,7 +13,7 @@ use net2::TcpBuilder; pub use actix_testing::*; -/// The `TestServer` type. +/// Start test server /// /// `TestServer` is very simple test server that simplify process of writing /// integration tests cases for actix web applications. @@ -43,88 +43,82 @@ pub use actix_testing::*; /// assert!(response.status().is_success()); /// } /// ``` -pub struct TestServer; +pub fn test_server>(factory: F) -> TestServer { + let (tx, rx) = mpsc::channel(); + + // run server in separate thread + thread::spawn(move || { + let sys = System::new("actix-test-server"); + let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap(); + let local_addr = tcp.local_addr().unwrap(); + + Server::build() + .listen("test", tcp, factory)? + .workers(1) + .disable_signals() + .start(); + + tx.send((System::current(), local_addr)).unwrap(); + sys.run() + }); + + let (system, addr) = rx.recv().unwrap(); + + let client = { + let connector = { + #[cfg(feature = "openssl")] + { + use open_ssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; + + let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); + builder.set_verify(SslVerifyMode::NONE); + let _ = builder + .set_alpn_protos(b"\x02h2\x08http/1.1") + .map_err(|e| log::error!("Can not set alpn protocol: {:?}", e)); + Connector::new() + .conn_lifetime(time::Duration::from_secs(0)) + .timeout(time::Duration::from_millis(3000)) + .ssl(builder.build()) + .finish() + } + #[cfg(not(feature = "openssl"))] + { + Connector::new() + .conn_lifetime(time::Duration::from_secs(0)) + .timeout(time::Duration::from_millis(3000)) + .finish() + } + }; + + Client::build().connector(connector).finish() + }; + actix_connect::start_default_resolver(); + + TestServer { + addr, + client, + system, + } +} + +/// Get first available unused address +pub fn unused_addr() -> net::SocketAddr { + let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); + let socket = TcpBuilder::new_v4().unwrap(); + socket.bind(&addr).unwrap(); + socket.reuse_address(true).unwrap(); + let tcp = socket.to_tcp_listener().unwrap(); + tcp.local_addr().unwrap() +} /// Test server controller -pub struct TestServerRuntime { +pub struct TestServer { addr: net::SocketAddr, client: Client, system: System, } impl TestServer { - #[allow(clippy::new_ret_no_self)] - /// Start new test server with application factory - pub fn start>(factory: F) -> TestServerRuntime { - let (tx, rx) = mpsc::channel(); - - // run server in separate thread - thread::spawn(move || { - let sys = System::new("actix-test-server"); - let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap(); - let local_addr = tcp.local_addr().unwrap(); - - Server::build() - .listen("test", tcp, factory)? - .workers(1) - .disable_signals() - .start(); - - tx.send((System::current(), local_addr)).unwrap(); - sys.run() - }); - - let (system, addr) = rx.recv().unwrap(); - - let client = { - let connector = { - #[cfg(feature = "openssl")] - { - use open_ssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; - - let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); - builder.set_verify(SslVerifyMode::NONE); - let _ = builder - .set_alpn_protos(b"\x02h2\x08http/1.1") - .map_err(|e| log::error!("Can not set alpn protocol: {:?}", e)); - Connector::new() - .conn_lifetime(time::Duration::from_secs(0)) - .timeout(time::Duration::from_millis(3000)) - .ssl(builder.build()) - .finish() - } - #[cfg(not(feature = "openssl"))] - { - Connector::new() - .conn_lifetime(time::Duration::from_secs(0)) - .timeout(time::Duration::from_millis(3000)) - .finish() - } - }; - - Client::build().connector(connector).finish() - }; - actix_connect::start_default_resolver(); - - TestServerRuntime { - addr, - client, - system, - } - } - - /// Get first available unused address - pub fn unused_addr() -> net::SocketAddr { - let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); - let socket = TcpBuilder::new_v4().unwrap(); - socket.bind(&addr).unwrap(); - socket.reuse_address(true).unwrap(); - let tcp = socket.to_tcp_listener().unwrap(); - tcp.local_addr().unwrap() - } -} - -impl TestServerRuntime { /// Construct test server url pub fn addr(&self) -> net::SocketAddr { self.addr @@ -258,7 +252,7 @@ impl TestServerRuntime { } } -impl Drop for TestServerRuntime { +impl Drop for TestServer { fn drop(&mut self) { self.stop() } From b81417c2fa980c7a2157796ca74b2dc68769b710 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 13 Dec 2019 10:59:02 +0600 Subject: [PATCH 124/176] fix warnings --- actix-http/src/h1/client.rs | 19 +++---------------- actix-http/src/h1/codec.rs | 15 ++++----------- actix-http/src/h1/encoder.rs | 19 ++++++------------- actix-http/src/h2/dispatcher.rs | 17 ++++++----------- actix-http/src/h2/mod.rs | 4 +--- actix-http/src/h2/service.rs | 18 +++++++----------- actix-http/src/lib.rs | 3 +-- 7 files changed, 28 insertions(+), 67 deletions(-) diff --git a/actix-http/src/h1/client.rs b/actix-http/src/h1/client.rs index bea629c4f..bcfc18cde 100644 --- a/actix-http/src/h1/client.rs +++ b/actix-http/src/h1/client.rs @@ -1,13 +1,8 @@ -#![allow(unused_imports, unused_variables, dead_code)] -use std::io::{self, Write}; -use std::rc::Rc; +use std::io; use actix_codec::{Decoder, Encoder}; use bitflags::bitflags; -use bytes::{BufMut, Bytes, BytesMut}; -use http::header::{ - HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING, UPGRADE, -}; +use bytes::{Bytes, BytesMut}; use http::{Method, Version}; use super::decoder::{PayloadDecoder, PayloadItem, PayloadType}; @@ -16,11 +11,7 @@ use super::{Message, MessageType}; use crate::body::BodySize; use crate::config::ServiceConfig; use crate::error::{ParseError, PayloadError}; -use crate::header::HeaderMap; -use crate::helpers; -use crate::message::{ - ConnectionType, Head, MessagePool, RequestHead, RequestHeadType, ResponseHead, -}; +use crate::message::{ConnectionType, RequestHeadType, ResponseHead}; bitflags! { struct Flags: u8 { @@ -30,8 +21,6 @@ bitflags! { } } -const AVERAGE_HEADER_SIZE: usize = 30; - /// HTTP/1 Codec pub struct ClientCodec { inner: ClientCodecInner, @@ -51,7 +40,6 @@ struct ClientCodecInner { // encoder part flags: Flags, - headers_size: u32, encoder: encoder::MessageEncoder, } @@ -80,7 +68,6 @@ impl ClientCodec { ctype: ConnectionType::Close, flags, - headers_size: 0, encoder: encoder::MessageEncoder::default(), }, } diff --git a/actix-http/src/h1/codec.rs b/actix-http/src/h1/codec.rs index 726d1c97f..5b75a4e56 100644 --- a/actix-http/src/h1/codec.rs +++ b/actix-http/src/h1/codec.rs @@ -1,12 +1,9 @@ -#![allow(unused_imports, unused_variables, dead_code)] -use std::io::Write; -use std::{fmt, io, net}; +use std::{fmt, io}; use actix_codec::{Decoder, Encoder}; use bitflags::bitflags; -use bytes::{BufMut, Bytes, BytesMut}; -use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING}; -use http::{Method, StatusCode, Version}; +use bytes::BytesMut; +use http::{Method, Version}; use super::decoder::{PayloadDecoder, PayloadItem, PayloadType}; use super::{decoder, encoder}; @@ -14,8 +11,7 @@ use super::{Message, MessageType}; use crate::body::BodySize; use crate::config::ServiceConfig; use crate::error::ParseError; -use crate::helpers; -use crate::message::{ConnectionType, Head, ResponseHead}; +use crate::message::ConnectionType; use crate::request::Request; use crate::response::Response; @@ -27,8 +23,6 @@ bitflags! { } } -const AVERAGE_HEADER_SIZE: usize = 30; - /// HTTP/1 Codec pub struct Codec { config: ServiceConfig, @@ -176,7 +170,6 @@ impl Encoder for Codec { }; // encode message - let len = dst.len(); self.encoder.encode( dst, &mut res, diff --git a/actix-http/src/h1/encoder.rs b/actix-http/src/h1/encoder.rs index e474d4f81..c3426c6e0 100644 --- a/actix-http/src/h1/encoder.rs +++ b/actix-http/src/h1/encoder.rs @@ -1,25 +1,18 @@ -#![allow(unused_imports, unused_variables, dead_code)] -use std::fmt::Write as FmtWrite; use std::io::Write; use std::marker::PhantomData; use std::ptr::copy_nonoverlapping; -use std::rc::Rc; use std::slice::from_raw_parts_mut; -use std::str::FromStr; -use std::{cmp, fmt, io, mem}; +use std::{cmp, io}; -use bytes::{buf::BufMutExt, BufMut, Bytes, BytesMut}; +use bytes::{buf::BufMutExt, BufMut, BytesMut}; use crate::body::BodySize; use crate::config::ServiceConfig; -use crate::header::{map, ContentEncoding}; +use crate::header::map; use crate::helpers; -use crate::http::header::{ - HeaderValue, ACCEPT_ENCODING, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING, -}; -use crate::http::{HeaderMap, Method, StatusCode, Version}; -use crate::message::{ConnectionType, Head, RequestHead, RequestHeadType, ResponseHead}; -use crate::request::Request; +use crate::http::header::{CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING}; +use crate::http::{HeaderMap, StatusCode, Version}; +use crate::message::{ConnectionType, RequestHeadType}; use crate::response::Response; const AVERAGE_HEADER_SIZE: usize = 30; diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index b827762cb..a4ec15fab 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -1,28 +1,23 @@ -use std::collections::VecDeque; use std::convert::TryFrom; use std::future::Future; use std::marker::PhantomData; +use std::net; use std::pin::Pin; use std::task::{Context, Poll}; -use std::{fmt, mem, net}; use actix_codec::{AsyncRead, AsyncWrite}; use actix_rt::time::{Delay, Instant}; use actix_service::Service; -use bitflags::bitflags; use bytes::{Bytes, BytesMut}; -use futures::{ready, Sink, Stream}; use h2::server::{Connection, SendResponse}; -use h2::{RecvStream, SendStream}; -use http::header::{ - HeaderValue, ACCEPT_ENCODING, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING, -}; -use log::{debug, error, trace}; +use h2::SendStream; +use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING}; +use log::{error, trace}; -use crate::body::{Body, BodySize, MessageBody, ResponseBody}; +use crate::body::{BodySize, MessageBody, ResponseBody}; use crate::cloneable::CloneableService; use crate::config::ServiceConfig; -use crate::error::{DispatchError, Error, ParseError, PayloadError, ResponseError}; +use crate::error::{DispatchError, Error}; use crate::helpers::DataFactory; use crate::httpmessage::HttpMessage; use crate::message::ResponseHead; diff --git a/actix-http/src/h2/mod.rs b/actix-http/src/h2/mod.rs index 21080c69a..6ba29bbcf 100644 --- a/actix-http/src/h2/mod.rs +++ b/actix-http/src/h2/mod.rs @@ -1,6 +1,4 @@ -#![allow(dead_code, unused_imports)] -use std::fmt; -use std::future::Future; +//! HTTP/2 implementation use std::pin::Pin; use std::task::{Context, Poll}; diff --git a/actix-http/src/h2/service.rs b/actix-http/src/h2/service.rs index da2499345..91d721baf 100644 --- a/actix-http/src/h2/service.rs +++ b/actix-http/src/h2/service.rs @@ -1,32 +1,28 @@ -use std::fmt::Debug; use std::future::Future; use std::marker::PhantomData; use std::pin::Pin; use std::task::{Context, Poll}; -use std::{io, net, rc}; +use std::{net, rc}; -use actix_codec::{AsyncRead, AsyncWrite, Framed}; +use actix_codec::{AsyncRead, AsyncWrite}; use actix_rt::net::TcpStream; use actix_service::{ fn_factory, fn_service, pipeline_factory, IntoServiceFactory, Service, ServiceFactory, }; use bytes::Bytes; -use futures::future::{ok, Ready}; -use futures::{ready, Stream}; -use h2::server::{self, Connection, Handshake}; -use h2::RecvStream; +use futures::future::ok; +use futures::ready; +use h2::server::{self, Handshake}; use log::error; use crate::body::MessageBody; use crate::cloneable::CloneableService; -use crate::config::{KeepAlive, ServiceConfig}; -use crate::error::{DispatchError, Error, ParseError, ResponseError}; +use crate::config::ServiceConfig; +use crate::error::{DispatchError, Error}; use crate::helpers::DataFactory; -use crate::payload::Payload; use crate::request::Request; use crate::response::Response; -use crate::Protocol; use super::dispatcher::Dispatcher; diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs index b682e5aa5..7a47012f8 100644 --- a/actix-http/src/lib.rs +++ b/actix-http/src/lib.rs @@ -4,8 +4,7 @@ clippy::type_complexity, clippy::too_many_arguments, clippy::new_without_default, - clippy::borrow_interior_mutable_const, -// clippy::write_with_newline + clippy::borrow_interior_mutable_const )] #[macro_use] From c1deaaeb2f98a7dd6e3cbce81947c495f281fdfe Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 13 Dec 2019 11:24:57 +0600 Subject: [PATCH 125/176] cleanup imports --- actix-http/CHANGES.md | 2 +- actix-http/Cargo.toml | 5 +- actix-http/src/body.rs | 4 +- actix-http/src/client/connection.rs | 2 +- actix-http/src/client/connector.rs | 6 +-- actix-http/src/client/h1proto.rs | 5 +- actix-http/src/client/h2proto.rs | 2 +- actix-http/src/client/pool.rs | 2 +- actix-http/src/config.rs | 2 +- actix-http/src/encoding/decoder.rs | 2 +- actix-http/src/encoding/encoder.rs | 3 +- actix-http/src/error.rs | 2 +- actix-http/src/h1/codec.rs | 10 +--- actix-http/src/h1/dispatcher.rs | 2 +- actix-http/src/h1/encoder.rs | 6 ++- actix-http/src/h1/expect.rs | 2 +- actix-http/src/h1/payload.rs | 4 +- actix-http/src/h1/service.rs | 4 +- actix-http/src/h1/upgrade.rs | 2 +- actix-http/src/h2/mod.rs | 2 +- actix-http/src/h2/service.rs | 8 +-- actix-http/src/payload.rs | 2 +- actix-http/src/response.rs | 2 +- actix-http/src/service.rs | 3 +- awc/CHANGES.md | 4 ++ awc/Cargo.toml | 3 +- awc/src/connect.rs | 78 ++++++++++++++++++----------- awc/src/frozen.rs | 2 +- awc/src/request.rs | 2 +- awc/src/response.rs | 2 +- awc/src/sender.rs | 6 +-- 31 files changed, 104 insertions(+), 77 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 587abaf77..212ce6a15 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## [1.0.0] - 2019-12-xx +## [1.0.0] - 2019-12-13 ### Added diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 63e3977a5..c0e7419c2 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -56,7 +56,9 @@ chrono = "0.4.6" derive_more = "0.99.2" either = "1.5.3" encoding_rs = "0.8" -futures = "0.3.1" +futures-core = "0.3.1" +futures-util = "0.3.1" +futures-channel = "0.3.1" fxhash = "0.2.1" h2 = "0.2.1" http = "0.2.0" @@ -92,6 +94,7 @@ actix-server = "1.0.0" actix-connect = { version = "1.0.0", features=["openssl"] } actix-http-test = { version = "1.0.0-alpha.3", features=["openssl"] } actix-tls = { version = "1.0.0", features=["openssl"] } +futures = "0.3.1" env_logger = "0.6" serde_derive = "1.0" open-ssl = { version="0.10", package = "openssl" } diff --git a/actix-http/src/body.rs b/actix-http/src/body.rs index ecb12fc23..850f97ee4 100644 --- a/actix-http/src/body.rs +++ b/actix-http/src/body.rs @@ -4,7 +4,7 @@ use std::task::{Context, Poll}; use std::{fmt, mem}; use bytes::{Bytes, BytesMut}; -use futures::Stream; +use futures_core::Stream; use pin_project::{pin_project, project}; use crate::error::Error; @@ -435,7 +435,7 @@ where #[cfg(test)] mod tests { use super::*; - use futures::future::poll_fn; + use futures_util::future::poll_fn; impl Body { pub(crate) fn get_ref(&self) -> &[u8] { diff --git a/actix-http/src/client/connection.rs b/actix-http/src/client/connection.rs index 566769c5e..0ca788b32 100644 --- a/actix-http/src/client/connection.rs +++ b/actix-http/src/client/connection.rs @@ -4,7 +4,7 @@ use std::{fmt, io, mem, time}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; use bytes::{Buf, Bytes}; -use futures::future::{err, Either, Future, FutureExt, LocalBoxFuture, Ready}; +use futures_util::future::{err, Either, Future, FutureExt, LocalBoxFuture, Ready}; use h2::client::SendRequest; use pin_project::{pin_project, project}; diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index a06afa7b0..055d4276d 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -337,7 +337,7 @@ where mod connect_impl { use std::task::{Context, Poll}; - use futures::future::{err, Either, Ready}; + use futures_util::future::{err, Either, Ready}; use super::*; use crate::client::connection::IoConnection; @@ -400,8 +400,8 @@ mod connect_impl { use std::pin::Pin; use std::task::{Context, Poll}; - use futures::future::Either; - use futures::ready; + use futures_core::ready; + use futures_util::future::Either; use super::*; use crate::client::connection::EitherConnection; diff --git a/actix-http/src/client/h1proto.rs b/actix-http/src/client/h1proto.rs index db4dede71..a0a20edf6 100644 --- a/actix-http/src/client/h1proto.rs +++ b/actix-http/src/client/h1proto.rs @@ -6,8 +6,9 @@ use std::{io, mem, time}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; use bytes::buf::BufMutExt; use bytes::{Bytes, BytesMut}; -use futures::future::poll_fn; -use futures::{SinkExt, Stream, StreamExt}; +use futures_core::Stream; +use futures_util::future::poll_fn; +use futures_util::{SinkExt, StreamExt}; use crate::error::PayloadError; use crate::h1; diff --git a/actix-http/src/client/h2proto.rs b/actix-http/src/client/h2proto.rs index ff8f21d00..eabf54e97 100644 --- a/actix-http/src/client/h2proto.rs +++ b/actix-http/src/client/h2proto.rs @@ -3,7 +3,7 @@ use std::time; use actix_codec::{AsyncRead, AsyncWrite}; use bytes::Bytes; -use futures::future::poll_fn; +use futures_util::future::poll_fn; use h2::{client::SendRequest, SendStream}; use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, TRANSFER_ENCODING}; use http::{request::Request, Method, Version}; diff --git a/actix-http/src/client/pool.rs b/actix-http/src/client/pool.rs index 0346c0614..acf76559a 100644 --- a/actix-http/src/client/pool.rs +++ b/actix-http/src/client/pool.rs @@ -11,7 +11,7 @@ use actix_rt::time::{delay_for, Delay}; use actix_service::Service; use actix_utils::{oneshot, task::LocalWaker}; use bytes::Bytes; -use futures::future::{poll_fn, FutureExt, LocalBoxFuture}; +use futures_util::future::{poll_fn, FutureExt, LocalBoxFuture}; use fxhash::FxHashMap; use h2::client::{handshake, Connection, SendRequest}; use http::uri::Authority; diff --git a/actix-http/src/config.rs b/actix-http/src/config.rs index 77633bdc2..be949aaef 100644 --- a/actix-http/src/config.rs +++ b/actix-http/src/config.rs @@ -6,7 +6,7 @@ use std::{fmt, net}; use actix_rt::time::{delay_for, delay_until, Delay, Instant}; use bytes::BytesMut; -use futures::{future, FutureExt}; +use futures_util::{future, FutureExt}; use time; // "Sun, 06 Nov 1994 08:49:37 GMT".len() diff --git a/actix-http/src/encoding/decoder.rs b/actix-http/src/encoding/decoder.rs index 10635b3b3..cdc4699d5 100644 --- a/actix-http/src/encoding/decoder.rs +++ b/actix-http/src/encoding/decoder.rs @@ -7,7 +7,7 @@ use actix_threadpool::{run, CpuFuture}; use brotli::DecompressorWriter; use bytes::Bytes; use flate2::write::{GzDecoder, ZlibDecoder}; -use futures::{ready, Stream}; +use futures_core::{ready, Stream}; use super::Writer; use crate::error::PayloadError; diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index c58e1f434..6ec122fa0 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -8,6 +8,7 @@ use actix_threadpool::{run, CpuFuture}; use brotli::CompressorWriter; use bytes::Bytes; use flate2::write::{GzEncoder, ZlibEncoder}; +use futures_core::ready; use crate::body::{Body, BodySize, MessageBody, ResponseBody}; use crate::http::header::{ContentEncoding, CONTENT_ENCODING}; @@ -101,7 +102,7 @@ impl MessageBody for Encoder { } if let Some(ref mut fut) = self.fut { - let mut encoder = match futures::ready!(Pin::new(fut).poll(cx)) { + let mut encoder = match ready!(Pin::new(fut).poll(cx)) { Ok(item) => item, Err(e) => return Poll::Ready(Some(Err(e.into()))), }; diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index 512b14ca7..bb18184d8 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -10,7 +10,7 @@ pub use actix_threadpool::BlockingError; use actix_utils::timeout::TimeoutError; use bytes::BytesMut; use derive_more::{Display, From}; -pub use futures::channel::oneshot::Canceled; +pub use futures_channel::oneshot::Canceled; use http::uri::InvalidUri; use http::{header, Error as HttpError, StatusCode}; use httparse; diff --git a/actix-http/src/h1/codec.rs b/actix-http/src/h1/codec.rs index 5b75a4e56..de2af9ee7 100644 --- a/actix-http/src/h1/codec.rs +++ b/actix-http/src/h1/codec.rs @@ -195,17 +195,11 @@ impl Encoder for Codec { #[cfg(test)] mod tests { - use std::{cmp, io}; - - use actix_codec::{AsyncRead, AsyncWrite}; - use bytes::{Buf, Bytes, BytesMut}; - use http::{Method, Version}; + use bytes::BytesMut; + use http::Method; use super::*; - use crate::error::ParseError; - use crate::h1::Message; use crate::httpmessage::HttpMessage; - use crate::request::Request; #[test] fn test_http_request_chunked_payload_and_next_message() { diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 1147465be..6b37be683 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -912,7 +912,7 @@ where #[cfg(test)] mod tests { use actix_service::IntoService; - use futures::future::{lazy, ok}; + use futures_util::future::{lazy, ok}; use super::*; use crate::error::Error; diff --git a/actix-http/src/h1/encoder.rs b/actix-http/src/h1/encoder.rs index c3426c6e0..4689906b4 100644 --- a/actix-http/src/h1/encoder.rs +++ b/actix-http/src/h1/encoder.rs @@ -521,12 +521,14 @@ fn write_camel_case(value: &[u8], buffer: &mut [u8]) { #[cfg(test)] mod tests { + use std::rc::Rc; + use bytes::Bytes; - //use std::rc::Rc; + use http::header::AUTHORIZATION; use super::*; use crate::http::header::{HeaderValue, CONTENT_TYPE}; - use http::header::AUTHORIZATION; + use crate::RequestHead; #[test] fn test_chunked_te() { diff --git a/actix-http/src/h1/expect.rs b/actix-http/src/h1/expect.rs index 187999358..6c08df08e 100644 --- a/actix-http/src/h1/expect.rs +++ b/actix-http/src/h1/expect.rs @@ -1,7 +1,7 @@ use std::task::{Context, Poll}; use actix_service::{Service, ServiceFactory}; -use futures::future::{ok, Ready}; +use futures_util::future::{ok, Ready}; use crate::error::Error; use crate::request::Request; diff --git a/actix-http/src/h1/payload.rs b/actix-http/src/h1/payload.rs index abf42dc89..6a348810c 100644 --- a/actix-http/src/h1/payload.rs +++ b/actix-http/src/h1/payload.rs @@ -7,7 +7,7 @@ use std::task::{Context, Poll}; use actix_utils::task::LocalWaker; use bytes::Bytes; -use futures::Stream; +use futures_core::Stream; use crate::error::PayloadError; @@ -226,7 +226,7 @@ impl Inner { #[cfg(test)] mod tests { use super::*; - use futures::future::poll_fn; + use futures_util::future::poll_fn; #[actix_rt::test] async fn test_unread_data() { diff --git a/actix-http/src/h1/service.rs b/actix-http/src/h1/service.rs index 6d5123843..fb5514da3 100644 --- a/actix-http/src/h1/service.rs +++ b/actix-http/src/h1/service.rs @@ -8,8 +8,8 @@ use std::{fmt, net}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_rt::net::TcpStream; use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory}; -use futures::future::{ok, Ready}; -use futures::ready; +use futures_core::ready; +use futures_util::future::{ok, Ready}; use crate::body::MessageBody; use crate::cloneable::CloneableService; diff --git a/actix-http/src/h1/upgrade.rs b/actix-http/src/h1/upgrade.rs index d02d4f075..22ba99e26 100644 --- a/actix-http/src/h1/upgrade.rs +++ b/actix-http/src/h1/upgrade.rs @@ -3,7 +3,7 @@ use std::task::{Context, Poll}; use actix_codec::Framed; use actix_service::{Service, ServiceFactory}; -use futures::future::Ready; +use futures_util::future::Ready; use crate::error::Error; use crate::h1::Codec; diff --git a/actix-http/src/h2/mod.rs b/actix-http/src/h2/mod.rs index 6ba29bbcf..b00969227 100644 --- a/actix-http/src/h2/mod.rs +++ b/actix-http/src/h2/mod.rs @@ -3,7 +3,7 @@ use std::pin::Pin; use std::task::{Context, Poll}; use bytes::Bytes; -use futures::Stream; +use futures_core::Stream; use h2::RecvStream; mod dispatcher; diff --git a/actix-http/src/h2/service.rs b/actix-http/src/h2/service.rs index 91d721baf..7cae99f5b 100644 --- a/actix-http/src/h2/service.rs +++ b/actix-http/src/h2/service.rs @@ -11,8 +11,8 @@ use actix_service::{ ServiceFactory, }; use bytes::Bytes; -use futures::future::ok; -use futures::ready; +use futures_core::ready; +use futures_util::future::ok; use h2::server::{self, Handshake}; use log::error; @@ -141,9 +141,9 @@ mod openssl { #[cfg(feature = "rustls")] mod rustls { use super::*; - use actix_tls::rustls::{Acceptor, ServerConfig, Session, TlsStream}; + use actix_tls::rustls::{Acceptor, ServerConfig, TlsStream}; use actix_tls::SslError; - use std::{fmt, io}; + use std::io; impl H2Service, S, B> where diff --git a/actix-http/src/payload.rs b/actix-http/src/payload.rs index 9f7f2a31f..54de6ed93 100644 --- a/actix-http/src/payload.rs +++ b/actix-http/src/payload.rs @@ -2,7 +2,7 @@ use std::pin::Pin; use std::task::{Context, Poll}; use bytes::Bytes; -use futures::Stream; +use futures_core::Stream; use h2::RecvStream; use crate::error::PayloadError; diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index be62151be..fcdcd7cdf 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -7,7 +7,7 @@ use std::task::{Context, Poll}; use std::{fmt, str}; use bytes::{Bytes, BytesMut}; -use futures::stream::Stream; +use futures_core::Stream; use serde::Serialize; use serde_json; diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index cb7e541ee..2d934cc19 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -7,7 +7,8 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_rt::net::TcpStream; use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory}; use bytes::Bytes; -use futures::{future::ok, ready, Future}; +use futures_core::{ready, Future}; +use futures_util::future::ok; use h2::server::{self, Handshake}; use pin_project::{pin_project, project}; diff --git a/awc/CHANGES.md b/awc/CHANGES.md index f4923db88..726a6d666 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [1.0.0] - 2019-12-13 + +* Release + ## [1.0.0-alpha.3] * Migrate to `std::future` diff --git a/awc/Cargo.toml b/awc/Cargo.toml index a183b9fee..a84e7295c 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -42,7 +42,7 @@ actix-rt = "1.0.0" base64 = "0.11" bytes = "0.5.2" derive_more = "0.99.2" -futures = "0.3.1" +futures-core = "0.3.1" log =" 0.4" mime = "0.3" percent-encoding = "2.1" @@ -63,5 +63,6 @@ actix-server = "1.0.0" actix-tls = { version = "1.0.0", features=["openssl", "rustls"] } brotli = "3.3.0" flate2 = "1.0.13" +futures = "0.3.1" env_logger = "0.6" webpki = "0.21" diff --git a/awc/src/connect.rs b/awc/src/connect.rs index 59a909df5..618d653f5 100644 --- a/awc/src/connect.rs +++ b/awc/src/connect.rs @@ -1,3 +1,4 @@ +use std::future::Future; use std::pin::Pin; use std::rc::Rc; use std::task::{Context, Poll}; @@ -12,7 +13,6 @@ use actix_http::h1::ClientCodec; use actix_http::http::HeaderMap; use actix_http::{RequestHead, RequestHeadType, ResponseHead}; use actix_service::Service; -use futures::future::{FutureExt, LocalBoxFuture}; use crate::response::ClientResponse; @@ -24,7 +24,7 @@ pub(crate) trait Connect { head: RequestHead, body: Body, addr: Option, - ) -> LocalBoxFuture<'static, Result>; + ) -> Pin>>>; fn send_request_extra( &mut self, @@ -32,16 +32,22 @@ pub(crate) trait Connect { extra_headers: Option, body: Body, addr: Option, - ) -> LocalBoxFuture<'static, Result>; + ) -> Pin>>>; /// Send request, returns Response and Framed fn open_tunnel( &mut self, head: RequestHead, addr: Option, - ) -> LocalBoxFuture< - 'static, - Result<(ResponseHead, Framed), SendRequestError>, + ) -> Pin< + Box< + dyn Future< + Output = Result< + (ResponseHead, Framed), + SendRequestError, + >, + >, + >, >; /// Send request and extra headers, returns Response and Framed @@ -50,9 +56,15 @@ pub(crate) trait Connect { head: Rc, extra_headers: Option, addr: Option, - ) -> LocalBoxFuture< - 'static, - Result<(ResponseHead, Framed), SendRequestError>, + ) -> Pin< + Box< + dyn Future< + Output = Result< + (ResponseHead, Framed), + SendRequestError, + >, + >, + >, >; } @@ -70,14 +82,14 @@ where head: RequestHead, body: Body, addr: Option, - ) -> LocalBoxFuture<'static, Result> { + ) -> Pin>>> { // connect to the host let fut = self.0.call(ClientConnect { uri: head.uri.clone(), addr, }); - async move { + Box::pin(async move { let connection = fut.await?; // send request @@ -85,8 +97,7 @@ where .send_request(RequestHeadType::from(head), body) .await .map(|(head, payload)| ClientResponse::new(head, payload)) - } - .boxed_local() + }) } fn send_request_extra( @@ -95,14 +106,14 @@ where extra_headers: Option, body: Body, addr: Option, - ) -> LocalBoxFuture<'static, Result> { + ) -> Pin>>> { // connect to the host let fut = self.0.call(ClientConnect { uri: head.uri.clone(), addr, }); - async move { + Box::pin(async move { let connection = fut.await?; // send request @@ -111,17 +122,22 @@ where .await?; Ok(ClientResponse::new(head, payload)) - } - .boxed_local() + }) } fn open_tunnel( &mut self, head: RequestHead, addr: Option, - ) -> LocalBoxFuture< - 'static, - Result<(ResponseHead, Framed), SendRequestError>, + ) -> Pin< + Box< + dyn Future< + Output = Result< + (ResponseHead, Framed), + SendRequestError, + >, + >, + >, > { // connect to the host let fut = self.0.call(ClientConnect { @@ -129,7 +145,7 @@ where addr, }); - async move { + Box::pin(async move { let connection = fut.await?; // send request @@ -138,8 +154,7 @@ where let framed = framed.map_io(|io| BoxedSocket(Box::new(Socket(io)))); Ok((head, framed)) - } - .boxed_local() + }) } fn open_tunnel_extra( @@ -147,9 +162,15 @@ where head: Rc, extra_headers: Option, addr: Option, - ) -> LocalBoxFuture< - 'static, - Result<(ResponseHead, Framed), SendRequestError>, + ) -> Pin< + Box< + dyn Future< + Output = Result< + (ResponseHead, Framed), + SendRequestError, + >, + >, + >, > { // connect to the host let fut = self.0.call(ClientConnect { @@ -157,7 +178,7 @@ where addr, }); - async move { + Box::pin(async move { let connection = fut.await?; // send request @@ -167,8 +188,7 @@ where let framed = framed.map_io(|io| BoxedSocket(Box::new(Socket(io)))); Ok((head, framed)) - } - .boxed_local() + }) } } diff --git a/awc/src/frozen.rs b/awc/src/frozen.rs index 748a15d3b..f7098863c 100644 --- a/awc/src/frozen.rs +++ b/awc/src/frozen.rs @@ -4,7 +4,7 @@ use std::rc::Rc; use std::time::Duration; use bytes::Bytes; -use futures::Stream; +use futures_core::Stream; use serde::Serialize; use actix_http::body::Body; diff --git a/awc/src/request.rs b/awc/src/request.rs index e8434aea9..67b063a8e 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -5,7 +5,7 @@ use std::time::Duration; use std::{fmt, net}; use bytes::Bytes; -use futures::Stream; +use futures_core::Stream; use percent_encoding::percent_encode; use serde::Serialize; diff --git a/awc/src/response.rs b/awc/src/response.rs index c1cbf9e25..20093c72d 100644 --- a/awc/src/response.rs +++ b/awc/src/response.rs @@ -5,7 +5,7 @@ use std::pin::Pin; use std::task::{Context, Poll}; use bytes::{Bytes, BytesMut}; -use futures::{ready, Future, Stream}; +use futures_core::{ready, Future, Stream}; use actix_http::cookie::Cookie; use actix_http::error::{CookieParseError, PayloadError}; diff --git a/awc/src/sender.rs b/awc/src/sender.rs index f6142ab2a..7381e77b7 100644 --- a/awc/src/sender.rs +++ b/awc/src/sender.rs @@ -7,7 +7,7 @@ use std::time::Duration; use actix_rt::time::{delay_for, Delay}; use bytes::Bytes; use derive_more::From; -use futures::{future::LocalBoxFuture, ready, Future, Stream}; +use futures_core::{ready, Future, Stream}; use serde::Serialize; use serde_json; @@ -49,7 +49,7 @@ impl Into for PrepForSendingError { #[must_use = "futures do nothing unless polled"] pub enum SendClientRequest { Fut( - LocalBoxFuture<'static, Result>, + Pin>>>, Option, bool, ), @@ -58,7 +58,7 @@ pub enum SendClientRequest { impl SendClientRequest { pub(crate) fn new( - send: LocalBoxFuture<'static, Result>, + send: Pin>>>, response_decompress: bool, timeout: Option, ) -> SendClientRequest { From 3d64d565d9aa7636f9faa56ff6bde59e73ce773a Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 13 Dec 2019 11:46:02 +0600 Subject: [PATCH 126/176] fix warnings --- actix-http/Cargo.toml | 2 +- actix-http/src/h1/decoder.rs | 2 +- actix-http/src/ws/frame.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index c0e7419c2..abe9dc961 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -50,7 +50,7 @@ actix-tls = { version = "1.0.0", optional = true } base64 = "0.11" bitflags = "1.2" -bytes = "0.5.2" +bytes = "0.5.3" copyless = "0.1.4" chrono = "0.4.6" derive_more = "0.99.2" diff --git a/actix-http/src/h1/decoder.rs b/actix-http/src/h1/decoder.rs index 87e2a1ec8..6a40f41a7 100644 --- a/actix-http/src/h1/decoder.rs +++ b/actix-http/src/h1/decoder.rs @@ -477,7 +477,7 @@ macro_rules! byte ( ($rdr:ident) => ({ if $rdr.len() > 0 { let b = $rdr[0]; - $rdr.split_to(1); + let _ = $rdr.split_to(1); b } else { return Poll::Pending diff --git a/actix-http/src/ws/frame.rs b/actix-http/src/ws/frame.rs index a280ff9c7..3983534ee 100644 --- a/actix-http/src/ws/frame.rs +++ b/actix-http/src/ws/frame.rs @@ -108,7 +108,7 @@ impl Parser { } // remove prefix - src.split_to(idx); + let _ = src.split_to(idx); // no need for body if length == 0 { From d006a7b31f81db36cd63f986858bc4fce81e5fc4 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 13 Dec 2019 12:10:45 +0600 Subject: [PATCH 127/176] update changes --- Cargo.toml | 2 +- awc/Cargo.toml | 6 +++--- test-server/CHANGES.md | 7 +++++++ test-server/Cargo.toml | 2 +- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b5b5577f8..7fd460d4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "2.0.0-alpha.4" +version = "2.0.0-alpha.5" authors = ["Nikolay Kim "] description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." readme = "README.md" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index a84e7295c..18fd93791 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -55,10 +55,10 @@ rust-tls = { version = "0.16.0", package="rustls", optional = true, features = [ [dev-dependencies] actix-connect = { version = "1.0.0", features=["openssl"] } -actix-web = { version = "2.0.0-alpha.3", features=["openssl"] } +actix-web = { version = "2.0.0-alpha.5", features=["openssl"] } actix-http = { version = "1.0.0", features=["openssl"] } -actix-http-test = { version = "1.0.0-alpha.3", features=["openssl"] } -actix-utils = "1.0.0" +actix-http-test = { version = "1.0.0", features=["openssl"] } +actix-utils = "1.0.3" actix-server = "1.0.0" actix-tls = { version = "1.0.0", features=["openssl", "rustls"] } brotli = "3.3.0" diff --git a/test-server/CHANGES.md b/test-server/CHANGES.md index 82fc1969e..5690afc64 100644 --- a/test-server/CHANGES.md +++ b/test-server/CHANGES.md @@ -1,5 +1,12 @@ # Changes +## [1.0.0] - 2019-12-13 + +### Changed + +* Replaced `TestServer::start()` with `test_server()` + + ## [1.0.0-alpha.3] - 2019-12-07 ### Changed diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index cc60259e6..3ed3ef687 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -40,7 +40,7 @@ actix-testing = "1.0.0" awc = "1.0.0" base64 = "0.11" -bytes = "0.5.2" +bytes = "0.5.3" futures = "0.3.1" http = "0.2.0" log = "0.4" From 8881c13e600c36e082b3a80365caf2b774712cfb Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 13 Dec 2019 12:16:43 +0600 Subject: [PATCH 128/176] update changes --- CHANGES.md | 6 ++++++ Cargo.toml | 4 ++-- test-server/Cargo.toml | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index c1ae04b96..1f8ffe849 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Changes +## [2.0.0-alpha.5] - 2019-12-13 + +### Added + +* Add test server, `test::start()` and `test::start_with()` + ## [2.0.0-alpha.4] - 2019-12-08 ### Deleted diff --git a/Cargo.toml b/Cargo.toml index 7fd460d4f..356020742 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,11 +71,11 @@ actix-macros = "0.1.0" actix-threadpool = "0.3.0" actix-tls = "1.0.0" -actix-web-codegen = "0.2.0-alpha.2" +actix-web-codegen = "0.2.0" actix-http = "1.0.0" awc = { version = "1.0.0", default-features = false } -bytes = "0.5.2" +bytes = "0.5.3" derive_more = "0.99.2" encoding_rs = "0.8" futures = "0.3.1" diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 3ed3ef687..dc679b09b 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -55,5 +55,5 @@ time = "0.1" open-ssl = { version="0.10", package="openssl", optional = true } [dev-dependencies] -actix-web = "2.0.0-alpha.4" +#actix-web = "2.0.0-alpha.4" actix-http = "1.0.0" From 232f71b3b529955b9effa7e410847faee076d694 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 13 Dec 2019 12:18:30 +0600 Subject: [PATCH 129/176] update changes --- actix-web-codegen/CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/actix-web-codegen/CHANGES.md b/actix-web-codegen/CHANGES.md index 2beea62cf..de676f688 100644 --- a/actix-web-codegen/CHANGES.md +++ b/actix-web-codegen/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [0.2.0] - 2019-12-13 + +* Generate code for actix-web 2.0 + ## [0.1.3] - 2019-10-14 * Bump up `syn` & `quote` to 1.0 From fac6dec3c96cfa1caaf2abdc233a43076f6830c3 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 13 Dec 2019 12:36:15 +0600 Subject: [PATCH 130/176] update deps --- actix-cors/Cargo.toml | 4 ++-- actix-files/Cargo.toml | 10 +++++----- actix-framed/Cargo.toml | 10 +++++----- actix-http/Cargo.toml | 2 +- actix-identity/Cargo.toml | 8 ++++---- actix-multipart/Cargo.toml | 12 +++++------- actix-session/Cargo.toml | 8 +++----- awc/Cargo.toml | 2 +- test-server/Cargo.toml | 2 +- 9 files changed, 27 insertions(+), 31 deletions(-) diff --git a/actix-cors/Cargo.toml b/actix-cors/Cargo.toml index 935166ebc..05885acfd 100644 --- a/actix-cors/Cargo.toml +++ b/actix-cors/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-cors" -version = "0.2.0-alpha.3" +version = "0.2.0" authors = ["Nikolay Kim "] description = "Cross-origin resource sharing (CORS) for Actix applications." readme = "README.md" @@ -17,7 +17,7 @@ name = "actix_cors" path = "src/lib.rs" [dependencies] -actix-web = "2.0.0-alpha.3" +actix-web = "2.0.0-alpha.5" actix-service = "1.0.0" derive_more = "0.99.2" futures = "0.3.1" diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index eb5f65ea0..5a65ad1f9 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-files" -version = "0.2.0-alpha.3" +version = "0.2.0" authors = ["Nikolay Kim "] description = "Static files support for actix web." readme = "README.md" @@ -18,11 +18,11 @@ name = "actix_files" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-alpha.3", default-features = false } -actix-http = "1.0.0-alpha.3" +actix-web = { version = "2.0.0-alpha.5", default-features = false } +actix-http = "1.0.0" actix-service = "1.0.0" bitflags = "1" -bytes = "0.5.2" +bytes = "0.5.3" futures = "0.3.1" derive_more = "0.99.2" log = "0.4" @@ -33,4 +33,4 @@ v_htmlescape = "0.4" [dev-dependencies] actix-rt = "1.0.0" -actix-web = { version = "2.0.0-alpha.3", features=["openssl"] } +actix-web = { version = "2.0.0-alpha.5", features=["openssl"] } diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml index b7e041765..8848bff28 100644 --- a/actix-framed/Cargo.toml +++ b/actix-framed/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-framed" -version = "0.3.0-alpha.1" +version = "0.3.0" authors = ["Nikolay Kim "] description = "Actix framed app server" readme = "README.md" @@ -24,9 +24,9 @@ actix-codec = "0.2.0" actix-service = "1.0.0" actix-router = "0.2.0" actix-rt = "1.0.0" -actix-http = "1.0.0-alpha.3" +actix-http = "1.0.0" -bytes = "0.5.2" +bytes = "0.5.3" futures = "0.3.1" pin-project = "0.4.6" log = "0.4" @@ -34,5 +34,5 @@ log = "0.4" [dev-dependencies] actix-server = "1.0.0" actix-connect = { version = "1.0.0", features=["openssl"] } -actix-http-test = { version = "1.0.0-alpha.3", features=["openssl"] } -actix-utils = "1.0.0" +actix-http-test = { version = "1.0.0", features=["openssl"] } +actix-utils = "1.0.3" diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index abe9dc961..b95879a28 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -92,7 +92,7 @@ fail-ure = { version = "0.1.5", package="failure", optional = true } [dev-dependencies] actix-server = "1.0.0" actix-connect = { version = "1.0.0", features=["openssl"] } -actix-http-test = { version = "1.0.0-alpha.3", features=["openssl"] } +actix-http-test = { version = "1.0.0", features=["openssl"] } actix-tls = { version = "1.0.0", features=["openssl"] } futures = "0.3.1" env_logger = "0.6" diff --git a/actix-identity/Cargo.toml b/actix-identity/Cargo.toml index 156b105d4..4e96203bf 100644 --- a/actix-identity/Cargo.toml +++ b/actix-identity/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-identity" -version = "0.2.0-alpha.3" +version = "0.2.0" authors = ["Nikolay Kim "] description = "Identity service for actix web framework." readme = "README.md" @@ -17,7 +17,7 @@ name = "actix_identity" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-alpha.3", default-features = false, features = ["secure-cookies"] } +actix-web = { version = "2.0.0-alpha.5", default-features = false, features = ["secure-cookies"] } actix-service = "1.0.0" futures = "0.3.1" serde = "1.0" @@ -26,5 +26,5 @@ time = "0.1.42" [dev-dependencies] actix-rt = "1.0.0" -actix-http = "1.0.0-alpha.3" -bytes = "0.5.2" \ No newline at end of file +actix-http = "1.0.0" +bytes = "0.5.3" \ No newline at end of file diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index 82b98bc09..b0ef36d10 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-multipart" -version = "0.2.0-alpha.3" +version = "0.2.0" authors = ["Nikolay Kim "] description = "Multipart support for actix web framework." readme = "README.md" @@ -9,8 +9,6 @@ homepage = "https://actix.rs" repository = "https://github.com/actix/actix-web.git" documentation = "https://docs.rs/actix-multipart/" license = "MIT/Apache-2.0" -exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] -workspace = ".." edition = "2018" [lib] @@ -18,10 +16,10 @@ name = "actix_multipart" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-alpha.3", default-features = false } +actix-web = { version = "2.0.0-alpha.5", default-features = false } actix-service = "1.0.0" -actix-utils = "1.0.0" -bytes = "0.5.2" +actix-utils = "1.0.3" +bytes = "0.5.3" derive_more = "0.99.2" httparse = "1.3" futures = "0.3.1" @@ -32,4 +30,4 @@ twoway = "0.2" [dev-dependencies] actix-rt = "1.0.0" -actix-http = "1.0.0-alpha.3" \ No newline at end of file +actix-http = "1.0.0" \ No newline at end of file diff --git a/actix-session/Cargo.toml b/actix-session/Cargo.toml index ef8f40e7e..f26401851 100644 --- a/actix-session/Cargo.toml +++ b/actix-session/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-session" -version = "0.3.0-alpha.3" +version = "0.3.0" authors = ["Nikolay Kim "] description = "Session for actix web framework." readme = "README.md" @@ -9,8 +9,6 @@ homepage = "https://actix.rs" repository = "https://github.com/actix/actix-web.git" documentation = "https://docs.rs/actix-session/" license = "MIT/Apache-2.0" -exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] -workspace = ".." edition = "2018" [lib] @@ -24,9 +22,9 @@ default = ["cookie-session"] cookie-session = ["actix-web/secure-cookies"] [dependencies] -actix-web = "2.0.0-alpha.3" +actix-web = "2.0.0-alpha.5" actix-service = "1.0.0" -bytes = "0.5.2" +bytes = "0.5.3" derive_more = "0.99.2" futures = "0.3.1" serde = "1.0" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 18fd93791..28931916c 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -40,7 +40,7 @@ actix-http = "1.0.0" actix-rt = "1.0.0" base64 = "0.11" -bytes = "0.5.2" +bytes = "0.5.3" derive_more = "0.99.2" futures-core = "0.3.1" log =" 0.4" diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index dc679b09b..a6173088a 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -55,5 +55,5 @@ time = "0.1" open-ssl = { version="0.10", package="openssl", optional = true } [dev-dependencies] -#actix-web = "2.0.0-alpha.4" +actix-web = "2.0.0-alpha.5" actix-http = "1.0.0" From c878f66d0593b01f4671eac2977af9b46ddfde47 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 13 Dec 2019 12:40:22 +0600 Subject: [PATCH 131/176] fix docs.rs features list --- Cargo.toml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 356020742..6f13b3765 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] edition = "2018" [package.metadata.docs.rs] -features = ["openssl", "compress", "secure-cookies", "client"] +features = ["openssl", "rustls", "compress", "secure-cookies"] [badges] travis-ci = { repository = "actix/actix-web", branch = "master" } @@ -106,14 +106,14 @@ lto = true opt-level = 3 codegen-units = 1 -[patch.crates-io] -actix-web = { path = "." } -actix-http = { path = "actix-http" } -actix-http-test = { path = "test-server" } -actix-web-codegen = { path = "actix-web-codegen" } -actix-cors = { path = "actix-cors" } -actix-identity = { path = "actix-identity" } -actix-session = { path = "actix-session" } -actix-files = { path = "actix-files" } -actix-multipart = { path = "actix-multipart" } -awc = { path = "awc" } +# [patch.crates-io] +# actix-web = { path = "." } +# actix-http = { path = "actix-http" } +# actix-http-test = { path = "test-server" } +# actix-web-codegen = { path = "actix-web-codegen" } +# actix-cors = { path = "actix-cors" } +# actix-identity = { path = "actix-identity" } +# actix-session = { path = "actix-session" } +# actix-files = { path = "actix-files" } +# actix-multipart = { path = "actix-multipart" } +# awc = { path = "awc" } From e8e0f98f96c1ae09fd2c2cf32f4c47a443e5e852 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 13 Dec 2019 12:41:48 +0600 Subject: [PATCH 132/176] fix docs.rs features list --- actix-http/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index b95879a28..dfa9874a7 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -15,7 +15,7 @@ license = "MIT/Apache-2.0" edition = "2018" [package.metadata.docs.rs] -features = ["openssl", "rustls", "fail", "compress", "secure-cookies"] +features = ["openssl", "rustls", "failure", "compress", "secure-cookies"] [lib] name = "actix_http" From cb705317b8f70ecb4681806836c70d4cb3a0aedf Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sun, 15 Dec 2019 13:28:54 +0600 Subject: [PATCH 133/176] compile with default-features off --- Cargo.toml | 22 +++++++++++----------- actix-http/Cargo.toml | 2 +- awc/Cargo.toml | 4 ++-- awc/src/sender.rs | 41 ++++++++++++++++++++++++++++++++++++----- src/lib.rs | 1 + src/middleware/mod.rs | 3 +++ src/types/form.rs | 8 ++++++++ src/types/json.rs | 8 ++++++++ src/types/payload.rs | 10 +++++++++- 9 files changed, 79 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6f13b3765..1659515e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -106,14 +106,14 @@ lto = true opt-level = 3 codegen-units = 1 -# [patch.crates-io] -# actix-web = { path = "." } -# actix-http = { path = "actix-http" } -# actix-http-test = { path = "test-server" } -# actix-web-codegen = { path = "actix-web-codegen" } -# actix-cors = { path = "actix-cors" } -# actix-identity = { path = "actix-identity" } -# actix-session = { path = "actix-session" } -# actix-files = { path = "actix-files" } -# actix-multipart = { path = "actix-multipart" } -# awc = { path = "awc" } +[patch.crates-io] +actix-web = { path = "." } +actix-http = { path = "actix-http" } +actix-http-test = { path = "test-server" } +actix-web-codegen = { path = "actix-web-codegen" } +actix-cors = { path = "actix-cors" } +actix-identity = { path = "actix-identity" } +actix-session = { path = "actix-session" } +actix-files = { path = "actix-files" } +actix-multipart = { path = "actix-multipart" } +awc = { path = "awc" } diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index dfa9874a7..6669c7932 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -42,7 +42,7 @@ secure-cookies = ["ring"] [dependencies] actix-service = "1.0.0" actix-codec = "0.2.0" -actix-connect = "1.0.0" +actix-connect = "1.0.1" actix-utils = "1.0.3" actix-rt = "1.0.0" actix-threadpool = "0.3.1" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 28931916c..26d1e85d2 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -22,7 +22,7 @@ path = "src/lib.rs" features = ["openssl", "rustls", "compress"] [features] -default = ["compress"] +default = [] #"compress"] # openssl openssl = ["open-ssl", "actix-http/openssl"] @@ -54,7 +54,7 @@ open-ssl = { version="0.10", package="openssl", optional = true } rust-tls = { version = "0.16.0", package="rustls", optional = true, features = ["dangerous_configuration"] } [dev-dependencies] -actix-connect = { version = "1.0.0", features=["openssl"] } +actix-connect = { version = "1.0.1", features=["openssl"] } actix-web = { version = "2.0.0-alpha.5", features=["openssl"] } actix-http = { version = "1.0.0", features=["openssl"] } actix-http-test = { version = "1.0.0", features=["openssl"] } diff --git a/awc/src/sender.rs b/awc/src/sender.rs index 7381e77b7..ec18f12e3 100644 --- a/awc/src/sender.rs +++ b/awc/src/sender.rs @@ -7,15 +7,21 @@ use std::time::Duration; use actix_rt::time::{delay_for, Delay}; use bytes::Bytes; use derive_more::From; -use futures_core::{ready, Future, Stream}; +use futures_core::{Future, Stream}; use serde::Serialize; use serde_json; use actix_http::body::{Body, BodyStream}; -use actix_http::encoding::Decoder; -use actix_http::http::header::{self, ContentEncoding, IntoHeaderValue}; +use actix_http::http::header::{self, IntoHeaderValue}; use actix_http::http::{Error as HttpError, HeaderMap, HeaderName}; -use actix_http::{Error, Payload, PayloadStream, RequestHead}; +use actix_http::{Error, RequestHead}; + +#[cfg(feature = "compress")] +use actix_http::encoding::Decoder; +#[cfg(feature = "compress")] +use actix_http::http::header::ContentEncoding; +#[cfg(feature = "compress")] +use actix_http::{Payload, PayloadStream}; use crate::error::{FreezeRequestError, InvalidUrl, SendRequestError}; use crate::response::ClientResponse; @@ -67,6 +73,7 @@ impl SendClientRequest { } } +#[cfg(feature = "compress")] impl Future for SendClientRequest { type Output = Result>>, SendRequestError>; @@ -83,7 +90,7 @@ impl Future for SendClientRequest { } } - let res = ready!(Pin::new(send).poll(cx)).map(|res| { + let res = futures_core::ready!(Pin::new(send).poll(cx)).map(|res| { res.map_body(|head, payload| { if *response_decompress { Payload::Stream(Decoder::from_headers( @@ -109,6 +116,30 @@ impl Future for SendClientRequest { } } +#[cfg(not(feature = "compress"))] +impl Future for SendClientRequest { + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.get_mut(); + match this { + SendClientRequest::Fut(send, delay, _) => { + if delay.is_some() { + match Pin::new(delay.as_mut().unwrap()).poll(cx) { + Poll::Pending => (), + _ => return Poll::Ready(Err(SendRequestError::Timeout)), + } + } + Pin::new(send).poll(cx) + } + SendClientRequest::Err(ref mut e) => match e.take() { + Some(e) => Poll::Ready(Err(e)), + None => panic!("Attempting to call completed future"), + }, + } + } +} + impl From for SendClientRequest { fn from(e: SendRequestError) -> Self { SendClientRequest::Err(Some(e)) diff --git a/src/lib.rs b/src/lib.rs index 7a1dbec0f..acb0da106 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -141,6 +141,7 @@ pub mod dev { pub use crate::types::readlines::Readlines; pub use actix_http::body::{Body, BodySize, MessageBody, ResponseBody, SizedStream}; + #[cfg(feature = "compress")] pub use actix_http::encoding::Decoder as Decompress; pub use actix_http::ResponseBuilder as HttpResponseBuilder; pub use actix_http::{ diff --git a/src/middleware/mod.rs b/src/middleware/mod.rs index 84e0758bf..be23230cf 100644 --- a/src/middleware/mod.rs +++ b/src/middleware/mod.rs @@ -1,5 +1,8 @@ //! Middlewares + +#[cfg(feature = "compress")] mod compress; +#[cfg(feature = "compress")] pub use self::compress::{BodyEncoding, Compress}; mod condition; diff --git a/src/types/form.rs b/src/types/form.rs index 977f88d0b..756d5fcc9 100644 --- a/src/types/form.rs +++ b/src/types/form.rs @@ -14,6 +14,7 @@ use futures::StreamExt; use serde::de::DeserializeOwned; use serde::Serialize; +#[cfg(feature = "compress")] use crate::dev::Decompress; use crate::error::UrlencodedError; use crate::extract::FromRequest; @@ -240,7 +241,10 @@ impl Default for FormConfig { /// * content-length is greater than 32k /// pub struct UrlEncoded { + #[cfg(feature = "compress")] stream: Option>, + #[cfg(not(feature = "compress"))] + stream: Option, limit: usize, length: Option, encoding: &'static Encoding, @@ -273,7 +277,11 @@ impl UrlEncoded { } }; + #[cfg(feature = "compress")] let payload = Decompress::from_headers(payload.take(), req.headers()); + #[cfg(not(feature = "compress"))] + let payload = payload.take(); + UrlEncoded { encoding, stream: Some(payload), diff --git a/src/types/json.rs b/src/types/json.rs index 8112d04f2..03c4a2db6 100644 --- a/src/types/json.rs +++ b/src/types/json.rs @@ -16,6 +16,7 @@ use serde_json; use actix_http::http::{header::CONTENT_LENGTH, StatusCode}; use actix_http::{HttpMessage, Payload, Response}; +#[cfg(feature = "compress")] use crate::dev::Decompress; use crate::error::{Error, JsonPayloadError}; use crate::extract::FromRequest; @@ -293,7 +294,10 @@ impl Default for JsonConfig { pub struct JsonBody { limit: usize, length: Option, + #[cfg(feature = "compress")] stream: Option>, + #[cfg(not(feature = "compress"))] + stream: Option, err: Option, fut: Option>>, } @@ -332,7 +336,11 @@ where .get(&CONTENT_LENGTH) .and_then(|l| l.to_str().ok()) .and_then(|s| s.parse::().ok()); + + #[cfg(feature = "compress")] let payload = Decompress::from_headers(payload.take(), req.headers()); + #[cfg(not(feature = "compress"))] + let payload = payload.take(); JsonBody { limit: 262_144, diff --git a/src/types/payload.rs b/src/types/payload.rs index 8e52a3b6c..7cb714a87 100644 --- a/src/types/payload.rs +++ b/src/types/payload.rs @@ -301,7 +301,10 @@ impl Default for PayloadConfig { pub struct HttpMessageBody { limit: usize, length: Option, + #[cfg(feature = "compress")] stream: Option>, + #[cfg(not(feature = "compress"))] + stream: Option, err: Option, fut: Option>>, } @@ -322,8 +325,13 @@ impl HttpMessageBody { } } + #[cfg(feature = "compress")] + let stream = Some(dev::Decompress::from_headers(payload.take(), req.headers())); + #[cfg(not(feature = "compress"))] + let stream = Some(payload.take()); + HttpMessageBody { - stream: Some(dev::Decompress::from_headers(payload.take(), req.headers())), + stream, limit: 262_144, length: len, fut: None, From a791aab41825ba0a0f10388583c79c1352f11665 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sun, 15 Dec 2019 13:36:05 +0600 Subject: [PATCH 134/176] prep awc release --- awc/CHANGES.md | 5 +++++ awc/Cargo.toml | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/awc/CHANGES.md b/awc/CHANGES.md index 726a6d666..d9b26e453 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,5 +1,10 @@ # Changes +## [1.0.1] - 2019-12-15 + +* Fix compilation with default features off + + ## [1.0.0] - 2019-12-13 * Release diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 26d1e85d2..863b7a1e4 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "awc" -version = "1.0.0" +version = "1.0.1" authors = ["Nikolay Kim "] description = "Actix http client." readme = "README.md" @@ -22,7 +22,7 @@ path = "src/lib.rs" features = ["openssl", "rustls", "compress"] [features] -default = [] #"compress"] +default = ["compress"] # openssl openssl = ["open-ssl", "actix-http/openssl"] From a153374b6149e8315bcb61ff846c107d4c70571c Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sun, 15 Dec 2019 22:45:38 +0600 Subject: [PATCH 135/176] migrate actix-web-actors --- Cargo.toml | 2 +- actix-web-actors/CHANGES.md | 4 + actix-web-actors/Cargo.toml | 19 ++-- actix-web-actors/src/context.rs | 61 +++++------ actix-web-actors/src/ws.rs | 164 ++++++++++++++++-------------- actix-web-actors/tests/test_ws.rs | 89 ++++++++-------- src/test.rs | 72 +++++++------ 7 files changed, 213 insertions(+), 198 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1659515e1..9438f3c7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,7 +94,7 @@ open-ssl = { version="0.10", package = "openssl", optional = true } rust-tls = { version = "0.16.0", package = "rustls", optional = true } [dev-dependencies] -# actix = "0.8.3" +actix = "0.9.0-alpha.1" rand = "0.7" env_logger = "0.6" serde_derive = "1.0" diff --git a/actix-web-actors/CHANGES.md b/actix-web-actors/CHANGES.md index 01e116baa..dff2dadf6 100644 --- a/actix-web-actors/CHANGES.md +++ b/actix-web-actors/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [2.0.0-alpha.1] - 2019-12-15 + +* Migrate to actix-web 2.0.0 + ## [1.0.4] - 2019-12-07 * Allow comma-separated websocket subprotocols without spaces (#1172) diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index a74aef046..d4fe45363 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web-actors" -version = "1.0.4" +version = "2.0.0-alpha.1" authors = ["Nikolay Kim "] description = "Actix actors support for actix web framework." readme = "README.md" @@ -9,8 +9,6 @@ homepage = "https://actix.rs" repository = "https://github.com/actix/actix-web.git" documentation = "https://docs.rs/actix-web-actors/" license = "MIT/Apache-2.0" -exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] -workspace = ".." edition = "2018" [lib] @@ -18,13 +16,14 @@ name = "actix_web_actors" path = "src/lib.rs" [dependencies] -actix = "0.8.3" -actix-web = "1.0.9" -actix-http = "0.2.11" -actix-codec = "0.1.2" -bytes = "0.4" -futures = "0.1.25" +actix = "0.9.0-alpha.1" +actix-web = "2.0.0-alpha.5" +actix-http = "1.0.0" +actix-codec = "0.2.0" +bytes = "0.5.2" +futures = "0.3.1" +pin-project = "0.4.6" [dev-dependencies] +actix-rt = "1.0.0" env_logger = "0.6" -actix-http-test = { version = "0.2.4", features=["ssl"] } diff --git a/actix-web-actors/src/context.rs b/actix-web-actors/src/context.rs index 31b29500a..6a403de12 100644 --- a/actix-web-actors/src/context.rs +++ b/actix-web-actors/src/context.rs @@ -1,4 +1,6 @@ use std::collections::VecDeque; +use std::pin::Pin; +use std::task::{Context, Poll}; use actix::dev::{ AsyncContextParts, ContextFut, ContextParts, Envelope, Mailbox, ToEnvelope, @@ -7,10 +9,10 @@ use actix::fut::ActorFuture; use actix::{ Actor, ActorContext, ActorState, Addr, AsyncContext, Handler, Message, SpawnHandle, }; -use actix_web::error::{Error, ErrorInternalServerError}; +use actix_web::error::Error; use bytes::Bytes; -use futures::sync::oneshot::Sender; -use futures::{Async, Future, Poll, Stream}; +use futures::channel::oneshot::Sender; +use futures::{Future, Stream}; /// Execution context for http actors pub struct HttpContext @@ -43,7 +45,7 @@ where #[inline] fn spawn(&mut self, fut: F) -> SpawnHandle where - F: ActorFuture + 'static, + F: ActorFuture + 'static, { self.inner.spawn(fut) } @@ -51,7 +53,7 @@ where #[inline] fn wait(&mut self, fut: F) where - F: ActorFuture + 'static, + F: ActorFuture + 'static, { self.inner.wait(fut) } @@ -81,7 +83,7 @@ where { #[inline] /// Create a new HTTP Context from a request and an actor - pub fn create(actor: A) -> impl Stream { + pub fn create(actor: A) -> impl Stream> { let mb = Mailbox::default(); let ctx = HttpContext { inner: ContextParts::new(mb.sender_producer()), @@ -91,7 +93,7 @@ where } /// Create a new HTTP Context - pub fn with_factory(f: F) -> impl Stream + pub fn with_factory(f: F) -> impl Stream> where F: FnOnce(&mut Self) -> A + 'static, { @@ -160,24 +162,23 @@ impl Stream for HttpContextFut where A: Actor>, { - type Item = Bytes; - type Error = Error; + type Item = Result; - fn poll(&mut self) -> Poll, Error> { + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { if self.fut.alive() { - match self.fut.poll() { - Ok(Async::NotReady) | Ok(Async::Ready(())) => (), - Err(_) => return Err(ErrorInternalServerError("error")), - } + let _ = Pin::new(&mut self.fut).poll(cx); } // frames if let Some(data) = self.fut.ctx().stream.pop_front() { - Ok(Async::Ready(data)) + Poll::Ready(data.map(|b| Ok(b))) } else if self.fut.alive() { - Ok(Async::NotReady) + Poll::Pending } else { - Ok(Async::Ready(None)) + Poll::Ready(None) } } } @@ -199,9 +200,9 @@ mod tests { use actix::Actor; use actix_web::http::StatusCode; - use actix_web::test::{block_on, call_service, init_service, TestRequest}; + use actix_web::test::{call_service, init_service, read_body, TestRequest}; use actix_web::{web, App, HttpResponse}; - use bytes::{Bytes, BytesMut}; + use bytes::Bytes; use super::*; @@ -223,31 +224,25 @@ mod tests { if self.count > 3 { ctx.write_eof() } else { - ctx.write(Bytes::from(format!("LINE-{}", self.count).as_bytes())); + ctx.write(Bytes::from(format!("LINE-{}", self.count))); ctx.run_later(Duration::from_millis(100), |slf, ctx| slf.write(ctx)); } } } - #[test] - fn test_default_resource() { + #[actix_rt::test] + async fn test_default_resource() { let mut srv = init_service(App::new().service(web::resource("/test").to(|| { HttpResponse::Ok().streaming(HttpContext::create(MyActor { count: 0 })) - }))); + }))) + .await; let req = TestRequest::with_uri("/test").to_request(); - let mut resp = call_service(&mut srv, req); + let resp = call_service(&mut srv, req).await; assert_eq!(resp.status(), StatusCode::OK); - let body = block_on(resp.take_body().fold( - BytesMut::new(), - move |mut body, chunk| { - body.extend_from_slice(&chunk); - Ok::<_, Error>(body) - }, - )) - .unwrap(); - assert_eq!(body.freeze(), Bytes::from_static(b"LINE-1LINE-2LINE-3")); + let body = read_body(resp).await; + assert_eq!(body, Bytes::from_static(b"LINE-1LINE-2LINE-3")); } } diff --git a/actix-web-actors/src/ws.rs b/actix-web-actors/src/ws.rs index 0b026e35a..b28aeade4 100644 --- a/actix-web-actors/src/ws.rs +++ b/actix-web-actors/src/ws.rs @@ -1,6 +1,8 @@ //! Websocket integration use std::collections::VecDeque; use std::io; +use std::pin::Pin; +use std::task::{Context, Poll}; use actix::dev::{ AsyncContextParts, ContextFut, ContextParts, Envelope, Mailbox, StreamHandler, @@ -16,20 +18,20 @@ use actix_http::ws::{hash_key, Codec}; pub use actix_http::ws::{ CloseCode, CloseReason, Frame, HandshakeError, Message, ProtocolError, }; - use actix_web::dev::HttpResponseBuilder; -use actix_web::error::{Error, ErrorInternalServerError, PayloadError}; +use actix_web::error::{Error, PayloadError}; use actix_web::http::{header, Method, StatusCode}; use actix_web::{HttpRequest, HttpResponse}; use bytes::{Bytes, BytesMut}; -use futures::sync::oneshot::Sender; -use futures::{Async, Future, Poll, Stream}; +use futures::channel::oneshot::Sender; +use futures::{Future, Stream}; /// Do websocket handshake and start ws actor. pub fn start(actor: A, req: &HttpRequest, stream: T) -> Result where - A: Actor> + StreamHandler, - T: Stream + 'static, + A: Actor> + + StreamHandler>, + T: Stream> + 'static, { let mut res = handshake(req)?; Ok(res.streaming(WebsocketContext::create(actor, stream))) @@ -52,8 +54,9 @@ pub fn start_with_addr( stream: T, ) -> Result<(Addr, HttpResponse), Error> where - A: Actor> + StreamHandler, - T: Stream + 'static, + A: Actor> + + StreamHandler>, + T: Stream> + 'static, { let mut res = handshake(req)?; let (addr, out_stream) = WebsocketContext::create_with_addr(actor, stream); @@ -70,8 +73,9 @@ pub fn start_with_protocols( stream: T, ) -> Result where - A: Actor> + StreamHandler, - T: Stream + 'static, + A: Actor> + + StreamHandler>, + T: Stream> + 'static, { let mut res = handshake_with_protocols(req, protocols)?; Ok(res.streaming(WebsocketContext::create(actor, stream))) @@ -202,14 +206,14 @@ where { fn spawn(&mut self, fut: F) -> SpawnHandle where - F: ActorFuture + 'static, + F: ActorFuture + 'static, { self.inner.spawn(fut) } fn wait(&mut self, fut: F) where - F: ActorFuture + 'static, + F: ActorFuture + 'static, { self.inner.wait(fut) } @@ -238,10 +242,10 @@ where { #[inline] /// Create a new Websocket context from a request and an actor - pub fn create(actor: A, stream: S) -> impl Stream + pub fn create(actor: A, stream: S) -> impl Stream> where - A: StreamHandler, - S: Stream + 'static, + A: StreamHandler>, + S: Stream> + 'static, { let (_, stream) = WebsocketContext::create_with_addr(actor, stream); stream @@ -256,10 +260,10 @@ where pub fn create_with_addr( actor: A, stream: S, - ) -> (Addr, impl Stream) + ) -> (Addr, impl Stream>) where - A: StreamHandler, - S: Stream + 'static, + A: StreamHandler>, + S: Stream> + 'static, { let mb = Mailbox::default(); let mut ctx = WebsocketContext { @@ -279,10 +283,10 @@ where actor: A, stream: S, codec: Codec, - ) -> impl Stream + ) -> impl Stream> where - A: StreamHandler, - S: Stream + 'static, + A: StreamHandler>, + S: Stream> + 'static, { let mb = Mailbox::default(); let mut ctx = WebsocketContext { @@ -298,11 +302,11 @@ where pub fn with_factory( stream: S, f: F, - ) -> impl Stream + ) -> impl Stream> where F: FnOnce(&mut Self) -> A + 'static, - A: StreamHandler, - S: Stream + 'static, + A: StreamHandler>, + S: Stream> + 'static, { let mb = Mailbox::default(); let mut ctx = WebsocketContext { @@ -346,14 +350,14 @@ where /// Send ping frame #[inline] - pub fn ping(&mut self, message: &str) { - self.write_raw(Message::Ping(message.to_string())); + pub fn ping(&mut self, message: &[u8]) { + self.write_raw(Message::Ping(Bytes::copy_from_slice(message))); } /// Send pong frame #[inline] - pub fn pong(&mut self, message: &str) { - self.write_raw(Message::Pong(message.to_string())); + pub fn pong(&mut self, message: &[u8]) { + self.write_raw(Message::Pong(Bytes::copy_from_slice(message))); } /// Send close frame @@ -415,30 +419,34 @@ impl Stream for WebsocketContextFut where A: Actor>, { - type Item = Bytes; - type Error = Error; + type Item = Result; - fn poll(&mut self) -> Poll, Error> { - if self.fut.alive() && self.fut.poll().is_err() { - return Err(ErrorInternalServerError("error")); + fn poll_next( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + let this = self.get_mut(); + + if this.fut.alive() { + let _ = Pin::new(&mut this.fut).poll(cx); } // encode messages - while let Some(item) = self.fut.ctx().messages.pop_front() { + while let Some(item) = this.fut.ctx().messages.pop_front() { if let Some(msg) = item { - self.encoder.encode(msg, &mut self.buf)?; + this.encoder.encode(msg, &mut this.buf)?; } else { - self.closed = true; + this.closed = true; break; } } - if !self.buf.is_empty() { - Ok(Async::Ready(Some(self.buf.take().freeze()))) - } else if self.fut.alive() && !self.closed { - Ok(Async::NotReady) + if !this.buf.is_empty() { + Poll::Ready(Some(Ok(this.buf.split().freeze()))) + } else if this.fut.alive() && !this.closed { + Poll::Pending } else { - Ok(Async::Ready(None)) + Poll::Ready(None) } } } @@ -454,7 +462,9 @@ where } } +#[pin_project::pin_project] struct WsStream { + #[pin] stream: S, decoder: Codec, buf: BytesMut, @@ -463,7 +473,7 @@ struct WsStream { impl WsStream where - S: Stream, + S: Stream>, { fn new(stream: S, codec: Codec) -> Self { Self { @@ -477,62 +487,64 @@ where impl Stream for WsStream where - S: Stream, + S: Stream>, { - type Item = Message; - type Error = ProtocolError; + type Item = Result; - fn poll(&mut self) -> Poll, Self::Error> { - if !self.closed { + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + let mut this = self.as_mut().project(); + + if !*this.closed { loop { - match self.stream.poll() { - Ok(Async::Ready(Some(chunk))) => { - self.buf.extend_from_slice(&chunk[..]); + this = self.as_mut().project(); + match Pin::new(&mut this.stream).poll_next(cx) { + Poll::Ready(Some(Ok(chunk))) => { + this.buf.extend_from_slice(&chunk[..]); } - Ok(Async::Ready(None)) => { - self.closed = true; + Poll::Ready(None) => { + *this.closed = true; break; } - Ok(Async::NotReady) => break, - Err(e) => { - return Err(ProtocolError::Io(io::Error::new( - io::ErrorKind::Other, - format!("{}", e), - ))); + Poll::Pending => break, + Poll::Ready(Some(Err(e))) => { + return Poll::Ready(Some(Err(ProtocolError::Io( + io::Error::new(io::ErrorKind::Other, format!("{}", e)), + )))); } } } } - match self.decoder.decode(&mut self.buf)? { + match this.decoder.decode(this.buf)? { None => { - if self.closed { - Ok(Async::Ready(None)) + if *this.closed { + Poll::Ready(None) } else { - Ok(Async::NotReady) + Poll::Pending } } Some(frm) => { let msg = match frm { - Frame::Text(data) => { - if let Some(data) = data { - Message::Text( - std::str::from_utf8(&data) - .map_err(|_| ProtocolError::BadEncoding)? - .to_string(), - ) - } else { - Message::Text(String::new()) - } - } - Frame::Binary(data) => Message::Binary( - data.map(|b| b.freeze()).unwrap_or_else(Bytes::new), + Frame::Text(data) => Message::Text( + std::str::from_utf8(&data) + .map_err(|e| { + ProtocolError::Io(io::Error::new( + io::ErrorKind::Other, + format!("{}", e), + )) + })? + .to_string(), ), + Frame::Binary(data) => Message::Binary(data), Frame::Ping(s) => Message::Ping(s), Frame::Pong(s) => Message::Pong(s), Frame::Close(reason) => Message::Close(reason), + Frame::Continuation(item) => Message::Continuation(item), }; - Ok(Async::Ready(Some(msg))) + Poll::Ready(Some(Ok(msg))) } } } diff --git a/actix-web-actors/tests/test_ws.rs b/actix-web-actors/tests/test_ws.rs index 687cf4314..076e375d3 100644 --- a/actix-web-actors/tests/test_ws.rs +++ b/actix-web-actors/tests/test_ws.rs @@ -1,10 +1,8 @@ use actix::prelude::*; -use actix_http::HttpService; -use actix_http_test::TestServer; -use actix_web::{web, App, HttpRequest}; +use actix_web::{test, web, App, HttpRequest}; use actix_web_actors::*; -use bytes::{Bytes, BytesMut}; -use futures::{Sink, Stream}; +use bytes::Bytes; +use futures::{SinkExt, StreamExt}; struct Ws; @@ -12,9 +10,13 @@ impl Actor for Ws { type Context = ws::WebsocketContext; } -impl StreamHandler for Ws { - fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) { - match msg { +impl StreamHandler> for Ws { + fn handle( + &mut self, + msg: Result, + ctx: &mut Self::Context, + ) { + match msg.unwrap() { ws::Message::Ping(msg) => ctx.pong(&msg), ws::Message::Text(text) => ctx.text(text), ws::Message::Binary(bin) => ctx.binary(bin), @@ -24,45 +26,42 @@ impl StreamHandler for Ws { } } -#[test] -fn test_simple() { - let mut srv = - TestServer::new(|| { - HttpService::new(App::new().service(web::resource("/").to( - |req: HttpRequest, stream: web::Payload| ws::start(Ws, &req, stream), - ))) - }); +#[actix_rt::test] +async fn test_simple() { + let mut srv = test::start(|| { + App::new().service(web::resource("/").to( + |req: HttpRequest, stream: web::Payload| { + async move { ws::start(Ws, &req, stream) } + }, + )) + }); // client service - let framed = srv.ws().unwrap(); - let framed = srv - .block_on(framed.send(ws::Message::Text("text".to_string()))) - .unwrap(); - let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!(item, Some(ws::Frame::Text(Some(BytesMut::from("text"))))); - - let framed = srv - .block_on(framed.send(ws::Message::Binary("text".into()))) - .unwrap(); - let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!( - item, - Some(ws::Frame::Binary(Some(Bytes::from_static(b"text").into()))) - ); - - let framed = srv - .block_on(framed.send(ws::Message::Ping("text".into()))) - .unwrap(); - let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!(item, Some(ws::Frame::Pong("text".to_string().into()))); - - let framed = srv - .block_on(framed.send(ws::Message::Close(Some(ws::CloseCode::Normal.into())))) + let mut framed = srv.ws().await.unwrap(); + framed + .send(ws::Message::Text("text".to_string())) + .await .unwrap(); - let (item, _framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap(); - assert_eq!( - item, - Some(ws::Frame::Close(Some(ws::CloseCode::Normal.into()))) - ); + let item = framed.next().await.unwrap().unwrap(); + assert_eq!(item, ws::Frame::Text(Bytes::from_static(b"text"))); + + framed + .send(ws::Message::Binary("text".into())) + .await + .unwrap(); + let item = framed.next().await.unwrap().unwrap(); + assert_eq!(item, ws::Frame::Binary(Bytes::from_static(b"text").into())); + + framed.send(ws::Message::Ping("text".into())).await.unwrap(); + let item = framed.next().await.unwrap().unwrap(); + assert_eq!(item, ws::Frame::Pong(Bytes::copy_from_slice(b"text"))); + + framed + .send(ws::Message::Close(Some(ws::CloseCode::Normal.into()))) + .await + .unwrap(); + + let item = framed.next().await.unwrap().unwrap(); + assert_eq!(item, ws::Frame::Close(Some(ws::CloseCode::Normal.into()))); } diff --git a/src/test.rs b/src/test.rs index 6d546702a..1e2c0eec7 100644 --- a/src/test.rs +++ b/src/test.rs @@ -910,6 +910,7 @@ impl Drop for TestServer { #[cfg(test)] mod tests { use actix_http::httpmessage::HttpMessage; + use futures::FutureExt; use serde::{Deserialize, Serialize}; use std::time::SystemTime; @@ -1095,41 +1096,46 @@ mod tests { assert!(res.status().is_success()); } - // #[actix_rt::test] - // fn test_actor() { - // use actix::Actor; + #[actix_rt::test] + async fn test_actor() { + use actix::Actor; - // struct MyActor; + struct MyActor; - // struct Num(usize); - // impl actix::Message for Num { - // type Result = usize; - // } - // impl actix::Actor for MyActor { - // type Context = actix::Context; - // } - // impl actix::Handler for MyActor { - // type Result = usize; - // fn handle(&mut self, msg: Num, _: &mut Self::Context) -> Self::Result { - // msg.0 - // } - // } + struct Num(usize); + impl actix::Message for Num { + type Result = usize; + } + impl actix::Actor for MyActor { + type Context = actix::Context; + } + impl actix::Handler for MyActor { + type Result = usize; + fn handle(&mut self, msg: Num, _: &mut Self::Context) -> Self::Result { + msg.0 + } + } - // let addr = run_on(|| MyActor.start()); - // let mut app = init_service(App::new().service( - // web::resource("/index.html").to(move || { - // addr.send(Num(1)).from_err().and_then(|res| { - // if res == 1 { - // HttpResponse::Ok() - // } else { - // HttpResponse::BadRequest() - // } - // }) - // }), - // )); + let addr = MyActor.start(); - // let req = TestRequest::post().uri("/index.html").to_request(); - // let res = block_fn(|| app.call(req)).unwrap(); - // assert!(res.status().is_success()); - // } + let mut app = init_service(App::new().service(web::resource("/index.html").to( + move || { + addr.send(Num(1)).map(|res| match res { + Ok(res) => { + if res == 1 { + Ok(HttpResponse::Ok()) + } else { + Ok(HttpResponse::BadRequest()) + } + } + Err(err) => Err(err), + }) + }, + ))) + .await; + + let req = TestRequest::post().uri("/index.html").to_request(); + let res = app.call(req).await.unwrap(); + assert!(res.status().is_success()); + } } From b0aa9395daf6e76c28a6216831b030e7713c662a Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sun, 15 Dec 2019 22:51:14 +0600 Subject: [PATCH 136/176] prep actix-web alpha.6 release --- CHANGES.md | 6 ++++++ Cargo.toml | 5 ++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 1f8ffe849..762a5d849 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Changes +## [2.0.0-alpha.6] - 2019-12-15 + +### Fixed + +* Fixed compilation with default features off + ## [2.0.0-alpha.5] - 2019-12-13 ### Added diff --git a/Cargo.toml b/Cargo.toml index 9438f3c7c..610ffb2b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "2.0.0-alpha.5" +version = "2.0.0-alpha.6" authors = ["Nikolay Kim "] description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." readme = "README.md" @@ -12,7 +12,6 @@ categories = ["network-programming", "asynchronous", "web-programming::http-server", "web-programming::websocket"] license = "MIT/Apache-2.0" -exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] edition = "2018" [package.metadata.docs.rs] @@ -73,7 +72,7 @@ actix-tls = "1.0.0" actix-web-codegen = "0.2.0" actix-http = "1.0.0" -awc = { version = "1.0.0", default-features = false } +awc = { version = "1.0.1", default-features = false } bytes = "0.5.3" derive_more = "0.99.2" From 30dcaf9da0a57336860d6137169658697b2e92b1 Mon Sep 17 00:00:00 2001 From: Andrii Radyk Date: Mon, 16 Dec 2019 02:43:19 +0100 Subject: [PATCH 137/176] fix deprecated Error::description (#1218) --- actix-http/src/cookie/parse.rs | 6 +----- actix-http/src/error.rs | 11 +++-------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/actix-http/src/cookie/parse.rs b/actix-http/src/cookie/parse.rs index 8c9d4b644..20aee9507 100644 --- a/actix-http/src/cookie/parse.rs +++ b/actix-http/src/cookie/parse.rs @@ -51,11 +51,7 @@ impl From for ParseError { } } -impl Error for ParseError { - fn description(&self) -> &str { - self.as_str() - } -} +impl Error for ParseError {} fn indexes_of(needle: &str, haystack: &str) -> Option<(usize, usize)> { let haystack_start = haystack.as_ptr() as usize; diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index bb18184d8..d252a0bb4 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -114,10 +114,6 @@ impl fmt::Debug for Error { } impl std::error::Error for Error { - fn description(&self) -> &str { - "actix-http::Error" - } - fn cause(&self) -> Option<&dyn std::error::Error> { None } @@ -966,7 +962,6 @@ mod tests { use super::*; use http::{Error as HttpError, StatusCode}; use httparse; - use std::error::Error as StdError; use std::io; #[test] @@ -995,7 +990,7 @@ mod tests { #[test] fn test_error_cause() { let orig = io::Error::new(io::ErrorKind::Other, "other"); - let desc = orig.description().to_owned(); + let desc = orig.to_string(); let e = Error::from(orig); assert_eq!(format!("{}", e.as_response_error()), desc); } @@ -1003,7 +998,7 @@ mod tests { #[test] fn test_error_display() { let orig = io::Error::new(io::ErrorKind::Other, "other"); - let desc = orig.description().to_owned(); + let desc = orig.to_string(); let e = Error::from(orig); assert_eq!(format!("{}", e), desc); } @@ -1045,7 +1040,7 @@ mod tests { match ParseError::from($from) { e @ $error => { let desc = format!("{}", e); - assert_eq!(desc, format!("IO error: {}", $from.description())); + assert_eq!(desc, format!("IO error: {}", $from)); } _ => unreachable!("{:?}", $from), } From 01613f334bdfe81abe4a6934e590ad7f70ba3935 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Mon, 16 Dec 2019 17:22:26 +0600 Subject: [PATCH 138/176] Move BodyEncoding to dev module #1220 --- CHANGES.md | 6 ++++++ actix-files/CHANGES.md | 6 +++++- actix-files/Cargo.toml | 4 ++-- actix-files/src/named.rs | 8 ++++---- src/lib.rs | 42 ++++++++++++++++++++++++++++++++++++++ src/middleware/compress.rs | 28 ++++--------------------- src/middleware/mod.rs | 2 +- 7 files changed, 64 insertions(+), 32 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 762a5d849..56813ce98 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Changes +## [2.0.0] - 2019-12-xx + +### Changed + +* Move `BodyEncoding` to `dev` module #1220 + ## [2.0.0-alpha.6] - 2019-12-15 ### Fixed diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index 5a33d361d..5bfd937a3 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -1,6 +1,10 @@ # Changes -## [0.2.0-alpha.7] - 2019-12-07 +## [0.2.0] - 2019-12-xx + +* Fix BodyEncoding trait import #1220 + +## [0.2.0-alpha.1] - 2019-12-07 * Migrate to `std::future` diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 5a65ad1f9..3eecdbabe 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -18,7 +18,7 @@ name = "actix_files" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-alpha.5", default-features = false } +actix-web = { version = "2.0.0-alpha.6", default-features = false } actix-http = "1.0.0" actix-service = "1.0.0" bitflags = "1" @@ -33,4 +33,4 @@ v_htmlescape = "0.4" [dev-dependencies] actix-rt = "1.0.0" -actix-web = { version = "2.0.0-alpha.5", features=["openssl"] } +actix-web = { version = "2.0.0-alpha.6", features=["openssl"] } diff --git a/actix-files/src/named.rs b/actix-files/src/named.rs index 0dcbd93b8..3d9398b48 100644 --- a/actix-files/src/named.rs +++ b/actix-files/src/named.rs @@ -12,11 +12,11 @@ use mime; use mime_guess::from_path; use actix_http::body::SizedStream; +use actix_web::dev::BodyEncoding; use actix_web::http::header::{ self, Charset, ContentDisposition, DispositionParam, DispositionType, ExtendedValue, }; use actix_web::http::{ContentEncoding, StatusCode}; -use actix_web::middleware::BodyEncoding; use actix_web::{Error, HttpMessage, HttpRequest, HttpResponse, Responder}; use futures::future::{ready, Ready}; @@ -268,7 +268,7 @@ impl NamedFile { ); }); if let Some(current_encoding) = self.encoding { - resp.encoding(current_encoding); + resp.set_encoding(current_encoding); } let reader = ChunkedReadFile { size: self.md.len(), @@ -335,7 +335,7 @@ impl NamedFile { }); // default compressing if let Some(current_encoding) = self.encoding { - resp.encoding(current_encoding); + resp.set_encoding(current_encoding); } resp.if_some(last_modified, |lm, resp| { @@ -356,7 +356,7 @@ impl NamedFile { if let Ok(rangesvec) = HttpRange::parse(rangesheader, length) { length = rangesvec[0].length; offset = rangesvec[0].start; - resp.encoding(ContentEncoding::Identity); + resp.set_encoding(ContentEncoding::Identity); resp.header( header::CONTENT_RANGE, format!( diff --git a/src/lib.rs b/src/lib.rs index acb0da106..d2f501e83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -158,6 +158,48 @@ pub mod dev { }; path } + + use crate::http::header::ContentEncoding; + use actix_http::{Response, ResponseBuilder}; + + struct Enc(ContentEncoding); + + /// Helper trait that allows to set specific encoding for response. + pub trait BodyEncoding { + fn encoding(&self) -> Option; + + fn set_encoding(&mut self, encoding: ContentEncoding) -> &mut Self; + } + + impl BodyEncoding for ResponseBuilder { + fn encoding(&self) -> Option { + if let Some(ref enc) = self.extensions().get::() { + Some(enc.0) + } else { + None + } + } + + fn set_encoding(&mut self, encoding: ContentEncoding) -> &mut Self { + self.extensions_mut().insert(Enc(encoding)); + self + } + } + + impl BodyEncoding for Response { + fn encoding(&self) -> Option { + if let Some(ref enc) = self.extensions().get::() { + Some(enc.0) + } else { + None + } + } + + fn set_encoding(&mut self, encoding: ContentEncoding) -> &mut Self { + self.extensions_mut().insert(Enc(encoding)); + self + } + } } pub mod client { diff --git a/src/middleware/compress.rs b/src/middleware/compress.rs index 0826606ba..c55c741e8 100644 --- a/src/middleware/compress.rs +++ b/src/middleware/compress.rs @@ -9,34 +9,14 @@ use std::task::{Context, Poll}; use actix_http::body::MessageBody; use actix_http::encoding::Encoder; use actix_http::http::header::{ContentEncoding, ACCEPT_ENCODING}; -use actix_http::{Error, Response, ResponseBuilder}; +use actix_http::Error; use actix_service::{Service, Transform}; use futures::future::{ok, Ready}; use pin_project::pin_project; +use crate::dev::BodyEncoding; use crate::service::{ServiceRequest, ServiceResponse}; -struct Enc(ContentEncoding); - -/// Helper trait that allows to set specific encoding for response. -pub trait BodyEncoding { - fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self; -} - -impl BodyEncoding for ResponseBuilder { - fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self { - self.extensions_mut().insert(Enc(encoding)); - self - } -} - -impl BodyEncoding for Response { - fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self { - self.extensions_mut().insert(Enc(encoding)); - self - } -} - #[derive(Debug, Clone)] /// `Middleware` for compressing response body. /// @@ -155,8 +135,8 @@ where match futures::ready!(this.fut.poll(cx)) { Ok(resp) => { - let enc = if let Some(enc) = resp.response().extensions().get::() { - enc.0 + let enc = if let Some(enc) = resp.response().encoding() { + enc } else { *this.encoding }; diff --git a/src/middleware/mod.rs b/src/middleware/mod.rs index be23230cf..f0d42cc2a 100644 --- a/src/middleware/mod.rs +++ b/src/middleware/mod.rs @@ -3,7 +3,7 @@ #[cfg(feature = "compress")] mod compress; #[cfg(feature = "compress")] -pub use self::compress::{BodyEncoding, Compress}; +pub use self::compress::Compress; mod condition; mod defaultheaders; From 3b860ebdc7aead34fede705b5a8cebe6b11da025 Mon Sep 17 00:00:00 2001 From: Rajasekharan Vengalil Date: Mon, 16 Dec 2019 23:34:25 -0800 Subject: [PATCH 139/176] Fix poll_ready call for WebSockets upgrade (#1219) * Fix poll_ready call for WebSockets upgrade * Poll upgrade service from H1ServiceHandler too --- actix-http/CHANGES.md | 6 +++ actix-http/src/h1/dispatcher.rs | 42 ++++------------ actix-http/src/h1/service.rs | 21 ++++++-- actix-http/src/service.rs | 21 ++++++-- actix-http/tests/test_ws.rs | 87 ++++++++++++++++++++++++++------- 5 files changed, 120 insertions(+), 57 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 212ce6a15..ae41610c3 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,5 +1,11 @@ # Changes +## [1.0.xx] - 2019-12-xx + +### Fixed + +* Poll upgrade service's readiness from HTTP service handlers + ## [1.0.0] - 2019-12-13 ### Added diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 6b37be683..8a2a4f030 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -66,7 +66,6 @@ where U::Error: fmt::Display, { Normal(InnerDispatcher), - UpgradeReadiness(InnerDispatcher, Request), Upgrade(U::Future), None, } @@ -764,8 +763,16 @@ where if let DispatcherState::Normal(inner) = std::mem::replace(&mut self.inner, DispatcherState::None) { - self.inner = - DispatcherState::UpgradeReadiness(inner, req); + let mut parts = FramedParts::with_read_buf( + inner.io, + inner.codec, + inner.read_buf, + ); + parts.write_buf = inner.write_buf; + let framed = Framed::from_parts(parts); + self.inner = DispatcherState::Upgrade( + inner.upgrade.unwrap().call((req, framed)), + ); return self.poll(cx); } else { panic!() @@ -815,35 +822,6 @@ where } } } - DispatcherState::UpgradeReadiness(ref mut inner, _) => { - let upgrade = inner.upgrade.as_mut().unwrap(); - match upgrade.poll_ready(cx) { - Poll::Ready(Ok(_)) => { - if let DispatcherState::UpgradeReadiness(inner, req) = - std::mem::replace(&mut self.inner, DispatcherState::None) - { - let mut parts = FramedParts::with_read_buf( - inner.io, - inner.codec, - inner.read_buf, - ); - parts.write_buf = inner.write_buf; - let framed = Framed::from_parts(parts); - self.inner = DispatcherState::Upgrade( - inner.upgrade.unwrap().call((req, framed)), - ); - self.poll(cx) - } else { - panic!() - } - } - Poll::Pending => Poll::Pending, - Poll::Ready(Err(e)) => { - error!("Upgrade handler readiness check error: {}", e); - Poll::Ready(Err(DispatchError::Upgrade)) - } - } - } DispatcherState::Upgrade(ref mut fut) => { unsafe { Pin::new_unchecked(fut) }.poll(cx).map_err(|e| { error!("Upgrade handler error: {}", e); diff --git a/actix-http/src/h1/service.rs b/actix-http/src/h1/service.rs index fb5514da3..beb577f9a 100644 --- a/actix-http/src/h1/service.rs +++ b/actix-http/src/h1/service.rs @@ -72,7 +72,7 @@ where Request = (Request, Framed), Response = (), >, - U::Error: fmt::Display, + U::Error: fmt::Display + Into, U::InitError: fmt::Debug, { /// Create simple tcp stream service @@ -115,7 +115,7 @@ mod openssl { Request = (Request, Framed, Codec>), Response = (), >, - U::Error: fmt::Display, + U::Error: fmt::Display + Into, U::InitError: fmt::Debug, { /// Create openssl based service @@ -255,7 +255,7 @@ where X::Error: Into, X::InitError: fmt::Debug, U: ServiceFactory), Response = ()>, - U::Error: fmt::Display, + U::Error: fmt::Display + Into, U::InitError: fmt::Debug, { type Config = (); @@ -412,7 +412,7 @@ where X: Service, X::Error: Into, U: Service), Response = ()>, - U::Error: fmt::Display, + U::Error: fmt::Display + Into, { type Request = (T, Option); type Response = (); @@ -440,6 +440,19 @@ where })? .is_ready() && ready; + + let ready = if let Some(ref mut upg) = self.upgrade { + upg.poll_ready(cx) + .map_err(|e| { + let e = e.into(); + log::error!("Http service readiness error: {:?}", e); + DispatchError::Service(e) + })? + .is_ready() + && ready + } else { + ready + }; if ready { Poll::Ready(Ok(())) diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index 2d934cc19..457c5ca1f 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -169,7 +169,7 @@ where Request = (Request, Framed), Response = (), >, - U::Error: fmt::Display, + U::Error: fmt::Display + Into, U::InitError: fmt::Debug, ::Future: 'static, { @@ -214,7 +214,7 @@ mod openssl { Request = (Request, Framed, h1::Codec>), Response = (), >, - U::Error: fmt::Display, + U::Error: fmt::Display + Into, U::InitError: fmt::Debug, ::Future: 'static, { @@ -335,7 +335,7 @@ where Request = (Request, Framed), Response = (), >, - U::Error: fmt::Display, + U::Error: fmt::Display + Into, U::InitError: fmt::Debug, ::Future: 'static, { @@ -493,7 +493,7 @@ where X: Service, X::Error: Into, U: Service), Response = ()>, - U::Error: fmt::Display, + U::Error: fmt::Display + Into, { type Request = (T, Protocol, Option); type Response = (); @@ -522,6 +522,19 @@ where .is_ready() && ready; + let ready = if let Some(ref mut upg) = self.upgrade { + upg.poll_ready(cx) + .map_err(|e| { + let e = e.into(); + log::error!("Http service readiness error: {:?}", e); + DispatchError::Service(e) + })? + .is_ready() + && ready + } else { + ready + }; + if ready { Poll::Ready(Ok(())) } else { diff --git a/actix-http/tests/test_ws.rs b/actix-http/tests/test_ws.rs index 2c1d6cdc1..7152fee48 100644 --- a/actix-http/tests/test_ws.rs +++ b/actix-http/tests/test_ws.rs @@ -1,24 +1,70 @@ +use std::cell::Cell; +use std::marker::PhantomData; +use std::pin::Pin; +use std::sync::{Arc, Mutex}; + use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_http::{body, h1, ws, Error, HttpService, Request, Response}; use actix_http_test::test_server; +use actix_service::{fn_factory, Service}; use actix_utils::framed::Dispatcher; use bytes::Bytes; use futures::future; -use futures::{SinkExt, StreamExt}; +use futures::task::{Context, Poll}; +use futures::{Future, SinkExt, StreamExt}; -async fn ws_service( - (req, mut framed): (Request, Framed), -) -> Result<(), Error> { - let res = ws::handshake(req.head()).unwrap().message_body(()); +struct WsService(Arc, Cell)>>); - framed - .send((res, body::BodySize::None).into()) - .await - .unwrap(); +impl WsService { + fn new() -> Self { + WsService(Arc::new(Mutex::new((PhantomData, Cell::new(false))))) + } - Dispatcher::new(framed.into_framed(ws::Codec::new()), service) - .await - .map_err(|_| panic!()) + fn set_polled(&mut self) { + *self.0.lock().unwrap().1.get_mut() = true; + } + + fn was_polled(&self) -> bool { + self.0.lock().unwrap().1.get() + } +} + +impl Clone for WsService { + fn clone(&self) -> Self { + WsService(self.0.clone()) + } +} + +impl Service for WsService +where + T: AsyncRead + AsyncWrite + Unpin + 'static, +{ + type Request = (Request, Framed); + type Response = (); + type Error = Error; + type Future = Pin>>>; + + fn poll_ready(&mut self, _ctx: &mut Context<'_>) -> Poll> { + self.set_polled(); + Poll::Ready(Ok(())) + } + + fn call(&mut self, (req, mut framed): Self::Request) -> Self::Future { + let fut = async move { + let res = ws::handshake(req.head()).unwrap().message_body(()); + + framed + .send((res, body::BodySize::None).into()) + .await + .unwrap(); + + Dispatcher::new(framed.into_framed(ws::Codec::new()), service) + .await + .map_err(|_| panic!()) + }; + + Box::pin(fut) + } } async fn service(msg: ws::Frame) -> Result { @@ -37,11 +83,16 @@ async fn service(msg: ws::Frame) -> Result { #[actix_rt::test] async fn test_simple() { - let mut srv = test_server(|| { - HttpService::build() - .upgrade(actix_service::fn_service(ws_service)) - .finish(|_| future::ok::<_, ()>(Response::NotFound())) - .tcp() + let ws_service = WsService::new(); + let mut srv = test_server({ + let ws_service = ws_service.clone(); + move || { + let ws_service = ws_service.clone(); + HttpService::build() + .upgrade(fn_factory(move || future::ok::<_, ()>(ws_service.clone()))) + .finish(|_| future::ok::<_, ()>(Response::NotFound())) + .tcp() + } }); // client service @@ -138,4 +189,6 @@ async fn test_simple() { item.unwrap().unwrap(), ws::Frame::Close(Some(ws::CloseCode::Normal.into())) ); + + assert!(ws_service.was_polled()); } From 1732ae8c79efe46df294e6b64a80ef8ddfbaa719 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 18 Dec 2019 09:30:14 +0600 Subject: [PATCH 140/176] fix Bodyencoding trait usage --- actix-files/src/named.rs | 6 +++--- actix-http/src/h1/service.rs | 2 +- awc/tests/test_client.rs | 3 ++- src/error.rs | 5 ++--- src/lib.rs | 14 ++++++++------ src/middleware/compress.rs | 2 +- tests/test_server.rs | 13 +++++++------ 7 files changed, 24 insertions(+), 21 deletions(-) diff --git a/actix-files/src/named.rs b/actix-files/src/named.rs index 3d9398b48..fdb055998 100644 --- a/actix-files/src/named.rs +++ b/actix-files/src/named.rs @@ -268,7 +268,7 @@ impl NamedFile { ); }); if let Some(current_encoding) = self.encoding { - resp.set_encoding(current_encoding); + resp.encoding(current_encoding); } let reader = ChunkedReadFile { size: self.md.len(), @@ -335,7 +335,7 @@ impl NamedFile { }); // default compressing if let Some(current_encoding) = self.encoding { - resp.set_encoding(current_encoding); + resp.encoding(current_encoding); } resp.if_some(last_modified, |lm, resp| { @@ -356,7 +356,7 @@ impl NamedFile { if let Ok(rangesvec) = HttpRange::parse(rangesheader, length) { length = rangesvec[0].length; offset = rangesvec[0].start; - resp.set_encoding(ContentEncoding::Identity); + resp.encoding(ContentEncoding::Identity); resp.header( header::CONTENT_RANGE, format!( diff --git a/actix-http/src/h1/service.rs b/actix-http/src/h1/service.rs index beb577f9a..22f9d03b7 100644 --- a/actix-http/src/h1/service.rs +++ b/actix-http/src/h1/service.rs @@ -440,7 +440,7 @@ where })? .is_ready() && ready; - + let ready = if let Some(ref mut upg) = self.upgrade { upg.poll_ready(cx) .map_err(|e| { diff --git a/awc/tests/test_client.rs b/awc/tests/test_client.rs index 73a4696de..48c23cfa2 100644 --- a/awc/tests/test_client.rs +++ b/awc/tests/test_client.rs @@ -15,8 +15,9 @@ use rand::Rng; use actix_http::HttpService; use actix_http_test::test_server; use actix_service::pipeline_factory; +use actix_web::dev::BodyEncoding; use actix_web::http::Cookie; -use actix_web::middleware::{BodyEncoding, Compress}; +use actix_web::middleware::Compress; use actix_web::{ http::header, test, web, App, Error, HttpMessage, HttpRequest, HttpResponse, }; diff --git a/src/error.rs b/src/error.rs index 2eec7c51b..31f6b9c5b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,7 +6,6 @@ use url::ParseError as UrlParseError; use crate::http::StatusCode; use crate::HttpResponse; -use serde_urlencoded::de; /// Errors which can occur when attempting to generate resource uri. #[derive(Debug, PartialEq, Display, From)] @@ -97,7 +96,7 @@ impl ResponseError for JsonPayloadError { pub enum PathError { /// Deserialize error #[display(fmt = "Path deserialize error: {}", _0)] - Deserialize(de::Error), + Deserialize(serde::de::value::Error), } /// Return `BadRequest` for `PathError` @@ -112,7 +111,7 @@ impl ResponseError for PathError { pub enum QueryPayloadError { /// Deserialize error #[display(fmt = "Query deserialize error: {}", _0)] - Deserialize(de::Error), + Deserialize(serde::de::value::Error), } /// Return `BadRequest` for `QueryPayloadError` diff --git a/src/lib.rs b/src/lib.rs index d2f501e83..bc89c6450 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -166,13 +166,15 @@ pub mod dev { /// Helper trait that allows to set specific encoding for response. pub trait BodyEncoding { - fn encoding(&self) -> Option; + /// Get content encoding + fn get_encoding(&self) -> Option; - fn set_encoding(&mut self, encoding: ContentEncoding) -> &mut Self; + /// Set content encoding + fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self; } impl BodyEncoding for ResponseBuilder { - fn encoding(&self) -> Option { + fn get_encoding(&self) -> Option { if let Some(ref enc) = self.extensions().get::() { Some(enc.0) } else { @@ -180,14 +182,14 @@ pub mod dev { } } - fn set_encoding(&mut self, encoding: ContentEncoding) -> &mut Self { + fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self { self.extensions_mut().insert(Enc(encoding)); self } } impl BodyEncoding for Response { - fn encoding(&self) -> Option { + fn get_encoding(&self) -> Option { if let Some(ref enc) = self.extensions().get::() { Some(enc.0) } else { @@ -195,7 +197,7 @@ pub mod dev { } } - fn set_encoding(&mut self, encoding: ContentEncoding) -> &mut Self { + fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self { self.extensions_mut().insert(Enc(encoding)); self } diff --git a/src/middleware/compress.rs b/src/middleware/compress.rs index c55c741e8..70006ab3c 100644 --- a/src/middleware/compress.rs +++ b/src/middleware/compress.rs @@ -135,7 +135,7 @@ where match futures::ready!(this.fut.poll(cx)) { Ok(resp) => { - let enc = if let Some(enc) = resp.response().encoding() { + let enc = if let Some(enc) = resp.response().get_encoding() { enc } else { *this.encoding diff --git a/tests/test_server.rs b/tests/test_server.rs index ca4aff638..011375221 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -12,8 +12,9 @@ use flate2::Compression; use futures::{future::ok, stream::once}; use rand::{distributions::Alphanumeric, Rng}; -use actix_web::middleware::{BodyEncoding, Compress}; -use actix_web::{dev, http, test, web, App, Error, HttpResponse}; +use actix_web::dev::BodyEncoding; +use actix_web::middleware::Compress; +use actix_web::{dev, test, web, App, Error, HttpResponse}; const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World \ @@ -668,7 +669,7 @@ async fn test_brotli_encoding_large_openssl() { let srv = test::start_with(test::config().openssl(builder.build()), move || { App::new().service(web::resource("/").route(web::to(|bytes: Bytes| { HttpResponse::Ok() - .encoding(http::ContentEncoding::Identity) + .encoding(actix_web::http::ContentEncoding::Identity) .body(bytes) }))) }); @@ -681,7 +682,7 @@ async fn test_brotli_encoding_large_openssl() { // client request let mut response = srv .post("/") - .header(http::header::CONTENT_ENCODING, "br") + .header(actix_web::http::header::CONTENT_ENCODING, "br") .send_body(enc) .await .unwrap(); @@ -716,7 +717,7 @@ async fn test_reading_deflate_encoding_large_random_rustls() { let srv = test::start_with(test::config().rustls(config), || { App::new().service(web::resource("/").route(web::to(|bytes: Bytes| { HttpResponse::Ok() - .encoding(http::ContentEncoding::Identity) + .encoding(actix_web::http::ContentEncoding::Identity) .body(bytes) }))) }); @@ -729,7 +730,7 @@ async fn test_reading_deflate_encoding_large_random_rustls() { // client request let req = srv .post("/") - .header(http::header::CONTENT_ENCODING, "deflate") + .header(actix_web::http::header::CONTENT_ENCODING, "deflate") .send_body(enc); let mut response = req.await.unwrap(); From 8c54054844fd859c04c89410af2f5e1fc8e63283 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 19 Dec 2019 09:56:14 +0600 Subject: [PATCH 141/176] Use .advance() intead of .split_to() --- actix-http/CHANGES.md | 2 +- actix-http/src/h1/decoder.rs | 4 ++-- actix-http/src/h1/dispatcher.rs | 6 +++--- actix-http/src/ws/frame.rs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index ae41610c3..eb8620cf8 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## [1.0.xx] - 2019-12-xx +## [1.0.1] - 2019-12-xx ### Fixed diff --git a/actix-http/src/h1/decoder.rs b/actix-http/src/h1/decoder.rs index 6a40f41a7..e113fd52d 100644 --- a/actix-http/src/h1/decoder.rs +++ b/actix-http/src/h1/decoder.rs @@ -5,7 +5,7 @@ use std::mem::MaybeUninit; use std::task::Poll; use actix_codec::Decoder; -use bytes::{Bytes, BytesMut}; +use bytes::{Buf, Bytes, BytesMut}; use http::header::{HeaderName, HeaderValue}; use http::{header, Method, StatusCode, Uri, Version}; use httparse; @@ -477,7 +477,7 @@ macro_rules! byte ( ($rdr:ident) => ({ if $rdr.len() > 0 { let b = $rdr[0]; - let _ = $rdr.split_to(1); + $rdr.advance(1); b } else { return Poll::Pending diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 8a2a4f030..6f4c09915 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -8,7 +8,7 @@ use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed, FramedParts}; use actix_rt::time::{delay_until, Delay, Instant}; use actix_service::Service; use bitflags::bitflags; -use bytes::BytesMut; +use bytes::{Buf, BytesMut}; use log::{error, trace}; use crate::body::{Body, BodySize, MessageBody, ResponseBody}; @@ -312,7 +312,7 @@ where } Poll::Pending => { if written > 0 { - let _ = self.write_buf.split_to(written); + self.write_buf.advance(written); } return Ok(true); } @@ -322,7 +322,7 @@ where if written == self.write_buf.len() { unsafe { self.write_buf.set_len(0) } } else { - let _ = self.write_buf.split_to(written); + self.write_buf.advance(written); } Ok(false) } diff --git a/actix-http/src/ws/frame.rs b/actix-http/src/ws/frame.rs index 3983534ee..3c70eb2bd 100644 --- a/actix-http/src/ws/frame.rs +++ b/actix-http/src/ws/frame.rs @@ -1,6 +1,6 @@ use std::convert::TryFrom; -use bytes::{BufMut, BytesMut}; +use bytes::{Buf, BufMut, BytesMut}; use log::debug; use rand; @@ -108,7 +108,7 @@ impl Parser { } // remove prefix - let _ = src.split_to(idx); + src.advance(idx); // no need for body if length == 0 { From 1d12ba9d5ff691178c3fe61f3f662ca7d98080d2 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 20 Dec 2019 13:50:07 +0600 Subject: [PATCH 142/176] Replace brotli with brotli2 #1224 --- Cargo.toml | 4 +- actix-http/CHANGES.md | 4 +- actix-http/Cargo.toml | 6 +- actix-http/src/encoding/decoder.rs | 6 +- actix-http/src/encoding/encoder.rs | 24 ++-- actix-http/src/encoding/mod.rs | 5 +- actix-http/src/error.rs | 10 ++ actix-http/src/h1/service.rs | 2 +- actix-http/src/service.rs | 2 +- awc/Cargo.toml | 2 +- awc/tests/test_client.rs | 10 +- tests/test_server.rs | 172 +++++++++++------------------ 12 files changed, 108 insertions(+), 139 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 610ffb2b1..0fab5d58c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,11 +93,11 @@ open-ssl = { version="0.10", package = "openssl", optional = true } rust-tls = { version = "0.16.0", package = "rustls", optional = true } [dev-dependencies] -actix = "0.9.0-alpha.1" +actix = "0.9.0-alpha.2" rand = "0.7" env_logger = "0.6" serde_derive = "1.0" -brotli = "3.3.0" +brotli2 = "0.3.2" flate2 = "1.0.13" [profile.release] diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index eb8620cf8..1c8e4f053 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,11 +1,13 @@ # Changes -## [1.0.1] - 2019-12-xx +## [1.0.1] - 2019-12-20 ### Fixed * Poll upgrade service's readiness from HTTP service handlers +* Replace brotli with brotli2 #1224 + ## [1.0.0] - 2019-12-13 ### Added diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 6669c7932..8512b2501 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "1.0.0" +version = "1.0.1" authors = ["Nikolay Kim "] description = "Actix http primitives" readme = "README.md" @@ -31,7 +31,7 @@ openssl = ["actix-tls/openssl", "actix-connect/openssl"] rustls = ["actix-tls/rustls", "actix-connect/rustls"] # enable compressison support -compress = ["flate2", "brotli"] +compress = ["flate2", "brotli2"] # failure integration. actix does not use failure anymore failure = ["fail-ure"] @@ -83,7 +83,7 @@ time = "0.1.42" ring = { version = "0.16.9", optional = true } # compression -brotli = { version = "3.3.0", optional = true } +brotli2 = { version="0.3.2", optional = true } flate2 = { version = "1.0.13", optional = true } # optional deps diff --git a/actix-http/src/encoding/decoder.rs b/actix-http/src/encoding/decoder.rs index cdc4699d5..b60435859 100644 --- a/actix-http/src/encoding/decoder.rs +++ b/actix-http/src/encoding/decoder.rs @@ -4,7 +4,7 @@ use std::pin::Pin; use std::task::{Context, Poll}; use actix_threadpool::{run, CpuFuture}; -use brotli::DecompressorWriter; +use brotli2::write::BrotliDecoder; use bytes::Bytes; use flate2::write::{GzDecoder, ZlibDecoder}; use futures_core::{ready, Stream}; @@ -31,7 +31,7 @@ where pub fn new(stream: S, encoding: ContentEncoding) -> Decoder { let decoder = match encoding { ContentEncoding::Br => Some(ContentDecoder::Br(Box::new( - DecompressorWriter::new(Writer::new(), 0), + BrotliDecoder::new(Writer::new()), ))), ContentEncoding::Deflate => Some(ContentDecoder::Deflate(Box::new( ZlibDecoder::new(Writer::new()), @@ -137,7 +137,7 @@ where enum ContentDecoder { Deflate(Box>), Gzip(Box>), - Br(Box>), + Br(Box>), } impl ContentDecoder { diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index 6ec122fa0..ca04845ab 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -5,7 +5,7 @@ use std::pin::Pin; use std::task::{Context, Poll}; use actix_threadpool::{run, CpuFuture}; -use brotli::CompressorWriter; +use brotli2::write::BrotliEncoder; use bytes::Bytes; use flate2::write::{GzEncoder, ZlibEncoder}; use futures_core::ready; @@ -17,7 +17,7 @@ use crate::{Error, ResponseHead}; use super::Writer; -const INPLACE: usize = 2049; +const INPLACE: usize = 1024; pub struct Encoder { eof: bool, @@ -174,7 +174,7 @@ fn update_head(encoding: ContentEncoding, head: &mut ResponseHead) { enum ContentEncoder { Deflate(ZlibEncoder), Gzip(GzEncoder), - Br(Box>), + Br(BrotliEncoder), } impl ContentEncoder { @@ -188,9 +188,9 @@ impl ContentEncoder { Writer::new(), flate2::Compression::fast(), ))), - ContentEncoding::Br => Some(ContentEncoder::Br(Box::new( - CompressorWriter::new(Writer::new(), 0, 3, 0), - ))), + ContentEncoding::Br => { + Some(ContentEncoder::Br(BrotliEncoder::new(Writer::new(), 3))) + } _ => None, } } @@ -198,12 +198,7 @@ impl ContentEncoder { #[inline] pub(crate) fn take(&mut self) -> Bytes { match *self { - ContentEncoder::Br(ref mut encoder) => { - let mut encoder_new = - Box::new(CompressorWriter::new(Writer::new(), 0, 3, 0)); - std::mem::swap(encoder, &mut encoder_new); - encoder_new.into_inner().freeze() - } + ContentEncoder::Br(ref mut encoder) => encoder.get_mut().take(), ContentEncoder::Deflate(ref mut encoder) => encoder.get_mut().take(), ContentEncoder::Gzip(ref mut encoder) => encoder.get_mut().take(), } @@ -211,7 +206,10 @@ impl ContentEncoder { fn finish(self) -> Result { match self { - ContentEncoder::Br(encoder) => Ok(encoder.into_inner().buf.freeze()), + ContentEncoder::Br(encoder) => match encoder.finish() { + Ok(writer) => Ok(writer.buf.freeze()), + Err(err) => Err(err), + }, ContentEncoder::Gzip(encoder) => match encoder.finish() { Ok(writer) => Ok(writer.buf.freeze()), Err(err) => Err(err), diff --git a/actix-http/src/encoding/mod.rs b/actix-http/src/encoding/mod.rs index 48cf83252..9eaf4104e 100644 --- a/actix-http/src/encoding/mod.rs +++ b/actix-http/src/encoding/mod.rs @@ -19,12 +19,10 @@ impl Writer { buf: BytesMut::with_capacity(8192), } } + fn take(&mut self) -> Bytes { self.buf.split().freeze() } - fn freeze(self) -> Bytes { - self.buf.freeze() - } } impl io::Write for Writer { @@ -32,6 +30,7 @@ impl io::Write for Writer { self.buf.extend_from_slice(buf); Ok(buf.len()) } + fn flush(&mut self) -> io::Result<()> { Ok(()) } diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index d252a0bb4..fd0fe927f 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -6,7 +6,9 @@ use std::str::Utf8Error; use std::string::FromUtf8Error; use std::{fmt, io, result}; +use actix_codec::{Decoder, Encoder}; pub use actix_threadpool::BlockingError; +use actix_utils::framed::DispatcherError as FramedDispatcherError; use actix_utils::timeout::TimeoutError; use bytes::BytesMut; use derive_more::{Display, From}; @@ -463,6 +465,14 @@ impl ResponseError for ContentTypeError { } } +impl ResponseError for FramedDispatcherError +where + E: fmt::Debug + fmt::Display, + ::Error: fmt::Debug, + ::Error: fmt::Debug, +{ +} + /// Helper type that can wrap any error and generate custom response. /// /// In following example any `io::Error` will be converted into "BAD REQUEST" diff --git a/actix-http/src/h1/service.rs b/actix-http/src/h1/service.rs index 22f9d03b7..69c8fc55c 100644 --- a/actix-http/src/h1/service.rs +++ b/actix-http/src/h1/service.rs @@ -165,7 +165,7 @@ mod rustls { Request = (Request, Framed, Codec>), Response = (), >, - U::Error: fmt::Display, + U::Error: fmt::Display + Into, U::InitError: fmt::Debug, { /// Create rustls based service diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index 457c5ca1f..82618289b 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -276,7 +276,7 @@ mod rustls { Request = (Request, Framed, h1::Codec>), Response = (), >, - U::Error: fmt::Display, + U::Error: fmt::Display + Into, U::InitError: fmt::Debug, ::Future: 'static, { diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 863b7a1e4..d1eaa7f69 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -61,7 +61,7 @@ actix-http-test = { version = "1.0.0", features=["openssl"] } actix-utils = "1.0.3" actix-server = "1.0.0" actix-tls = { version = "1.0.0", features=["openssl", "rustls"] } -brotli = "3.3.0" +brotli2 = "0.3.2" flate2 = "1.0.13" futures = "0.3.1" env_logger = "0.6" diff --git a/awc/tests/test_client.rs b/awc/tests/test_client.rs index 48c23cfa2..f8fed5bfd 100644 --- a/awc/tests/test_client.rs +++ b/awc/tests/test_client.rs @@ -4,7 +4,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::time::Duration; -use brotli::CompressorWriter; +use brotli2::write::BrotliEncoder; use bytes::Bytes; use flate2::read::GzDecoder; use flate2::write::GzEncoder; @@ -500,9 +500,9 @@ async fn test_client_gzip_encoding_large_random() { async fn test_client_brotli_encoding() { let srv = test::start(|| { App::new().service(web::resource("/").route(web::to(|data: Bytes| { - let mut e = CompressorWriter::new(Vec::new(), 0, 5, 0); + let mut e = BrotliEncoder::new(Vec::new(), 5); e.write_all(&data).unwrap(); - let data = e.into_inner(); + let data = e.finish().unwrap(); HttpResponse::Ok() .header("content-encoding", "br") .body(data) @@ -527,9 +527,9 @@ async fn test_client_brotli_encoding_large_random() { let srv = test::start(|| { App::new().service(web::resource("/").route(web::to(|data: Bytes| { - let mut e = CompressorWriter::new(Vec::new(), 0, 5, 0); + let mut e = BrotliEncoder::new(Vec::new(), 5); e.write_all(&data).unwrap(); - let data = e.into_inner(); + let data = e.finish().unwrap(); HttpResponse::Ok() .header("content-encoding", "br") .body(data) diff --git a/tests/test_server.rs b/tests/test_server.rs index 011375221..5019157e2 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -1,15 +1,17 @@ use std::io::{Read, Write}; +use std::pin::Pin; +use std::task::{Context, Poll}; use actix_http::http::header::{ ContentEncoding, ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_LENGTH, TRANSFER_ENCODING, }; -use brotli::{CompressorWriter, DecompressorWriter}; +use brotli2::write::{BrotliDecoder, BrotliEncoder}; use bytes::Bytes; use flate2::read::GzDecoder; use flate2::write::{GzEncoder, ZlibDecoder, ZlibEncoder}; use flate2::Compression; -use futures::{future::ok, stream::once}; +use futures::{ready, Future}; use rand::{distributions::Alphanumeric, Rng}; use actix_web::dev::BodyEncoding; @@ -38,6 +40,42 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World"; +struct TestBody { + data: Bytes, + chunk_size: usize, + delay: actix_rt::time::Delay, +} + +impl TestBody { + fn new(data: Bytes, chunk_size: usize) -> Self { + TestBody { + data, + chunk_size, + delay: actix_rt::time::delay_for(std::time::Duration::from_millis(10)), + } + } +} + +impl futures::Stream for TestBody { + type Item = Result; + + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + ready!(Pin::new(&mut self.delay).poll(cx)); + + self.delay = actix_rt::time::delay_for(std::time::Duration::from_millis(10)); + let chunk_size = std::cmp::min(self.chunk_size, self.data.len()); + let chunk = self.data.split_to(chunk_size); + if chunk.is_empty() { + Poll::Ready(None) + } else { + Poll::Ready(Some(Ok(chunk))) + } + } +} + #[actix_rt::test] async fn test_body() { let srv = test::start(|| { @@ -248,7 +286,7 @@ async fn test_body_chunked_implicit() { .wrap(Compress::new(ContentEncoding::Gzip)) .service(web::resource("/").route(web::get().to(move || { HttpResponse::Ok() - .streaming(once(ok::<_, Error>(Bytes::from_static(STR.as_ref())))) + .streaming(TestBody::new(Bytes::from_static(STR.as_ref()), 24)) }))) }); @@ -281,7 +319,7 @@ async fn test_body_br_streaming() { App::new().wrap(Compress::new(ContentEncoding::Br)).service( web::resource("/").route(web::to(move || { HttpResponse::Ok() - .streaming(once(ok::<_, Error>(Bytes::from_static(STR.as_ref())))) + .streaming(TestBody::new(Bytes::from_static(STR.as_ref()), 24)) })), ) }); @@ -297,11 +335,13 @@ async fn test_body_br_streaming() { // read response let bytes = response.body().await.unwrap(); + println!("TEST: {:?}", bytes.len()); // decode br - let mut e = DecompressorWriter::new(Vec::with_capacity(2048), 0); + let mut e = BrotliDecoder::new(Vec::with_capacity(2048)); e.write_all(bytes.as_ref()).unwrap(); - let dec = e.into_inner().unwrap(); + let dec = e.finish().unwrap(); + println!("T: {:?}", Bytes::copy_from_slice(&dec)); assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); } @@ -333,7 +373,7 @@ async fn test_no_chunking() { HttpResponse::Ok() .no_chunking() .content_length(STR.len() as u64) - .streaming(once(ok::<_, Error>(Bytes::from_static(STR.as_ref())))) + .streaming(TestBody::new(Bytes::from_static(STR.as_ref()), 24)) }))) }); @@ -397,9 +437,9 @@ async fn test_body_brotli() { let bytes = response.body().await.unwrap(); // decode brotli - let mut e = DecompressorWriter::new(Vec::with_capacity(2048), 0); + let mut e = BrotliDecoder::new(Vec::with_capacity(2048)); e.write_all(bytes.as_ref()).unwrap(); - let dec = e.into_inner().unwrap(); + let dec = e.finish().unwrap(); assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); } @@ -608,9 +648,9 @@ async fn test_brotli_encoding() { ) }); - let mut e = CompressorWriter::new(Vec::new(), 0, 3, 0); + let mut e = BrotliEncoder::new(Vec::new(), 5); e.write_all(STR.as_ref()).unwrap(); - let enc = e.into_inner(); + let enc = e.finish().unwrap(); // client request let request = srv @@ -627,17 +667,24 @@ async fn test_brotli_encoding() { #[actix_rt::test] async fn test_brotli_encoding_large() { - let data = STR.repeat(10); + let data = rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(320_000) + .collect::(); + let srv = test::start_with(test::config().h1(), || { App::new().service( web::resource("/") - .route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))), + .data(web::PayloadConfig::new(320_000)) + .route(web::to(move |body: Bytes| { + HttpResponse::Ok().streaming(TestBody::new(body, 10240)) + })), ) }); - let mut e = CompressorWriter::new(Vec::new(), 0, 3, 0); + let mut e = BrotliEncoder::new(Vec::new(), 5); e.write_all(data.as_ref()).unwrap(); - let enc = e.into_inner(); + let enc = e.finish().unwrap(); // client request let request = srv @@ -648,7 +695,7 @@ async fn test_brotli_encoding_large() { assert!(response.status().is_success()); // read response - let bytes = response.body().await.unwrap(); + let bytes = response.body().limit(320_000).await.unwrap(); assert_eq!(bytes, Bytes::from(data)); } @@ -675,9 +722,9 @@ async fn test_brotli_encoding_large_openssl() { }); // body - let mut e = CompressorWriter::new(Vec::new(), 0, 3, 0); + let mut e = BrotliEncoder::new(Vec::new(), 3); e.write_all(data.as_ref()).unwrap(); - let enc = e.into_inner(); + let enc = e.finish().unwrap(); // client request let mut response = srv @@ -731,7 +778,7 @@ async fn test_reading_deflate_encoding_large_random_rustls() { let req = srv .post("/") .header(actix_web::http::header::CONTENT_ENCODING, "deflate") - .send_body(enc); + .send_stream(TestBody::new(Bytes::from(enc), 1024)); let mut response = req.await.unwrap(); assert!(response.status().is_success()); @@ -742,93 +789,6 @@ async fn test_reading_deflate_encoding_large_random_rustls() { assert_eq!(bytes, Bytes::from(data)); } -// #[cfg(all(feature = "tls", feature = "ssl"))] -// #[test] -// fn test_reading_deflate_encoding_large_random_nativetls() { -// use native_tls::{Identity, TlsAcceptor}; -// use openssl::ssl::{ -// SslAcceptor, SslConnector, SslFiletype, SslMethod, SslVerifyMode, -// }; -// use std::fs::File; -// use std::sync::mpsc; - -// use actix::{Actor, System}; -// let (tx, rx) = mpsc::channel(); - -// // load ssl keys -// let mut file = File::open("tests/identity.pfx").unwrap(); -// let mut identity = vec![]; -// file.read_to_end(&mut identity).unwrap(); -// let identity = Identity::from_pkcs12(&identity, "1").unwrap(); -// let acceptor = TlsAcceptor::new(identity).unwrap(); - -// // load ssl keys -// let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); -// builder -// .set_private_key_file("tests/key.pem", SslFiletype::PEM) -// .unwrap(); -// builder -// .set_certificate_chain_file("tests/cert.pem") -// .unwrap(); - -// let data = rand::thread_rng() -// .sample_iter(&Alphanumeric) -// .take(160_000) -// .collect::(); - -// let addr = test::TestServer::unused_addr(); -// thread::spawn(move || { -// System::run(move || { -// server::new(|| { -// App::new().handler("/", |req: &HttpRequest| { -// req.body() -// .and_then(|bytes: Bytes| { -// Ok(HttpResponse::Ok() -// .content_encoding(http::ContentEncoding::Identity) -// .body(bytes)) -// }) -// .responder() -// }) -// }) -// .bind_tls(addr, acceptor) -// .unwrap() -// .start(); -// let _ = tx.send(System::current()); -// }); -// }); -// let sys = rx.recv().unwrap(); - -// let mut rt = System::new("test"); - -// // client connector -// let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); -// builder.set_verify(SslVerifyMode::NONE); -// let conn = client::ClientConnector::with_connector(builder.build()).start(); - -// // encode data -// let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); -// e.write_all(data.as_ref()).unwrap(); -// let enc = e.finish().unwrap(); - -// // client request -// let request = client::ClientRequest::build() -// .uri(format!("https://{}/", addr)) -// .method(http::Method::POST) -// .header(http::header::CONTENT_ENCODING, "deflate") -// .with_connector(conn) -// .body(enc) -// .unwrap(); -// let response = rt.block_on(request.send()).unwrap(); -// assert!(response.status().is_success()); - -// // read response -// let bytes = rt.block_on(response.body()).unwrap(); -// assert_eq!(bytes.len(), data.len()); -// assert_eq!(bytes, Bytes::from(data)); - -// let _ = sys.stop(); -// } - // #[test] // fn test_server_cookies() { // use actix_web::http; From fbbb4a86e91b207354383b2627b0d55465bbcb79 Mon Sep 17 00:00:00 2001 From: tglman Date: Fri, 20 Dec 2019 07:59:07 +0000 Subject: [PATCH 143/176] feat: add access to the session also from immutable references (#1225) --- actix-session/CHANGES.md | 5 +++++ actix-session/src/lib.rs | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/actix-session/CHANGES.md b/actix-session/CHANGES.md index 0c9dca564..b4ef66c35 100644 --- a/actix-session/CHANGES.md +++ b/actix-session/CHANGES.md @@ -1,5 +1,10 @@ # Changes + +## [0.3.0-alpha.4] - 2019-12-xx + +* Allow access to sessions also from not mutable references to the request + ## [0.3.0-alpha.3] - 2019-12-xx * Add access to the session from RequestHead for use of session from guard methods diff --git a/actix-session/src/lib.rs b/actix-session/src/lib.rs index ef44e5213..ac60901c3 100644 --- a/actix-session/src/lib.rs +++ b/actix-session/src/lib.rs @@ -85,23 +85,23 @@ pub struct Session(Rc>); /// Helper trait that allows to get session pub trait UserSession { - fn get_session(&mut self) -> Session; + fn get_session(&self) -> Session; } impl UserSession for HttpRequest { - fn get_session(&mut self) -> Session { + fn get_session(&self) -> Session { Session::get_session(&mut *self.extensions_mut()) } } impl UserSession for ServiceRequest { - fn get_session(&mut self) -> Session { + fn get_session(&self) -> Session { Session::get_session(&mut *self.extensions_mut()) } } impl UserSession for RequestHead { - fn get_session(&mut self) -> Session { + fn get_session(&self) -> Session { Session::get_session(&mut *self.extensions_mut()) } } From a08d8dab7060b5ff2c5a97522786cfbfa27fd7ac Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 20 Dec 2019 16:04:51 +0600 Subject: [PATCH 144/176] AppConfig::secure() is always false. #1202 --- CHANGES.md | 5 +++ src/app.rs | 19 +--------- src/app_service.rs | 11 ++---- src/config.rs | 34 ++++++++--------- src/server.rs | 79 +++++++++++++++++++++++++++++----------- src/test.rs | 66 +++++++++++++++++++++++---------- tests/test_httpserver.rs | 12 +++--- 7 files changed, 135 insertions(+), 91 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 56813ce98..a8cfb8a26 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,11 @@ * Move `BodyEncoding` to `dev` module #1220 +### Fixed + +* Fix `AppConfig::secure()` is always false. #1202 + + ## [2.0.0-alpha.6] - 2019-12-15 ### Fixed diff --git a/src/app.rs b/src/app.rs index e0f56a1ac..ccf2e88e0 100644 --- a/src/app.rs +++ b/src/app.rs @@ -12,7 +12,7 @@ use actix_service::{ use futures::future::{FutureExt, LocalBoxFuture}; use crate::app_service::{AppEntry, AppInit, AppRoutingFactory}; -use crate::config::{AppConfig, AppConfigInner, ServiceConfig}; +use crate::config::ServiceConfig; use crate::data::{Data, DataFactory}; use crate::dev::ResourceDef; use crate::error::Error; @@ -36,7 +36,6 @@ pub struct App { factory_ref: Rc>>, data: Vec>, data_factories: Vec, - config: AppConfigInner, external: Vec, _t: PhantomData, } @@ -52,7 +51,6 @@ impl App { services: Vec::new(), default: None, factory_ref: fref, - config: AppConfigInner::default(), external: Vec::new(), _t: PhantomData, } @@ -225,18 +223,6 @@ where self } - /// Set server host name. - /// - /// Host name is used by application router as a hostname for url generation. - /// Check [ConnectionInfo](./dev/struct.ConnectionInfo.html#method.host) - /// documentation for more information. - /// - /// By default host name is set to a "localhost" value. - pub fn hostname(mut self, val: &str) -> Self { - self.config.host = val.to_owned(); - self - } - /// Default service to be used if no matching resource could be found. /// /// It is possible to use services like `Resource`, `Route`. @@ -383,7 +369,6 @@ where services: self.services, default: self.default, factory_ref: self.factory_ref, - config: self.config, external: self.external, _t: PhantomData, } @@ -445,7 +430,6 @@ where services: self.services, default: self.default, factory_ref: self.factory_ref, - config: self.config, external: self.external, _t: PhantomData, } @@ -472,7 +456,6 @@ where external: RefCell::new(self.external), default: self.default, factory_ref: self.factory_ref, - config: RefCell::new(AppConfig(Rc::new(self.config))), } } } diff --git a/src/app_service.rs b/src/app_service.rs index bb42a3dd9..28e245228 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -41,7 +41,6 @@ where pub(crate) endpoint: T, pub(crate) data: Rc>>, pub(crate) data_factories: Rc>, - pub(crate) config: RefCell, pub(crate) services: Rc>>>, pub(crate) default: Option>, pub(crate) factory_ref: Rc>>, @@ -58,7 +57,7 @@ where InitError = (), >, { - type Config = (); + type Config = AppConfig; type Request = Request; type Response = ServiceResponse; type Error = T::Error; @@ -66,7 +65,7 @@ where type Service = AppInitService; type Future = AppInitResult; - fn new_service(&self, _: ()) -> Self::Future { + fn new_service(&self, config: AppConfig) -> Self::Future { // update resource default service let default = self.default.clone().unwrap_or_else(|| { Rc::new(boxed::factory(fn_service(|req: ServiceRequest| { @@ -75,11 +74,7 @@ where }); // App config - let mut config = AppService::new( - self.config.borrow().clone(), - default.clone(), - self.data.clone(), - ); + let mut config = AppService::new(config, default.clone(), self.data.clone()); // register services std::mem::replace(&mut *self.services.borrow_mut(), Vec::new()) diff --git a/src/config.rs b/src/config.rs index d57791e1a..6ce96767d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -124,14 +124,20 @@ impl AppService { } #[derive(Clone)] -pub struct AppConfig(pub(crate) Rc); +pub struct AppConfig(Rc); + +struct AppConfigInner { + secure: bool, + host: String, + addr: SocketAddr, +} impl AppConfig { - pub(crate) fn new(inner: AppConfigInner) -> Self { - AppConfig(Rc::new(inner)) + pub(crate) fn new(secure: bool, addr: SocketAddr, host: String) -> Self { + AppConfig(Rc::new(AppConfigInner { secure, addr, host })) } - /// Set server host name. + /// Server host name. /// /// Host name is used by application router as a hostname for url generation. /// Check [ConnectionInfo](./struct.ConnectionInfo.html#method.host) @@ -153,19 +159,13 @@ impl AppConfig { } } -pub(crate) struct AppConfigInner { - pub(crate) secure: bool, - pub(crate) host: String, - pub(crate) addr: SocketAddr, -} - -impl Default for AppConfigInner { - fn default() -> AppConfigInner { - AppConfigInner { - secure: false, - addr: "127.0.0.1:8080".parse().unwrap(), - host: "localhost:8080".to_owned(), - } +impl Default for AppConfig { + fn default() -> Self { + AppConfig::new( + false, + "127.0.0.1:8080".parse().unwrap(), + "localhost:8080".to_owned(), + ) } } diff --git a/src/server.rs b/src/server.rs index d6835ffa8..72ef7255b 100644 --- a/src/server.rs +++ b/src/server.rs @@ -6,7 +6,9 @@ use actix_http::{ body::MessageBody, Error, HttpService, KeepAlive, Protocol, Request, Response, }; use actix_server::{Server, ServerBuilder}; -use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory}; +use actix_service::{ + map_config, pipeline_factory, IntoServiceFactory, Service, ServiceFactory, +}; use futures::future::ok; use net2::TcpBuilder; @@ -16,12 +18,15 @@ use actix_tls::openssl::{AlpnError, SslAcceptor, SslAcceptorBuilder}; #[cfg(feature = "rustls")] use actix_tls::rustls::ServerConfig as RustlsServerConfig; +use crate::config::AppConfig; + struct Socket { scheme: &'static str, addr: net::SocketAddr, } struct Config { + host: Option, keep_alive: KeepAlive, client_timeout: u64, client_shutdown: u64, @@ -52,14 +57,13 @@ pub struct HttpServer where F: Fn() -> I + Send + Clone + 'static, I: IntoServiceFactory, - S: ServiceFactory, + S: ServiceFactory, S::Error: Into, S::InitError: fmt::Debug, S::Response: Into>, B: MessageBody, { pub(super) factory: F, - pub(super) host: Option, config: Arc>, backlog: i32, sockets: Vec, @@ -71,7 +75,7 @@ impl HttpServer where F: Fn() -> I + Send + Clone + 'static, I: IntoServiceFactory, - S: ServiceFactory, + S: ServiceFactory, S::Error: Into + 'static, S::InitError: fmt::Debug, S::Response: Into> + 'static, @@ -82,8 +86,8 @@ where pub fn new(factory: F) -> Self { HttpServer { factory, - host: None, config: Arc::new(Mutex::new(Config { + host: None, keep_alive: KeepAlive::Timeout(5), client_timeout: 5000, client_shutdown: 5000, @@ -184,8 +188,8 @@ where /// documentation for more information. /// /// By default host name is set to a "localhost" value. - pub fn server_hostname>(mut self, val: T) -> Self { - self.host = Some(val.as_ref().to_owned()); + pub fn server_hostname>(self, val: T) -> Self { + self.config.lock().unwrap().host = Some(val.as_ref().to_owned()); self } @@ -246,11 +250,17 @@ where lst, move || { let c = cfg.lock().unwrap(); + let cfg = AppConfig::new( + false, + addr, + c.host.clone().unwrap_or_else(|| format!("{}", addr)), + ); + HttpService::build() .keep_alive(c.keep_alive) .client_timeout(c.client_timeout) .local_addr(addr) - .finish(factory()) + .finish(map_config(factory().into_factory(), move |_| cfg.clone())) .tcp() }, )?; @@ -288,11 +298,16 @@ where lst, move || { let c = cfg.lock().unwrap(); + let cfg = AppConfig::new( + true, + addr, + c.host.clone().unwrap_or_else(|| format!("{}", addr)), + ); HttpService::build() .keep_alive(c.keep_alive) .client_timeout(c.client_timeout) .client_disconnect(c.client_shutdown) - .finish(factory()) + .finish(map_config(factory().into_factory(), move |_| cfg.clone())) .openssl(acceptor.clone()) }, )?; @@ -330,11 +345,16 @@ where lst, move || { let c = cfg.lock().unwrap(); + let cfg = AppConfig::new( + true, + addr, + c.host.clone().unwrap_or_else(|| format!("{}", addr)), + ); HttpService::build() .keep_alive(c.keep_alive) .client_timeout(c.client_timeout) .client_disconnect(c.client_shutdown) - .finish(factory()) + .finish(map_config(factory().into_factory(), move |_| cfg.clone())) .rustls(config.clone()) }, )?; @@ -435,24 +455,31 @@ where let cfg = self.config.clone(); let factory = self.factory.clone(); - // todo duplicated: + let socket_addr = net::SocketAddr::new( + net::IpAddr::V4(net::Ipv4Addr::new(127, 0, 0, 1)), + 8080, + ); self.sockets.push(Socket { scheme: "http", - addr: net::SocketAddr::new( - net::IpAddr::V4(net::Ipv4Addr::new(127, 0, 0, 1)), - 8080, - ), + addr: socket_addr, }); let addr = format!("actix-web-service-{:?}", lst.local_addr()?); self.builder = self.builder.listen_uds(addr, lst, move || { let c = cfg.lock().unwrap(); + let config = AppConfig::new( + false, + socket_addr, + c.host.clone().unwrap_or_else(|| format!("{}", socket_addr)), + ); pipeline_factory(|io: UnixStream| ok((io, Protocol::Http1, None))).and_then( HttpService::build() .keep_alive(c.keep_alive) .client_timeout(c.client_timeout) - .finish(factory()), + .finish(map_config(factory().into_factory(), move |_| { + config.clone() + })), ) })?; Ok(self) @@ -470,12 +497,13 @@ where let cfg = self.config.clone(); let factory = self.factory.clone(); + let socket_addr = net::SocketAddr::new( + net::IpAddr::V4(net::Ipv4Addr::new(127, 0, 0, 1)), + 8080, + ); self.sockets.push(Socket { scheme: "http", - addr: net::SocketAddr::new( - net::IpAddr::V4(net::Ipv4Addr::new(127, 0, 0, 1)), - 8080, - ), + addr: socket_addr, }); self.builder = self.builder.bind_uds( @@ -483,12 +511,19 @@ where addr, move || { let c = cfg.lock().unwrap(); + let config = AppConfig::new( + false, + socket_addr, + c.host.clone().unwrap_or_else(|| format!("{}", socket_addr)), + ); pipeline_factory(|io: UnixStream| ok((io, Protocol::Http1, None))) .and_then( HttpService::build() .keep_alive(c.keep_alive) .client_timeout(c.client_timeout) - .finish(factory()), + .finish(map_config(factory().into_factory(), move |_| { + config.clone() + })), ) }, )?; @@ -500,7 +535,7 @@ impl HttpServer where F: Fn() -> I + Send + Clone + 'static, I: IntoServiceFactory, - S: ServiceFactory, + S: ServiceFactory, S::Error: Into, S::InitError: fmt::Debug, S::Response: Into>, diff --git a/src/test.rs b/src/test.rs index 1e2c0eec7..8de1ba141 100644 --- a/src/test.rs +++ b/src/test.rs @@ -12,7 +12,9 @@ use actix_http::{cookie::Cookie, ws, Extensions, HttpService, Request}; use actix_router::{Path, ResourceDef, Url}; use actix_rt::System; use actix_server::Server; -use actix_service::{IntoService, IntoServiceFactory, Service, ServiceFactory}; +use actix_service::{ + map_config, IntoService, IntoServiceFactory, Service, ServiceFactory, +}; use awc::error::PayloadError; use awc::{Client, ClientRequest, ClientResponse, Connector}; use bytes::{Bytes, BytesMut}; @@ -25,7 +27,7 @@ use serde_json; pub use actix_http::test::TestBuffer; -use crate::config::{AppConfig, AppConfigInner}; +use crate::config::AppConfig; use crate::data::Data; use crate::dev::{Body, MessageBody, Payload}; use crate::request::HttpRequestPool; @@ -79,7 +81,7 @@ pub async fn init_service( where R: IntoServiceFactory, S: ServiceFactory< - Config = (), + Config = AppConfig, Request = Request, Response = ServiceResponse, Error = E, @@ -87,7 +89,7 @@ where S::InitError: std::fmt::Debug, { let srv = app.into_factory(); - srv.new_service(()).await.unwrap() + srv.new_service(AppConfig::default()).await.unwrap() } /// Calls service and waits for response future completion. @@ -296,7 +298,7 @@ where pub struct TestRequest { req: HttpTestRequest, rmap: ResourceMap, - config: AppConfigInner, + config: AppConfig, path: Path, app_data: Extensions, } @@ -306,7 +308,7 @@ impl Default for TestRequest { TestRequest { req: HttpTestRequest::default(), rmap: ResourceMap::new(ResourceDef::new("")), - config: AppConfigInner::default(), + config: AppConfig::default(), path: Path::new(Url::new(Uri::default())), app_data: Extensions::new(), } @@ -462,7 +464,7 @@ impl TestRequest { head, payload, Rc::new(self.rmap), - AppConfig::new(self.config), + self.config.clone(), Rc::new(self.app_data), HttpRequestPool::create(), )) @@ -483,7 +485,7 @@ impl TestRequest { head, payload, Rc::new(self.rmap), - AppConfig::new(self.config), + self.config.clone(), Rc::new(self.app_data), HttpRequestPool::create(), ) @@ -499,7 +501,7 @@ impl TestRequest { head, Payload::None, Rc::new(self.rmap), - AppConfig::new(self.config), + self.config.clone(), Rc::new(self.app_data), HttpRequestPool::create(), ); @@ -538,7 +540,7 @@ pub fn start(factory: F) -> TestServer where F: Fn() -> I + Send + Clone + 'static, I: IntoServiceFactory, - S: ServiceFactory + 'static, + S: ServiceFactory + 'static, S::Error: Into + 'static, S::InitError: fmt::Debug, S::Response: Into> + 'static, @@ -577,7 +579,7 @@ pub fn start_with(cfg: TestServerConfig, factory: F) -> TestServer where F: Fn() -> I + Send + Clone + 'static, I: IntoServiceFactory, - S: ServiceFactory + 'static, + S: ServiceFactory + 'static, S::Error: Into + 'static, S::InitError: fmt::Debug, S::Response: Into> + 'static, @@ -607,63 +609,87 @@ where match cfg.stream { StreamType::Tcp => match cfg.tp { HttpVer::Http1 => builder.listen("test", tcp, move || { + let cfg = + AppConfig::new(false, local_addr, format!("{}", local_addr)); HttpService::build() .client_timeout(ctimeout) - .h1(factory()) + .h1(map_config(factory().into_factory(), move |_| cfg.clone())) .tcp() }), HttpVer::Http2 => builder.listen("test", tcp, move || { + let cfg = + AppConfig::new(false, local_addr, format!("{}", local_addr)); HttpService::build() .client_timeout(ctimeout) - .h2(factory()) + .h2(map_config(factory().into_factory(), move |_| cfg.clone())) .tcp() }), HttpVer::Both => builder.listen("test", tcp, move || { + let cfg = + AppConfig::new(false, local_addr, format!("{}", local_addr)); HttpService::build() .client_timeout(ctimeout) - .finish(factory()) + .finish(map_config(factory().into_factory(), move |_| { + cfg.clone() + })) .tcp() }), }, #[cfg(feature = "openssl")] StreamType::Openssl(acceptor) => match cfg.tp { HttpVer::Http1 => builder.listen("test", tcp, move || { + let cfg = + AppConfig::new(true, local_addr, format!("{}", local_addr)); HttpService::build() .client_timeout(ctimeout) - .h1(factory()) + .h1(map_config(factory().into_factory(), move |_| cfg.clone())) .openssl(acceptor.clone()) }), HttpVer::Http2 => builder.listen("test", tcp, move || { + let cfg = + AppConfig::new(true, local_addr, format!("{}", local_addr)); HttpService::build() .client_timeout(ctimeout) - .h2(factory()) + .h2(map_config(factory().into_factory(), move |_| cfg.clone())) .openssl(acceptor.clone()) }), HttpVer::Both => builder.listen("test", tcp, move || { + let cfg = + AppConfig::new(true, local_addr, format!("{}", local_addr)); HttpService::build() .client_timeout(ctimeout) - .finish(factory()) + .finish(map_config(factory().into_factory(), move |_| { + cfg.clone() + })) .openssl(acceptor.clone()) }), }, #[cfg(feature = "rustls")] StreamType::Rustls(config) => match cfg.tp { HttpVer::Http1 => builder.listen("test", tcp, move || { + let cfg = + AppConfig::new(true, local_addr, format!("{}", local_addr)); HttpService::build() .client_timeout(ctimeout) - .h1(factory()) + .h1(map_config(factory().into_factory(), move |_| cfg.clone())) .rustls(config.clone()) }), HttpVer::Http2 => builder.listen("test", tcp, move || { + let cfg = + AppConfig::new(true, local_addr, format!("{}", local_addr)); HttpService::build() .client_timeout(ctimeout) - .h2(factory()) + .h2(map_config(factory().into_factory(), move |_| cfg.clone())) .rustls(config.clone()) }), HttpVer::Both => builder.listen("test", tcp, move || { + let cfg = + AppConfig::new(true, local_addr, format!("{}", local_addr)); HttpService::build() .client_timeout(ctimeout) - .finish(factory()) + .finish(map_config(factory().into_factory(), move |_| { + cfg.clone() + })) .rustls(config.clone()) }), }, diff --git a/tests/test_httpserver.rs b/tests/test_httpserver.rs index d19c46ee7..f345a70f0 100644 --- a/tests/test_httpserver.rs +++ b/tests/test_httpserver.rs @@ -5,8 +5,7 @@ use std::{net, thread, time::Duration}; #[cfg(feature = "openssl")] use open_ssl::ssl::SslAcceptorBuilder; -use actix_http::Response; -use actix_web::{web, App, HttpServer}; +use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer}; fn unused_addr() -> net::SocketAddr { let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); @@ -28,7 +27,7 @@ async fn test_start() { let srv = HttpServer::new(|| { App::new().service( - web::resource("/").route(web::to(|| Response::Ok().body("test"))), + web::resource("/").route(web::to(|| HttpResponse::Ok().body("test"))), ) }) .workers(1) @@ -99,9 +98,10 @@ async fn test_start_ssl() { let builder = ssl_acceptor().unwrap(); let srv = HttpServer::new(|| { - App::new().service( - web::resource("/").route(web::to(|| Response::Ok().body("test"))), - ) + App::new().service(web::resource("/").route(web::to(|req: HttpRequest| { + assert!(req.app_config().secure()); + HttpResponse::Ok().body("test") + }))) }) .workers(1) .shutdown_timeout(1) From 20248daedad5bee1fcfe3847ea02c38d6ba85a0e Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 20 Dec 2019 16:11:51 +0600 Subject: [PATCH 145/176] Allow to set peer_addr for TestRequest #1074 --- CHANGES.md | 2 ++ src/test.rs | 27 +++++++++++++++++++++++---- tests/test_httpserver.rs | 4 +++- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index a8cfb8a26..e669ae38c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,8 @@ * Move `BodyEncoding` to `dev` module #1220 +* Allow to set `peer_addr` for TestRequest #1074 + ### Fixed * Fix `AppConfig::secure()` is always false. #1202 diff --git a/src/test.rs b/src/test.rs index 8de1ba141..4113a7c15 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,5 +1,6 @@ //! Various helpers for Actix applications to use during testing. use std::convert::TryFrom; +use std::net::SocketAddr; use std::rc::Rc; use std::sync::mpsc; use std::{fmt, net, thread, time}; @@ -300,6 +301,7 @@ pub struct TestRequest { rmap: ResourceMap, config: AppConfig, path: Path, + peer_addr: Option, app_data: Extensions, } @@ -310,6 +312,7 @@ impl Default for TestRequest { rmap: ResourceMap::new(ResourceDef::new("")), config: AppConfig::default(), path: Path::new(Url::new(Uri::default())), + peer_addr: None, app_data: Extensions::new(), } } @@ -409,6 +412,12 @@ impl TestRequest { self } + /// Set peer addr + pub fn peer_addr(mut self, addr: SocketAddr) -> Self { + self.peer_addr = Some(addr); + self + } + /// Set request payload pub fn set_payload>(mut self, data: B) -> Self { self.req.set_payload(data); @@ -451,12 +460,15 @@ impl TestRequest { /// Complete request creation and generate `Request` instance pub fn to_request(mut self) -> Request { - self.req.finish() + let mut req = self.req.finish(); + req.head_mut().peer_addr = self.peer_addr; + req } /// Complete request creation and generate `ServiceRequest` instance pub fn to_srv_request(mut self) -> ServiceRequest { - let (head, payload) = self.req.finish().into_parts(); + let (mut head, payload) = self.req.finish().into_parts(); + head.peer_addr = self.peer_addr; self.path.get_mut().update(&head.uri); ServiceRequest::new(HttpRequest::new( @@ -477,7 +489,8 @@ impl TestRequest { /// Complete request creation and generate `HttpRequest` instance pub fn to_http_request(mut self) -> HttpRequest { - let (head, payload) = self.req.finish().into_parts(); + let (mut head, payload) = self.req.finish().into_parts(); + head.peer_addr = self.peer_addr; self.path.get_mut().update(&head.uri); HttpRequest::new( @@ -493,7 +506,8 @@ impl TestRequest { /// Complete request creation and generate `HttpRequest` and `Payload` instances pub fn to_http_parts(mut self) -> (HttpRequest, Payload) { - let (head, payload) = self.req.finish().into_parts(); + let (mut head, payload) = self.req.finish().into_parts(); + head.peer_addr = self.peer_addr; self.path.get_mut().update(&head.uri); let req = HttpRequest::new( @@ -950,9 +964,14 @@ mod tests { .set(header::Date(SystemTime::now().into())) .param("test", "123") .data(10u32) + .peer_addr("127.0.0.1:8081".parse().unwrap()) .to_http_request(); assert!(req.headers().contains_key(header::CONTENT_TYPE)); assert!(req.headers().contains_key(header::DATE)); + assert_eq!( + req.head().peer_addr, + Some("127.0.0.1:8081".parse().unwrap()) + ); assert_eq!(&req.match_info()["test"], "123"); assert_eq!(req.version(), Version::HTTP_2); let data = req.get_app_data::().unwrap(); diff --git a/tests/test_httpserver.rs b/tests/test_httpserver.rs index f345a70f0..48d8b3872 100644 --- a/tests/test_httpserver.rs +++ b/tests/test_httpserver.rs @@ -5,7 +5,7 @@ use std::{net, thread, time::Duration}; #[cfg(feature = "openssl")] use open_ssl::ssl::SslAcceptorBuilder; -use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer}; +use actix_web::{web, App, HttpResponse, HttpServer}; fn unused_addr() -> net::SocketAddr { let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); @@ -90,6 +90,8 @@ fn ssl_acceptor() -> std::io::Result { #[actix_rt::test] #[cfg(feature = "openssl")] async fn test_start_ssl() { + use actix_web::HttpRequest; + let addr = unused_addr(); let (tx, rx) = mpsc::channel(); From c877840c07d657fd7379d4bc8381f7c38c29edd2 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 20 Dec 2019 17:13:09 +0600 Subject: [PATCH 146/176] rename App::register_data to App::app_data and HttpRequest::app_data returns Option<&T> instead of Option<&Data> --- CHANGES.md | 4 ++++ MIGRATION.md | 5 +++++ src/app.rs | 33 +++++++++++++++++++++++++++++---- src/app_service.rs | 10 +++++++++- src/data.rs | 12 ++++++------ src/request.rs | 21 +++++---------------- src/resource.rs | 13 +++++-------- src/scope.rs | 32 +++++++++++++------------------- src/test.rs | 19 +++++++++++++------ src/types/form.rs | 2 +- src/types/json.rs | 35 ++++++++++++++++++----------------- src/types/path.rs | 4 ++-- src/types/payload.rs | 2 +- src/types/query.rs | 4 ++-- tests/test_server.rs | 2 +- 15 files changed, 114 insertions(+), 84 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e669ae38c..54c5bb06c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,10 @@ * Allow to set `peer_addr` for TestRequest #1074 +* Rename `App::register_data()` to `App::app_data()` + +* `HttpRequest::app_data()` returns `Option<&T>` instead of `Option<&Data>` + ### Fixed * Fix `AppConfig::secure()` is always false. #1202 diff --git a/MIGRATION.md b/MIGRATION.md index dd3a1b043..357a4e4ca 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,5 +1,10 @@ ## 2.0.0 +* `App::register_data()` renamed to `App::app_data()` and accepts any type `T: 'static`. + Stored data is available via `HttpRequest::app_data()` method at runtime. + +* Extractor configuration must be registered with `App::app_data()` instead of `App::data()` + * Sync handlers has been removed. `.to_async()` method has been renamed to `.to()` replace `fn` with `async fn` to convert sync handler to async diff --git a/src/app.rs b/src/app.rs index ccf2e88e0..962ff4b47 100644 --- a/src/app.rs +++ b/src/app.rs @@ -5,6 +5,7 @@ use std::marker::PhantomData; use std::rc::Rc; use actix_http::body::{Body, MessageBody}; +use actix_http::Extensions; use actix_service::boxed::{self, BoxServiceFactory}; use actix_service::{ apply, apply_fn_factory, IntoServiceFactory, ServiceFactory, Transform, @@ -37,6 +38,7 @@ pub struct App { data: Vec>, data_factories: Vec, external: Vec, + extensions: Extensions, _t: PhantomData, } @@ -52,6 +54,7 @@ impl App { default: None, factory_ref: fref, external: Vec::new(), + extensions: Extensions::new(), _t: PhantomData, } } @@ -133,10 +136,15 @@ where self } - /// Set application data. Application data could be accessed - /// by using `Data` extractor where `T` is data type. - pub fn register_data(mut self, data: Data) -> Self { - self.data.push(Box::new(data)); + /// Set application level arbitrary data item. + /// + /// Application data stored with `App::app_data()` method is available + /// via `HttpRequest::app_data()` method at runtime. + /// + /// This method could be used for storing `Data` as well, in that case + /// data could be accessed by using `Data` extractor. + pub fn app_data(mut self, ext: U) -> Self { + self.extensions.insert(ext); self } @@ -370,6 +378,7 @@ where default: self.default, factory_ref: self.factory_ref, external: self.external, + extensions: self.extensions, _t: PhantomData, } } @@ -431,6 +440,7 @@ where default: self.default, factory_ref: self.factory_ref, external: self.external, + extensions: self.extensions, _t: PhantomData, } } @@ -456,6 +466,7 @@ where external: RefCell::new(self.external), default: self.default, factory_ref: self.factory_ref, + extensions: RefCell::new(Some(self.extensions)), } } } @@ -539,6 +550,20 @@ mod tests { assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); } + #[actix_rt::test] + async fn test_extension() { + let mut srv = init_service(App::new().app_data(10usize).service( + web::resource("/").to(|req: HttpRequest| { + assert_eq!(*req.app_data::().unwrap(), 10); + HttpResponse::Ok() + }), + )) + .await; + let req = TestRequest::default().to_request(); + let resp = srv.call(req).await.unwrap(); + assert_eq!(resp.status(), StatusCode::OK); + } + #[actix_rt::test] async fn test_wrap() { let mut srv = init_service( diff --git a/src/app_service.rs b/src/app_service.rs index 28e245228..ccfefbc68 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -39,6 +39,7 @@ where >, { pub(crate) endpoint: T, + pub(crate) extensions: RefCell>, pub(crate) data: Rc>>, pub(crate) data_factories: Rc>, pub(crate) services: Rc>>>, @@ -114,6 +115,12 @@ where data: self.data.clone(), data_factories: Vec::new(), data_factories_fut: self.data_factories.iter().map(|f| f()).collect(), + extensions: Some( + self.extensions + .borrow_mut() + .take() + .unwrap_or_else(Extensions::new), + ), config, rmap, _t: PhantomData, @@ -134,6 +141,7 @@ where data: Rc>>, data_factories: Vec>, data_factories_fut: Vec, ()>>>, + extensions: Option, _t: PhantomData, } @@ -172,7 +180,7 @@ where if this.endpoint.is_some() && this.data_factories_fut.is_empty() { // create app data container - let mut data = Extensions::new(); + let mut data = this.extensions.take().unwrap(); for f in this.data.iter() { f.create(&mut data); } diff --git a/src/data.rs b/src/data.rs index e8928188f..eaa2db388 100644 --- a/src/data.rs +++ b/src/data.rs @@ -56,7 +56,7 @@ pub(crate) trait DataFactory { /// /// let app = App::new() /// // Store `MyData` in application storage. -/// .register_data(data.clone()) +/// .app_data(data.clone()) /// .service( /// web::resource("/index.html").route( /// web::get().to(index))); @@ -107,8 +107,8 @@ impl FromRequest for Data { #[inline] fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { - if let Some(st) = req.get_app_data::() { - ok(st) + if let Some(st) = req.app_data::>() { + ok(st.clone()) } else { log::debug!( "Failed to construct App-level Data extractor. \ @@ -165,9 +165,9 @@ mod tests { } #[actix_rt::test] - async fn test_register_data_extractor() { + async fn test_app_data_extractor() { let mut srv = - init_service(App::new().register_data(Data::new(10usize)).service( + init_service(App::new().app_data(Data::new(10usize)).service( web::resource("/").to(|_: web::Data| HttpResponse::Ok()), )) .await; @@ -177,7 +177,7 @@ mod tests { assert_eq!(resp.status(), StatusCode::OK); let mut srv = - init_service(App::new().register_data(Data::new(10u32)).service( + init_service(App::new().app_data(Data::new(10u32)).service( web::resource("/").to(|_: web::Data| HttpResponse::Ok()), )) .await; diff --git a/src/request.rs b/src/request.rs index 46b8fe387..b51438950 100644 --- a/src/request.rs +++ b/src/request.rs @@ -8,7 +8,6 @@ use actix_router::{Path, Url}; use futures::future::{ok, Ready}; use crate::config::AppConfig; -use crate::data::Data; use crate::error::UrlGenerationError; use crate::extract::FromRequest; use crate::info::ConnectionInfo; @@ -207,25 +206,15 @@ impl HttpRequest { &self.0.config } - /// Get an application data stored with `App::data()` method during + /// Get an application data stored with `App::extension()` method during /// application configuration. pub fn app_data(&self) -> Option<&T> { - if let Some(st) = self.0.app_data.get::>() { + if let Some(st) = self.0.app_data.get::() { Some(&st) } else { None } } - - /// Get an application data stored with `App::data()` method during - /// application configuration. - pub fn get_app_data(&self) -> Option> { - if let Some(st) = self.0.app_data.get::>() { - Some(st.clone()) - } else { - None - } - } } impl HttpMessage for HttpRequest { @@ -467,8 +456,8 @@ mod tests { } #[actix_rt::test] - async fn test_app_data() { - let mut srv = init_service(App::new().data(10usize).service( + async fn test_data() { + let mut srv = init_service(App::new().app_data(10usize).service( web::resource("/").to(|req: HttpRequest| { if req.app_data::().is_some() { HttpResponse::Ok() @@ -483,7 +472,7 @@ mod tests { let resp = call_service(&mut srv, req).await; assert_eq!(resp.status(), StatusCode::OK); - let mut srv = init_service(App::new().data(10u32).service( + let mut srv = init_service(App::new().app_data(10u32).service( web::resource("/").to(|req: HttpRequest| { if req.app_data::().is_some() { HttpResponse::Ok() diff --git a/src/resource.rs b/src/resource.rs index 41d663d3d..8fc5973e0 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -192,16 +192,13 @@ where /// } /// ``` pub fn data(self, data: U) -> Self { - self.register_data(Data::new(data)) + self.app_data(Data::new(data)) } /// Set or override application data. /// - /// This method has the same effect as [`Resource::data`](#method.data), - /// except that instead of taking a value of some type `T`, it expects a - /// value of type `Data`. Use a `Data` extractor to retrieve its - /// value. - pub fn register_data(mut self, data: Data) -> Self { + /// This method overrides data stored with [`App::app_data()`](#method.app_data) + pub fn app_data(mut self, data: U) -> Self { if self.data.is_none() { self.data = Some(Extensions::new()); } @@ -754,11 +751,11 @@ mod tests { App::new() .data(1.0f64) .data(1usize) - .register_data(web::Data::new('-')) + .app_data(web::Data::new('-')) .service( web::resource("/test") .data(10usize) - .register_data(web::Data::new('*')) + .app_data(web::Data::new('*')) .guard(guard::Get()) .to( |data1: web::Data, diff --git a/src/scope.rs b/src/scope.rs index 26ace6892..5a965292b 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -148,15 +148,13 @@ where /// } /// ``` pub fn data(self, data: U) -> Self { - self.register_data(Data::new(data)) + self.app_data(Data::new(data)) } /// Set or override application data. /// - /// This method has the same effect as [`Scope::data`](#method.data), except - /// that instead of taking a value of some type `T`, it expects a value of - /// type `Data`. Use a `Data` extractor to retrieve its value. - pub fn register_data(mut self, data: Data) -> Self { + /// This method overrides data stored with [`App::app_data()`](#method.app_data) + pub fn app_data(mut self, data: U) -> Self { if self.data.is_none() { self.data = Some(Extensions::new()); } @@ -1122,21 +1120,17 @@ mod tests { } #[actix_rt::test] - async fn test_override_register_data() { - let mut srv = init_service( - App::new().register_data(web::Data::new(1usize)).service( - web::scope("app") - .register_data(web::Data::new(10usize)) - .route( - "/t", - web::get().to(|data: web::Data| { - assert_eq!(*data, 10); - let _ = data.clone(); - HttpResponse::Ok() - }), - ), + async fn test_override_app_data() { + let mut srv = init_service(App::new().app_data(web::Data::new(1usize)).service( + web::scope("app").app_data(web::Data::new(10usize)).route( + "/t", + web::get().to(|data: web::Data| { + assert_eq!(*data, 10); + let _ = data.clone(); + HttpResponse::Ok() + }), ), - ) + )) .await; let req = TestRequest::with_uri("/app/t").to_request(); diff --git a/src/test.rs b/src/test.rs index 4113a7c15..a10490fd5 100644 --- a/src/test.rs +++ b/src/test.rs @@ -451,6 +451,13 @@ impl TestRequest { self } + /// Set application data. This is equivalent of `App::app_data()` method + /// for testing purpose. + pub fn app_data(mut self, data: T) -> Self { + self.app_data.insert(data); + self + } + #[cfg(test)] /// Set request config pub(crate) fn rmap(mut self, rmap: ResourceMap) -> Self { @@ -964,6 +971,7 @@ mod tests { .set(header::Date(SystemTime::now().into())) .param("test", "123") .data(10u32) + .app_data(20u64) .peer_addr("127.0.0.1:8081".parse().unwrap()) .to_http_request(); assert!(req.headers().contains_key(header::CONTENT_TYPE)); @@ -974,14 +982,13 @@ mod tests { ); assert_eq!(&req.match_info()["test"], "123"); assert_eq!(req.version(), Version::HTTP_2); - let data = req.get_app_data::().unwrap(); - assert!(req.get_app_data::().is_none()); - assert_eq!(*data, 10); + let data = req.app_data::>().unwrap(); + assert!(req.app_data::>().is_none()); assert_eq!(*data.get_ref(), 10); - assert!(req.app_data::().is_none()); - let data = req.app_data::().unwrap(); - assert_eq!(*data, 10); + assert!(req.app_data::().is_none()); + let data = req.app_data::().unwrap(); + assert_eq!(*data, 20); } #[actix_rt::test] diff --git a/src/types/form.rs b/src/types/form.rs index 756d5fcc9..333ecbd64 100644 --- a/src/types/form.rs +++ b/src/types/form.rs @@ -190,7 +190,7 @@ impl Responder for Form { /// let app = App::new().service( /// web::resource("/index.html") /// // change `Form` extractor configuration -/// .data( +/// .app_data( /// web::Form::::configure(|cfg| cfg.limit(4097)) /// ) /// .route(web::get().to(index)) diff --git a/src/types/json.rs b/src/types/json.rs index 03c4a2db6..adb425cd9 100644 --- a/src/types/json.rs +++ b/src/types/json.rs @@ -224,17 +224,18 @@ where /// /// fn main() { /// let app = App::new().service( -/// web::resource("/index.html").data( -/// // change json extractor configuration -/// web::Json::::configure(|cfg| { -/// cfg.limit(4096) -/// .content_type(|mime| { // <- accept text/plain content type -/// mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN -/// }) -/// .error_handler(|err, req| { // <- create custom error response -/// error::InternalError::from_response( -/// err, HttpResponse::Conflict().finish()).into() -/// }) +/// web::resource("/index.html") +/// .app_data( +/// // change json extractor configuration +/// web::Json::::configure(|cfg| { +/// cfg.limit(4096) +/// .content_type(|mime| { // <- accept text/plain content type +/// mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN +/// }) +/// .error_handler(|err, req| { // <- create custom error response +/// error::InternalError::from_response( +/// err, HttpResponse::Conflict().finish()).into() +/// }) /// })) /// .route(web::post().to(index)) /// ); @@ -462,7 +463,7 @@ mod tests { header::HeaderValue::from_static("16"), ) .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .data(JsonConfig::default().limit(10).error_handler(|err, _| { + .app_data(JsonConfig::default().limit(10).error_handler(|err, _| { let msg = MyObject { name: "invalid request".to_string(), }; @@ -514,7 +515,7 @@ mod tests { header::HeaderValue::from_static("16"), ) .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .data(JsonConfig::default().limit(10)) + .app_data(JsonConfig::default().limit(10)) .to_http_parts(); let s = Json::::from_request(&req, &mut pl).await; @@ -531,7 +532,7 @@ mod tests { header::HeaderValue::from_static("16"), ) .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .data( + .app_data( JsonConfig::default() .limit(10) .error_handler(|_, _| JsonPayloadError::ContentType.into()), @@ -604,7 +605,7 @@ mod tests { header::HeaderValue::from_static("16"), ) .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .data(JsonConfig::default().limit(4096)) + .app_data(JsonConfig::default().limit(4096)) .to_http_parts(); let s = Json::::from_request(&req, &mut pl).await; @@ -622,7 +623,7 @@ mod tests { header::HeaderValue::from_static("16"), ) .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .data(JsonConfig::default().content_type(|mime: mime::Mime| { + .app_data(JsonConfig::default().content_type(|mime: mime::Mime| { mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN })) .to_http_parts(); @@ -642,7 +643,7 @@ mod tests { header::HeaderValue::from_static("16"), ) .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .data(JsonConfig::default().content_type(|mime: mime::Mime| { + .app_data(JsonConfig::default().content_type(|mime: mime::Mime| { mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN })) .to_http_parts(); diff --git a/src/types/path.rs b/src/types/path.rs index d1a5f1fb9..9af5a0b9a 100644 --- a/src/types/path.rs +++ b/src/types/path.rs @@ -212,7 +212,7 @@ where /// fn main() { /// let app = App::new().service( /// web::resource("/messages/{folder}") -/// .data(PathConfig::default().error_handler(|err, req| { +/// .app_data(PathConfig::default().error_handler(|err, req| { /// error::InternalError::from_response( /// err, /// HttpResponse::Conflict().finish(), @@ -358,7 +358,7 @@ mod tests { #[actix_rt::test] async fn test_custom_err_handler() { let (req, mut pl) = TestRequest::with_uri("/name/user1/") - .data(PathConfig::default().error_handler(|err, _| { + .app_data(PathConfig::default().error_handler(|err, _| { error::InternalError::from_response( err, HttpResponse::Conflict().finish(), diff --git a/src/types/payload.rs b/src/types/payload.rs index 7cb714a87..dd7a84f32 100644 --- a/src/types/payload.rs +++ b/src/types/payload.rs @@ -176,7 +176,7 @@ impl FromRequest for Bytes { /// fn main() { /// let app = App::new().service( /// web::resource("/index.html") -/// .data(String::configure(|cfg| { // <- limit size of the payload +/// .app_data(String::configure(|cfg| { // <- limit size of the payload /// cfg.limit(4096) /// })) /// .route(web::get().to(index)) // <- register handler with extractor params diff --git a/src/types/query.rs b/src/types/query.rs index 9d62f31c6..696e10b94 100644 --- a/src/types/query.rs +++ b/src/types/query.rs @@ -185,7 +185,7 @@ where /// /// fn main() { /// let app = App::new().service( -/// web::resource("/index.html").data( +/// web::resource("/index.html").app_data( /// // change query extractor configuration /// web::Query::::configure(|cfg| { /// cfg.error_handler(|err, req| { // <- create custom error response @@ -273,7 +273,7 @@ mod tests { #[actix_rt::test] async fn test_custom_error_responder() { let req = TestRequest::with_uri("/name/user1/") - .data(QueryConfig::default().error_handler(|e, _| { + .app_data(QueryConfig::default().error_handler(|e, _| { let resp = HttpResponse::UnprocessableEntity().finish(); InternalError::from_response(e, resp).into() })) diff --git a/tests/test_server.rs b/tests/test_server.rs index 5019157e2..1916b372c 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -675,7 +675,7 @@ async fn test_brotli_encoding_large() { let srv = test::start_with(test::config().h1(), || { App::new().service( web::resource("/") - .data(web::PayloadConfig::new(320_000)) + .app_data(web::PayloadConfig::new(320_000)) .route(web::to(move |body: Bytes| { HttpResponse::Ok().streaming(TestBody::new(body, 10240)) })), From 74fa4060c2138dd070e9bfac65429d2ff81568cf Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 20 Dec 2019 17:27:32 +0600 Subject: [PATCH 147/176] fix awc tests --- awc/tests/test_client.rs | 96 +++++++++++++++++++-------------- awc/tests/test_rustls_client.rs | 11 ++-- awc/tests/test_ssl_client.rs | 14 +++-- 3 files changed, 73 insertions(+), 48 deletions(-) diff --git a/awc/tests/test_client.rs b/awc/tests/test_client.rs index f8fed5bfd..af63c8163 100644 --- a/awc/tests/test_client.rs +++ b/awc/tests/test_client.rs @@ -14,8 +14,8 @@ use rand::Rng; use actix_http::HttpService; use actix_http_test::test_server; -use actix_service::pipeline_factory; -use actix_web::dev::BodyEncoding; +use actix_service::{map_config, pipeline_factory, IntoServiceFactory}; +use actix_web::dev::{AppConfig, BodyEncoding}; use actix_web::http::Cookie; use actix_web::middleware::Compress; use actix_web::{ @@ -170,10 +170,12 @@ async fn test_connection_reuse() { ok(io) }) .and_then( - HttpService::new( + HttpService::new(map_config( App::new() - .service(web::resource("/").route(web::to(|| HttpResponse::Ok()))), - ) + .service(web::resource("/").route(web::to(|| HttpResponse::Ok()))) + .into_factory(), + |_| AppConfig::default(), + )) .tcp(), ) }); @@ -206,10 +208,12 @@ async fn test_connection_force_close() { ok(io) }) .and_then( - HttpService::new( + HttpService::new(map_config( App::new() - .service(web::resource("/").route(web::to(|| HttpResponse::Ok()))), - ) + .service(web::resource("/").route(web::to(|| HttpResponse::Ok()))) + .into_factory(), + |_| AppConfig::default(), + )) .tcp(), ) }); @@ -235,22 +239,25 @@ async fn test_connection_server_close() { let num = Arc::new(AtomicUsize::new(0)); let num2 = num.clone(); - let srv = test_server(move || { - let num2 = num2.clone(); - pipeline_factory(move |io| { - num2.fetch_add(1, Ordering::Relaxed); - ok(io) - }) - .and_then( - HttpService::new( - App::new().service( - web::resource("/") - .route(web::to(|| HttpResponse::Ok().force_close().finish())), - ), + let srv = + test_server(move || { + let num2 = num2.clone(); + pipeline_factory(move |io| { + num2.fetch_add(1, Ordering::Relaxed); + ok(io) + }) + .and_then( + HttpService::new(map_config( + App::new() + .service(web::resource("/").route(web::to(|| { + HttpResponse::Ok().force_close().finish() + }))) + .into_factory(), + |_| AppConfig::default(), + )) + .tcp(), ) - .tcp(), - ) - }); + }); let client = awc::Client::default(); @@ -280,8 +287,14 @@ async fn test_connection_wait_queue() { ok(io) }) .and_then( - HttpService::new(App::new().service( - web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))), + HttpService::new(map_config( + App::new() + .service( + web::resource("/") + .route(web::to(|| HttpResponse::Ok().body(STR))), + ) + .into_factory(), + |_| AppConfig::default(), )) .tcp(), ) @@ -317,22 +330,25 @@ async fn test_connection_wait_queue_force_close() { let num = Arc::new(AtomicUsize::new(0)); let num2 = num.clone(); - let srv = test_server(move || { - let num2 = num2.clone(); - pipeline_factory(move |io| { - num2.fetch_add(1, Ordering::Relaxed); - ok(io) - }) - .and_then( - HttpService::new( - App::new().service( - web::resource("/") - .route(web::to(|| HttpResponse::Ok().force_close().body(STR))), - ), + let srv = + test_server(move || { + let num2 = num2.clone(); + pipeline_factory(move |io| { + num2.fetch_add(1, Ordering::Relaxed); + ok(io) + }) + .and_then( + HttpService::new(map_config( + App::new() + .service(web::resource("/").route(web::to(|| { + HttpResponse::Ok().force_close().body(STR) + }))) + .into_factory(), + |_| AppConfig::default(), + )) + .tcp(), ) - .tcp(), - ) - }); + }); let client = awc::Client::build() .connector(awc::Connector::new().limit(1).finish()) diff --git a/awc/tests/test_rustls_client.rs b/awc/tests/test_rustls_client.rs index 46db518aa..5da5c1899 100644 --- a/awc/tests/test_rustls_client.rs +++ b/awc/tests/test_rustls_client.rs @@ -4,9 +4,9 @@ use std::sync::Arc; use actix_http::HttpService; use actix_http_test::test_server; -use actix_service::{pipeline_factory, ServiceFactory}; +use actix_service::{map_config, pipeline_factory, ServiceFactory, IntoServiceFactory}; use actix_web::http::Version; -use actix_web::{web, App, HttpResponse}; +use actix_web::{web, App, HttpResponse, dev::AppConfig}; use futures::future::ok; use open_ssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslVerifyMode}; use rust_tls::ClientConfig; @@ -62,8 +62,11 @@ async fn _test_connection_reuse_h2() { }) .and_then( HttpService::build() - .h2(App::new() - .service(web::resource("/").route(web::to(|| HttpResponse::Ok())))) + .h2(map_config(App::new() + .service(web::resource("/").route(web::to(|| HttpResponse::Ok()))) + .into_factory(), + |_| AppConfig::default(), + )) .openssl(ssl_acceptor()) .map_err(|_| ()), ) diff --git a/awc/tests/test_ssl_client.rs b/awc/tests/test_ssl_client.rs index d36e303fa..8edc83a9e 100644 --- a/awc/tests/test_ssl_client.rs +++ b/awc/tests/test_ssl_client.rs @@ -4,9 +4,9 @@ use std::sync::Arc; use actix_http::HttpService; use actix_http_test::test_server; -use actix_service::{pipeline_factory, ServiceFactory}; +use actix_service::{map_config, pipeline_factory, IntoServiceFactory, ServiceFactory}; use actix_web::http::Version; -use actix_web::{web, App, HttpResponse}; +use actix_web::{dev::AppConfig, web, App, HttpResponse}; use futures::future::ok; use open_ssl::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod, SslVerifyMode}; @@ -44,8 +44,14 @@ async fn test_connection_reuse_h2() { }) .and_then( HttpService::build() - .h2(App::new() - .service(web::resource("/").route(web::to(|| HttpResponse::Ok())))) + .h2(map_config( + App::new() + .service( + web::resource("/").route(web::to(|| HttpResponse::Ok())), + ) + .into_factory(), + |_| AppConfig::default(), + )) .openssl(ssl_acceptor()) .map_err(|_| ()), ) From 8b8a9a995d74a8aea5efaa99ffa3e629701e84a4 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 20 Dec 2019 17:36:48 +0600 Subject: [PATCH 148/176] bump ver --- CHANGES.md | 2 +- Cargo.toml | 2 +- actix-cors/Cargo.toml | 2 +- actix-files/Cargo.toml | 6 +++--- actix-identity/Cargo.toml | 4 ++-- actix-multipart/Cargo.toml | 2 +- actix-session/Cargo.toml | 2 +- actix-web-actors/Cargo.toml | 8 ++++---- actix-web-codegen/Cargo.toml | 2 +- awc/Cargo.toml | 4 ++-- awc/tests/test_rustls_client.rs | 15 +++++++++------ src/app.rs | 5 +++-- test-server/Cargo.toml | 4 ++-- 13 files changed, 31 insertions(+), 27 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 54c5bb06c..281879685 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## [2.0.0] - 2019-12-xx +## [2.0.0-rc] - 2019-12-20 ### Changed diff --git a/Cargo.toml b/Cargo.toml index 0fab5d58c..aba3c6546 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "2.0.0-alpha.6" +version = "2.0.0-rc" authors = ["Nikolay Kim "] description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." readme = "README.md" diff --git a/actix-cors/Cargo.toml b/actix-cors/Cargo.toml index 05885acfd..392168e42 100644 --- a/actix-cors/Cargo.toml +++ b/actix-cors/Cargo.toml @@ -17,7 +17,7 @@ name = "actix_cors" path = "src/lib.rs" [dependencies] -actix-web = "2.0.0-alpha.5" +actix-web = "2.0.0-rc" actix-service = "1.0.0" derive_more = "0.99.2" futures = "0.3.1" diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 3eecdbabe..7f11605a1 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -18,8 +18,8 @@ name = "actix_files" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-alpha.6", default-features = false } -actix-http = "1.0.0" +actix-web = { version = "2.0.0-rc", default-features = false } +actix-http = "1.0.1" actix-service = "1.0.0" bitflags = "1" bytes = "0.5.3" @@ -33,4 +33,4 @@ v_htmlescape = "0.4" [dev-dependencies] actix-rt = "1.0.0" -actix-web = { version = "2.0.0-alpha.6", features=["openssl"] } +actix-web = { version = "2.0.0-rc", features=["openssl"] } diff --git a/actix-identity/Cargo.toml b/actix-identity/Cargo.toml index 4e96203bf..a5058b8cd 100644 --- a/actix-identity/Cargo.toml +++ b/actix-identity/Cargo.toml @@ -17,7 +17,7 @@ name = "actix_identity" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-alpha.5", default-features = false, features = ["secure-cookies"] } +actix-web = { version = "2.0.0-rc", default-features = false, features = ["secure-cookies"] } actix-service = "1.0.0" futures = "0.3.1" serde = "1.0" @@ -26,5 +26,5 @@ time = "0.1.42" [dev-dependencies] actix-rt = "1.0.0" -actix-http = "1.0.0" +actix-http = "1.0.1" bytes = "0.5.3" \ No newline at end of file diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index b0ef36d10..bbf83ed00 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -16,7 +16,7 @@ name = "actix_multipart" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-alpha.5", default-features = false } +actix-web = { version = "2.0.0-rc", default-features = false } actix-service = "1.0.0" actix-utils = "1.0.3" bytes = "0.5.3" diff --git a/actix-session/Cargo.toml b/actix-session/Cargo.toml index f26401851..ed80174ff 100644 --- a/actix-session/Cargo.toml +++ b/actix-session/Cargo.toml @@ -22,7 +22,7 @@ default = ["cookie-session"] cookie-session = ["actix-web/secure-cookies"] [dependencies] -actix-web = "2.0.0-alpha.5" +actix-web = "2.0.0-rc" actix-service = "1.0.0" bytes = "0.5.3" derive_more = "0.99.2" diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index d4fe45363..6f573e442 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web-actors" -version = "2.0.0-alpha.1" +version = "2.0.0" authors = ["Nikolay Kim "] description = "Actix actors support for actix web framework." readme = "README.md" @@ -16,9 +16,9 @@ name = "actix_web_actors" path = "src/lib.rs" [dependencies] -actix = "0.9.0-alpha.1" -actix-web = "2.0.0-alpha.5" -actix-http = "1.0.0" +actix = "0.9.0" +actix-web = "2.0.0-rc" +actix-http = "1.0.1" actix-codec = "0.2.0" bytes = "0.5.2" futures = "0.3.1" diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 71facfe9d..3fe561deb 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -18,5 +18,5 @@ proc-macro2 = "^1" [dev-dependencies] actix-rt = { version = "1.0.0" } -actix-web = { version = "2.0.0-alpha.4" } +actix-web = { version = "2.0.0-rc" } futures = { version = "0.3.1" } diff --git a/awc/Cargo.toml b/awc/Cargo.toml index d1eaa7f69..723cd59de 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -55,8 +55,8 @@ rust-tls = { version = "0.16.0", package="rustls", optional = true, features = [ [dev-dependencies] actix-connect = { version = "1.0.1", features=["openssl"] } -actix-web = { version = "2.0.0-alpha.5", features=["openssl"] } -actix-http = { version = "1.0.0", features=["openssl"] } +actix-web = { version = "2.0.0-rc", features=["openssl"] } +actix-http = { version = "1.0.1", features=["openssl"] } actix-http-test = { version = "1.0.0", features=["openssl"] } actix-utils = "1.0.3" actix-server = "1.0.0" diff --git a/awc/tests/test_rustls_client.rs b/awc/tests/test_rustls_client.rs index 5da5c1899..68acf7f71 100644 --- a/awc/tests/test_rustls_client.rs +++ b/awc/tests/test_rustls_client.rs @@ -4,9 +4,9 @@ use std::sync::Arc; use actix_http::HttpService; use actix_http_test::test_server; -use actix_service::{map_config, pipeline_factory, ServiceFactory, IntoServiceFactory}; +use actix_service::{map_config, pipeline_factory, IntoServiceFactory, ServiceFactory}; use actix_web::http::Version; -use actix_web::{web, App, HttpResponse, dev::AppConfig}; +use actix_web::{dev::AppConfig, web, App, HttpResponse}; use futures::future::ok; use open_ssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslVerifyMode}; use rust_tls::ClientConfig; @@ -62,10 +62,13 @@ async fn _test_connection_reuse_h2() { }) .and_then( HttpService::build() - .h2(map_config(App::new() - .service(web::resource("/").route(web::to(|| HttpResponse::Ok()))) - .into_factory(), - |_| AppConfig::default(), + .h2(map_config( + App::new() + .service( + web::resource("/").route(web::to(|| HttpResponse::Ok())), + ) + .into_factory(), + |_| AppConfig::default(), )) .openssl(ssl_acceptor()) .map_err(|_| ()), diff --git a/src/app.rs b/src/app.rs index 962ff4b47..a060eb53e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -78,8 +78,9 @@ where /// an application instance. Http server constructs an application /// instance for each thread, thus application data must be constructed /// multiple times. If you want to share data between different - /// threads, a shared object should be used, e.g. `Arc`. Application - /// data does not need to be `Send` or `Sync`. + /// threads, a shared object should be used, e.g. `Arc`. Internally `Data` type + /// uses `Arc` so data could be created outside of app factory and clones could + /// be stored via `App::app_data()` method. /// /// ```rust /// use std::cell::Cell; diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index a6173088a..54cd01686 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -55,5 +55,5 @@ time = "0.1" open-ssl = { version="0.10", package="openssl", optional = true } [dev-dependencies] -actix-web = "2.0.0-alpha.5" -actix-http = "1.0.0" +actix-web = "2.0.0-rc" +actix-http = "1.0.1" From e5a50f423d79132f8acbab8b715480e734772de6 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 20 Dec 2019 17:45:35 +0600 Subject: [PATCH 149/176] Make web::Data deref to Arc #1214 --- CHANGES.md | 2 ++ src/data.rs | 20 +++++++++++--------- src/resource.rs | 6 +++--- src/scope.rs | 4 ++-- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 281879685..66b214bdc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,8 @@ * Allow to set `peer_addr` for TestRequest #1074 +* Make web::Data deref to Arc #1214 + * Rename `App::register_data()` to `App::app_data()` * `HttpRequest::app_data()` returns `Option<&T>` instead of `Option<&Data>` diff --git a/src/data.rs b/src/data.rs index eaa2db388..2867d68fa 100644 --- a/src/data.rs +++ b/src/data.rs @@ -87,10 +87,10 @@ impl Data { } impl Deref for Data { - type Target = T; + type Target = Arc; - fn deref(&self) -> &T { - self.0.as_ref() + fn deref(&self) -> &Arc { + &self.0 } } @@ -144,11 +144,13 @@ mod tests { #[actix_rt::test] async fn test_data_extractor() { - let mut srv = - init_service(App::new().data(10usize).service( - web::resource("/").to(|_: web::Data| HttpResponse::Ok()), - )) - .await; + let mut srv = init_service(App::new().data("TEST".to_string()).service( + web::resource("/").to(|data: web::Data| { + assert_eq!(data.to_lowercase(), "test"); + HttpResponse::Ok() + }), + )) + .await; let req = TestRequest::default().to_request(); let resp = srv.call(req).await.unwrap(); @@ -220,7 +222,7 @@ mod tests { let mut srv = init_service(App::new().data(1usize).service( web::resource("/").data(10usize).route(web::get().to( |data: web::Data| { - assert_eq!(*data, 10); + assert_eq!(**data, 10); let _ = data.clone(); HttpResponse::Ok() }, diff --git a/src/resource.rs b/src/resource.rs index 8fc5973e0..2ee084415 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -761,9 +761,9 @@ mod tests { |data1: web::Data, data2: web::Data, data3: web::Data| { - assert_eq!(*data1, 10); - assert_eq!(*data2, '*'); - assert_eq!(*data3, 1.0); + assert_eq!(**data1, 10); + assert_eq!(**data2, '*'); + assert_eq!(**data3, 1.0); HttpResponse::Ok() }, ), diff --git a/src/scope.rs b/src/scope.rs index 5a965292b..18e775e61 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -1106,7 +1106,7 @@ mod tests { web::scope("app").data(10usize).route( "/t", web::get().to(|data: web::Data| { - assert_eq!(*data, 10); + assert_eq!(**data, 10); let _ = data.clone(); HttpResponse::Ok() }), @@ -1125,7 +1125,7 @@ mod tests { web::scope("app").app_data(web::Data::new(10usize)).route( "/t", web::get().to(|data: web::Data| { - assert_eq!(*data, 10); + assert_eq!(**data, 10); let _ = data.clone(); HttpResponse::Ok() }), From 2b4256baab0247923c6caf2af055919c15a4100c Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 20 Dec 2019 17:49:05 +0600 Subject: [PATCH 150/176] add links to configs --- src/types/path.rs | 2 ++ src/types/query.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/types/path.rs b/src/types/path.rs index 9af5a0b9a..a37cb8f12 100644 --- a/src/types/path.rs +++ b/src/types/path.rs @@ -15,6 +15,8 @@ use crate::FromRequest; #[derive(PartialEq, Eq, PartialOrd, Ord)] /// Extract typed information from the request's path. /// +/// [**PathConfig**](struct.PathConfig.html) allows to configure extraction process. +/// /// ## Example /// /// ```rust diff --git a/src/types/query.rs b/src/types/query.rs index 696e10b94..a6c18d9be 100644 --- a/src/types/query.rs +++ b/src/types/query.rs @@ -19,6 +19,8 @@ use crate::request::HttpRequest; /// be decoded into any type which depends upon data ordering e.g. tuples or tuple-structs. /// Attempts to do so will *fail at runtime*. /// +/// [**QueryConfig**](struct.QueryConfig.html) allows to configure extraction process. +/// /// ## Example /// /// ```rust From 48476362a31e303904066e5acdb26cc8fd1fa533 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 20 Dec 2019 17:59:34 +0600 Subject: [PATCH 151/176] update changes --- actix-cors/CHANGES.md | 4 ++++ actix-files/CHANGES.md | 2 +- actix-identity/CHANGES.md | 4 ++++ actix-multipart/CHANGES.md | 6 +++++- actix-session/CHANGES.md | 3 +++ actix-web-actors/CHANGES.md | 4 ++++ 6 files changed, 21 insertions(+), 2 deletions(-) diff --git a/actix-cors/CHANGES.md b/actix-cors/CHANGES.md index e53abc790..8022ea4e8 100644 --- a/actix-cors/CHANGES.md +++ b/actix-cors/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [0.2.0] - 2019-12-20 + +* Release + ## [0.2.0-alpha.3] - 2019-12-07 * Migrate to actix-web 2.0.0 diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index 5bfd937a3..5712c9243 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## [0.2.0] - 2019-12-xx +## [0.2.0] - 2019-12-20 * Fix BodyEncoding trait import #1220 diff --git a/actix-identity/CHANGES.md b/actix-identity/CHANGES.md index 74a204055..8e89acc15 100644 --- a/actix-identity/CHANGES.md +++ b/actix-identity/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [0.2.0] - 2019-12-20 + +* Use actix-web 2.0 + ## [0.1.0] - 2019-06-xx * Move identity middleware to separate crate diff --git a/actix-multipart/CHANGES.md b/actix-multipart/CHANGES.md index c0792b84c..31f326d05 100644 --- a/actix-multipart/CHANGES.md +++ b/actix-multipart/CHANGES.md @@ -1,6 +1,10 @@ # Changes -## [2.0.0-alpha.4] - 2019-12-xx +## [0.2.0] - 2019-12-20 + +* Release + +## [0.2.0-alpha.4] - 2019-12-xx * Multipart handling now handles Pending during read of boundary #1205 diff --git a/actix-session/CHANGES.md b/actix-session/CHANGES.md index b4ef66c35..e4306fa9d 100644 --- a/actix-session/CHANGES.md +++ b/actix-session/CHANGES.md @@ -1,5 +1,8 @@ # Changes +## [0.3.0] - 2019-12-20 + +* Release ## [0.3.0-alpha.4] - 2019-12-xx diff --git a/actix-web-actors/CHANGES.md b/actix-web-actors/CHANGES.md index dff2dadf6..66ff7ed6c 100644 --- a/actix-web-actors/CHANGES.md +++ b/actix-web-actors/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [2.0.0] - 2019-12-20 + +* Release + ## [2.0.0-alpha.1] - 2019-12-15 * Migrate to actix-web 2.0.0 From 0cb1b0642f17ca2906a02bcaa9405ce8368fec89 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 20 Dec 2019 23:18:59 +0600 Subject: [PATCH 152/176] add test server data test --- src/test.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/test.rs b/src/test.rs index a10490fd5..5f5fdc487 100644 --- a/src/test.rs +++ b/src/test.rs @@ -962,7 +962,7 @@ mod tests { use std::time::SystemTime; use super::*; - use crate::{http::header, web, App, HttpResponse}; + use crate::{http::header, web, App, HttpResponse, Responder}; #[actix_rt::test] async fn test_basics() { @@ -1148,6 +1148,25 @@ mod tests { assert!(res.status().is_success()); } + #[actix_rt::test] + async fn test_server_data() { + async fn handler(data: web::Data) -> impl Responder { + assert_eq!(**data, 10); + HttpResponse::Ok() + } + + let mut app = init_service( + App::new() + .data(10usize) + .service(web::resource("/index.html").to(handler)), + ) + .await; + + let req = TestRequest::post().uri("/index.html").to_request(); + let res = app.call(req).await.unwrap(); + assert!(res.status().is_success()); + } + #[actix_rt::test] async fn test_actor() { use actix::Actor; From 3751a4018e7a541ab81fdce60a7c3cd16c714e64 Mon Sep 17 00:00:00 2001 From: Darin Date: Fri, 20 Dec 2019 21:47:18 -0500 Subject: [PATCH 153/176] fixed test::init_service api docs (missing await) (#1230) --- src/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test.rs b/src/test.rs index 5f5fdc487..2aa7e142b 100644 --- a/src/test.rs +++ b/src/test.rs @@ -66,7 +66,7 @@ pub fn default_service( /// let mut app = test::init_service( /// App::new() /// .service(web::resource("/test").to(|| async { HttpResponse::Ok() })) -/// ); +/// ).await; /// /// // Create request object /// let req = test::TestRequest::with_uri("/test").to_request(); From f45db1f909d9247e5cba5bef1d897f17e3fd9241 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 22 Dec 2019 16:43:41 +0900 Subject: [PATCH 154/176] Enable GitHub Actions and fix file URL behavior (#1232) * Use GitHub Actions * Fix unused imports on Windows * Fix test for Windows * Stop to run CI for i686-pc-windows-msvc for now * Use `/` instead of `\` on Windows * Add entry to changelog * Prepare actix-files release --- .github/workflows/main.yml | 67 ++++++++++++++++++++++++++++++++++++++ actix-files/CHANGES.md | 4 +++ actix-files/Cargo.toml | 2 +- actix-files/src/lib.rs | 5 +-- src/server.rs | 12 +++++-- 5 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..693291fd3 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,67 @@ +name: CI + +on: [push, pull_request] + +env: + VCPKGRS_DYNAMIC: 1 + +jobs: + build_and_test: + strategy: + fail-fast: false + matrix: + toolchain: + - x86_64-pc-windows-msvc + # - i686-pc-windows-msvc + - x86_64-apple-darwin + version: + - stable + - nightly + include: + - toolchain: x86_64-pc-windows-msvc + os: windows-latest + arch: x64 + # - toolchain: i686-pc-windows-msvc + # os: windows-latest + # arch: x86 + - toolchain: x86_64-apple-darwin + os: macOS-latest + + name: ${{ matrix.version }} - ${{ matrix.toolchain }} + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@master + + - name: Install ${{ matrix.version }} + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.version }}-${{ matrix.toolchain }} + default: true + + - name: Install OpenSSL + if: matrix.os == 'windows-latest' + run: | + vcpkg integrate install + vcpkg install openssl:${{ matrix.arch }}-windows + + - name: check nightly + if: matrix.version == 'nightly' + uses: actions-rs/cargo@v1 + with: + command: check + args: --all --benches --bins --examples --tests + + - name: check stable + if: matrix.version == 'stable' + uses: actions-rs/cargo@v1 + with: + command: check + args: --all --bins --examples --tests + + - name: tests + if: matrix.toolchain != 'x86_64-pc-windows-gnu' + uses: actions-rs/cargo@v1 + with: + command: test + args: --all --all-features -- --nocapture diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index 5712c9243..c4918b56d 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [0.2.1] - 2019-12-22 + +* Use the same format for file URLs regardless of platforms + ## [0.2.0] - 2019-12-20 * Fix BodyEncoding trait import #1220 diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 7f11605a1..6920a3090 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-files" -version = "0.2.0" +version = "0.2.1" authors = ["Nikolay Kim "] description = "Static files support for actix web." readme = "README.md" diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index c45caf375..ac26f70cc 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -155,7 +155,7 @@ impl Directory { // show file url as relative to static path macro_rules! encode_file_url { ($path:ident) => { - utf8_percent_encode(&$path.to_string_lossy(), CONTROLS) + utf8_percent_encode(&$path, CONTROLS) }; } @@ -178,7 +178,8 @@ fn directory_listing( if dir.is_visible(&entry) { let entry = entry.unwrap(); let p = match entry.path().strip_prefix(&dir.path) { - Ok(p) => base.join(p), + Ok(p) if cfg!(windows) => base.join(p).to_string_lossy().replace("\\", "/"), + Ok(p) => base.join(p).to_string_lossy().into_owned(), Err(_) => continue, }; diff --git a/src/server.rs b/src/server.rs index 72ef7255b..2830f8743 100644 --- a/src/server.rs +++ b/src/server.rs @@ -3,16 +3,22 @@ use std::sync::{Arc, Mutex}; use std::{fmt, io, net}; use actix_http::{ - body::MessageBody, Error, HttpService, KeepAlive, Protocol, Request, Response, + body::MessageBody, Error, HttpService, KeepAlive, Request, Response, }; use actix_server::{Server, ServerBuilder}; use actix_service::{ - map_config, pipeline_factory, IntoServiceFactory, Service, ServiceFactory, + map_config, IntoServiceFactory, Service, ServiceFactory, }; -use futures::future::ok; use net2::TcpBuilder; +#[cfg(unix)] +use actix_http::Protocol; +#[cfg(unix)] +use actix_service::pipeline_factory; +#[cfg(unix)] +use futures::future::ok; + #[cfg(feature = "openssl")] use actix_tls::openssl::{AlpnError, SslAcceptor, SslAcceptorBuilder}; #[cfg(feature = "rustls")] From c7f39157799d4ef5703e4b80e7d6215be6ef570e Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sun, 22 Dec 2019 16:39:25 +0400 Subject: [PATCH 155/176] update actix-service dep --- Cargo.toml | 4 +- actix-cors/Cargo.toml | 2 +- actix-files/Cargo.toml | 2 +- actix-files/src/lib.rs | 4 +- actix-framed/Cargo.toml | 4 +- actix-http/Cargo.toml | 2 +- actix-identity/Cargo.toml | 2 +- actix-multipart/Cargo.toml | 2 +- actix-session/Cargo.toml | 2 +- awc/Cargo.toml | 2 +- awc/tests/test_client.rs | 87 +++++++++++++++------------------ awc/tests/test_rustls_client.rs | 8 ++- awc/tests/test_ssl_client.rs | 8 ++- src/server.rs | 22 +++------ src/test.rs | 24 ++++----- test-server/Cargo.toml | 2 +- 16 files changed, 76 insertions(+), 101 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index aba3c6546..28720beb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,8 +60,8 @@ rustls = ["actix-tls/rustls", "awc/rustls", "rust-tls"] [dependencies] actix-codec = "0.2.0" -actix-service = "1.0.0" -actix-utils = "1.0.3" +actix-service = "1.0.1" +actix-utils = "1.0.4" actix-router = "0.2.0" actix-rt = "1.0.0" actix-server = "1.0.0" diff --git a/actix-cors/Cargo.toml b/actix-cors/Cargo.toml index 392168e42..3fcd92f4f 100644 --- a/actix-cors/Cargo.toml +++ b/actix-cors/Cargo.toml @@ -18,7 +18,7 @@ path = "src/lib.rs" [dependencies] actix-web = "2.0.0-rc" -actix-service = "1.0.0" +actix-service = "1.0.1" derive_more = "0.99.2" futures = "0.3.1" diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 6920a3090..104eb3dfa 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -20,7 +20,7 @@ path = "src/lib.rs" [dependencies] actix-web = { version = "2.0.0-rc", default-features = false } actix-http = "1.0.1" -actix-service = "1.0.0" +actix-service = "1.0.1" bitflags = "1" bytes = "0.5.3" futures = "0.3.1" diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index ac26f70cc..4d111e8a7 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -178,7 +178,9 @@ fn directory_listing( if dir.is_visible(&entry) { let entry = entry.unwrap(); let p = match entry.path().strip_prefix(&dir.path) { - Ok(p) if cfg!(windows) => base.join(p).to_string_lossy().replace("\\", "/"), + Ok(p) if cfg!(windows) => { + base.join(p).to_string_lossy().replace("\\", "/") + } Ok(p) => base.join(p).to_string_lossy().into_owned(), Err(_) => continue, }; diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml index 8848bff28..8466e4a81 100644 --- a/actix-framed/Cargo.toml +++ b/actix-framed/Cargo.toml @@ -21,10 +21,10 @@ path = "src/lib.rs" [dependencies] actix-codec = "0.2.0" -actix-service = "1.0.0" +actix-service = "1.0.1" actix-router = "0.2.0" actix-rt = "1.0.0" -actix-http = "1.0.0" +actix-http = "1.0.1" bytes = "0.5.3" futures = "0.3.1" diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 8512b2501..367dbafec 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -40,7 +40,7 @@ failure = ["fail-ure"] secure-cookies = ["ring"] [dependencies] -actix-service = "1.0.0" +actix-service = "1.0.1" actix-codec = "0.2.0" actix-connect = "1.0.1" actix-utils = "1.0.3" diff --git a/actix-identity/Cargo.toml b/actix-identity/Cargo.toml index a5058b8cd..b30246f0b 100644 --- a/actix-identity/Cargo.toml +++ b/actix-identity/Cargo.toml @@ -18,7 +18,7 @@ path = "src/lib.rs" [dependencies] actix-web = { version = "2.0.0-rc", default-features = false, features = ["secure-cookies"] } -actix-service = "1.0.0" +actix-service = "1.0.1" futures = "0.3.1" serde = "1.0" serde_json = "1.0" diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index bbf83ed00..6c683cb1a 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -17,7 +17,7 @@ path = "src/lib.rs" [dependencies] actix-web = { version = "2.0.0-rc", default-features = false } -actix-service = "1.0.0" +actix-service = "1.0.1" actix-utils = "1.0.3" bytes = "0.5.3" derive_more = "0.99.2" diff --git a/actix-session/Cargo.toml b/actix-session/Cargo.toml index ed80174ff..5989cc0d6 100644 --- a/actix-session/Cargo.toml +++ b/actix-session/Cargo.toml @@ -23,7 +23,7 @@ cookie-session = ["actix-web/secure-cookies"] [dependencies] actix-web = "2.0.0-rc" -actix-service = "1.0.0" +actix-service = "1.0.1" bytes = "0.5.3" derive_more = "0.99.2" futures = "0.3.1" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 723cd59de..67e0a3ee4 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -35,7 +35,7 @@ compress = ["actix-http/compress"] [dependencies] actix-codec = "0.2.0" -actix-service = "1.0.0" +actix-service = "1.0.1" actix-http = "1.0.0" actix-rt = "1.0.0" diff --git a/awc/tests/test_client.rs b/awc/tests/test_client.rs index af63c8163..69e40ad25 100644 --- a/awc/tests/test_client.rs +++ b/awc/tests/test_client.rs @@ -172,8 +172,7 @@ async fn test_connection_reuse() { .and_then( HttpService::new(map_config( App::new() - .service(web::resource("/").route(web::to(|| HttpResponse::Ok()))) - .into_factory(), + .service(web::resource("/").route(web::to(|| HttpResponse::Ok()))), |_| AppConfig::default(), )) .tcp(), @@ -210,8 +209,7 @@ async fn test_connection_force_close() { .and_then( HttpService::new(map_config( App::new() - .service(web::resource("/").route(web::to(|| HttpResponse::Ok()))) - .into_factory(), + .service(web::resource("/").route(web::to(|| HttpResponse::Ok()))), |_| AppConfig::default(), )) .tcp(), @@ -239,25 +237,23 @@ async fn test_connection_server_close() { let num = Arc::new(AtomicUsize::new(0)); let num2 = num.clone(); - let srv = - test_server(move || { - let num2 = num2.clone(); - pipeline_factory(move |io| { - num2.fetch_add(1, Ordering::Relaxed); - ok(io) - }) - .and_then( - HttpService::new(map_config( - App::new() - .service(web::resource("/").route(web::to(|| { - HttpResponse::Ok().force_close().finish() - }))) - .into_factory(), - |_| AppConfig::default(), - )) - .tcp(), - ) - }); + let srv = test_server(move || { + let num2 = num2.clone(); + pipeline_factory(move |io| { + num2.fetch_add(1, Ordering::Relaxed); + ok(io) + }) + .and_then( + HttpService::new(map_config( + App::new().service( + web::resource("/") + .route(web::to(|| HttpResponse::Ok().force_close().finish())), + ), + |_| AppConfig::default(), + )) + .tcp(), + ) + }); let client = awc::Client::default(); @@ -288,12 +284,9 @@ async fn test_connection_wait_queue() { }) .and_then( HttpService::new(map_config( - App::new() - .service( - web::resource("/") - .route(web::to(|| HttpResponse::Ok().body(STR))), - ) - .into_factory(), + App::new().service( + web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))), + ), |_| AppConfig::default(), )) .tcp(), @@ -330,25 +323,23 @@ async fn test_connection_wait_queue_force_close() { let num = Arc::new(AtomicUsize::new(0)); let num2 = num.clone(); - let srv = - test_server(move || { - let num2 = num2.clone(); - pipeline_factory(move |io| { - num2.fetch_add(1, Ordering::Relaxed); - ok(io) - }) - .and_then( - HttpService::new(map_config( - App::new() - .service(web::resource("/").route(web::to(|| { - HttpResponse::Ok().force_close().body(STR) - }))) - .into_factory(), - |_| AppConfig::default(), - )) - .tcp(), - ) - }); + let srv = test_server(move || { + let num2 = num2.clone(); + pipeline_factory(move |io| { + num2.fetch_add(1, Ordering::Relaxed); + ok(io) + }) + .and_then( + HttpService::new(map_config( + App::new().service( + web::resource("/") + .route(web::to(|| HttpResponse::Ok().force_close().body(STR))), + ), + |_| AppConfig::default(), + )) + .tcp(), + ) + }); let client = awc::Client::build() .connector(awc::Connector::new().limit(1).finish()) diff --git a/awc/tests/test_rustls_client.rs b/awc/tests/test_rustls_client.rs index 68acf7f71..5170555f0 100644 --- a/awc/tests/test_rustls_client.rs +++ b/awc/tests/test_rustls_client.rs @@ -63,11 +63,9 @@ async fn _test_connection_reuse_h2() { .and_then( HttpService::build() .h2(map_config( - App::new() - .service( - web::resource("/").route(web::to(|| HttpResponse::Ok())), - ) - .into_factory(), + App::new().service( + web::resource("/").route(web::to(|| HttpResponse::Ok())), + ), |_| AppConfig::default(), )) .openssl(ssl_acceptor()) diff --git a/awc/tests/test_ssl_client.rs b/awc/tests/test_ssl_client.rs index 8edc83a9e..94a061acf 100644 --- a/awc/tests/test_ssl_client.rs +++ b/awc/tests/test_ssl_client.rs @@ -45,11 +45,9 @@ async fn test_connection_reuse_h2() { .and_then( HttpService::build() .h2(map_config( - App::new() - .service( - web::resource("/").route(web::to(|| HttpResponse::Ok())), - ) - .into_factory(), + App::new().service( + web::resource("/").route(web::to(|| HttpResponse::Ok())), + ), |_| AppConfig::default(), )) .openssl(ssl_acceptor()) diff --git a/src/server.rs b/src/server.rs index 2830f8743..f460afe48 100644 --- a/src/server.rs +++ b/src/server.rs @@ -2,13 +2,9 @@ use std::marker::PhantomData; use std::sync::{Arc, Mutex}; use std::{fmt, io, net}; -use actix_http::{ - body::MessageBody, Error, HttpService, KeepAlive, Request, Response, -}; +use actix_http::{body::MessageBody, Error, HttpService, KeepAlive, Request, Response}; use actix_server::{Server, ServerBuilder}; -use actix_service::{ - map_config, IntoServiceFactory, Service, ServiceFactory, -}; +use actix_service::{map_config, IntoServiceFactory, Service, ServiceFactory}; use net2::TcpBuilder; @@ -266,7 +262,7 @@ where .keep_alive(c.keep_alive) .client_timeout(c.client_timeout) .local_addr(addr) - .finish(map_config(factory().into_factory(), move |_| cfg.clone())) + .finish(map_config(factory(), move |_| cfg.clone())) .tcp() }, )?; @@ -313,7 +309,7 @@ where .keep_alive(c.keep_alive) .client_timeout(c.client_timeout) .client_disconnect(c.client_shutdown) - .finish(map_config(factory().into_factory(), move |_| cfg.clone())) + .finish(map_config(factory(), move |_| cfg.clone())) .openssl(acceptor.clone()) }, )?; @@ -360,7 +356,7 @@ where .keep_alive(c.keep_alive) .client_timeout(c.client_timeout) .client_disconnect(c.client_shutdown) - .finish(map_config(factory().into_factory(), move |_| cfg.clone())) + .finish(map_config(factory(), move |_| cfg.clone())) .rustls(config.clone()) }, )?; @@ -483,9 +479,7 @@ where HttpService::build() .keep_alive(c.keep_alive) .client_timeout(c.client_timeout) - .finish(map_config(factory().into_factory(), move |_| { - config.clone() - })), + .finish(map_config(factory(), move |_| config.clone())), ) })?; Ok(self) @@ -527,9 +521,7 @@ where HttpService::build() .keep_alive(c.keep_alive) .client_timeout(c.client_timeout) - .finish(map_config(factory().into_factory(), move |_| { - config.clone() - })), + .finish(map_config(factory(), move |_| config.clone())), ) }, )?; diff --git a/src/test.rs b/src/test.rs index 2aa7e142b..912e9b473 100644 --- a/src/test.rs +++ b/src/test.rs @@ -634,7 +634,7 @@ where AppConfig::new(false, local_addr, format!("{}", local_addr)); HttpService::build() .client_timeout(ctimeout) - .h1(map_config(factory().into_factory(), move |_| cfg.clone())) + .h1(map_config(factory(), move |_| cfg.clone())) .tcp() }), HttpVer::Http2 => builder.listen("test", tcp, move || { @@ -642,7 +642,7 @@ where AppConfig::new(false, local_addr, format!("{}", local_addr)); HttpService::build() .client_timeout(ctimeout) - .h2(map_config(factory().into_factory(), move |_| cfg.clone())) + .h2(map_config(factory(), move |_| cfg.clone())) .tcp() }), HttpVer::Both => builder.listen("test", tcp, move || { @@ -650,9 +650,7 @@ where AppConfig::new(false, local_addr, format!("{}", local_addr)); HttpService::build() .client_timeout(ctimeout) - .finish(map_config(factory().into_factory(), move |_| { - cfg.clone() - })) + .finish(map_config(factory(), move |_| cfg.clone())) .tcp() }), }, @@ -663,7 +661,7 @@ where AppConfig::new(true, local_addr, format!("{}", local_addr)); HttpService::build() .client_timeout(ctimeout) - .h1(map_config(factory().into_factory(), move |_| cfg.clone())) + .h1(map_config(factory(), move |_| cfg.clone())) .openssl(acceptor.clone()) }), HttpVer::Http2 => builder.listen("test", tcp, move || { @@ -671,7 +669,7 @@ where AppConfig::new(true, local_addr, format!("{}", local_addr)); HttpService::build() .client_timeout(ctimeout) - .h2(map_config(factory().into_factory(), move |_| cfg.clone())) + .h2(map_config(factory(), move |_| cfg.clone())) .openssl(acceptor.clone()) }), HttpVer::Both => builder.listen("test", tcp, move || { @@ -679,9 +677,7 @@ where AppConfig::new(true, local_addr, format!("{}", local_addr)); HttpService::build() .client_timeout(ctimeout) - .finish(map_config(factory().into_factory(), move |_| { - cfg.clone() - })) + .finish(map_config(factory(), move |_| cfg.clone())) .openssl(acceptor.clone()) }), }, @@ -692,7 +688,7 @@ where AppConfig::new(true, local_addr, format!("{}", local_addr)); HttpService::build() .client_timeout(ctimeout) - .h1(map_config(factory().into_factory(), move |_| cfg.clone())) + .h1(map_config(factory(), move |_| cfg.clone())) .rustls(config.clone()) }), HttpVer::Http2 => builder.listen("test", tcp, move || { @@ -700,7 +696,7 @@ where AppConfig::new(true, local_addr, format!("{}", local_addr)); HttpService::build() .client_timeout(ctimeout) - .h2(map_config(factory().into_factory(), move |_| cfg.clone())) + .h2(map_config(factory(), move |_| cfg.clone())) .rustls(config.clone()) }), HttpVer::Both => builder.listen("test", tcp, move || { @@ -708,9 +704,7 @@ where AppConfig::new(true, local_addr, format!("{}", local_addr)); HttpService::build() .client_timeout(ctimeout) - .finish(map_config(factory().into_factory(), move |_| { - cfg.clone() - })) + .finish(map_config(factory(), move |_| cfg.clone())) .rustls(config.clone()) }), }, diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 54cd01686..52a2da8da 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -30,7 +30,7 @@ default = [] openssl = ["open-ssl", "awc/openssl"] [dependencies] -actix-service = "1.0.0" +actix-service = "1.0.1" actix-codec = "0.2.0" actix-connect = "1.0.0" actix-utils = "1.0.3" From 6a0cd2dced8b085865e966e2d40baf1b8ad9d640 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sun, 22 Dec 2019 17:12:22 +0400 Subject: [PATCH 156/176] Rename HttpServer::start() to HttpServer::run() --- CHANGES.md | 7 +++++++ Cargo.toml | 2 +- MIGRATION.md | 3 +++ actix-session/src/lib.rs | 2 +- awc/tests/test_client.rs | 2 +- awc/tests/test_rustls_client.rs | 2 +- awc/tests/test_ssl_client.rs | 2 +- examples/basic.rs | 2 +- examples/uds.rs | 2 +- src/lib.rs | 2 +- src/server.rs | 18 +++++++----------- tests/test_httpserver.rs | 4 ++-- 12 files changed, 27 insertions(+), 21 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 66b214bdc..0291f75ab 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,12 @@ # Changes +## [2.0.0] - 2019-12-xx + +### Changed + +* Rename `HttpServer::start()` to `HttpServer::run()` + + ## [2.0.0-rc] - 2019-12-20 ### Changed diff --git a/Cargo.toml b/Cargo.toml index 28720beb5..7ffaab75b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "2.0.0-rc" +version = "2.0.0" authors = ["Nikolay Kim "] description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." readme = "README.md" diff --git a/MIGRATION.md b/MIGRATION.md index 357a4e4ca..0ced4493d 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,5 +1,8 @@ ## 2.0.0 +* `HttpServer::start()` renamed to `HttpServer::run()`. It also possible to + `.await` on `run` method result, in that case it awaits server exit. + * `App::register_data()` renamed to `App::app_data()` and accepts any type `T: 'static`. Stored data is available via `HttpRequest::app_data()` method at runtime. diff --git a/actix-session/src/lib.rs b/actix-session/src/lib.rs index ac60901c3..b6e5dd331 100644 --- a/actix-session/src/lib.rs +++ b/actix-session/src/lib.rs @@ -37,7 +37,7 @@ //! ) //! .service(web::resource("/").to(|| HttpResponse::Ok()))) //! .bind("127.0.0.1:59880")? -//! .start() +//! .run() //! .await //! } //! ``` diff --git a/awc/tests/test_client.rs b/awc/tests/test_client.rs index 69e40ad25..8fb04b005 100644 --- a/awc/tests/test_client.rs +++ b/awc/tests/test_client.rs @@ -14,7 +14,7 @@ use rand::Rng; use actix_http::HttpService; use actix_http_test::test_server; -use actix_service::{map_config, pipeline_factory, IntoServiceFactory}; +use actix_service::{map_config, pipeline_factory}; use actix_web::dev::{AppConfig, BodyEncoding}; use actix_web::http::Cookie; use actix_web::middleware::Compress; diff --git a/awc/tests/test_rustls_client.rs b/awc/tests/test_rustls_client.rs index 5170555f0..1d7eb7bc5 100644 --- a/awc/tests/test_rustls_client.rs +++ b/awc/tests/test_rustls_client.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use actix_http::HttpService; use actix_http_test::test_server; -use actix_service::{map_config, pipeline_factory, IntoServiceFactory, ServiceFactory}; +use actix_service::{map_config, pipeline_factory, ServiceFactory}; use actix_web::http::Version; use actix_web::{dev::AppConfig, web, App, HttpResponse}; use futures::future::ok; diff --git a/awc/tests/test_ssl_client.rs b/awc/tests/test_ssl_client.rs index 94a061acf..d3995b4be 100644 --- a/awc/tests/test_ssl_client.rs +++ b/awc/tests/test_ssl_client.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use actix_http::HttpService; use actix_http_test::test_server; -use actix_service::{map_config, pipeline_factory, IntoServiceFactory, ServiceFactory}; +use actix_service::{map_config, pipeline_factory, ServiceFactory}; use actix_web::http::Version; use actix_web::{dev::AppConfig, web, App, HttpResponse}; use futures::future::ok; diff --git a/examples/basic.rs b/examples/basic.rs index b5b69fce2..bd6f8146f 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -42,6 +42,6 @@ async fn main() -> std::io::Result<()> { }) .bind("127.0.0.1:8080")? .workers(1) - .start() + .run() .await } diff --git a/examples/uds.rs b/examples/uds.rs index 8db4cf230..77f245d99 100644 --- a/examples/uds.rs +++ b/examples/uds.rs @@ -45,7 +45,7 @@ async fn main() -> std::io::Result<()> { }) .bind_uds("/Users/fafhrd91/uds-test")? .workers(1) - .start() + .run() .await } diff --git a/src/lib.rs b/src/lib.rs index bc89c6450..b8d358d81 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,7 @@ //! web::resource("/{name}/{id}/index.html").to(index)) //! ) //! .bind("127.0.0.1:8080")? -//! .start() +//! .run() //! .await //! } //! ``` diff --git a/src/server.rs b/src/server.rs index f460afe48..11cfbb6bc 100644 --- a/src/server.rs +++ b/src/server.rs @@ -38,21 +38,17 @@ struct Config { /// /// Create new http server with application factory. /// -/// ```rust -/// use std::io; +/// ```rust,no_run /// use actix_web::{web, App, HttpResponse, HttpServer}; /// -/// fn main() -> io::Result<()> { -/// let sys = actix_rt::System::new("example"); // <- create Actix runtime -/// +/// #[actix_rt::main] +/// async fn main() -> std::io::Result<()> { /// HttpServer::new( /// || App::new() /// .service(web::resource("/").to(|| HttpResponse::Ok()))) /// .bind("127.0.0.1:59090")? -/// .start(); -/// -/// # actix_rt::System::current().stop(); -/// sys.run() +/// .run() +/// .await /// } /// ``` pub struct HttpServer @@ -557,11 +553,11 @@ where /// async fn main() -> io::Result<()> { /// HttpServer::new(|| App::new().service(web::resource("/").to(|| HttpResponse::Ok()))) /// .bind("127.0.0.1:0")? - /// .start() + /// .run() /// .await /// } /// ``` - pub fn start(self) -> Server { + pub fn run(self) -> Server { self.builder.start() } } diff --git a/tests/test_httpserver.rs b/tests/test_httpserver.rs index 48d8b3872..ecd5c9ffb 100644 --- a/tests/test_httpserver.rs +++ b/tests/test_httpserver.rs @@ -42,7 +42,7 @@ async fn test_start() { .disable_signals() .bind(format!("{}", addr)) .unwrap() - .start(); + .run(); let _ = tx.send((srv, actix_rt::System::current())); let _ = sys.run(); @@ -111,7 +111,7 @@ async fn test_start_ssl() { .disable_signals() .bind_openssl(format!("{}", addr), builder) .unwrap() - .start(); + .run(); let _ = tx.send((srv, actix_rt::System::current())); let _ = sys.run(); From 1c75e6876b0d749c41d27f8ebce480beafe8d649 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sun, 22 Dec 2019 17:16:07 +0400 Subject: [PATCH 157/176] update migration --- Cargo.toml | 6 +++--- MIGRATION.md | 4 ++-- README.md | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7ffaab75b..04a559f1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,11 +67,11 @@ actix-rt = "1.0.0" actix-server = "1.0.0" actix-testing = "1.0.0" actix-macros = "0.1.0" -actix-threadpool = "0.3.0" +actix-threadpool = "0.3.1" actix-tls = "1.0.0" actix-web-codegen = "0.2.0" -actix-http = "1.0.0" +actix-http = "1.0.1" awc = { version = "1.0.1", default-features = false } bytes = "0.5.3" @@ -93,7 +93,7 @@ open-ssl = { version="0.10", package = "openssl", optional = true } rust-tls = { version = "0.16.0", package = "rustls", optional = true } [dev-dependencies] -actix = "0.9.0-alpha.2" +actix = "0.9.0" rand = "0.7" env_logger = "0.6" serde_derive = "1.0" diff --git a/MIGRATION.md b/MIGRATION.md index 0ced4493d..000c5b46b 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -9,10 +9,10 @@ * Extractor configuration must be registered with `App::app_data()` instead of `App::data()` * Sync handlers has been removed. `.to_async()` method has been renamed to `.to()` - replace `fn` with `async fn` to convert sync handler to async -* `TestServer::new()` renamed to `TestServer::start()` +* `actix_http_test::TestServer` moved to `actix_web::test` module. To start + test server use `test::start()` or `test_start_with_config()` methods ## 1.0.1 diff --git a/README.md b/README.md index 579e87e87..99f09b0fa 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ async fn index(info: web::Path<(u32, String)>) -> impl Responder { async fn main() -> std::io::Result<()> { HttpServer::new(|| App::new().service(index)) .bind("127.0.0.1:8080")? - .start() + .run() .await } ``` From 7882f545e5ff3efaadb2095ca2dfcbd59135855d Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 25 Dec 2019 12:10:28 +0400 Subject: [PATCH 158/176] Allow to gracefully stop test server via TestServer::stop() --- CHANGES.md | 2 ++ src/data.rs | 46 +++++++++++++++++++++++++++++++++++++++++++++- src/test.rs | 21 ++++++++++++--------- 3 files changed, 59 insertions(+), 10 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 0291f75ab..31ebee53e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,8 @@ * Rename `HttpServer::start()` to `HttpServer::run()` +* Allow to gracefully stop test server via `TestServer::stop()` + ## [2.0.0-rc] - 2019-12-20 diff --git a/src/data.rs b/src/data.rs index 2867d68fa..c36418942 100644 --- a/src/data.rs +++ b/src/data.rs @@ -136,10 +136,11 @@ impl DataFactory for Data { #[cfg(test)] mod tests { use actix_service::Service; + use std::sync::atomic::{AtomicUsize, Ordering}; use super::*; use crate::http::StatusCode; - use crate::test::{init_service, TestRequest}; + use crate::test::{self, init_service, TestRequest}; use crate::{web, App, HttpResponse}; #[actix_rt::test] @@ -234,4 +235,47 @@ mod tests { let resp = srv.call(req).await.unwrap(); assert_eq!(resp.status(), StatusCode::OK); } + + #[actix_rt::test] + async fn test_data_drop() { + struct TestData(Arc); + + impl TestData { + fn new(inner: Arc) -> Self { + let _ = inner.fetch_add(1, Ordering::SeqCst); + Self(inner) + } + } + + impl Clone for TestData { + fn clone(&self) -> Self { + let inner = self.0.clone(); + let _ = inner.fetch_add(1, Ordering::SeqCst); + Self(inner) + } + } + + impl Drop for TestData { + fn drop(&mut self) { + let _ = self.0.fetch_sub(1, Ordering::SeqCst); + } + } + + let num = Arc::new(AtomicUsize::new(0)); + let data = TestData::new(num.clone()); + assert_eq!(num.load(Ordering::SeqCst), 1); + + let srv = test::start(move || { + let data = data.clone(); + + App::new() + .data(data) + .service(web::resource("/").to(|_data: Data| async { "ok" })) + }); + + assert!(srv.get("/").send().await.unwrap().status().is_success()); + srv.stop().await; + + assert_eq!(num.load(Ordering::SeqCst), 0); + } } diff --git a/src/test.rs b/src/test.rs index 912e9b473..9c06b7337 100644 --- a/src/test.rs +++ b/src/test.rs @@ -11,8 +11,7 @@ use actix_http::http::{Error as HttpError, Method, StatusCode, Uri, Version}; use actix_http::test::TestRequest as HttpTestRequest; use actix_http::{cookie::Cookie, ws, Extensions, HttpService, Request}; use actix_router::{Path, ResourceDef, Url}; -use actix_rt::System; -use actix_server::Server; +use actix_rt::{time::delay_for, System}; use actix_service::{ map_config, IntoService, IntoServiceFactory, Service, ServiceFactory, }; @@ -30,7 +29,7 @@ pub use actix_http::test::TestBuffer; use crate::config::AppConfig; use crate::data::Data; -use crate::dev::{Body, MessageBody, Payload}; +use crate::dev::{Body, MessageBody, Payload, Server}; use crate::request::HttpRequestPool; use crate::rmap::ResourceMap; use crate::service::{ServiceRequest, ServiceResponse}; @@ -627,7 +626,7 @@ where let ctimeout = cfg.client_timeout; let builder = Server::build().workers(1).disable_signals(); - match cfg.stream { + let srv = match cfg.stream { StreamType::Tcp => match cfg.tp { HttpVer::Http1 => builder.listen("test", tcp, move || { let cfg = @@ -712,11 +711,11 @@ where .unwrap() .start(); - tx.send((System::current(), local_addr)).unwrap(); + tx.send((System::current(), srv, local_addr)).unwrap(); sys.run() }); - let (system, addr) = rx.recv().unwrap(); + let (system, server, addr) = rx.recv().unwrap(); let client = { let connector = { @@ -752,6 +751,7 @@ where addr, client, system, + server, } } @@ -848,6 +848,7 @@ pub struct TestServer { client: awc::Client, system: actix_rt::System, ssl: bool, + server: Server, } impl TestServer { @@ -936,15 +937,17 @@ impl TestServer { self.ws_at("/").await } - /// Stop http server - fn stop(&mut self) { + /// Gracefully stop http server + pub async fn stop(self) { + self.server.stop(true).await; self.system.stop(); + delay_for(time::Duration::from_millis(100)).await; } } impl Drop for TestServer { fn drop(&mut self) { - self.stop() + self.system.stop() } } From f86ce0390ecfc66504fc878483a7ca26bf99da38 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 25 Dec 2019 20:13:52 +0400 Subject: [PATCH 159/176] allow to specify multi pattern for resources --- CHANGES.md | 1 + Cargo.toml | 4 ++-- actix-cors/src/lib.rs | 2 +- actix-framed/src/helpers.rs | 2 +- actix-framed/src/route.rs | 2 +- actix-identity/src/lib.rs | 2 +- actix-session/src/cookie.rs | 2 +- src/lib.rs | 13 +++++++------ src/middleware/defaultheaders.rs | 2 +- src/middleware/errhandlers.rs | 2 +- src/resource.rs | 28 +++++++++++++++++++++++----- src/service.rs | 14 +++++++------- src/types/form.rs | 2 +- src/types/json.rs | 2 +- src/types/payload.rs | 4 ++-- src/web.rs | 5 +++-- 16 files changed, 54 insertions(+), 33 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 31ebee53e..b0193c0ed 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,7 @@ * Allow to gracefully stop test server via `TestServer::stop()` +* Allow to specify multi-patterns for resources ## [2.0.0-rc] - 2019-12-20 diff --git a/Cargo.toml b/Cargo.toml index 04a559f1f..1adae97b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,7 +62,7 @@ rustls = ["actix-tls/rustls", "awc/rustls", "rust-tls"] actix-codec = "0.2.0" actix-service = "1.0.1" actix-utils = "1.0.4" -actix-router = "0.2.0" +actix-router = "0.2.1" actix-rt = "1.0.0" actix-server = "1.0.0" actix-testing = "1.0.0" @@ -115,4 +115,4 @@ actix-identity = { path = "actix-identity" } actix-session = { path = "actix-session" } actix-files = { path = "actix-files" } actix-multipart = { path = "actix-multipart" } -awc = { path = "awc" } +awc = { path = "awc" } \ No newline at end of file diff --git a/actix-cors/src/lib.rs b/actix-cors/src/lib.rs index ddb20d2b5..429fe9eab 100644 --- a/actix-cors/src/lib.rs +++ b/actix-cors/src/lib.rs @@ -814,7 +814,7 @@ where res } } - .boxed_local(), + .boxed_local(), ) } } diff --git a/actix-framed/src/helpers.rs b/actix-framed/src/helpers.rs index d08ca25ac..29492e45b 100644 --- a/actix-framed/src/helpers.rs +++ b/actix-framed/src/helpers.rs @@ -66,7 +66,7 @@ where service }) } - .boxed_local() + .boxed_local() } } diff --git a/actix-framed/src/route.rs b/actix-framed/src/route.rs index 03e48e4d2..793f46273 100644 --- a/actix-framed/src/route.rs +++ b/actix-framed/src/route.rs @@ -154,6 +154,6 @@ where } Ok(()) } - .boxed_local() + .boxed_local() } } diff --git a/actix-identity/src/lib.rs b/actix-identity/src/lib.rs index 5dfd2ae65..b10a419dd 100644 --- a/actix-identity/src/lib.rs +++ b/actix-identity/src/lib.rs @@ -294,7 +294,7 @@ where Err(err) => Ok(req.error_response(err)), } } - .boxed_local() + .boxed_local() } } diff --git a/actix-session/src/cookie.rs b/actix-session/src/cookie.rs index 5d66d6537..75eef0c01 100644 --- a/actix-session/src/cookie.rs +++ b/actix-session/src/cookie.rs @@ -354,7 +354,7 @@ where } }) } - .boxed_local() + .boxed_local() } } diff --git a/src/lib.rs b/src/lib.rs index b8d358d81..a9965229c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -151,12 +151,13 @@ pub mod dev { pub use actix_server::Server; pub use actix_service::{Service, Transform}; - pub(crate) fn insert_slash(path: &str) -> String { - let mut path = path.to_owned(); - if !path.is_empty() && !path.starts_with('/') { - path.insert(0, '/'); - }; - path + pub(crate) fn insert_slash(mut patterns: Vec) -> Vec { + for path in &mut patterns { + if !path.is_empty() && !path.starts_with('/') { + path.insert(0, '/'); + }; + } + patterns } use crate::http::header::ContentEncoding; diff --git a/src/middleware/defaultheaders.rs b/src/middleware/defaultheaders.rs index 464be1ace..ba001c77b 100644 --- a/src/middleware/defaultheaders.rs +++ b/src/middleware/defaultheaders.rs @@ -150,7 +150,7 @@ where } Ok(res) } - .boxed_local() + .boxed_local() } } diff --git a/src/middleware/errhandlers.rs b/src/middleware/errhandlers.rs index ed1e4c999..71886af0b 100644 --- a/src/middleware/errhandlers.rs +++ b/src/middleware/errhandlers.rs @@ -140,7 +140,7 @@ where Ok(res) } } - .boxed_local() + .boxed_local() } } diff --git a/src/resource.rs b/src/resource.rs index 2ee084415..d60d50967 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -6,6 +6,7 @@ use std::rc::Rc; use std::task::{Context, Poll}; use actix_http::{Error, Extensions, Response}; +use actix_router::IntoPattern; use actix_service::boxed::{self, BoxService, BoxServiceFactory}; use actix_service::{ apply, apply_fn_factory, IntoServiceFactory, Service, ServiceFactory, Transform, @@ -48,7 +49,7 @@ type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Err /// Default behavior could be overriden with `default_resource()` method. pub struct Resource { endpoint: T, - rdef: String, + rdef: Vec, name: Option, routes: Vec, data: Option, @@ -58,12 +59,12 @@ pub struct Resource { } impl Resource { - pub fn new(path: &str) -> Resource { + pub fn new(path: T) -> Resource { let fref = Rc::new(RefCell::new(None)); Resource { routes: Vec::new(), - rdef: path.to_string(), + rdef: path.patterns(), name: None, endpoint: ResourceEndpoint::new(fref.clone()), factory_ref: fref, @@ -381,9 +382,9 @@ where Some(std::mem::replace(&mut self.guards, Vec::new())) }; let mut rdef = if config.is_root() || !self.rdef.is_empty() { - ResourceDef::new(&insert_slash(&self.rdef)) + ResourceDef::new(insert_slash(self.rdef.clone())) } else { - ResourceDef::new(&self.rdef) + ResourceDef::new(self.rdef.clone()) }; if let Some(ref name) = self.name { *rdef.name_mut() = name.clone(); @@ -660,6 +661,23 @@ mod tests { assert_eq!(resp.status(), StatusCode::OK); } + #[actix_rt::test] + async fn test_pattern() { + let mut srv = + init_service(App::new().service(web::resource(["/test", "/test2"]).to(|| { + async { + Ok::<_, Error>(HttpResponse::Ok()) + } + }))) + .await; + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + let req = TestRequest::with_uri("/test2").to_request(); + let resp = call_service(&mut srv, req).await; + assert_eq!(resp.status(), StatusCode::OK); + } + #[actix_rt::test] async fn test_default_resource() { let mut srv = init_service( diff --git a/src/service.rs b/src/service.rs index b58fb5b4e..e51be9964 100644 --- a/src/service.rs +++ b/src/service.rs @@ -8,7 +8,7 @@ use actix_http::{ Error, Extensions, HttpMessage, Payload, PayloadStream, RequestHead, Response, ResponseHead, }; -use actix_router::{Path, Resource, ResourceDef, Url}; +use actix_router::{IntoPattern, Path, Resource, ResourceDef, Url}; use actix_service::{IntoServiceFactory, ServiceFactory}; use crate::config::{AppConfig, AppService}; @@ -422,16 +422,16 @@ impl fmt::Debug for ServiceResponse { } pub struct WebService { - rdef: String, + rdef: Vec, name: Option, guards: Vec>, } impl WebService { /// Create new `WebService` instance. - pub fn new(path: &str) -> Self { + pub fn new(path: T) -> Self { WebService { - rdef: path.to_string(), + rdef: path.patterns(), name: None, guards: Vec::new(), } @@ -491,7 +491,7 @@ impl WebService { struct WebServiceImpl { srv: T, - rdef: String, + rdef: Vec, name: Option, guards: Vec>, } @@ -514,9 +514,9 @@ where }; let mut rdef = if config.is_root() || !self.rdef.is_empty() { - ResourceDef::new(&insert_slash(&self.rdef)) + ResourceDef::new(insert_slash(self.rdef)) } else { - ResourceDef::new(&self.rdef) + ResourceDef::new(self.rdef) }; if let Some(ref name) = self.name { *rdef.name_mut() = name.clone(); diff --git a/src/types/form.rs b/src/types/form.rs index 333ecbd64..d917345e1 100644 --- a/src/types/form.rs +++ b/src/types/form.rs @@ -365,7 +365,7 @@ where .map_err(|_| UrlencodedError::Parse) } } - .boxed_local(), + .boxed_local(), ); self.poll(cx) } diff --git a/src/types/json.rs b/src/types/json.rs index adb425cd9..fb00bf7a6 100644 --- a/src/types/json.rs +++ b/src/types/json.rs @@ -396,7 +396,7 @@ where } Ok(serde_json::from_slice::(&body)?) } - .boxed_local(), + .boxed_local(), ); self.poll(cx) diff --git a/src/types/payload.rs b/src/types/payload.rs index dd7a84f32..449e6c5b0 100644 --- a/src/types/payload.rs +++ b/src/types/payload.rs @@ -229,7 +229,7 @@ impl FromRequest for String { .ok_or_else(|| ErrorBadRequest("Can not decode body"))?) } } - .boxed_local(), + .boxed_local(), ) } } @@ -391,7 +391,7 @@ impl Future for HttpMessageBody { } Ok(body.freeze()) } - .boxed_local(), + .boxed_local(), ); self.poll(cx) } diff --git a/src/web.rs b/src/web.rs index 51094c32e..50d99479a 100644 --- a/src/web.rs +++ b/src/web.rs @@ -1,5 +1,6 @@ //! Essentials helper functions and types for application registration. use actix_http::http::Method; +use actix_router::IntoPattern; use futures::Future; pub use actix_http::Response as HttpResponse; @@ -50,7 +51,7 @@ pub use crate::types::*; /// .route(web::head().to(|| HttpResponse::MethodNotAllowed())) /// ); /// ``` -pub fn resource(path: &str) -> Resource { +pub fn resource(path: T) -> Resource { Resource::new(path) } @@ -249,7 +250,7 @@ where /// .finish(my_service) /// ); /// ``` -pub fn service(path: &str) -> WebService { +pub fn service(path: T) -> WebService { WebService::new(path) } From 7b3c99b9337a8a237469c4bc37e512205f39b982 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 25 Dec 2019 20:17:22 +0400 Subject: [PATCH 160/176] prep actix-framed release --- actix-framed/Cargo.toml | 3 +-- actix-framed/changes.md | 4 ++++ src/resource.rs | 14 +++++++------- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml index 8466e4a81..7e322e1d4 100644 --- a/actix-framed/Cargo.toml +++ b/actix-framed/Cargo.toml @@ -13,7 +13,6 @@ categories = ["network-programming", "asynchronous", "web-programming::websocket"] license = "MIT/Apache-2.0" edition = "2018" -workspace =".." [lib] name = "actix_framed" @@ -22,7 +21,7 @@ path = "src/lib.rs" [dependencies] actix-codec = "0.2.0" actix-service = "1.0.1" -actix-router = "0.2.0" +actix-router = "0.2.1" actix-rt = "1.0.0" actix-http = "1.0.1" diff --git a/actix-framed/changes.md b/actix-framed/changes.md index 6e67e00d8..41c7aed0e 100644 --- a/actix-framed/changes.md +++ b/actix-framed/changes.md @@ -1,5 +1,9 @@ # Changes +## [0.3.0] - 2019-12-25 + +* Migrate to actix-http 1.0 + ## [0.2.1] - 2019-07-20 * Remove unneeded actix-utils dependency diff --git a/src/resource.rs b/src/resource.rs index d60d50967..d03024a07 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -663,13 +663,13 @@ mod tests { #[actix_rt::test] async fn test_pattern() { - let mut srv = - init_service(App::new().service(web::resource(["/test", "/test2"]).to(|| { - async { - Ok::<_, Error>(HttpResponse::Ok()) - } - }))) - .await; + let mut srv = init_service( + App::new().service( + web::resource(["/test", "/test2"]) + .to(|| async { Ok::<_, Error>(HttpResponse::Ok()) }), + ), + ) + .await; let req = TestRequest::with_uri("/test").to_request(); let resp = call_service(&mut srv, req).await; assert_eq!(resp.status(), StatusCode::OK); From 642ae161c01fa303c0439fa63a2a2db1d3243348 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 25 Dec 2019 20:21:00 +0400 Subject: [PATCH 161/176] prep actix-web release --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index b0193c0ed..2f96dce5d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## [2.0.0] - 2019-12-xx +## [2.0.0] - 2019-12-25 ### Changed From 6db909a3e7fe7ad91c642531f98fc7bdc3f40469 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 25 Dec 2019 20:27:30 +0400 Subject: [PATCH 162/176] update migration --- MIGRATION.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MIGRATION.md b/MIGRATION.md index 000c5b46b..14bd52bbb 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -14,6 +14,9 @@ * `actix_http_test::TestServer` moved to `actix_web::test` module. To start test server use `test::start()` or `test_start_with_config()` methods +* `ResponseError` trait has been reafctored. `ResponseError::error_response()` renders + http response. + ## 1.0.1 From a4ad5e6b69e1f9205d76ff40d56260bc125bbd08 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 25 Dec 2019 20:52:20 +0400 Subject: [PATCH 163/176] update timeouts for test server --- src/test.rs | 4 ++-- test-server/src/lib.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test.rs b/src/test.rs index 9c06b7337..bb0d05cf7 100644 --- a/src/test.rs +++ b/src/test.rs @@ -730,7 +730,7 @@ where .map_err(|e| log::error!("Can not set alpn protocol: {:?}", e)); Connector::new() .conn_lifetime(time::Duration::from_secs(0)) - .timeout(time::Duration::from_millis(3000)) + .timeout(time::Duration::from_millis(30000)) .ssl(builder.build()) .finish() } @@ -738,7 +738,7 @@ where { Connector::new() .conn_lifetime(time::Duration::from_secs(0)) - .timeout(time::Duration::from_millis(3000)) + .timeout(time::Duration::from_millis(30000)) .finish() } }; diff --git a/test-server/src/lib.rs b/test-server/src/lib.rs index ff564c3e1..27326c67a 100644 --- a/test-server/src/lib.rs +++ b/test-server/src/lib.rs @@ -77,7 +77,7 @@ pub fn test_server>(factory: F) -> TestServer { .map_err(|e| log::error!("Can not set alpn protocol: {:?}", e)); Connector::new() .conn_lifetime(time::Duration::from_secs(0)) - .timeout(time::Duration::from_millis(3000)) + .timeout(time::Duration::from_millis(30000)) .ssl(builder.build()) .finish() } @@ -85,7 +85,7 @@ pub fn test_server>(factory: F) -> TestServer { { Connector::new() .conn_lifetime(time::Duration::from_secs(0)) - .timeout(time::Duration::from_millis(3000)) + .timeout(time::Duration::from_millis(30000)) .finish() } }; From 7bd22702907dfde91f9e8697cfb223a5c89b85ab Mon Sep 17 00:00:00 2001 From: wojciechkepka <46892771+wojciechkepka@users.noreply.github.com> Date: Thu, 26 Dec 2019 11:42:07 +0100 Subject: [PATCH 164/176] Fix link to example in readme.md (#1236) * Fix link to example in readme.md * Add links to openssl and rustls examples * Rustls should be uppercase --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 99f09b0fa..3c231d998 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,8 @@ async fn main() -> std::io::Result<()> { [Askama](https://github.com/actix/examples/tree/master/template_askama/) templates * [Diesel integration](https://github.com/actix/examples/tree/master/diesel/) * [r2d2](https://github.com/actix/examples/tree/master/r2d2/) -* [SSL / HTTP/2.0](https://github.com/actix/examples/tree/master/tls/) +* [OpenSSL](https://github.com/actix/examples/tree/master/openssl/) +* [Rustls](https://github.com/actix/examples/tree/master/rustls/) * [Tcp/Websocket chat](https://github.com/actix/examples/tree/master/websocket-chat/) * [Json](https://github.com/actix/examples/tree/master/json/) From bcb5086c9183de549ddcd2a96543d7af96dbd2d6 Mon Sep 17 00:00:00 2001 From: Jonathan Brookins Date: Mon, 30 Dec 2019 10:16:04 -0500 Subject: [PATCH 165/176] Added 2.0.0 rustls feature name change (#1244) --- MIGRATION.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/MIGRATION.md b/MIGRATION.md index 14bd52bbb..0f05b3059 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -17,6 +17,20 @@ * `ResponseError` trait has been reafctored. `ResponseError::error_response()` renders http response. +* Feature `rust-tls` renamed to `rustls` + + instead of + + ```rust + actix-web = { version = "2.0.0", features = ["rust-tls"] } + ``` + + use + + ```rust + actix-web = { version = "2.0.0", features = ["rustls"] } + ``` + ## 1.0.1 From 67793c5d92e06f3ec9c2131c580df842889b3718 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Mon, 30 Dec 2019 21:22:04 +0600 Subject: [PATCH 166/176] add ssl feature migration --- MIGRATION.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/MIGRATION.md b/MIGRATION.md index 0f05b3059..91d614e52 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -20,7 +20,7 @@ * Feature `rust-tls` renamed to `rustls` instead of - + ```rust actix-web = { version = "2.0.0", features = ["rust-tls"] } ``` @@ -31,6 +31,20 @@ actix-web = { version = "2.0.0", features = ["rustls"] } ``` +* Feature `ssl` renamed to `openssl` + + instead of + + ```rust + actix-web = { version = "2.0.0", features = ["ssl"] } + ``` + + use + + ```rust + actix-web = { version = "2.0.0", features = ["openssl"] } + ``` + ## 1.0.1 From 2803fcbe229203326007d31490e3ba87499425f6 Mon Sep 17 00:00:00 2001 From: Jeremy Wright Date: Thu, 2 Jan 2020 19:45:17 -0700 Subject: [PATCH 167/176] Small grammaritical update to lib.rs (#1248) --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index a9965229c..d51005cfe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,7 +47,7 @@ //! configure servers. //! //! * [web](web/index.html): This module -//! provide essentials helper functions and types for application registration. +//! provides essential helper functions and types for application registration. //! //! * [HttpRequest](struct.HttpRequest.html) and //! [HttpResponse](struct.HttpResponse.html): These structs From f5fd6bc49fd0886cf4a1c76de44c259aff7426c9 Mon Sep 17 00:00:00 2001 From: linkmauve Date: Mon, 6 Jan 2020 16:15:04 +0100 Subject: [PATCH 168/176] Fix actix-http examples (#1259) Fix actix-http examples --- actix-http/examples/echo.rs | 4 +++- actix-http/examples/echo2.rs | 4 +++- actix-http/examples/hello-world.rs | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/actix-http/examples/echo.rs b/actix-http/examples/echo.rs index 5b2894f89..3d57a472a 100644 --- a/actix-http/examples/echo.rs +++ b/actix-http/examples/echo.rs @@ -7,7 +7,8 @@ use futures::StreamExt; use http::header::HeaderValue; use log::info; -fn main() -> io::Result<()> { +#[actix_rt::main] +async fn main() -> io::Result<()> { env::set_var("RUST_LOG", "echo=info"); env_logger::init(); @@ -37,4 +38,5 @@ fn main() -> io::Result<()> { .tcp() })? .run() + .await } diff --git a/actix-http/examples/echo2.rs b/actix-http/examples/echo2.rs index 07d181277..f89ea2dfb 100644 --- a/actix-http/examples/echo2.rs +++ b/actix-http/examples/echo2.rs @@ -19,7 +19,8 @@ async fn handle_request(mut req: Request) -> Result { .body(body)) } -fn main() -> io::Result<()> { +#[actix_rt::main] +async fn main() -> io::Result<()> { env::set_var("RUST_LOG", "echo=info"); env_logger::init(); @@ -28,4 +29,5 @@ fn main() -> io::Result<()> { HttpService::build().finish(handle_request).tcp() })? .run() + .await } diff --git a/actix-http/examples/hello-world.rs b/actix-http/examples/hello-world.rs index 7d8576869..4134ee734 100644 --- a/actix-http/examples/hello-world.rs +++ b/actix-http/examples/hello-world.rs @@ -6,7 +6,8 @@ use futures::future; use http::header::HeaderValue; use log::info; -fn main() -> io::Result<()> { +#[actix_rt::main] +async fn main() -> io::Result<()> { env::set_var("RUST_LOG", "hello_world=info"); env_logger::init(); @@ -24,4 +25,5 @@ fn main() -> io::Result<()> { .tcp() })? .run() + .await } From 51ab4fb73dd1fce8de44184d08cc916c060a4d41 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 10 Jan 2020 03:30:45 +0900 Subject: [PATCH 169/176] Tweak actions to use cache and not to be stuck on the way (#1264) --- .github/workflows/macos.yml | 59 ++++++++++++++++++++++++++ .github/workflows/main.yml | 67 ----------------------------- .github/workflows/windows.yml | 79 +++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 67 deletions(-) create mode 100644 .github/workflows/macos.yml delete mode 100644 .github/workflows/main.yml create mode 100644 .github/workflows/windows.yml diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 000000000..f50ae2f05 --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,59 @@ +name: CI (macOS) + +on: [push, pull_request] + +jobs: + build_and_test: + strategy: + fail-fast: false + matrix: + version: + - stable + - nightly + + name: ${{ matrix.version }} - x86_64-apple-darwin + runs-on: macOS-latest + + steps: + - uses: actions/checkout@master + + - name: Install ${{ matrix.version }} + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.version }}-x86_64-apple-darwin + profile: minimal + override: true + + - name: Generate Cargo.lock + uses: actions-rs/cargo@v1 + with: + command: update + - name: Cache cargo registry + uses: actions/cache@v1 + with: + path: ~/.cargo/registry + key: ${{ matrix.version }}-x86_64-apple-darwin-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + - name: Cache cargo index + uses: actions/cache@v1 + with: + path: ~/.cargo/git + key: ${{ matrix.version }}-x86_64-apple-darwin-cargo-index-${{ hashFiles('**/Cargo.lock') }} + - name: Cache cargo build + uses: actions/cache@v1 + with: + path: target + key: ${{ matrix.version }}-x86_64-apple-darwin-cargo-build-${{ hashFiles('**/Cargo.lock') }} + + - name: check build + uses: actions-rs/cargo@v1 + with: + command: check + args: --all --bins --examples --tests + + - name: tests + uses: actions-rs/cargo@v1 + with: + command: test + args: --all --all-features --no-fail-fast -- --nocapture + --skip=test_h2_content_length + --skip=test_reading_deflate_encoding_large_random_rustls diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 693291fd3..000000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: CI - -on: [push, pull_request] - -env: - VCPKGRS_DYNAMIC: 1 - -jobs: - build_and_test: - strategy: - fail-fast: false - matrix: - toolchain: - - x86_64-pc-windows-msvc - # - i686-pc-windows-msvc - - x86_64-apple-darwin - version: - - stable - - nightly - include: - - toolchain: x86_64-pc-windows-msvc - os: windows-latest - arch: x64 - # - toolchain: i686-pc-windows-msvc - # os: windows-latest - # arch: x86 - - toolchain: x86_64-apple-darwin - os: macOS-latest - - name: ${{ matrix.version }} - ${{ matrix.toolchain }} - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@master - - - name: Install ${{ matrix.version }} - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.version }}-${{ matrix.toolchain }} - default: true - - - name: Install OpenSSL - if: matrix.os == 'windows-latest' - run: | - vcpkg integrate install - vcpkg install openssl:${{ matrix.arch }}-windows - - - name: check nightly - if: matrix.version == 'nightly' - uses: actions-rs/cargo@v1 - with: - command: check - args: --all --benches --bins --examples --tests - - - name: check stable - if: matrix.version == 'stable' - uses: actions-rs/cargo@v1 - with: - command: check - args: --all --bins --examples --tests - - - name: tests - if: matrix.toolchain != 'x86_64-pc-windows-gnu' - uses: actions-rs/cargo@v1 - with: - command: test - args: --all --all-features -- --nocapture diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 000000000..9aa3d3ba4 --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,79 @@ +name: CI (Windows) + +on: [push, pull_request] + +env: + VCPKGRS_DYNAMIC: 1 + +jobs: + build_and_test: + strategy: + fail-fast: false + matrix: + version: + - stable + - nightly + + name: ${{ matrix.version }} - x86_64-pc-windows-msvc + runs-on: windows-latest + + steps: + - uses: actions/checkout@master + + - name: Install ${{ matrix.version }} + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.version }}-x86_64-pc-windows-msvc + profile: minimal + override: true + + - name: Generate Cargo.lock + uses: actions-rs/cargo@v1 + with: + command: update + - name: Cache cargo registry + uses: actions/cache@v1 + with: + path: ~/.cargo/registry + key: ${{ matrix.version }}-x86_64-pc-windows-msvc-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + - name: Cache cargo index + uses: actions/cache@v1 + with: + path: ~/.cargo/git + key: ${{ matrix.version }}-x86_64-pc-windows-msvc-cargo-index-${{ hashFiles('**/Cargo.lock') }} + - name: Cache cargo build + uses: actions/cache@v1 + with: + path: target + key: ${{ matrix.version }}-x86_64-pc-windows-msvc-cargo-build-${{ hashFiles('**/Cargo.lock') }} + - name: Cache vcpkg package + uses: actions/cache@v1 + id: cache-vcpkg + with: + path: C:\vcpkg + key: windows_x64-${{ matrix.version }}-vcpkg + + - name: Install OpenSSL + if: steps.cache-vcpkg.outputs.cache-hit != 'true' + run: | + vcpkg integrate install + vcpkg install openssl:x64-windows + + - name: check build + uses: actions-rs/cargo@v1 + with: + command: check + args: --all --bins --examples --tests + + - name: tests + uses: actions-rs/cargo@v1 + with: + command: test + args: --all --all-features --no-fail-fast -- --nocapture + --skip=test_h2_content_length + --skip=test_reading_deflate_encoding_large_random_rustls + --skip=test_params + --skip=test_simple + --skip=test_expect_continue + --skip=test_http10_keepalive + --skip=test_slow_request From f6ff056b8a496d1e629ecab16ba50c7f8e5094a3 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 10 Jan 2020 11:26:54 +0600 Subject: [PATCH 170/176] Fix panic with already borrowed: BorrowMutError #1263 --- actix-identity/CHANGES.md | 4 +++ actix-identity/Cargo.toml | 7 ++--- actix-identity/src/lib.rs | 60 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 65 insertions(+), 6 deletions(-) diff --git a/actix-identity/CHANGES.md b/actix-identity/CHANGES.md index 8e89acc15..594c21388 100644 --- a/actix-identity/CHANGES.md +++ b/actix-identity/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [0.2.1] - 2020-01-10 + +* Fix panic with already borrowed: BorrowMutError #1263 + ## [0.2.0] - 2019-12-20 * Use actix-web 2.0 diff --git a/actix-identity/Cargo.toml b/actix-identity/Cargo.toml index b30246f0b..8cd6b1271 100644 --- a/actix-identity/Cargo.toml +++ b/actix-identity/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-identity" -version = "0.2.0" +version = "0.2.1" authors = ["Nikolay Kim "] description = "Identity service for actix web framework." readme = "README.md" @@ -10,15 +10,14 @@ repository = "https://github.com/actix/actix-web.git" documentation = "https://docs.rs/actix-identity/" license = "MIT/Apache-2.0" edition = "2018" -workspace = ".." [lib] name = "actix_identity" path = "src/lib.rs" [dependencies] -actix-web = { version = "2.0.0-rc", default-features = false, features = ["secure-cookies"] } -actix-service = "1.0.1" +actix-web = { version = "2.0.0", default-features = false, features = ["secure-cookies"] } +actix-service = "1.0.2" futures = "0.3.1" serde = "1.0" serde_json = "1.0" diff --git a/actix-identity/src/lib.rs b/actix-identity/src/lib.rs index b10a419dd..3b9626991 100644 --- a/actix-identity/src/lib.rs +++ b/actix-identity/src/lib.rs @@ -251,6 +251,15 @@ pub struct IdentityServiceMiddleware { service: Rc>, } +impl Clone for IdentityServiceMiddleware { + fn clone(&self) -> Self { + Self { + backend: self.backend.clone(), + service: self.service.clone(), + } + } +} + impl Service for IdentityServiceMiddleware where B: 'static, @@ -279,7 +288,9 @@ where req.extensions_mut() .insert(IdentityItem { id, changed: false }); - let mut res = srv.borrow_mut().call(req).await?; + // https://github.com/actix/actix-web/issues/1263 + let fut = { srv.borrow_mut().call(req) }; + let mut res = fut.await?; let id = res.request().extensions_mut().remove::(); if let Some(id) = id { @@ -606,9 +617,10 @@ mod tests { use std::borrow::Borrow; use super::*; + use actix_service::into_service; use actix_web::http::StatusCode; use actix_web::test::{self, TestRequest}; - use actix_web::{web, App, Error, HttpResponse}; + use actix_web::{error, web, App, Error, HttpResponse}; const COOKIE_KEY_MASTER: [u8; 32] = [0; 32]; const COOKIE_NAME: &'static str = "actix_auth"; @@ -1045,6 +1057,7 @@ mod tests { assert_logged_in(resp, Some(COOKIE_LOGIN)).await; } + // https://github.com/actix/actix-web/issues/1263 #[actix_rt::test] async fn test_identity_cookie_updated_on_visit_deadline() { let mut srv = create_identity_server(|c| { @@ -1069,4 +1082,47 @@ mod tests { ); assert_logged_in(resp, Some(COOKIE_LOGIN)).await; } + + #[actix_rt::test] + async fn test_borrowed_mut_error() { + use futures::future::{lazy, ok, Ready}; + + struct Ident; + impl IdentityPolicy for Ident { + type Future = Ready, Error>>; + type ResponseFuture = Ready>; + + fn from_request(&self, _: &mut ServiceRequest) -> Self::Future { + ok(Some("test".to_string())) + } + + fn to_response( + &self, + _: Option, + _: bool, + _: &mut ServiceResponse, + ) -> Self::ResponseFuture { + ok(()) + } + } + + let mut srv = IdentityServiceMiddleware { + backend: Rc::new(Ident), + service: Rc::new(RefCell::new(into_service(|_: ServiceRequest| { + async move { + actix_rt::time::delay_for(std::time::Duration::from_secs(100)).await; + Err::(error::ErrorBadRequest("error")) + } + }))), + }; + + let mut srv2 = srv.clone(); + let req = TestRequest::default().to_srv_request(); + actix_rt::spawn(async move { + let _ = srv2.call(req).await; + }); + actix_rt::time::delay_for(std::time::Duration::from_millis(50)).await; + + let _ = lazy(|cx| srv.poll_ready(cx)).await; + } } From e66312b664f183a80eae6a9de3e9c8667b08241e Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 10 Jan 2020 11:36:59 +0600 Subject: [PATCH 171/176] add extra constraints --- Cargo.toml | 8 ++++---- actix-http/src/cloneable.rs | 16 +++++----------- actix-http/src/h1/service.rs | 2 +- actix-http/src/h2/service.rs | 2 +- actix-http/src/service.rs | 2 +- 5 files changed, 12 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1adae97b8..9e1b559eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,9 +60,9 @@ rustls = ["actix-tls/rustls", "awc/rustls", "rust-tls"] [dependencies] actix-codec = "0.2.0" -actix-service = "1.0.1" -actix-utils = "1.0.4" -actix-router = "0.2.1" +actix-service = "1.0.2" +actix-utils = "1.0.6" +actix-router = "0.2.4" actix-rt = "1.0.0" actix-server = "1.0.0" actix-testing = "1.0.0" @@ -115,4 +115,4 @@ actix-identity = { path = "actix-identity" } actix-session = { path = "actix-session" } actix-files = { path = "actix-files" } actix-multipart = { path = "actix-multipart" } -awc = { path = "awc" } \ No newline at end of file +awc = { path = "awc" } diff --git a/actix-http/src/cloneable.rs b/actix-http/src/cloneable.rs index 90d198b9c..65c6bec21 100644 --- a/actix-http/src/cloneable.rs +++ b/actix-http/src/cloneable.rs @@ -6,27 +6,21 @@ use actix_service::Service; #[doc(hidden)] /// Service that allows to turn non-clone service to a service with `Clone` impl -pub(crate) struct CloneableService(Rc>); +pub(crate) struct CloneableService(Rc>); -impl CloneableService { - pub(crate) fn new(service: T) -> Self - where - T: Service, - { +impl CloneableService { + pub(crate) fn new(service: T) -> Self { Self(Rc::new(UnsafeCell::new(service))) } } -impl Clone for CloneableService { +impl Clone for CloneableService { fn clone(&self) -> Self { Self(self.0.clone()) } } -impl Service for CloneableService -where - T: Service, -{ +impl Service for CloneableService { type Request = T::Request; type Response = T::Response; type Error = T::Error; diff --git a/actix-http/src/h1/service.rs b/actix-http/src/h1/service.rs index 69c8fc55c..4d1a1dc1b 100644 --- a/actix-http/src/h1/service.rs +++ b/actix-http/src/h1/service.rs @@ -364,7 +364,7 @@ where } /// `Service` implementation for HTTP1 transport -pub struct H1ServiceHandler { +pub struct H1ServiceHandler { srv: CloneableService, expect: CloneableService, upgrade: Option>, diff --git a/actix-http/src/h2/service.rs b/actix-http/src/h2/service.rs index 7cae99f5b..ff3f69faf 100644 --- a/actix-http/src/h2/service.rs +++ b/actix-http/src/h2/service.rs @@ -246,7 +246,7 @@ where } /// `Service` implementation for http/2 transport -pub struct H2ServiceHandler { +pub struct H2ServiceHandler { srv: CloneableService, cfg: ServiceConfig, on_connect: Option Box>>, diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index 82618289b..51de95135 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -443,7 +443,7 @@ where } /// `Service` implementation for http transport -pub struct HttpServiceHandler { +pub struct HttpServiceHandler { srv: CloneableService, expect: CloneableService, upgrade: Option>, From abb462ef8540b170d518fea968a3adb13d809ddc Mon Sep 17 00:00:00 2001 From: linkmauve Date: Fri, 10 Jan 2020 18:34:31 +0100 Subject: [PATCH 172/176] Replace sha1 dependency with sha-1 (#1258) * Replace sha1 dependency with sha-1 This other crate is being maintained, and it offers better performances when using the `asm` feature (especially [on AArch64](https://github.com/RustCrypto/hashes/pull/97)). * Update CHANGES.md with the sha-1 migration * Add a test for hash_key() --- CHANGES.md | 6 ++++++ actix-http/Cargo.toml | 2 +- actix-http/src/ws/proto.rs | 13 ++++++++++--- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 2f96dce5d..29f78e0b1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Changes +## [2.0.NEXT] - 2020-01-xx + +### Changed + +* Use `sha-1` crate instead of unmaintained `sha1` crate + ## [2.0.0] - 2019-12-25 ### Changed diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 367dbafec..93aaa756e 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -74,7 +74,7 @@ rand = "0.7" regex = "1.3" serde = "1.0" serde_json = "1.0" -sha1 = "0.6" +sha-1 = "0.8" slab = "0.4" serde_urlencoded = "0.6.1" time = "0.1.42" diff --git a/actix-http/src/ws/proto.rs b/actix-http/src/ws/proto.rs index ad42b7a6b..60af6f08b 100644 --- a/actix-http/src/ws/proto.rs +++ b/actix-http/src/ws/proto.rs @@ -207,12 +207,13 @@ static WS_GUID: &str = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // TODO: hash is always same size, we dont need String pub fn hash_key(key: &[u8]) -> String { + use sha1::Digest; let mut hasher = sha1::Sha1::new(); - hasher.update(key); - hasher.update(WS_GUID.as_bytes()); + hasher.input(key); + hasher.input(WS_GUID.as_bytes()); - base64::encode(&hasher.digest().bytes()) + base64::encode(hasher.result().as_ref()) } #[cfg(test)] @@ -277,6 +278,12 @@ mod test { assert_eq!(format!("{}", OpCode::Bad), "BAD"); } + #[test] + fn test_hash_key() { + let hash = hash_key(b"hello actix-web"); + assert_eq!(&hash, "cR1dlyUUJKp0s/Bel25u5TgvC3E="); + } + #[test] fn closecode_from_u16() { assert_eq!(CloseCode::from(1000u16), CloseCode::Normal); From 7c974ee6687229a61760331baf3f749843c23d6c Mon Sep 17 00:00:00 2001 From: Jacob Brown Date: Fri, 10 Jan 2020 12:55:20 -0600 Subject: [PATCH 173/176] Update doc comment for `HttpRequest::app_data` (#1265) * update doc comment for `HttpRequest::app_data` * add `no_run` to doc comment * add `ignore` to doc comment * Update src/request.rs Co-Authored-By: Yuki Okushi Co-authored-by: Yuki Okushi --- src/request.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/request.rs b/src/request.rs index b51438950..cd9c72313 100644 --- a/src/request.rs +++ b/src/request.rs @@ -206,8 +206,14 @@ impl HttpRequest { &self.0.config } - /// Get an application data stored with `App::extension()` method during - /// application configuration. + /// Get an application data object stored with `App::data` or `App::app_data` + /// methods during application configuration. + /// + /// If `App::data` was used to store object, use `Data`: + /// + /// ```rust,ignore + /// let opt_t = req.app_data::>(); + /// ``` pub fn app_data(&self) -> Option<&T> { if let Some(st) = self.0.app_data.get::() { Some(&st) From bca41f8d4006bb567eb1ab258c49b24d65e7ef59 Mon Sep 17 00:00:00 2001 From: Peter Hall Date: Fri, 10 Jan 2020 19:53:17 +0000 Subject: [PATCH 174/176] Changes to Cors builder (#1266) Co-authored-by: Yuki Okushi --- MIGRATION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIGRATION.md b/MIGRATION.md index 91d614e52..529f9714d 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -44,7 +44,7 @@ ```rust actix-web = { version = "2.0.0", features = ["openssl"] } ``` - +* `Cors` builder now requires that you call `.finish()` to construct the middleware ## 1.0.1 From 412e54ce1071e22da11a8a53e89f807814e7e0e4 Mon Sep 17 00:00:00 2001 From: Adam Kewley Date: Wed, 15 Jan 2020 19:09:58 +0000 Subject: [PATCH 175/176] Fixed documentation typo for actix-files (#1278) --- actix-files/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index 4d111e8a7..d910b7d5f 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -275,7 +275,7 @@ impl Files { /// /// `File` uses `ThreadPool` for blocking filesystem operations. /// By default pool with 5x threads of available cpus is used. - /// Pool size can be changed by setting ACTIX_CPU_POOL environment variable. + /// Pool size can be changed by setting ACTIX_THREADPOOL environment variable. pub fn new>(path: &str, dir: T) -> Files { let orig_dir = dir.into(); let dir = match orig_dir.canonicalize() { From 3a5b62b5502d8c2ba5d824599171bb381f6b1b49 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 16 Jan 2020 23:17:17 +0900 Subject: [PATCH 176/176] Add dependencies instruction (#1281) --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3c231d998..0b55c5bae 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,16 @@ Actix web is a simple, pragmatic and extremely fast web framework for Rust. ## Example +Dependencies: + +```toml +[dependencies] +actix-web = "2" +actix-rt = "1" +``` + +Code: + ```rust use actix_web::{get, web, App, HttpServer, Responder}; @@ -65,7 +75,7 @@ async fn main() -> std::io::Result<()> { * [Multipart streams](https://github.com/actix/examples/tree/master/multipart/) * [Simple websocket](https://github.com/actix/examples/tree/master/websocket/) * [Tera](https://github.com/actix/examples/tree/master/template_tera/) / - [Askama](https://github.com/actix/examples/tree/master/template_askama/) templates +* [Askama](https://github.com/actix/examples/tree/master/template_askama/) templates * [Diesel integration](https://github.com/actix/examples/tree/master/diesel/) * [r2d2](https://github.com/actix/examples/tree/master/r2d2/) * [OpenSSL](https://github.com/actix/examples/tree/master/openssl/)