From b36fdc46db7b606615c417d538059d30427b82fd Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Wed, 17 Jul 2019 18:45:17 -0400 Subject: [PATCH 01/11] Remove several usages of 'unsafe' (#968) * Replace UnsafeCell in DateServiceInner with Cell The previous API was extremely dangerous - calling `get_ref()` followed by `reset()` would trigger instant UB, without requiring any `unsafe` blocks in the caller. By making DateInner `Copy`, we can use a normal `Cell` instead of an `UnsafeCell`. This makes it impossible to cause UB (or even panic) with the API. * Split unsafe block HttpServiceHandlerResponse Also add explanation of the safety of the usage of `unsafe` * Replace UnsafeCell with RefCell in PayloadRef This ensures that a mistake in the usage of 'get_mut' will cause a panic, not undefined behavior. --- actix-http/src/config.rs | 24 ++++++++++++------------ actix-http/src/service.rs | 22 ++++++++++++---------- actix-multipart/src/server.rs | 29 +++++++++++++---------------- 3 files changed, 37 insertions(+), 38 deletions(-) diff --git a/actix-http/src/config.rs b/actix-http/src/config.rs index aba50a814..7d0e27635 100644 --- a/actix-http/src/config.rs +++ b/actix-http/src/config.rs @@ -1,4 +1,4 @@ -use std::cell::UnsafeCell; +use std::cell::Cell; use std::fmt; use std::fmt::Write; use std::rc::Rc; @@ -172,6 +172,7 @@ impl ServiceConfig { } } +#[derive(Copy, Clone)] struct Date { bytes: [u8; DATE_VALUE_LENGTH], pos: usize, @@ -205,28 +206,28 @@ impl fmt::Write for Date { struct DateService(Rc); struct DateServiceInner { - current: UnsafeCell>, + current: Cell>, } impl DateServiceInner { fn new() -> Self { DateServiceInner { - current: UnsafeCell::new(None), + current: Cell::new(None), } } - fn get_ref(&self) -> &Option<(Date, Instant)> { - unsafe { &*self.current.get() } + fn get(&self) -> Option<(Date, Instant)> { + self.current.get() } fn reset(&self) { - unsafe { (&mut *self.current.get()).take() }; + self.current.set(None); } fn update(&self) { let now = Instant::now(); let date = Date::new(); - *(unsafe { &mut *self.current.get() }) = Some((date, now)); + self.current.set(Some((date, now))); } } @@ -236,7 +237,7 @@ impl DateService { } fn check_date(&self) { - if self.0.get_ref().is_none() { + if self.0.get().is_none() { self.0.update(); // periodic date update @@ -252,14 +253,13 @@ impl DateService { fn now(&self) -> Instant { self.check_date(); - self.0.get_ref().as_ref().unwrap().1 + self.0.get().unwrap().1 } - fn date(&self) -> &Date { + fn date(&self) -> Date { self.check_date(); - let item = self.0.get_ref().as_ref().unwrap(); - &item.0 + self.0.get().unwrap().0 } } diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index be021b252..09b8077b3 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -466,16 +466,18 @@ where State::Unknown(ref mut data) => { if let Some(ref mut item) = data { loop { - unsafe { - let b = item.1.bytes_mut(); - let n = try_ready!(item.0.poll_read(b)); - if n == 0 { - return Ok(Async::Ready(())); - } - item.1.advance_mut(n); - if item.1.len() >= HTTP2_PREFACE.len() { - break; - } + // Safety - we only write to the returned slice. + let b = unsafe { item.1.bytes_mut() }; + let n = try_ready!(item.0.poll_read(b)); + if n == 0 { + return Ok(Async::Ready(())); + } + // 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 { diff --git a/actix-multipart/src/server.rs b/actix-multipart/src/server.rs index c28a8d6af..70eb61cb9 100644 --- a/actix-multipart/src/server.rs +++ b/actix-multipart/src/server.rs @@ -1,5 +1,5 @@ //! Multipart payload support -use std::cell::{Cell, RefCell, UnsafeCell}; +use std::cell::{Cell, RefCell, RefMut}; use std::marker::PhantomData; use std::rc::Rc; use std::{cmp, fmt}; @@ -112,7 +112,7 @@ impl Stream for Multipart { Err(err) } else if self.safety.current() { let mut inner = self.inner.as_mut().unwrap().borrow_mut(); - if let Some(payload) = inner.payload.get_mut(&self.safety) { + if let Some(mut payload) = inner.payload.get_mut(&self.safety) { payload.poll_stream()?; } inner.poll(&self.safety) @@ -265,12 +265,12 @@ impl InnerMultipart { } } - let headers = if let Some(payload) = self.payload.get_mut(safety) { + let headers = if let Some(mut payload) = self.payload.get_mut(safety) { match self.state { // read until first boundary InnerState::FirstBoundary => { match InnerMultipart::skip_until_boundary( - payload, + &mut *payload, &self.boundary, )? { Some(eof) => { @@ -286,7 +286,7 @@ impl InnerMultipart { } // read boundary InnerState::Boundary => { - match InnerMultipart::read_boundary(payload, &self.boundary)? { + match InnerMultipart::read_boundary(&mut *payload, &self.boundary)? { None => return Ok(Async::NotReady), Some(eof) => { if eof { @@ -303,7 +303,7 @@ impl InnerMultipart { // read field headers for next field if self.state == InnerState::Headers { - if let Some(headers) = InnerMultipart::read_headers(payload)? { + if let Some(headers) = InnerMultipart::read_headers(&mut *payload)? { self.state = InnerState::Boundary; headers } else { @@ -411,7 +411,7 @@ impl Stream for Field { fn poll(&mut self) -> Poll, Self::Error> { if self.safety.current() { let mut inner = self.inner.borrow_mut(); - if let Some(payload) = inner.payload.as_ref().unwrap().get_mut(&self.safety) + if let Some(mut payload) = inner.payload.as_ref().unwrap().get_mut(&self.safety) { payload.poll_stream()?; } @@ -582,12 +582,12 @@ impl InnerField { return Ok(Async::Ready(None)); } - let result = if let Some(payload) = self.payload.as_ref().unwrap().get_mut(s) { + 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(payload, len)? + InnerField::read_len(&mut *payload, len)? } else { - InnerField::read_stream(payload, &self.boundary)? + InnerField::read_stream(&mut *payload, &self.boundary)? }; match res { @@ -618,7 +618,7 @@ impl InnerField { } struct PayloadRef { - payload: Rc>, + payload: Rc>, } impl PayloadRef { @@ -628,15 +628,12 @@ impl PayloadRef { } } - fn get_mut<'a, 'b>(&'a self, s: &'b Safety) -> Option<&'a mut PayloadBuffer> + fn get_mut<'a, 'b>(&'a self, s: &'b Safety) -> Option> where 'a: 'b, { - // Unsafe: Invariant is inforced by Safety Safety is used as ref counter, - // only top most ref can have mutable access to payload. if s.current() { - let payload: &mut PayloadBuffer = unsafe { &mut *self.payload.get() }; - Some(payload) + Some(self.payload.borrow_mut()) } else { None } From d03296237eb6cbfa019355eca5747032b0836539 Mon Sep 17 00:00:00 2001 From: Rotem Yaari Date: Thu, 18 Jul 2019 11:31:18 +0300 Subject: [PATCH 02/11] Log error results in Logger middleware (closes #938) (#984) * Log error results in Logger middleware (closes #938) * Log internal server errors with an ERROR log level * Logger middleware: don't log 500 internal server errors, as Actix now logs them always * Changelog --- CHANGES.md | 11 +++++++++++ actix-http/src/response.rs | 3 +++ src/middleware/logger.rs | 9 ++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index cb5f51126..d64a22397 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,16 @@ # Changes +## [1.0.5] - ? + +### Added + +* Actix now logs errors resulting in "internal server error" responses always, with the `error` + logging level + +### Fixed + +* Restored logging of errors through the `Logger` middleware + ## [1.0.4] - 2019-07-17 ### Added diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index ce986a472..124bf5f92 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -52,6 +52,9 @@ impl Response { #[inline] pub fn from_error(error: Error) -> Response { let mut resp = error.as_response_error().render_response(); + if resp.head.status == StatusCode::INTERNAL_SERVER_ERROR { + error!("Internal Server Error: {:?}", error); + } resp.error = Some(error); resp } diff --git a/src/middleware/logger.rs b/src/middleware/logger.rs index 9e332fb83..f450f0481 100644 --- a/src/middleware/logger.rs +++ b/src/middleware/logger.rs @@ -9,12 +9,13 @@ use actix_service::{Service, Transform}; use bytes::Bytes; use futures::future::{ok, FutureResult}; use futures::{Async, Future, Poll}; +use log::debug; use regex::Regex; use time; use crate::dev::{BodySize, MessageBody, ResponseBody}; use crate::error::{Error, Result}; -use crate::http::{HeaderName, HttpTryFrom}; +use crate::http::{HeaderName, HttpTryFrom, StatusCode}; use crate::service::{ServiceRequest, ServiceResponse}; use crate::HttpResponse; @@ -202,6 +203,12 @@ where fn poll(&mut self) -> Poll { let res = futures::try_ready!(self.fut.poll()); + if let Some(error) = res.response().error() { + if res.response().head().status != StatusCode::INTERNAL_SERVER_ERROR { + debug!("Error in response: {:?}", error); + } + } + if let Some(ref mut format) = self.format { for unit in &mut format.0 { unit.render_response(res.response()); From fbdda8acb191dcaaa02f582ed8fce0b9bb96a51e Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 18 Jul 2019 17:24:12 +0600 Subject: [PATCH 03/11] Unix domain sockets (HttpServer::bind_uds) #92 --- CHANGES.md | 5 +++- Cargo.toml | 9 ++++--- actix-multipart/src/server.rs | 11 +++++--- examples/uds.rs | 49 +++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/server.rs | 32 +++++++++++++++++++++++ test-server/CHANGES.md | 4 +++ test-server/Cargo.toml | 4 +-- test-server/src/lib.rs | 3 ++- 9 files changed, 108 insertions(+), 10 deletions(-) create mode 100644 examples/uds.rs diff --git a/CHANGES.md b/CHANGES.md index d64a22397..80abfc599 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,9 +1,11 @@ # Changes -## [1.0.5] - ? +## [1.0.5] - 2019-07-xx ### Added +* Unix domain sockets (HttpServer::bind_uds) #92 + * Actix now logs errors resulting in "internal server error" responses always, with the `error` logging level @@ -11,6 +13,7 @@ * Restored logging of errors through the `Logger` middleware + ## [1.0.4] - 2019-07-17 ### Added diff --git a/Cargo.toml b/Cargo.toml index 866a7628f..40817a1f7 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 = ["ssl", "brotli", "flate2-zlib", "secure-cookies", "client", "rust-tls"] +features = ["ssl", "brotli", "flate2-zlib", "secure-cookies", "client", "rust-tls", "uds"] [badges] travis-ci = { repository = "actix/actix-web", branch = "master" } @@ -68,6 +68,9 @@ ssl = ["openssl", "actix-server/ssl", "awc/ssl"] # rustls rust-tls = ["rustls", "actix-server/rust-tls"] +# unix domain sockets support +uds = ["actix-server/uds"] + [dependencies] actix-codec = "0.1.2" actix-service = "0.4.1" @@ -76,8 +79,8 @@ actix-router = "0.1.5" actix-rt = "0.2.4" actix-web-codegen = "0.1.2" actix-http = "0.2.6" -actix-server = "0.5.1" -actix-server-config = "0.1.1" +actix-server = "0.6.0" +actix-server-config = "0.1.2" actix-threadpool = "0.1.1" awc = { version = "0.2.1", optional = true } diff --git a/actix-multipart/src/server.rs b/actix-multipart/src/server.rs index 70eb61cb9..e2111bb7b 100644 --- a/actix-multipart/src/server.rs +++ b/actix-multipart/src/server.rs @@ -286,7 +286,10 @@ impl InnerMultipart { } // read boundary InnerState::Boundary => { - match InnerMultipart::read_boundary(&mut *payload, &self.boundary)? { + match InnerMultipart::read_boundary( + &mut *payload, + &self.boundary, + )? { None => return Ok(Async::NotReady), Some(eof) => { if eof { @@ -411,7 +414,8 @@ impl Stream for Field { fn poll(&mut self) -> Poll, Self::Error> { 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) + if let Some(mut payload) = + inner.payload.as_ref().unwrap().get_mut(&self.safety) { payload.poll_stream()?; } @@ -582,7 +586,8 @@ impl InnerField { return Ok(Async::Ready(None)); } - let result = if let Some(mut payload) = self.payload.as_ref().unwrap().get_mut(s) { + 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)? diff --git a/examples/uds.rs b/examples/uds.rs new file mode 100644 index 000000000..4d6eca40c --- /dev/null +++ b/examples/uds.rs @@ -0,0 +1,49 @@ +use futures::IntoFuture; + +use actix_web::{ + get, middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer, +}; + +#[get("/resource1/{name}/index.html")] +fn index(req: HttpRequest, name: web::Path) -> String { + println!("REQ: {:?}", req); + format!("Hello: {}!\r\n", name) +} + +fn index_async(req: HttpRequest) -> impl IntoFuture { + println!("REQ: {:?}", req); + Ok("Hello world!\r\n") +} + +#[get("/")] +fn no_params() -> &'static str { + "Hello world!\r\n" +} + +fn main() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info"); + env_logger::init(); + + HttpServer::new(|| { + App::new() + .wrap(middleware::DefaultHeaders::new().header("X-Version", "0.2")) + .wrap(middleware::Compress::default()) + .wrap(middleware::Logger::default()) + .service(index) + .service(no_params) + .service( + web::resource("/resource2/index.html") + .wrap( + middleware::DefaultHeaders::new().header("X-Version-R2", "0.3"), + ) + .default_service( + web::route().to(|| HttpResponse::MethodNotAllowed()), + ) + .route(web::get().to_async(index_async)), + ) + .service(web::resource("/test1.html").to(|| "Test\r\n")) + }) + .bind_uds("/Users/fafhrd91/uds-test")? + .workers(1) + .run() +} diff --git a/src/lib.rs b/src/lib.rs index 857bc294e..c7cdf1475 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -78,6 +78,7 @@ //! `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)] diff --git a/src/server.rs b/src/server.rs index 43236fa34..aa654e576 100644 --- a/src/server.rs +++ b/src/server.rs @@ -434,6 +434,38 @@ where } Ok(self) } + + #[cfg(feature = "uds")] + /// Start listening for incoming unix domain connections. + /// + /// This method is available with `uds` feature. + pub fn bind_uds(mut self, addr: A) -> io::Result + where + A: AsRef, + { + let cfg = self.config.clone(); + let factory = self.factory.clone(); + self.sockets.push(Socket { + scheme: "http", + addr: net::SocketAddr::new( + net::IpAddr::V4(net::Ipv4Addr::new(127, 0, 0, 1)), + 8080, + ), + }); + + self.builder = self.builder.bind_uds( + format!("actix-web-service-{:?}", addr.as_ref()), + addr, + move || { + let c = cfg.lock(); + HttpService::build() + .keep_alive(c.keep_alive) + .client_timeout(c.client_timeout) + .finish(factory()) + }, + )?; + Ok(self) + } } impl HttpServer diff --git a/test-server/CHANGES.md b/test-server/CHANGES.md index f3e98010a..33314421b 100644 --- a/test-server/CHANGES.md +++ b/test-server/CHANGES.md @@ -1,5 +1,9 @@ # Changes +## [0.2.4] - 2019-07-18 + +* Update actix-server to 0.6 + ## [0.2.3] - 2019-07-16 * Add `delete`, `options`, `patch` methods to `TestServerRunner` diff --git a/test-server/Cargo.toml b/test-server/Cargo.toml index 445ed33e6..512f65e1c 100644 --- a/test-server/Cargo.toml +++ b/test-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http-test" -version = "0.2.3" +version = "0.2.4" authors = ["Nikolay Kim "] description = "Actix http test server" readme = "README.md" @@ -33,7 +33,7 @@ ssl = ["openssl", "actix-server/ssl", "awc/ssl"] actix-codec = "0.1.2" actix-rt = "0.2.2" actix-service = "0.4.1" -actix-server = "0.5.1" +actix-server = "0.6.0" actix-utils = "0.4.1" awc = "0.2.2" diff --git a/test-server/src/lib.rs b/test-server/src/lib.rs index 38405cd55..ce73e181b 100644 --- a/test-server/src/lib.rs +++ b/test-server/src/lib.rs @@ -12,6 +12,7 @@ use futures::future::lazy; use futures::{Future, IntoFuture, Stream}; use http::Method; use net2::TcpBuilder; +use tokio_tcp::TcpStream; thread_local! { static RT: RefCell = { @@ -109,7 +110,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 From 29098f8397ff8ec8b8d615d72c5a08bec08fec4d Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Thu, 18 Jul 2019 13:25:50 +0200 Subject: [PATCH 04/11] Add support for downcasting response errors (#986) * Add support for downcasting response errors * Added test for error casting --- actix-http/src/error.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index 368ee6dde..c4d663ade 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -4,6 +4,7 @@ use std::io::Write; use std::str::Utf8Error; use std::string::FromUtf8Error; use std::{fmt, io, result}; +use std::any::TypeId; pub use actix_threadpool::BlockingError; use actix_utils::timeout::TimeoutError; @@ -51,6 +52,11 @@ impl Error { pub fn as_response_error(&self) -> &dyn ResponseError { self.cause.as_ref() } + + /// Similar to `as_response_error` but downcasts. + pub fn as_error(&self) -> Option<&T> { + ResponseError::downcast_ref(self.cause.as_ref()) + } } /// Error that can be converted to `Response` @@ -73,6 +79,22 @@ pub trait ResponseError: fmt::Debug + fmt::Display { ); resp.set_body(Body::from(buf)) } + + #[doc(hidden)] + fn __private_get_type_id__(&self) -> TypeId where Self: 'static { + TypeId::of::() + } +} + +impl ResponseError + 'static { + /// Downcasts a response error to a specific type. + pub fn downcast_ref(&self) -> Option<&T> { + if self.__private_get_type_id__() == TypeId::of::() { + unsafe { Some(&*(self as *const ResponseError as *const T)) } + } else { + None + } + } } impl fmt::Display for Error { @@ -1044,6 +1066,16 @@ mod tests { assert_eq!(resp.status(), StatusCode::OK); } + #[test] + fn test_error_casting() { + let err = PayloadError::Overflow; + let resp_err: &ResponseError = &err; + let err = resp_err.downcast_ref::().unwrap(); + assert_eq!(err.to_string(), "A payload reached size limit."); + let not_err = resp_err.downcast_ref::(); + assert!(not_err.is_none()); + } + #[test] fn test_error_helpers() { let r: Response = ErrorBadRequest("err").into(); From 9c3789cbd0c607d58f024afd2e51dc2f6a1198d5 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 18 Jul 2019 17:37:41 +0600 Subject: [PATCH 05/11] revert DateServiceInner changes --- actix-http/CHANGES.md | 7 +++++++ actix-http/src/config.rs | 31 +++++++++++++++---------------- actix-http/src/error.rs | 7 +++++-- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 84033531d..cc695fa63 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,5 +1,12 @@ # Changes +## [0.2.7] - 2019-07-18 + +### Added + +* Add support for downcasting response errors #986 + + ## [0.2.6] - 2019-07-17 ### Changed diff --git a/actix-http/src/config.rs b/actix-http/src/config.rs index 7d0e27635..bdfecef30 100644 --- a/actix-http/src/config.rs +++ b/actix-http/src/config.rs @@ -1,4 +1,4 @@ -use std::cell::Cell; +use std::cell::UnsafeCell; use std::fmt; use std::fmt::Write; use std::rc::Rc; @@ -162,13 +162,17 @@ impl ServiceConfig { pub fn set_date(&self, dst: &mut BytesMut) { let mut buf: [u8; 39] = [0; 39]; buf[..6].copy_from_slice(b"date: "); - buf[6..35].copy_from_slice(&self.0.timer.date().bytes); + self.0 + .timer + .set_date(|date| buf[6..35].copy_from_slice(&date.bytes)); buf[35..].copy_from_slice(b"\r\n\r\n"); dst.extend_from_slice(&buf); } pub(crate) fn set_date_header(&self, dst: &mut BytesMut) { - dst.extend_from_slice(&self.0.timer.date().bytes); + self.0 + .timer + .set_date(|date| dst.extend_from_slice(&date.bytes)); } } @@ -206,28 +210,24 @@ impl fmt::Write for Date { struct DateService(Rc); struct DateServiceInner { - current: Cell>, + current: UnsafeCell>, } impl DateServiceInner { fn new() -> Self { DateServiceInner { - current: Cell::new(None), + current: UnsafeCell::new(None), } } - fn get(&self) -> Option<(Date, Instant)> { - self.current.get() - } - fn reset(&self) { - self.current.set(None); + unsafe { (&mut *self.current.get()).take() }; } fn update(&self) { let now = Instant::now(); let date = Date::new(); - self.current.set(Some((date, now))); + *(unsafe { &mut *self.current.get() }) = Some((date, now)); } } @@ -237,7 +237,7 @@ impl DateService { } fn check_date(&self) { - if self.0.get().is_none() { + if unsafe { (&*self.0.current.get()).is_none() } { self.0.update(); // periodic date update @@ -253,13 +253,12 @@ impl DateService { fn now(&self) -> Instant { self.check_date(); - self.0.get().unwrap().1 + unsafe { (&*self.0.current.get()).as_ref().unwrap().1 } } - fn date(&self) -> Date { + fn set_date(&self, mut f: F) { self.check_date(); - - self.0.get().unwrap().0 + f(&unsafe { (&*self.0.current.get()).as_ref().unwrap().0 }) } } diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index c4d663ade..cbb009a72 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -1,10 +1,10 @@ //! Error and Result module +use std::any::TypeId; use std::cell::RefCell; use std::io::Write; use std::str::Utf8Error; use std::string::FromUtf8Error; use std::{fmt, io, result}; -use std::any::TypeId; pub use actix_threadpool::BlockingError; use actix_utils::timeout::TimeoutError; @@ -81,7 +81,10 @@ pub trait ResponseError: fmt::Debug + fmt::Display { } #[doc(hidden)] - fn __private_get_type_id__(&self) -> TypeId where Self: 'static { + fn __private_get_type_id__(&self) -> TypeId + where + Self: 'static, + { TypeId::of::() } } From b6ff786ed361cb2d1c4111629dd9c96e697a0cfe Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 18 Jul 2019 17:50:10 +0600 Subject: [PATCH 06/11] update dependencies --- Cargo.toml | 6 +++--- actix-framed/Cargo.toml | 6 +++--- actix-http/Cargo.toml | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 40817a1f7..f2ed91603 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "1.0.4" +version = "1.0.5" authors = ["Nikolay Kim "] description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." readme = "README.md" @@ -82,7 +82,7 @@ actix-http = "0.2.6" actix-server = "0.6.0" actix-server-config = "0.1.2" actix-threadpool = "0.1.1" -awc = { version = "0.2.1", optional = true } +awc = { version = "0.2.2", optional = true } bytes = "0.4" derive_more = "0.15.0" @@ -107,7 +107,7 @@ rustls = { version = "0.15", optional = true } [dev-dependencies] actix = { version = "0.8.3" } actix-http = { version = "0.2.5", features=["ssl", "brotli", "flate2-zlib"] } -actix-http-test = { version = "0.2.2", features=["ssl"] } +actix-http-test = { version = "0.2.4", features=["ssl"] } rand = "0.7" env_logger = "0.6" serde_derive = "1.0" diff --git a/actix-framed/Cargo.toml b/actix-framed/Cargo.toml index c2ab26fa2..5fbd262d0 100644 --- a/actix-framed/Cargo.toml +++ b/actix-framed/Cargo.toml @@ -26,13 +26,13 @@ actix-utils = "0.4.0" actix-router = "0.1.2" actix-rt = "0.2.2" actix-http = "0.2.0" -actix-server-config = "0.1.1" +actix-server-config = "0.1.2" bytes = "0.4" futures = "0.1.25" log = "0.4" [dev-dependencies] -actix-server = { version = "0.5.0", features=["ssl"] } +actix-server = { version = "0.6.0", features=["ssl"] } actix-connect = { version = "0.2.0", features=["ssl"] } -actix-http-test = { version = "0.2.0", features=["ssl"] } +actix-http-test = { version = "0.2.4", features=["ssl"] } diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index ae12cbc41..0cbf5867f 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "0.2.6" +version = "0.2.7" authors = ["Nikolay Kim "] description = "Actix http primitives" readme = "README.md" @@ -48,7 +48,7 @@ actix-service = "0.4.1" actix-codec = "0.1.2" actix-connect = "0.2.1" actix-utils = "0.4.4" -actix-server-config = "0.1.1" +actix-server-config = "0.1.2" actix-threadpool = "0.1.1" base64 = "0.10" @@ -97,9 +97,9 @@ chrono = "0.4.6" [dev-dependencies] actix-rt = "0.2.2" -actix-server = { version = "0.5.0", features=["ssl"] } +actix-server = { version = "0.6.0", features=["ssl"] } actix-connect = { version = "0.2.0", features=["ssl"] } -actix-http-test = { version = "0.2.0", features=["ssl"] } +actix-http-test = { version = "0.2.4", features=["ssl"] } env_logger = "0.6" serde_derive = "1.0" openssl = { version="0.10" } From 6b7df6b242c1cf844ff95b26d963eb275b3d0ada Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 18 Jul 2019 17:51:51 +0600 Subject: [PATCH 07/11] prep 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 80abfc599..754c67f26 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## [1.0.5] - 2019-07-xx +## [1.0.5] - 2019-07-18 ### Added diff --git a/Cargo.toml b/Cargo.toml index f2ed91603..538ea0d38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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.6" +actix-http = "0.2.7" actix-server = "0.6.0" actix-server-config = "0.1.2" actix-threadpool = "0.1.1" From 3650f6d7b888c823c2af60c507904e4700c77b3a Mon Sep 17 00:00:00 2001 From: Anton Lazarev Date: Thu, 18 Jul 2019 21:28:43 -0700 Subject: [PATCH 08/11] Re-implement Host predicate (#989) * update HostGuard implementation * update/add tests for new HostGuard implementation --- src/guard.rs | 166 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 116 insertions(+), 50 deletions(-) diff --git a/src/guard.rs b/src/guard.rs index 6522a9845..6fd6d1d2c 100644 --- a/src/guard.rs +++ b/src/guard.rs @@ -26,7 +26,7 @@ //! ``` #![allow(non_snake_case)] -use actix_http::http::{self, header, HttpTryFrom}; +use actix_http::http::{self, header, HttpTryFrom, uri::Uri}; use actix_http::RequestHead; /// Trait defines resource guards. Guards are used for routes selection. @@ -256,45 +256,68 @@ impl Guard for HeaderGuard { } } -// /// Return predicate that matches if request contains specified Host name. -// /// -// /// ```rust,ignore -// /// # extern crate actix_web; -// /// use actix_web::{pred, App, HttpResponse}; -// /// -// /// fn main() { -// /// App::new().resource("/index.html", |r| { -// /// r.route() -// /// .guard(pred::Host("www.rust-lang.org")) -// /// .f(|_| HttpResponse::MethodNotAllowed()) -// /// }); -// /// } -// /// ``` -// pub fn Host>(host: H) -> HostGuard { -// HostGuard(host.as_ref().to_string(), None) -// } +/// Return predicate that matches if request contains specified Host name. +/// +/// ```rust,ignore +/// # extern crate actix_web; +/// use actix_web::{guard::Host, App, HttpResponse}; +/// +/// fn main() { +/// App::new().resource("/index.html", |r| { +/// r.route() +/// .guard(Host("www.rust-lang.org")) +/// .f(|_| HttpResponse::MethodNotAllowed()) +/// }); +/// } +/// ``` +pub fn Host>(host: H) -> HostGuard { + HostGuard(host.as_ref().to_string(), None) +} -// #[doc(hidden)] -// pub struct HostGuard(String, Option); +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) +} -// impl HostGuard { -// /// Set reuest scheme to match -// pub fn scheme>(&mut self, scheme: H) { -// self.1 = Some(scheme.as_ref().to_string()) -// } -// } +#[doc(hidden)] +pub struct HostGuard(String, Option); -// impl Guard for HostGuard { -// fn check(&self, _req: &RequestHead) -> bool { -// // let info = req.connection_info(); -// // if let Some(ref scheme) = self.1 { -// // self.0 == info.host() && scheme == info.scheme() -// // } else { -// // self.0 == info.host() -// // } -// false -// } -// } +impl HostGuard { + /// Set request scheme to match + pub fn scheme>(mut self, scheme: H) -> HostGuard { + self.1 = Some(scheme.as_ref().to_string()); + self + } +} + +impl Guard for HostGuard { + fn check(&self, req: &RequestHead) -> bool { + let req_host_uri = if let Some(uri) = get_host_uri(req) { + uri + } else { + return false; + }; + + if let Some(uri_host) = req_host_uri.host() { + if self.0 != uri_host { + return false; + } + } else { + return false; + } + + if let Some(ref scheme) = self.1 { + if let Some(ref req_host_uri_scheme) = req_host_uri.scheme_str() { + return scheme == req_host_uri_scheme; + } + } + + true + } +} #[cfg(test)] mod tests { @@ -318,21 +341,64 @@ mod tests { assert!(!pred.check(req.head())); } - // #[test] - // fn test_host() { - // let req = TestServiceRequest::default() - // .header( - // header::HOST, - // header::HeaderValue::from_static("www.rust-lang.org"), - // ) - // .request(); + #[test] + fn test_host() { + let req = TestRequest::default() + .header( + header::HOST, + header::HeaderValue::from_static("www.rust-lang.org"), + ) + .to_http_request(); - // let pred = Host("www.rust-lang.org"); - // assert!(pred.check(&req)); + let pred = Host("www.rust-lang.org"); + assert!(pred.check(req.head())); - // let pred = Host("localhost"); - // assert!(!pred.check(&req)); - // } + 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_host_scheme() { + let req = TestRequest::default() + .header( + header::HOST, + header::HeaderValue::from_static("https://www.rust-lang.org"), + ) + .to_http_request(); + + let pred = Host("www.rust-lang.org").scheme("https"); + assert!(pred.check(req.head())); + + let pred = Host("www.rust-lang.org"); + assert!(pred.check(req.head())); + + let pred = Host("www.rust-lang.org").scheme("http"); + 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").scheme("https"); + assert!(!pred.check(req.head())); + + let pred = Host("localhost"); + assert!(!pred.check(req.head())); + } #[test] fn test_methods() { From cccd82965653e951854ee293235ded2fb09a3baa Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Fri, 19 Jul 2019 11:07:52 +0600 Subject: [PATCH 09/11] update changes --- CHANGES.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 754c67f26..bc022e388 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,12 @@ # Changes +## [1.0.6] - 2019-xx-xx + +### Added + +* Re-implement Host predicate (#989) + + ## [1.0.5] - 2019-07-18 ### Added From c808364c072f0b1e5fefc59229facce6ef3595c1 Mon Sep 17 00:00:00 2001 From: jesskfullwood <38404589+jesskfullwood@users.noreply.github.com> Date: Fri, 19 Jul 2019 10:47:44 +0100 Subject: [PATCH 10/11] make Query payload public (#991) --- CHANGES.md | 4 ++++ src/types/query.rs | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index bc022e388..d56e5ce0f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,10 @@ * Re-implement Host predicate (#989) +### Changed + +* `Query` payload made `pub`. Allows user to pattern-match the payload. + ## [1.0.5] - 2019-07-18 diff --git a/src/types/query.rs b/src/types/query.rs index 2ad7106d0..d00f4600d 100644 --- a/src/types/query.rs +++ b/src/types/query.rs @@ -36,7 +36,7 @@ use crate::request::HttpRequest; /// // 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 { +/// fn index(web::Query(info): web::Query) -> String { /// format!("Authorization request for client with id={} and type={:?}!", info.id, info.response_type) /// } /// @@ -45,7 +45,7 @@ use crate::request::HttpRequest; /// web::resource("/index.html").route(web::get().to(index))); // <- use `Query` extractor /// } /// ``` -pub struct Query(T); +pub struct Query(pub T); impl Query { /// Deconstruct to a inner value From f8320fedd83e159ed78789815ad0765919d50284 Mon Sep 17 00:00:00 2001 From: jesskfullwood <38404589+jesskfullwood@users.noreply.github.com> Date: Fri, 19 Jul 2019 12:37:49 +0100 Subject: [PATCH 11/11] add note about Query decoding (#992) --- src/types/query.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/types/query.rs b/src/types/query.rs index d00f4600d..60b07085d 100644 --- a/src/types/query.rs +++ b/src/types/query.rs @@ -12,9 +12,12 @@ use crate::error::QueryPayloadError; use crate::extract::FromRequest; use crate::request::HttpRequest; -#[derive(PartialEq, Eq, PartialOrd, Ord)] /// Extract typed information from the request's query. /// +/// **Note**: A query string consists of unordered `key=value` pairs, therefore it cannot +/// be decoded into any type which depends upon data ordering e.g. tuples or tuple-structs. +/// Attempts to do so will *fail at runtime*. +/// /// ## Example /// /// ```rust @@ -33,9 +36,9 @@ use crate::request::HttpRequest; /// response_type: ResponseType, /// } /// -/// // 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"` +/// // 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 { /// format!("Authorization request for client with id={} and type={:?}!", info.id, info.response_type) /// } @@ -45,6 +48,7 @@ use crate::request::HttpRequest; /// web::resource("/index.html").route(web::get().to(index))); // <- use `Query` extractor /// } /// ``` +#[derive(PartialEq, Eq, PartialOrd, Ord)] pub struct Query(pub T); impl Query { @@ -162,6 +166,8 @@ where /// Query extractor configuration /// +/// ## Example +/// /// ```rust /// #[macro_use] extern crate serde_derive; /// use actix_web::{error, web, App, FromRequest, HttpResponse};