Merge branch 'master' into add_start_with_addr

This commit is contained in:
naerbnic 2019-07-19 20:18:13 -07:00 committed by GitHub
commit 30ba8d1de4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 354 additions and 112 deletions

View File

@ -1,5 +1,30 @@
# Changes # Changes
## [1.0.6] - 2019-xx-xx
### Added
* Re-implement Host predicate (#989)
### Changed
* `Query` payload made `pub`. Allows user to pattern-match the payload.
## [1.0.5] - 2019-07-18
### Added
* Unix domain sockets (HttpServer::bind_uds) #92
* 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 ## [1.0.4] - 2019-07-17
### Added ### Added

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-web" name = "actix-web"
version = "1.0.4" version = "1.0.5"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust." description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust."
readme = "README.md" readme = "README.md"
@ -16,7 +16,7 @@ exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"]
edition = "2018" edition = "2018"
[package.metadata.docs.rs] [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] [badges]
travis-ci = { repository = "actix/actix-web", branch = "master" } travis-ci = { repository = "actix/actix-web", branch = "master" }
@ -68,6 +68,9 @@ ssl = ["openssl", "actix-server/ssl", "awc/ssl"]
# rustls # rustls
rust-tls = ["rustls", "actix-server/rust-tls"] rust-tls = ["rustls", "actix-server/rust-tls"]
# unix domain sockets support
uds = ["actix-server/uds"]
[dependencies] [dependencies]
actix-codec = "0.1.2" actix-codec = "0.1.2"
actix-service = "0.4.1" actix-service = "0.4.1"
@ -75,11 +78,11 @@ actix-utils = "0.4.4"
actix-router = "0.1.5" actix-router = "0.1.5"
actix-rt = "0.2.4" actix-rt = "0.2.4"
actix-web-codegen = "0.1.2" actix-web-codegen = "0.1.2"
actix-http = "0.2.6" actix-http = "0.2.7"
actix-server = "0.5.1" actix-server = "0.6.0"
actix-server-config = "0.1.1" actix-server-config = "0.1.2"
actix-threadpool = "0.1.1" actix-threadpool = "0.1.1"
awc = { version = "0.2.1", optional = true } awc = { version = "0.2.2", optional = true }
bytes = "0.4" bytes = "0.4"
derive_more = "0.15.0" derive_more = "0.15.0"
@ -104,7 +107,7 @@ rustls = { version = "0.15", optional = true }
[dev-dependencies] [dev-dependencies]
actix = { version = "0.8.3" } actix = { version = "0.8.3" }
actix-http = { version = "0.2.5", features=["ssl", "brotli", "flate2-zlib"] } 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" rand = "0.7"
env_logger = "0.6" env_logger = "0.6"
serde_derive = "1.0" serde_derive = "1.0"

View File

@ -26,13 +26,13 @@ actix-utils = "0.4.0"
actix-router = "0.1.2" actix-router = "0.1.2"
actix-rt = "0.2.2" actix-rt = "0.2.2"
actix-http = "0.2.0" actix-http = "0.2.0"
actix-server-config = "0.1.1" actix-server-config = "0.1.2"
bytes = "0.4" bytes = "0.4"
futures = "0.1.25" futures = "0.1.25"
log = "0.4" log = "0.4"
[dev-dependencies] [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-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"] }

View File

@ -1,5 +1,12 @@
# Changes # Changes
## [0.2.7] - 2019-07-18
### Added
* Add support for downcasting response errors #986
## [0.2.6] - 2019-07-17 ## [0.2.6] - 2019-07-17
### Changed ### Changed

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-http" name = "actix-http"
version = "0.2.6" version = "0.2.7"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix http primitives" description = "Actix http primitives"
readme = "README.md" readme = "README.md"
@ -48,7 +48,7 @@ actix-service = "0.4.1"
actix-codec = "0.1.2" actix-codec = "0.1.2"
actix-connect = "0.2.1" actix-connect = "0.2.1"
actix-utils = "0.4.4" actix-utils = "0.4.4"
actix-server-config = "0.1.1" actix-server-config = "0.1.2"
actix-threadpool = "0.1.1" actix-threadpool = "0.1.1"
base64 = "0.10" base64 = "0.10"
@ -97,9 +97,9 @@ chrono = "0.4.6"
[dev-dependencies] [dev-dependencies]
actix-rt = "0.2.2" 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-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" env_logger = "0.6"
serde_derive = "1.0" serde_derive = "1.0"
openssl = { version="0.10" } openssl = { version="0.10" }

View File

@ -162,16 +162,21 @@ impl ServiceConfig {
pub fn set_date(&self, dst: &mut BytesMut) { pub fn set_date(&self, dst: &mut BytesMut) {
let mut buf: [u8; 39] = [0; 39]; let mut buf: [u8; 39] = [0; 39];
buf[..6].copy_from_slice(b"date: "); 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"); buf[35..].copy_from_slice(b"\r\n\r\n");
dst.extend_from_slice(&buf); dst.extend_from_slice(&buf);
} }
pub(crate) fn set_date_header(&self, dst: &mut BytesMut) { 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));
} }
} }
#[derive(Copy, Clone)]
struct Date { struct Date {
bytes: [u8; DATE_VALUE_LENGTH], bytes: [u8; DATE_VALUE_LENGTH],
pos: usize, pos: usize,
@ -215,10 +220,6 @@ impl DateServiceInner {
} }
} }
fn get_ref(&self) -> &Option<(Date, Instant)> {
unsafe { &*self.current.get() }
}
fn reset(&self) { fn reset(&self) {
unsafe { (&mut *self.current.get()).take() }; unsafe { (&mut *self.current.get()).take() };
} }
@ -236,7 +237,7 @@ impl DateService {
} }
fn check_date(&self) { fn check_date(&self) {
if self.0.get_ref().is_none() { if unsafe { (&*self.0.current.get()).is_none() } {
self.0.update(); self.0.update();
// periodic date update // periodic date update
@ -252,14 +253,12 @@ impl DateService {
fn now(&self) -> Instant { fn now(&self) -> Instant {
self.check_date(); self.check_date();
self.0.get_ref().as_ref().unwrap().1 unsafe { (&*self.0.current.get()).as_ref().unwrap().1 }
} }
fn date(&self) -> &Date { fn set_date<F: FnMut(&Date)>(&self, mut f: F) {
self.check_date(); self.check_date();
f(&unsafe { (&*self.0.current.get()).as_ref().unwrap().0 })
let item = self.0.get_ref().as_ref().unwrap();
&item.0
} }
} }

View File

@ -1,4 +1,5 @@
//! Error and Result module //! Error and Result module
use std::any::TypeId;
use std::cell::RefCell; use std::cell::RefCell;
use std::io::Write; use std::io::Write;
use std::str::Utf8Error; use std::str::Utf8Error;
@ -51,6 +52,11 @@ impl Error {
pub fn as_response_error(&self) -> &dyn ResponseError { pub fn as_response_error(&self) -> &dyn ResponseError {
self.cause.as_ref() self.cause.as_ref()
} }
/// Similar to `as_response_error` but downcasts.
pub fn as_error<T: ResponseError + 'static>(&self) -> Option<&T> {
ResponseError::downcast_ref(self.cause.as_ref())
}
} }
/// Error that can be converted to `Response` /// Error that can be converted to `Response`
@ -73,6 +79,25 @@ pub trait ResponseError: fmt::Debug + fmt::Display {
); );
resp.set_body(Body::from(buf)) resp.set_body(Body::from(buf))
} }
#[doc(hidden)]
fn __private_get_type_id__(&self) -> TypeId
where
Self: 'static,
{
TypeId::of::<Self>()
}
}
impl ResponseError + 'static {
/// Downcasts a response error to a specific type.
pub fn downcast_ref<T: ResponseError + 'static>(&self) -> Option<&T> {
if self.__private_get_type_id__() == TypeId::of::<T>() {
unsafe { Some(&*(self as *const ResponseError as *const T)) }
} else {
None
}
}
} }
impl fmt::Display for Error { impl fmt::Display for Error {
@ -1044,6 +1069,16 @@ mod tests {
assert_eq!(resp.status(), StatusCode::OK); 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::<PayloadError>().unwrap();
assert_eq!(err.to_string(), "A payload reached size limit.");
let not_err = resp_err.downcast_ref::<ContentTypeError>();
assert!(not_err.is_none());
}
#[test] #[test]
fn test_error_helpers() { fn test_error_helpers() {
let r: Response = ErrorBadRequest("err").into(); let r: Response = ErrorBadRequest("err").into();

View File

@ -52,6 +52,9 @@ impl Response<Body> {
#[inline] #[inline]
pub fn from_error(error: Error) -> Response { pub fn from_error(error: Error) -> Response {
let mut resp = error.as_response_error().render_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.error = Some(error);
resp resp
} }

View File

@ -466,18 +466,20 @@ where
State::Unknown(ref mut data) => { State::Unknown(ref mut data) => {
if let Some(ref mut item) = data { if let Some(ref mut item) = data {
loop { loop {
unsafe { // Safety - we only write to the returned slice.
let b = item.1.bytes_mut(); let b = unsafe { item.1.bytes_mut() };
let n = try_ready!(item.0.poll_read(b)); let n = try_ready!(item.0.poll_read(b));
if n == 0 { if n == 0 {
return Ok(Async::Ready(())); return Ok(Async::Ready(()));
} }
item.1.advance_mut(n); // 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() { if item.1.len() >= HTTP2_PREFACE.len() {
break; break;
} }
} }
}
} else { } else {
panic!() panic!()
} }

View File

@ -1,5 +1,5 @@
//! Multipart payload support //! Multipart payload support
use std::cell::{Cell, RefCell, UnsafeCell}; use std::cell::{Cell, RefCell, RefMut};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::rc::Rc; use std::rc::Rc;
use std::{cmp, fmt}; use std::{cmp, fmt};
@ -112,7 +112,7 @@ impl Stream for Multipart {
Err(err) Err(err)
} else if self.safety.current() { } else if self.safety.current() {
let mut inner = self.inner.as_mut().unwrap().borrow_mut(); 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()?; payload.poll_stream()?;
} }
inner.poll(&self.safety) 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 { match self.state {
// read until first boundary // read until first boundary
InnerState::FirstBoundary => { InnerState::FirstBoundary => {
match InnerMultipart::skip_until_boundary( match InnerMultipart::skip_until_boundary(
payload, &mut *payload,
&self.boundary, &self.boundary,
)? { )? {
Some(eof) => { Some(eof) => {
@ -286,7 +286,10 @@ impl InnerMultipart {
} }
// read boundary // read boundary
InnerState::Boundary => { InnerState::Boundary => {
match InnerMultipart::read_boundary(payload, &self.boundary)? { match InnerMultipart::read_boundary(
&mut *payload,
&self.boundary,
)? {
None => return Ok(Async::NotReady), None => return Ok(Async::NotReady),
Some(eof) => { Some(eof) => {
if eof { if eof {
@ -303,7 +306,7 @@ impl InnerMultipart {
// read field headers for next field // read field headers for next field
if self.state == InnerState::Headers { 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; self.state = InnerState::Boundary;
headers headers
} else { } else {
@ -411,7 +414,8 @@ impl Stream for Field {
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
if self.safety.current() { if self.safety.current() {
let mut inner = self.inner.borrow_mut(); 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()?; payload.poll_stream()?;
} }
@ -582,12 +586,13 @@ impl InnerField {
return Ok(Async::Ready(None)); 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 { if !self.eof {
let res = if let Some(ref mut len) = self.length { let res = if let Some(ref mut len) = self.length {
InnerField::read_len(payload, len)? InnerField::read_len(&mut *payload, len)?
} else { } else {
InnerField::read_stream(payload, &self.boundary)? InnerField::read_stream(&mut *payload, &self.boundary)?
}; };
match res { match res {
@ -618,7 +623,7 @@ impl InnerField {
} }
struct PayloadRef { struct PayloadRef {
payload: Rc<UnsafeCell<PayloadBuffer>>, payload: Rc<RefCell<PayloadBuffer>>,
} }
impl PayloadRef { impl PayloadRef {
@ -628,15 +633,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<RefMut<'a, PayloadBuffer>>
where where
'a: 'b, '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() { if s.current() {
let payload: &mut PayloadBuffer = unsafe { &mut *self.payload.get() }; Some(self.payload.borrow_mut())
Some(payload)
} else { } else {
None None
} }

49
examples/uds.rs Normal file
View File

@ -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>) -> String {
println!("REQ: {:?}", req);
format!("Hello: {}!\r\n", name)
}
fn index_async(req: HttpRequest) -> impl IntoFuture<Item = &'static str, Error = Error> {
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()
}

View File

@ -26,7 +26,7 @@
//! ``` //! ```
#![allow(non_snake_case)] #![allow(non_snake_case)]
use actix_http::http::{self, header, HttpTryFrom}; use actix_http::http::{self, header, HttpTryFrom, uri::Uri};
use actix_http::RequestHead; use actix_http::RequestHead;
/// Trait defines resource guards. Guards are used for routes selection. /// 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. /// Return predicate that matches if request contains specified Host name.
// /// ///
// /// ```rust,ignore /// ```rust,ignore
// /// # extern crate actix_web; /// # extern crate actix_web;
// /// use actix_web::{pred, App, HttpResponse}; /// use actix_web::{guard::Host, App, HttpResponse};
// /// ///
// /// fn main() { /// fn main() {
// /// App::new().resource("/index.html", |r| { /// App::new().resource("/index.html", |r| {
// /// r.route() /// r.route()
// /// .guard(pred::Host("www.rust-lang.org")) /// .guard(Host("www.rust-lang.org"))
// /// .f(|_| HttpResponse::MethodNotAllowed()) /// .f(|_| HttpResponse::MethodNotAllowed())
// /// }); /// });
// /// } /// }
// /// ``` /// ```
// pub fn Host<H: AsRef<str>>(host: H) -> HostGuard { pub fn Host<H: AsRef<str>>(host: H) -> HostGuard {
// HostGuard(host.as_ref().to_string(), None) HostGuard(host.as_ref().to_string(), None)
// } }
// #[doc(hidden)] fn get_host_uri(req: &RequestHead) -> Option<Uri> {
// pub struct HostGuard(String, Option<String>); 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 { #[doc(hidden)]
// /// Set reuest scheme to match pub struct HostGuard(String, Option<String>);
// pub fn scheme<H: AsRef<str>>(&mut self, scheme: H) {
// self.1 = Some(scheme.as_ref().to_string())
// }
// }
// impl Guard for HostGuard { impl HostGuard {
// fn check(&self, _req: &RequestHead) -> bool { /// Set request scheme to match
// // let info = req.connection_info(); pub fn scheme<H: AsRef<str>>(mut self, scheme: H) -> HostGuard {
// // if let Some(ref scheme) = self.1 { self.1 = Some(scheme.as_ref().to_string());
// // self.0 == info.host() && scheme == info.scheme() self
// // } else { }
// // self.0 == info.host() }
// // }
// false 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)] #[cfg(test)]
mod tests { mod tests {
@ -318,21 +341,64 @@ mod tests {
assert!(!pred.check(req.head())); assert!(!pred.check(req.head()));
} }
// #[test] #[test]
// fn test_host() { fn test_host() {
// let req = TestServiceRequest::default() let req = TestRequest::default()
// .header( .header(
// header::HOST, header::HOST,
// header::HeaderValue::from_static("www.rust-lang.org"), header::HeaderValue::from_static("www.rust-lang.org"),
// ) )
// .request(); .to_http_request();
// let pred = Host("www.rust-lang.org"); let pred = Host("www.rust-lang.org");
// assert!(pred.check(&req)); assert!(pred.check(req.head()));
// let pred = Host("localhost"); let pred = Host("www.rust-lang.org").scheme("https");
// assert!(!pred.check(&req)); 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] #[test]
fn test_methods() { fn test_methods() {

View File

@ -78,6 +78,7 @@
//! `c` compiler (default enabled) //! `c` compiler (default enabled)
//! * `flate2-rust` - experimental rust based implementation for //! * `flate2-rust` - experimental rust based implementation for
//! `gzip`, `deflate` compression. //! `gzip`, `deflate` compression.
//! * `uds` - Unix domain support, enables `HttpServer::bind_uds()` method.
//! //!
#![allow(clippy::type_complexity, clippy::new_without_default)] #![allow(clippy::type_complexity, clippy::new_without_default)]

View File

@ -9,12 +9,13 @@ use actix_service::{Service, Transform};
use bytes::Bytes; use bytes::Bytes;
use futures::future::{ok, FutureResult}; use futures::future::{ok, FutureResult};
use futures::{Async, Future, Poll}; use futures::{Async, Future, Poll};
use log::debug;
use regex::Regex; use regex::Regex;
use time; use time;
use crate::dev::{BodySize, MessageBody, ResponseBody}; use crate::dev::{BodySize, MessageBody, ResponseBody};
use crate::error::{Error, Result}; use crate::error::{Error, Result};
use crate::http::{HeaderName, HttpTryFrom}; use crate::http::{HeaderName, HttpTryFrom, StatusCode};
use crate::service::{ServiceRequest, ServiceResponse}; use crate::service::{ServiceRequest, ServiceResponse};
use crate::HttpResponse; use crate::HttpResponse;
@ -202,6 +203,12 @@ where
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let res = futures::try_ready!(self.fut.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 { if let Some(ref mut format) = self.format {
for unit in &mut format.0 { for unit in &mut format.0 {
unit.render_response(res.response()); unit.render_response(res.response());

View File

@ -434,6 +434,38 @@ where
} }
Ok(self) Ok(self)
} }
#[cfg(feature = "uds")]
/// Start listening for incoming unix domain connections.
///
/// This method is available with `uds` feature.
pub fn bind_uds<A>(mut self, addr: A) -> io::Result<Self>
where
A: AsRef<std::path::Path>,
{
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<F, I, S, B> HttpServer<F, I, S, B> impl<F, I, S, B> HttpServer<F, I, S, B>

View File

@ -12,9 +12,12 @@ use crate::error::QueryPayloadError;
use crate::extract::FromRequest; use crate::extract::FromRequest;
use crate::request::HttpRequest; use crate::request::HttpRequest;
#[derive(PartialEq, Eq, PartialOrd, Ord)]
/// Extract typed information from the request's query. /// 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 /// ## Example
/// ///
/// ```rust /// ```rust
@ -33,10 +36,10 @@ use crate::request::HttpRequest;
/// response_type: ResponseType, /// response_type: ResponseType,
/// } /// }
/// ///
/// // Use `Query` extractor for query information. /// // Use `Query` extractor for query information (and destructure it within the signature).
/// // This handler get called only if request's query contains `username` field /// // 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"` /// // The correct request for this handler would be `/index.html?id=64&response_type=Code"`.
/// fn index(info: web::Query<AuthRequest>) -> String { /// fn index(web::Query(info): web::Query<AuthRequest>) -> String {
/// format!("Authorization request for client with id={} and type={:?}!", info.id, info.response_type) /// format!("Authorization request for client with id={} and type={:?}!", info.id, info.response_type)
/// } /// }
/// ///
@ -45,7 +48,8 @@ use crate::request::HttpRequest;
/// web::resource("/index.html").route(web::get().to(index))); // <- use `Query` extractor /// web::resource("/index.html").route(web::get().to(index))); // <- use `Query` extractor
/// } /// }
/// ``` /// ```
pub struct Query<T>(T); #[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct Query<T>(pub T);
impl<T> Query<T> { impl<T> Query<T> {
/// Deconstruct to a inner value /// Deconstruct to a inner value
@ -162,6 +166,8 @@ where
/// Query extractor configuration /// Query extractor configuration
/// ///
/// ## Example
///
/// ```rust /// ```rust
/// #[macro_use] extern crate serde_derive; /// #[macro_use] extern crate serde_derive;
/// use actix_web::{error, web, App, FromRequest, HttpResponse}; /// use actix_web::{error, web, App, FromRequest, HttpResponse};

View File

@ -1,5 +1,9 @@
# Changes # Changes
## [0.2.4] - 2019-07-18
* Update actix-server to 0.6
## [0.2.3] - 2019-07-16 ## [0.2.3] - 2019-07-16
* Add `delete`, `options`, `patch` methods to `TestServerRunner` * Add `delete`, `options`, `patch` methods to `TestServerRunner`

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-http-test" name = "actix-http-test"
version = "0.2.3" version = "0.2.4"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix http test server" description = "Actix http test server"
readme = "README.md" readme = "README.md"
@ -33,7 +33,7 @@ ssl = ["openssl", "actix-server/ssl", "awc/ssl"]
actix-codec = "0.1.2" actix-codec = "0.1.2"
actix-rt = "0.2.2" actix-rt = "0.2.2"
actix-service = "0.4.1" actix-service = "0.4.1"
actix-server = "0.5.1" actix-server = "0.6.0"
actix-utils = "0.4.1" actix-utils = "0.4.1"
awc = "0.2.2" awc = "0.2.2"

View File

@ -12,6 +12,7 @@ use futures::future::lazy;
use futures::{Future, IntoFuture, Stream}; use futures::{Future, IntoFuture, Stream};
use http::Method; use http::Method;
use net2::TcpBuilder; use net2::TcpBuilder;
use tokio_tcp::TcpStream;
thread_local! { thread_local! {
static RT: RefCell<Inner> = { static RT: RefCell<Inner> = {
@ -109,7 +110,7 @@ pub struct TestServerRuntime {
impl TestServer { impl TestServer {
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
/// Start new test server with application factory /// Start new test server with application factory
pub fn new<F: StreamServiceFactory>(factory: F) -> TestServerRuntime { pub fn new<F: StreamServiceFactory<TcpStream>>(factory: F) -> TestServerRuntime {
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
// run server in separate thread // run server in separate thread