mirror of
https://github.com/fafhrd91/actix-web
synced 2025-07-12 21:43:41 +02:00
Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
9306631d6e | |||
487a713ca0 | |||
095ad328ee | |||
a38afa0cec | |||
9619698543 | |||
4b1a471b35 | |||
b6039b0bff | |||
d8fa43034f | |||
92f993e054 | |||
c172deb0f3 | |||
dee6aed010 | |||
76f021a6e3 | |||
2f244ea028 | |||
5f5ddc8f01 | |||
8b473745cb | |||
18575ee1ee | |||
e58b38fd13 | |||
b043c34632 | |||
b748bf3b0d | |||
be12d5e6fc | |||
7c4941f868 | |||
d1f5c457c4 | |||
c26c5fd9a4 | |||
4a73d1c8c1 | |||
7c395fcc83 | |||
54c33a7aff | |||
47d80382b2 | |||
ba816a8562 | |||
6f75b0e95e | |||
b3cc43bb9b |
@ -38,7 +38,7 @@ script:
|
|||||||
- |
|
- |
|
||||||
if [[ "$TRAVIS_RUST_VERSION" == "beta" ]]; then
|
if [[ "$TRAVIS_RUST_VERSION" == "beta" ]]; then
|
||||||
bash <(curl https://raw.githubusercontent.com/xd009642/tarpaulin/master/travis-install.sh)
|
bash <(curl https://raw.githubusercontent.com/xd009642/tarpaulin/master/travis-install.sh)
|
||||||
USE_SKEPTIC=1 cargo tarpaulin --out Xml
|
USE_SKEPTIC=1 cargo tarpaulin --out Xml --no-count
|
||||||
bash <(curl -s https://codecov.io/bash)
|
bash <(curl -s https://codecov.io/bash)
|
||||||
echo "Uploaded code coverage"
|
echo "Uploaded code coverage"
|
||||||
fi
|
fi
|
||||||
|
30
CHANGES.md
30
CHANGES.md
@ -1,5 +1,35 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
|
## 0.6.4 (2018-05-11)
|
||||||
|
|
||||||
|
* Fix segfault in ServerSettings::get_response_builder()
|
||||||
|
|
||||||
|
|
||||||
|
## 0.6.3 (2018-05-10)
|
||||||
|
|
||||||
|
* Add `Router::with_async()` method for async handler registration.
|
||||||
|
|
||||||
|
* Added error response functions for 501,502,503,504
|
||||||
|
|
||||||
|
* Fix client request timeout handling
|
||||||
|
|
||||||
|
|
||||||
|
## 0.6.2 (2018-05-09)
|
||||||
|
|
||||||
|
* WsWriter trait is optional.
|
||||||
|
|
||||||
|
|
||||||
|
## 0.6.1 (2018-05-08)
|
||||||
|
|
||||||
|
* Fix http/2 payload streaming #215
|
||||||
|
|
||||||
|
* Fix connector's default `keep-alive` and `lifetime` settings #212
|
||||||
|
|
||||||
|
* Send `ErrorNotFound` instead of `ErrorBadRequest` when path extractor fails #214
|
||||||
|
|
||||||
|
* Allow to exclude certain endpoints from logging #211
|
||||||
|
|
||||||
|
|
||||||
## 0.6.0 (2018-05-08)
|
## 0.6.0 (2018-05-08)
|
||||||
|
|
||||||
* Add route scopes #202
|
* Add route scopes #202
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-web"
|
name = "actix-web"
|
||||||
version = "0.6.0"
|
version = "0.6.4"
|
||||||
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
MIGRATION.md
16
MIGRATION.md
@ -1,5 +1,7 @@
|
|||||||
## Migration from 0.5 to 0.6
|
## Migration from 0.5 to 0.6
|
||||||
|
|
||||||
|
* `Path<T>` extractor return `ErrorNotFound` on failure instead of `ErrorBadRequest`
|
||||||
|
|
||||||
* `ws::Message::Close` now includes optional close reason.
|
* `ws::Message::Close` now includes optional close reason.
|
||||||
`ws::CloseCode::Status` and `ws::CloseCode::Empty` have been removed.
|
`ws::CloseCode::Status` and `ws::CloseCode::Empty` have been removed.
|
||||||
|
|
||||||
@ -11,6 +13,17 @@
|
|||||||
* `HttpRequest::extensions()` returns read only reference to the request's Extension
|
* `HttpRequest::extensions()` returns read only reference to the request's Extension
|
||||||
`HttpRequest::extensions_mut()` returns mutable reference.
|
`HttpRequest::extensions_mut()` returns mutable reference.
|
||||||
|
|
||||||
|
* 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,
|
||||||
|
RequestSession, Session, SessionBackend, SessionImpl, SessionStorage};`
|
||||||
|
|
||||||
* `FromRequest::from_request()` accepts mutable reference to a request
|
* `FromRequest::from_request()` accepts mutable reference to a request
|
||||||
|
|
||||||
* `FromRequest::Result` has to implement `Into<Reply<Self>>`
|
* `FromRequest::Result` has to implement `Into<Reply<Self>>`
|
||||||
@ -33,6 +46,9 @@
|
|||||||
let q = Query::<HashMap<String, String>>::extract(req);
|
let q = Query::<HashMap<String, String>>::extract(req);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* Websocket operations are implemented as `WsWriter` trait.
|
||||||
|
you need to use `use actix_web::ws::WsWriter`
|
||||||
|
|
||||||
|
|
||||||
## Migration from 0.4 to 0.5
|
## Migration from 0.4 to 0.5
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ extern crate actix_web;
|
|||||||
use actix_web::{http, server, App, Path};
|
use actix_web::{http, server, App, Path};
|
||||||
|
|
||||||
fn index(info: Path<(u32, String)>) -> String {
|
fn index(info: Path<(u32, String)>) -> String {
|
||||||
format!("Hello {}! id:{}", info.0, info.1)
|
format!("Hello {}! id:{}", info.1, info.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
9
build.rs
9
build.rs
@ -1,8 +1,15 @@
|
|||||||
extern crate version_check;
|
extern crate version_check;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
match version_check::is_min_version("1.26.0") {
|
||||||
|
Some((true, _)) => println!("cargo:rustc-cfg=actix_impl_trait"),
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
match version_check::is_nightly() {
|
match version_check::is_nightly() {
|
||||||
Some(true) => println!("cargo:rustc-cfg=actix_nightly"),
|
Some(true) => {
|
||||||
|
println!("cargo:rustc-cfg=actix_nightly");
|
||||||
|
println!("cargo:rustc-cfg=actix_impl_trait");
|
||||||
|
}
|
||||||
Some(false) => (),
|
Some(false) => (),
|
||||||
None => (),
|
None => (),
|
||||||
};
|
};
|
||||||
|
@ -223,8 +223,8 @@ impl Default for ClientConnector {
|
|||||||
pool: Rc::new(Pool::new(Rc::clone(&_modified))),
|
pool: Rc::new(Pool::new(Rc::clone(&_modified))),
|
||||||
pool_modified: _modified,
|
pool_modified: _modified,
|
||||||
connector: builder.build().unwrap(),
|
connector: builder.build().unwrap(),
|
||||||
conn_lifetime: Duration::from_secs(15),
|
conn_lifetime: Duration::from_secs(75),
|
||||||
conn_keep_alive: Duration::from_secs(75),
|
conn_keep_alive: Duration::from_secs(15),
|
||||||
limit: 100,
|
limit: 100,
|
||||||
limit_per_host: 0,
|
limit_per_host: 0,
|
||||||
acquired: 0,
|
acquired: 0,
|
||||||
@ -243,8 +243,8 @@ impl Default for ClientConnector {
|
|||||||
subscriber: None,
|
subscriber: None,
|
||||||
pool: Rc::new(Pool::new(Rc::clone(&_modified))),
|
pool: Rc::new(Pool::new(Rc::clone(&_modified))),
|
||||||
pool_modified: _modified,
|
pool_modified: _modified,
|
||||||
conn_lifetime: Duration::from_secs(15),
|
conn_lifetime: Duration::from_secs(75),
|
||||||
conn_keep_alive: Duration::from_secs(75),
|
conn_keep_alive: Duration::from_secs(15),
|
||||||
limit: 100,
|
limit: 100,
|
||||||
limit_per_host: 0,
|
limit_per_host: 0,
|
||||||
acquired: 0,
|
acquired: 0,
|
||||||
|
@ -49,6 +49,7 @@ use httpresponse::HttpResponse;
|
|||||||
impl ResponseError for SendRequestError {
|
impl ResponseError for SendRequestError {
|
||||||
fn error_response(&self) -> HttpResponse {
|
fn error_response(&self) -> HttpResponse {
|
||||||
match *self {
|
match *self {
|
||||||
|
SendRequestError::Timeout => HttpResponse::GatewayTimeout(),
|
||||||
SendRequestError::Connector(_) => HttpResponse::BadGateway(),
|
SendRequestError::Connector(_) => HttpResponse::BadGateway(),
|
||||||
_ => HttpResponse::InternalServerError(),
|
_ => HttpResponse::InternalServerError(),
|
||||||
}.into()
|
}.into()
|
||||||
|
@ -194,6 +194,7 @@ impl Future for SendRequest {
|
|||||||
self.state = State::Send(pl);
|
self.state = State::Send(pl);
|
||||||
}
|
}
|
||||||
State::Send(mut pl) => {
|
State::Send(mut pl) => {
|
||||||
|
pl.poll_timeout()?;
|
||||||
pl.poll_write().map_err(|e| {
|
pl.poll_write().map_err(|e| {
|
||||||
io::Error::new(io::ErrorKind::Other, format!("{}", e).as_str())
|
io::Error::new(io::ErrorKind::Other, format!("{}", e).as_str())
|
||||||
})?;
|
})?;
|
||||||
@ -315,7 +316,7 @@ impl Pipeline {
|
|||||||
{
|
{
|
||||||
Async::NotReady => need_run = true,
|
Async::NotReady => need_run = true,
|
||||||
Async::Ready(_) => {
|
Async::Ready(_) => {
|
||||||
let _ = self.poll_timeout().map_err(|e| {
|
self.poll_timeout().map_err(|e| {
|
||||||
io::Error::new(io::ErrorKind::Other, format!("{}", e))
|
io::Error::new(io::ErrorKind::Other, format!("{}", e))
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
@ -371,16 +372,15 @@ impl Pipeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_timeout(&mut self) -> Poll<(), SendRequestError> {
|
fn poll_timeout(&mut self) -> Result<(), SendRequestError> {
|
||||||
if self.timeout.is_some() {
|
if self.timeout.is_some() {
|
||||||
match self.timeout.as_mut().unwrap().poll() {
|
match self.timeout.as_mut().unwrap().poll() {
|
||||||
Ok(Async::Ready(())) => Err(SendRequestError::Timeout),
|
Ok(Async::Ready(())) => return Err(SendRequestError::Timeout),
|
||||||
Ok(Async::NotReady) => Ok(Async::NotReady),
|
Ok(Async::NotReady) => (),
|
||||||
Err(_) => unreachable!(),
|
Err(_) => unreachable!(),
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Ok(Async::NotReady)
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
88
src/error.rs
88
src/error.rs
@ -757,6 +757,46 @@ where
|
|||||||
InternalError::new(err, StatusCode::INTERNAL_SERVER_ERROR).into()
|
InternalError::new(err, StatusCode::INTERNAL_SERVER_ERROR).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper function that creates wrapper of any error and
|
||||||
|
/// generate *NOT IMPLEMENTED* response.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn ErrorNotImplemented<T>(err: T) -> Error
|
||||||
|
where
|
||||||
|
T: Send + Sync + fmt::Debug + fmt::Display + 'static,
|
||||||
|
{
|
||||||
|
InternalError::new(err, StatusCode::NOT_IMPLEMENTED).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function that creates wrapper of any error and
|
||||||
|
/// generate *BAD GATEWAY* response.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn ErrorBadGateway<T>(err: T) -> Error
|
||||||
|
where
|
||||||
|
T: Send + Sync + fmt::Debug + fmt::Display + 'static,
|
||||||
|
{
|
||||||
|
InternalError::new(err, StatusCode::BAD_GATEWAY).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function that creates wrapper of any error and
|
||||||
|
/// generate *SERVICE UNAVAILABLE* response.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn ErrorServiceUnavailable<T>(err: T) -> Error
|
||||||
|
where
|
||||||
|
T: Send + Sync + fmt::Debug + fmt::Display + 'static,
|
||||||
|
{
|
||||||
|
InternalError::new(err, StatusCode::SERVICE_UNAVAILABLE).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function that creates wrapper of any error and
|
||||||
|
/// generate *GATEWAY TIMEOUT* response.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn ErrorGatewayTimeout<T>(err: T) -> Error
|
||||||
|
where
|
||||||
|
T: Send + Sync + fmt::Debug + fmt::Display + 'static,
|
||||||
|
{
|
||||||
|
InternalError::new(err, StatusCode::GATEWAY_TIMEOUT).into()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -912,4 +952,52 @@ mod tests {
|
|||||||
let resp: HttpResponse = err.error_response();
|
let resp: HttpResponse = err.error_response();
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_helpers() {
|
||||||
|
let r: HttpResponse = ErrorBadRequest("err").into();
|
||||||
|
assert_eq!(r.status(), StatusCode::BAD_REQUEST);
|
||||||
|
|
||||||
|
let r: HttpResponse = ErrorUnauthorized("err").into();
|
||||||
|
assert_eq!(r.status(), StatusCode::UNAUTHORIZED);
|
||||||
|
|
||||||
|
let r: HttpResponse = ErrorForbidden("err").into();
|
||||||
|
assert_eq!(r.status(), StatusCode::FORBIDDEN);
|
||||||
|
|
||||||
|
let r: HttpResponse = ErrorNotFound("err").into();
|
||||||
|
assert_eq!(r.status(), StatusCode::NOT_FOUND);
|
||||||
|
|
||||||
|
let r: HttpResponse = ErrorMethodNotAllowed("err").into();
|
||||||
|
assert_eq!(r.status(), StatusCode::METHOD_NOT_ALLOWED);
|
||||||
|
|
||||||
|
let r: HttpResponse = ErrorRequestTimeout("err").into();
|
||||||
|
assert_eq!(r.status(), StatusCode::REQUEST_TIMEOUT);
|
||||||
|
|
||||||
|
let r: HttpResponse = ErrorConflict("err").into();
|
||||||
|
assert_eq!(r.status(), StatusCode::CONFLICT);
|
||||||
|
|
||||||
|
let r: HttpResponse = ErrorGone("err").into();
|
||||||
|
assert_eq!(r.status(), StatusCode::GONE);
|
||||||
|
|
||||||
|
let r: HttpResponse = ErrorPreconditionFailed("err").into();
|
||||||
|
assert_eq!(r.status(), StatusCode::PRECONDITION_FAILED);
|
||||||
|
|
||||||
|
let r: HttpResponse = ErrorExpectationFailed("err").into();
|
||||||
|
assert_eq!(r.status(), StatusCode::EXPECTATION_FAILED);
|
||||||
|
|
||||||
|
let r: HttpResponse = ErrorInternalServerError("err").into();
|
||||||
|
assert_eq!(r.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||||
|
|
||||||
|
let r: HttpResponse = ErrorNotImplemented("err").into();
|
||||||
|
assert_eq!(r.status(), StatusCode::NOT_IMPLEMENTED);
|
||||||
|
|
||||||
|
let r: HttpResponse = ErrorBadGateway("err").into();
|
||||||
|
assert_eq!(r.status(), StatusCode::BAD_GATEWAY);
|
||||||
|
|
||||||
|
let r: HttpResponse = ErrorServiceUnavailable("err").into();
|
||||||
|
assert_eq!(r.status(), StatusCode::SERVICE_UNAVAILABLE);
|
||||||
|
|
||||||
|
let r: HttpResponse = ErrorGatewayTimeout("err").into();
|
||||||
|
assert_eq!(r.status(), StatusCode::GATEWAY_TIMEOUT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ use serde::de::{self, DeserializeOwned};
|
|||||||
use serde_urlencoded;
|
use serde_urlencoded;
|
||||||
|
|
||||||
use de::PathDeserializer;
|
use de::PathDeserializer;
|
||||||
use error::{Error, ErrorBadRequest};
|
use error::{Error, ErrorNotFound, ErrorBadRequest};
|
||||||
use handler::{AsyncResult, FromRequest};
|
use handler::{AsyncResult, FromRequest};
|
||||||
use httpmessage::{HttpMessage, MessageBody, UrlEncoded};
|
use httpmessage::{HttpMessage, MessageBody, UrlEncoded};
|
||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
@ -108,7 +108,7 @@ where
|
|||||||
fn from_request(req: &HttpRequest<S>, _: &Self::Config) -> Self::Result {
|
fn from_request(req: &HttpRequest<S>, _: &Self::Config) -> Self::Result {
|
||||||
let req = req.clone();
|
let req = req.clone();
|
||||||
de::Deserialize::deserialize(PathDeserializer::new(&req))
|
de::Deserialize::deserialize(PathDeserializer::new(&req))
|
||||||
.map_err(|e| e.into())
|
.map_err(ErrorNotFound)
|
||||||
.map(|inner| Path { inner })
|
.map(|inner| Path { inner })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -492,14 +492,15 @@ where
|
|||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// /// extract path info using serde
|
/// /// extract path info using serde
|
||||||
/// fn index(state: State<MyApp>, info: Path<Info>) -> String {
|
/// fn index(data: (State<MyApp>, Path<Info>)) -> String {
|
||||||
/// format!("{} {}!", state.msg, info.username)
|
/// let (state, path) = data;
|
||||||
|
/// format!("{} {}!", state.msg, path.username)
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::with_state(MyApp{msg: "Welcome"}).resource(
|
/// let app = App::with_state(MyApp{msg: "Welcome"}).resource(
|
||||||
/// "/{username}/index.html", // <- define path parameters
|
/// "/{username}/index.html", // <- define path parameters
|
||||||
/// |r| r.method(http::Method::GET).with2(index)); // <- use `with` extractor
|
/// |r| r.method(http::Method::GET).with(index)); // <- use `with` extractor
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct State<S>(HttpRequest<S>);
|
pub struct State<S>(HttpRequest<S>);
|
||||||
|
@ -175,6 +175,9 @@ pub use httprequest::HttpRequest;
|
|||||||
pub use httpresponse::HttpResponse;
|
pub use httpresponse::HttpResponse;
|
||||||
pub use json::Json;
|
pub use json::Json;
|
||||||
pub use scope::Scope;
|
pub use scope::Scope;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[deprecated(since = "0.6.2", note = "please use `use actix_web::ws::WsWriter`")]
|
||||||
pub use ws::WsWriter;
|
pub use ws::WsWriter;
|
||||||
|
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
@ -210,6 +213,7 @@ pub mod dev {
|
|||||||
pub use resource::ResourceHandler;
|
pub use resource::ResourceHandler;
|
||||||
pub use route::Route;
|
pub use route::Route;
|
||||||
pub use router::{Resource, ResourceType, Router};
|
pub use router::{Resource, ResourceType, Router};
|
||||||
|
pub use with::ExtractorConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod http {
|
pub mod http {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Request logging middleware
|
//! Request logging middleware
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fmt;
|
use std::fmt::{self, Display, Formatter};
|
||||||
use std::fmt::{Display, Formatter};
|
|
||||||
|
|
||||||
use libc;
|
use libc;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
@ -74,6 +74,7 @@ use middleware::{Finished, Middleware, Started};
|
|||||||
///
|
///
|
||||||
pub struct Logger {
|
pub struct Logger {
|
||||||
format: Format,
|
format: Format,
|
||||||
|
exclude: HashSet<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Logger {
|
impl Logger {
|
||||||
@ -81,8 +82,15 @@ impl Logger {
|
|||||||
pub fn new(format: &str) -> Logger {
|
pub fn new(format: &str) -> Logger {
|
||||||
Logger {
|
Logger {
|
||||||
format: Format::new(format),
|
format: Format::new(format),
|
||||||
|
exclude: HashSet::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ignore and do not log access info for specified path.
|
||||||
|
pub fn exclude<T: Into<String>>(mut self, path: T) -> Self {
|
||||||
|
self.exclude.insert(path.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Logger {
|
impl Default for Logger {
|
||||||
@ -94,6 +102,7 @@ impl Default for Logger {
|
|||||||
fn default() -> Logger {
|
fn default() -> Logger {
|
||||||
Logger {
|
Logger {
|
||||||
format: Format::default(),
|
format: Format::default(),
|
||||||
|
exclude: HashSet::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,21 +111,23 @@ struct StartTime(time::Tm);
|
|||||||
|
|
||||||
impl Logger {
|
impl Logger {
|
||||||
fn log<S>(&self, req: &mut HttpRequest<S>, resp: &HttpResponse) {
|
fn log<S>(&self, req: &mut HttpRequest<S>, resp: &HttpResponse) {
|
||||||
let entry_time = req.extensions().get::<StartTime>().unwrap().0;
|
if let Some(entry_time) = req.extensions().get::<StartTime>() {
|
||||||
|
let render = |fmt: &mut Formatter| {
|
||||||
let render = |fmt: &mut Formatter| {
|
for unit in &self.format.0 {
|
||||||
for unit in &self.format.0 {
|
unit.render(fmt, req, resp, entry_time.0)?;
|
||||||
unit.render(fmt, req, resp, entry_time)?;
|
}
|
||||||
}
|
Ok(())
|
||||||
Ok(())
|
};
|
||||||
};
|
info!("{}", FormatDisplay(&render));
|
||||||
info!("{}", FormatDisplay(&render));
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> Middleware<S> for Logger {
|
impl<S> Middleware<S> for Logger {
|
||||||
fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> {
|
fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> {
|
||||||
req.extensions_mut().insert(StartTime(time::now()));
|
if !self.exclude.contains(req.path()) {
|
||||||
|
req.extensions_mut().insert(StartTime(time::now()));
|
||||||
|
}
|
||||||
Ok(Started::Done)
|
Ok(Started::Done)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,8 +492,8 @@ impl<S: 'static, H> ProcessResponse<S, H> {
|
|||||||
if let Some(err) = self.resp.error() {
|
if let Some(err) = self.resp.error() {
|
||||||
if self.resp.status().is_server_error() {
|
if self.resp.status().is_server_error() {
|
||||||
error!(
|
error!(
|
||||||
"Error occured during request handling: {}",
|
"Error occured during request handling, status: {} {}",
|
||||||
err
|
self.resp.status(), err
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
warn!(
|
warn!(
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use futures::Future;
|
||||||
use http::{Method, StatusCode};
|
use http::{Method, StatusCode};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
use error::Error;
|
||||||
use handler::{AsyncResult, FromRequest, Handler, Responder};
|
use handler::{AsyncResult, FromRequest, Handler, Responder};
|
||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
use httpresponse::HttpResponse;
|
use httpresponse::HttpResponse;
|
||||||
@ -183,6 +185,25 @@ impl<S: 'static> ResourceHandler<S> {
|
|||||||
self.routes.last_mut().unwrap().with(handler);
|
self.routes.last_mut().unwrap().with(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Register a new route and add async handler.
|
||||||
|
///
|
||||||
|
/// This is shortcut for:
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// Application::resource("/", |r| r.route().with_async(index)
|
||||||
|
/// ```
|
||||||
|
pub fn with_async<T, F, R, I, E>(&mut self, handler: F)
|
||||||
|
where
|
||||||
|
F: Fn(T) -> R + 'static,
|
||||||
|
R: Future<Item = I, Error = E> + 'static,
|
||||||
|
I: Responder + 'static,
|
||||||
|
E: Into<Error> + 'static,
|
||||||
|
T: FromRequest<S> + 'static,
|
||||||
|
{
|
||||||
|
self.routes.push(Route::default());
|
||||||
|
self.routes.last_mut().unwrap().with_async(handler);
|
||||||
|
}
|
||||||
|
|
||||||
/// Register a resource middleware
|
/// Register a resource middleware
|
||||||
///
|
///
|
||||||
/// This is similar to `App's` middlewares, but
|
/// This is similar to `App's` middlewares, but
|
||||||
|
74
src/route.rs
74
src/route.rs
@ -13,7 +13,7 @@ use httpresponse::HttpResponse;
|
|||||||
use middleware::{Finished as MiddlewareFinished, Middleware,
|
use middleware::{Finished as MiddlewareFinished, Middleware,
|
||||||
Response as MiddlewareResponse, Started as MiddlewareStarted};
|
Response as MiddlewareResponse, Started as MiddlewareStarted};
|
||||||
use pred::Predicate;
|
use pred::Predicate;
|
||||||
use with::{ExtractorConfig, With, With2, With3};
|
use with::{ExtractorConfig, With, With2, With3, WithAsync};
|
||||||
|
|
||||||
/// Resource route definition
|
/// Resource route definition
|
||||||
///
|
///
|
||||||
@ -129,6 +129,34 @@ impl<S: 'static> Route<S> {
|
|||||||
/// |r| r.method(http::Method::GET).with(index)); // <- use `with` extractor
|
/// |r| r.method(http::Method::GET).with(index)); // <- use `with` extractor
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// It is possible to use tuples for specifing multiple extractors for one
|
||||||
|
/// handler function.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # extern crate bytes;
|
||||||
|
/// # extern crate actix_web;
|
||||||
|
/// # extern crate futures;
|
||||||
|
/// #[macro_use] extern crate serde_derive;
|
||||||
|
/// # use std::collections::HashMap;
|
||||||
|
/// use actix_web::{http, App, Query, Path, Result, Json};
|
||||||
|
///
|
||||||
|
/// #[derive(Deserialize)]
|
||||||
|
/// struct Info {
|
||||||
|
/// username: String,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// /// extract path info using serde
|
||||||
|
/// fn index(info: (Path<Info>, Query<HashMap<String, String>>, Json<Info>)) -> Result<String> {
|
||||||
|
/// Ok(format!("Welcome {}!", info.0.username))
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let app = App::new().resource(
|
||||||
|
/// "/{username}/index.html", // <- define path parameters
|
||||||
|
/// |r| r.method(http::Method::GET).with(index)); // <- use `with` extractor
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
pub fn with<T, F, R>(&mut self, handler: F) -> ExtractorConfig<S, T>
|
pub fn with<T, F, R>(&mut self, handler: F) -> ExtractorConfig<S, T>
|
||||||
where
|
where
|
||||||
F: Fn(T) -> R + 'static,
|
F: Fn(T) -> R + 'static,
|
||||||
@ -140,6 +168,49 @@ impl<S: 'static> Route<S> {
|
|||||||
cfg
|
cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set async handler function, use request extractor for parameters.
|
||||||
|
/// Also this method needs to be used if your handler function returns
|
||||||
|
/// `impl Future<>`
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # extern crate bytes;
|
||||||
|
/// # extern crate actix_web;
|
||||||
|
/// # extern crate futures;
|
||||||
|
/// #[macro_use] extern crate serde_derive;
|
||||||
|
/// use actix_web::{App, Path, Error, http};
|
||||||
|
/// use futures::Future;
|
||||||
|
///
|
||||||
|
/// #[derive(Deserialize)]
|
||||||
|
/// struct Info {
|
||||||
|
/// username: String,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// /// extract path info using serde
|
||||||
|
/// fn index(info: Path<Info>) -> Box<Future<Item=&'static str, Error=Error>> {
|
||||||
|
/// unimplemented!()
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let app = App::new().resource(
|
||||||
|
/// "/{username}/index.html", // <- define path parameters
|
||||||
|
/// |r| r.method(http::Method::GET)
|
||||||
|
/// .with_async(index)); // <- use `with` extractor
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn with_async<T, F, R, I, E>(&mut self, handler: F) -> ExtractorConfig<S, T>
|
||||||
|
where
|
||||||
|
F: Fn(T) -> R + 'static,
|
||||||
|
R: Future<Item = I, Error = E> + 'static,
|
||||||
|
I: Responder + 'static,
|
||||||
|
E: Into<Error> + 'static,
|
||||||
|
T: FromRequest<S> + 'static,
|
||||||
|
{
|
||||||
|
let cfg = ExtractorConfig::default();
|
||||||
|
self.h(WithAsync::new(handler, Clone::clone(&cfg)));
|
||||||
|
cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
/// Set handler function, use request extractor for both parameters.
|
/// Set handler function, use request extractor for both parameters.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
@ -189,6 +260,7 @@ impl<S: 'static> Route<S> {
|
|||||||
(cfg1, cfg2)
|
(cfg1, cfg2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
/// Set handler function, use request extractor for all parameters.
|
/// Set handler function, use request extractor for all parameters.
|
||||||
pub fn with3<T1, T2, T3, F, R>(
|
pub fn with3<T1, T2, T3, F, R>(
|
||||||
&mut self, handler: F,
|
&mut self, handler: F,
|
||||||
|
@ -76,7 +76,7 @@ impl<S: 'static> Scope<S> {
|
|||||||
mem::replace(&mut self.filters, Vec::new())
|
mem::replace(&mut self.filters, Vec::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add match predicate to scoupe.
|
/// Add match predicate to scope.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # extern crate actix_web;
|
/// # extern crate actix_web;
|
||||||
@ -787,10 +787,10 @@ mod tests {
|
|||||||
let mut app = App::new()
|
let mut app = App::new()
|
||||||
.scope("app", |scope| {
|
.scope("app", |scope| {
|
||||||
scope
|
scope
|
||||||
.route("/path1", Method::GET, |r: HttpRequest<_>| {
|
.route("/path1", Method::GET, |_: HttpRequest<_>| {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
})
|
})
|
||||||
.route("/path1", Method::DELETE, |r: HttpRequest<_>| {
|
.route("/path1", Method::DELETE, |_: HttpRequest<_>| {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -343,24 +343,27 @@ impl<H: 'static> Entry<H> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn poll_payload(&mut self) {
|
fn poll_payload(&mut self) {
|
||||||
if !self.flags.contains(EntryFlags::REOF) {
|
while !self.flags.contains(EntryFlags::REOF)
|
||||||
if self.payload.need_read() == PayloadStatus::Read {
|
&& self.payload.need_read() == PayloadStatus::Read
|
||||||
if let Err(err) = self.recv.release_capacity().release_capacity(32_768) {
|
{
|
||||||
self.payload.set_error(PayloadError::Http2(err))
|
|
||||||
}
|
|
||||||
} else if let Err(err) = self.recv.release_capacity().release_capacity(0) {
|
|
||||||
self.payload.set_error(PayloadError::Http2(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.recv.poll() {
|
match self.recv.poll() {
|
||||||
Ok(Async::Ready(Some(chunk))) => {
|
Ok(Async::Ready(Some(chunk))) => {
|
||||||
|
let l = chunk.len();
|
||||||
self.payload.feed_data(chunk);
|
self.payload.feed_data(chunk);
|
||||||
|
if let Err(err) = self.recv.release_capacity().release_capacity(l) {
|
||||||
|
self.payload.set_error(PayloadError::Http2(err));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(Async::Ready(None)) => {
|
Ok(Async::Ready(None)) => {
|
||||||
self.flags.insert(EntryFlags::REOF);
|
self.flags.insert(EntryFlags::REOF);
|
||||||
|
self.payload.feed_eof();
|
||||||
|
}
|
||||||
|
Ok(Async::NotReady) => break,
|
||||||
|
Err(err) => {
|
||||||
|
self.payload.set_error(PayloadError::Http2(err));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
Ok(Async::NotReady) => (),
|
|
||||||
Err(err) => self.payload.set_error(PayloadError::Http2(err)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ use body::Body;
|
|||||||
use httpresponse::{HttpResponse, HttpResponseBuilder, HttpResponsePool};
|
use httpresponse::{HttpResponse, HttpResponseBuilder, HttpResponsePool};
|
||||||
|
|
||||||
/// Various server settings
|
/// Various server settings
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ServerSettings {
|
pub struct ServerSettings {
|
||||||
addr: Option<net::SocketAddr>,
|
addr: Option<net::SocketAddr>,
|
||||||
secure: bool,
|
secure: bool,
|
||||||
@ -28,6 +27,18 @@ pub struct ServerSettings {
|
|||||||
unsafe impl Sync for ServerSettings {}
|
unsafe impl Sync for ServerSettings {}
|
||||||
unsafe impl Send for ServerSettings {}
|
unsafe impl Send for ServerSettings {}
|
||||||
|
|
||||||
|
impl Clone for ServerSettings {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
ServerSettings {
|
||||||
|
addr: self.addr,
|
||||||
|
secure: self.secure,
|
||||||
|
host: self.host.clone(),
|
||||||
|
cpu_pool: self.cpu_pool.clone(),
|
||||||
|
responses: HttpResponsePool::pool(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct InnerCpuPool {
|
struct InnerCpuPool {
|
||||||
cpu_pool: UnsafeCell<Option<CpuPool>>,
|
cpu_pool: UnsafeCell<Option<CpuPool>>,
|
||||||
}
|
}
|
||||||
|
195
src/with.rs
195
src/with.rs
@ -9,6 +9,62 @@ use handler::{AsyncResult, AsyncResultItem, FromRequest, Handler, Responder};
|
|||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
use httpresponse::HttpResponse;
|
use httpresponse::HttpResponse;
|
||||||
|
|
||||||
|
/// Extractor configuration
|
||||||
|
///
|
||||||
|
/// `Route::with()` and `Route::with_async()` returns instance
|
||||||
|
/// of the `ExtractorConfig` type. It could be used for extractor configuration.
|
||||||
|
///
|
||||||
|
/// In this example `Form<FormData>` configured.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # extern crate actix_web;
|
||||||
|
/// #[macro_use] extern crate serde_derive;
|
||||||
|
/// use actix_web::{App, Form, Result, http};
|
||||||
|
///
|
||||||
|
/// #[derive(Deserialize)]
|
||||||
|
/// struct FormData {
|
||||||
|
/// username: String,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn index(form: Form<FormData>) -> Result<String> {
|
||||||
|
/// Ok(format!("Welcome {}!", form.username))
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let app = App::new().resource(
|
||||||
|
/// "/index.html", |r| {
|
||||||
|
/// r.method(http::Method::GET)
|
||||||
|
/// .with(index)
|
||||||
|
/// .limit(4096);} // <- change form extractor configuration
|
||||||
|
/// );
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Same could be donce with multiple extractors
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # extern crate actix_web;
|
||||||
|
/// #[macro_use] extern crate serde_derive;
|
||||||
|
/// use actix_web::{App, Form, Path, Result, http};
|
||||||
|
///
|
||||||
|
/// #[derive(Deserialize)]
|
||||||
|
/// struct FormData {
|
||||||
|
/// username: String,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn index(data: (Path<(String,)>, Form<FormData>)) -> Result<String> {
|
||||||
|
/// Ok(format!("Welcome {}!", data.1.username))
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let app = App::new().resource(
|
||||||
|
/// "/index.html", |r| {
|
||||||
|
/// r.method(http::Method::GET)
|
||||||
|
/// .with(index)
|
||||||
|
/// .1.limit(4096);} // <- change form extractor configuration
|
||||||
|
/// );
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
pub struct ExtractorConfig<S: 'static, T: FromRequest<S>> {
|
pub struct ExtractorConfig<S: 'static, T: FromRequest<S>> {
|
||||||
cfg: Rc<UnsafeCell<T::Config>>,
|
cfg: Rc<UnsafeCell<T::Config>>,
|
||||||
}
|
}
|
||||||
@ -167,6 +223,145 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct WithAsync<T, S, F, R, I, E>
|
||||||
|
where
|
||||||
|
F: Fn(T) -> R,
|
||||||
|
R: Future<Item = I, Error = E>,
|
||||||
|
I: Responder,
|
||||||
|
E: Into<E>,
|
||||||
|
T: FromRequest<S>,
|
||||||
|
S: 'static,
|
||||||
|
{
|
||||||
|
hnd: Rc<UnsafeCell<F>>,
|
||||||
|
cfg: ExtractorConfig<S, T>,
|
||||||
|
_s: PhantomData<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, S, F, R, I, E> WithAsync<T, S, F, R, I, E>
|
||||||
|
where
|
||||||
|
F: Fn(T) -> R,
|
||||||
|
R: Future<Item = I, Error = E>,
|
||||||
|
I: Responder,
|
||||||
|
E: Into<Error>,
|
||||||
|
T: FromRequest<S>,
|
||||||
|
S: 'static,
|
||||||
|
{
|
||||||
|
pub fn new(f: F, cfg: ExtractorConfig<S, T>) -> Self {
|
||||||
|
WithAsync {
|
||||||
|
cfg,
|
||||||
|
hnd: Rc::new(UnsafeCell::new(f)),
|
||||||
|
_s: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, S, F, R, I, E> Handler<S> for WithAsync<T, S, F, R, I, E>
|
||||||
|
where
|
||||||
|
F: Fn(T) -> R + 'static,
|
||||||
|
R: Future<Item = I, Error = E> + 'static,
|
||||||
|
I: Responder + 'static,
|
||||||
|
E: Into<Error> + 'static,
|
||||||
|
T: FromRequest<S> + 'static,
|
||||||
|
S: 'static,
|
||||||
|
{
|
||||||
|
type Result = AsyncResult<HttpResponse>;
|
||||||
|
|
||||||
|
fn handle(&mut self, req: HttpRequest<S>) -> Self::Result {
|
||||||
|
let mut fut = WithAsyncHandlerFut {
|
||||||
|
req,
|
||||||
|
started: false,
|
||||||
|
hnd: Rc::clone(&self.hnd),
|
||||||
|
cfg: self.cfg.clone(),
|
||||||
|
fut1: None,
|
||||||
|
fut2: None,
|
||||||
|
fut3: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
match fut.poll() {
|
||||||
|
Ok(Async::Ready(resp)) => AsyncResult::ok(resp),
|
||||||
|
Ok(Async::NotReady) => AsyncResult::async(Box::new(fut)),
|
||||||
|
Err(e) => AsyncResult::err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WithAsyncHandlerFut<T, S, F, R, I, E>
|
||||||
|
where
|
||||||
|
F: Fn(T) -> R,
|
||||||
|
R: Future<Item = I, Error = E> + 'static,
|
||||||
|
I: Responder + 'static,
|
||||||
|
E: Into<Error> + 'static,
|
||||||
|
T: FromRequest<S> + 'static,
|
||||||
|
S: 'static,
|
||||||
|
{
|
||||||
|
started: bool,
|
||||||
|
hnd: Rc<UnsafeCell<F>>,
|
||||||
|
cfg: ExtractorConfig<S, T>,
|
||||||
|
req: HttpRequest<S>,
|
||||||
|
fut1: Option<Box<Future<Item = T, Error = Error>>>,
|
||||||
|
fut2: Option<R>,
|
||||||
|
fut3: Option<Box<Future<Item = HttpResponse, Error = Error>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, S, F, R, I, E> Future for WithAsyncHandlerFut<T, S, F, R, I, E>
|
||||||
|
where
|
||||||
|
F: Fn(T) -> R,
|
||||||
|
R: Future<Item = I, Error = E> + 'static,
|
||||||
|
I: Responder + 'static,
|
||||||
|
E: Into<Error> + 'static,
|
||||||
|
T: FromRequest<S> + 'static,
|
||||||
|
S: 'static,
|
||||||
|
{
|
||||||
|
type Item = HttpResponse;
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||||
|
if let Some(ref mut fut) = self.fut3 {
|
||||||
|
return fut.poll();
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.fut2.is_some() {
|
||||||
|
return match self.fut2.as_mut().unwrap().poll() {
|
||||||
|
Ok(Async::NotReady) => Ok(Async::NotReady),
|
||||||
|
Ok(Async::Ready(r)) => match r.respond_to(&self.req) {
|
||||||
|
Ok(r) => match r.into().into() {
|
||||||
|
AsyncResultItem::Err(err) => Err(err),
|
||||||
|
AsyncResultItem::Ok(resp) => Ok(Async::Ready(resp)),
|
||||||
|
AsyncResultItem::Future(fut) => {
|
||||||
|
self.fut3 = Some(fut);
|
||||||
|
self.poll()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
},
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let item = if !self.started {
|
||||||
|
self.started = true;
|
||||||
|
let reply = T::from_request(&self.req, self.cfg.as_ref()).into();
|
||||||
|
match reply.into() {
|
||||||
|
AsyncResultItem::Err(err) => return Err(err),
|
||||||
|
AsyncResultItem::Ok(msg) => msg,
|
||||||
|
AsyncResultItem::Future(fut) => {
|
||||||
|
self.fut1 = Some(fut);
|
||||||
|
return self.poll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match self.fut1.as_mut().unwrap().poll()? {
|
||||||
|
Async::Ready(item) => item,
|
||||||
|
Async::NotReady => return Ok(Async::NotReady),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let hnd: &mut F = unsafe { &mut *self.hnd.get() };
|
||||||
|
self.fut2 = Some((*hnd)(item));
|
||||||
|
self.poll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct With2<T1, T2, S, F, R>
|
pub struct With2<T1, T2, S, F, R>
|
||||||
where
|
where
|
||||||
F: Fn(T1, T2) -> R,
|
F: Fn(T1, T2) -> R,
|
||||||
|
@ -518,24 +518,22 @@ impl ClientWriter {
|
|||||||
fn as_mut(&mut self) -> &mut Inner {
|
fn as_mut(&mut self) -> &mut Inner {
|
||||||
unsafe { &mut *self.inner.get() }
|
unsafe { &mut *self.inner.get() }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl WsWriter for ClientWriter {
|
|
||||||
/// Send text frame
|
/// Send text frame
|
||||||
#[inline]
|
#[inline]
|
||||||
fn text<T: Into<Binary>>(&mut self, text: T) {
|
pub fn text<T: Into<Binary>>(&mut self, text: T) {
|
||||||
self.write(Frame::message(text.into(), OpCode::Text, true, true));
|
self.write(Frame::message(text.into(), OpCode::Text, true, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send binary frame
|
/// Send binary frame
|
||||||
#[inline]
|
#[inline]
|
||||||
fn binary<B: Into<Binary>>(&mut self, data: B) {
|
pub fn binary<B: Into<Binary>>(&mut self, data: B) {
|
||||||
self.write(Frame::message(data, OpCode::Binary, true, true));
|
self.write(Frame::message(data, OpCode::Binary, true, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send ping frame
|
/// Send ping frame
|
||||||
#[inline]
|
#[inline]
|
||||||
fn ping(&mut self, message: &str) {
|
pub fn ping(&mut self, message: &str) {
|
||||||
self.write(Frame::message(
|
self.write(Frame::message(
|
||||||
Vec::from(message),
|
Vec::from(message),
|
||||||
OpCode::Ping,
|
OpCode::Ping,
|
||||||
@ -546,7 +544,7 @@ impl WsWriter for ClientWriter {
|
|||||||
|
|
||||||
/// Send pong frame
|
/// Send pong frame
|
||||||
#[inline]
|
#[inline]
|
||||||
fn pong(&mut self, message: &str) {
|
pub fn pong(&mut self, message: &str) {
|
||||||
self.write(Frame::message(
|
self.write(Frame::message(
|
||||||
Vec::from(message),
|
Vec::from(message),
|
||||||
OpCode::Pong,
|
OpCode::Pong,
|
||||||
@ -557,7 +555,39 @@ impl WsWriter for ClientWriter {
|
|||||||
|
|
||||||
/// Send close frame
|
/// Send close frame
|
||||||
#[inline]
|
#[inline]
|
||||||
fn close(&mut self, reason: Option<CloseReason>) {
|
pub fn close(&mut self, reason: Option<CloseReason>) {
|
||||||
self.write(Frame::close(reason, true));
|
self.write(Frame::close(reason, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WsWriter for ClientWriter {
|
||||||
|
/// Send text frame
|
||||||
|
#[inline]
|
||||||
|
fn send_text<T: Into<Binary>>(&mut self, text: T) {
|
||||||
|
self.text(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send binary frame
|
||||||
|
#[inline]
|
||||||
|
fn send_binary<B: Into<Binary>>(&mut self, data: B) {
|
||||||
|
self.binary(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send ping frame
|
||||||
|
#[inline]
|
||||||
|
fn send_ping(&mut self, message: &str) {
|
||||||
|
self.ping(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send pong frame
|
||||||
|
#[inline]
|
||||||
|
fn send_pong(&mut self, message: &str) {
|
||||||
|
self.pong(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send close frame
|
||||||
|
#[inline]
|
||||||
|
fn send_close(&mut self, reason: Option<CloseReason>) {
|
||||||
|
self.close(reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -149,6 +149,46 @@ where
|
|||||||
Drain::new(rx)
|
Drain::new(rx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Send text frame
|
||||||
|
#[inline]
|
||||||
|
pub fn text<T: Into<Binary>>(&mut self, text: T) {
|
||||||
|
self.write(Frame::message(text.into(), OpCode::Text, true, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send binary frame
|
||||||
|
#[inline]
|
||||||
|
pub fn binary<B: Into<Binary>>(&mut self, data: B) {
|
||||||
|
self.write(Frame::message(data, OpCode::Binary, true, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send ping frame
|
||||||
|
#[inline]
|
||||||
|
pub fn ping(&mut self, message: &str) {
|
||||||
|
self.write(Frame::message(
|
||||||
|
Vec::from(message),
|
||||||
|
OpCode::Ping,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send pong frame
|
||||||
|
#[inline]
|
||||||
|
pub fn pong(&mut self, message: &str) {
|
||||||
|
self.write(Frame::message(
|
||||||
|
Vec::from(message),
|
||||||
|
OpCode::Pong,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send close frame
|
||||||
|
#[inline]
|
||||||
|
pub fn close(&mut self, reason: Option<CloseReason>) {
|
||||||
|
self.write(Frame::close(reason, false));
|
||||||
|
}
|
||||||
|
|
||||||
/// Check if connection still open
|
/// Check if connection still open
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn connected(&self) -> bool {
|
pub fn connected(&self) -> bool {
|
||||||
@ -181,42 +221,32 @@ where
|
|||||||
{
|
{
|
||||||
/// Send text frame
|
/// Send text frame
|
||||||
#[inline]
|
#[inline]
|
||||||
fn text<T: Into<Binary>>(&mut self, text: T) {
|
fn send_text<T: Into<Binary>>(&mut self, text: T) {
|
||||||
self.write(Frame::message(text.into(), OpCode::Text, true, false));
|
self.text(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send binary frame
|
/// Send binary frame
|
||||||
#[inline]
|
#[inline]
|
||||||
fn binary<B: Into<Binary>>(&mut self, data: B) {
|
fn send_binary<B: Into<Binary>>(&mut self, data: B) {
|
||||||
self.write(Frame::message(data, OpCode::Binary, true, false));
|
self.binary(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send ping frame
|
/// Send ping frame
|
||||||
#[inline]
|
#[inline]
|
||||||
fn ping(&mut self, message: &str) {
|
fn send_ping(&mut self, message: &str) {
|
||||||
self.write(Frame::message(
|
self.ping(message)
|
||||||
Vec::from(message),
|
|
||||||
OpCode::Ping,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send pong frame
|
/// Send pong frame
|
||||||
#[inline]
|
#[inline]
|
||||||
fn pong(&mut self, message: &str) {
|
fn send_pong(&mut self, message: &str) {
|
||||||
self.write(Frame::message(
|
self.pong(message)
|
||||||
Vec::from(message),
|
|
||||||
OpCode::Pong,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send close frame
|
/// Send close frame
|
||||||
#[inline]
|
#[inline]
|
||||||
fn close(&mut self, reason: Option<CloseReason>) {
|
fn send_close(&mut self, reason: Option<CloseReason>) {
|
||||||
self.write(Frame::close(reason, false));
|
self.close(reason)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,15 +343,15 @@ where
|
|||||||
/// Common writing methods for a websocket.
|
/// Common writing methods for a websocket.
|
||||||
pub trait WsWriter {
|
pub trait WsWriter {
|
||||||
/// Send a text
|
/// Send a text
|
||||||
fn text<T: Into<Binary>>(&mut self, text: T);
|
fn send_text<T: Into<Binary>>(&mut self, text: T);
|
||||||
/// Send a binary
|
/// Send a binary
|
||||||
fn binary<B: Into<Binary>>(&mut self, data: B);
|
fn send_binary<B: Into<Binary>>(&mut self, data: B);
|
||||||
/// Send a ping message
|
/// Send a ping message
|
||||||
fn ping(&mut self, message: &str);
|
fn send_ping(&mut self, message: &str);
|
||||||
/// Send a pong message
|
/// Send a pong message
|
||||||
fn pong(&mut self, message: &str);
|
fn send_pong(&mut self, message: &str);
|
||||||
/// Close the connection
|
/// Close the connection
|
||||||
fn close(&mut self, reason: Option<CloseReason>);
|
fn send_close(&mut self, reason: Option<CloseReason>);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -9,6 +9,7 @@ extern crate tokio_core;
|
|||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
|
||||||
|
use std::io;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use actix::*;
|
use actix::*;
|
||||||
@ -377,6 +378,83 @@ fn test_path_and_query_extractor2_async4() {
|
|||||||
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(actix_impl_trait)]
|
||||||
|
fn test_impl_trait(
|
||||||
|
data: (Json<Value>, Path<PParam>, Query<PParam>),
|
||||||
|
) -> impl Future<Item = String, Error = io::Error> {
|
||||||
|
Timeout::new(Duration::from_millis(10), &Arbiter::handle())
|
||||||
|
.unwrap()
|
||||||
|
.and_then(move |_| {
|
||||||
|
Ok(format!(
|
||||||
|
"Welcome {} - {}!",
|
||||||
|
data.1.username,
|
||||||
|
(data.0).0
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(actix_impl_trait)]
|
||||||
|
fn test_impl_trait_err(
|
||||||
|
_data: (Json<Value>, Path<PParam>, Query<PParam>),
|
||||||
|
) -> impl Future<Item = String, Error = io::Error> {
|
||||||
|
Timeout::new(Duration::from_millis(10), &Arbiter::handle())
|
||||||
|
.unwrap()
|
||||||
|
.and_then(move |_| Err(io::Error::new(io::ErrorKind::Other, "other")))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(actix_impl_trait)]
|
||||||
|
#[test]
|
||||||
|
fn test_path_and_query_extractor2_async4_impl_trait() {
|
||||||
|
let mut srv = test::TestServer::new(|app| {
|
||||||
|
app.resource("/{username}/index.html", |r| {
|
||||||
|
r.route().with_async(test_impl_trait)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// client request
|
||||||
|
let request = srv.post()
|
||||||
|
.uri(srv.url("/test1/index.html?username=test2"))
|
||||||
|
.header("content-type", "application/json")
|
||||||
|
.body("{\"test\": 1}")
|
||||||
|
.unwrap();
|
||||||
|
let response = srv.execute(request.send()).unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
|
||||||
|
// read response
|
||||||
|
let bytes = srv.execute(response.body()).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
bytes,
|
||||||
|
Bytes::from_static(b"Welcome test1 - {\"test\":1}!")
|
||||||
|
);
|
||||||
|
|
||||||
|
// client request
|
||||||
|
let request = srv.get()
|
||||||
|
.uri(srv.url("/test1/index.html"))
|
||||||
|
.finish()
|
||||||
|
.unwrap();
|
||||||
|
let response = srv.execute(request.send()).unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(actix_impl_trait)]
|
||||||
|
#[test]
|
||||||
|
fn test_path_and_query_extractor2_async4_impl_trait_err() {
|
||||||
|
let mut srv = test::TestServer::new(|app| {
|
||||||
|
app.resource("/{username}/index.html", |r| {
|
||||||
|
r.route().with_async(test_impl_trait_err)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// client request
|
||||||
|
let request = srv.post()
|
||||||
|
.uri(srv.url("/test1/index.html?username=test2"))
|
||||||
|
.header("content-type", "application/json")
|
||||||
|
.body("{\"test\": 1}")
|
||||||
|
.unwrap();
|
||||||
|
let response = srv.execute(request.send()).unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_non_ascii_route() {
|
fn test_non_ascii_route() {
|
||||||
let mut srv = test::TestServer::new(|app| {
|
let mut srv = test::TestServer::new(|app| {
|
||||||
|
@ -809,7 +809,7 @@ fn test_h2() {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
let _res = core.run(tcp);
|
let _res = core.run(tcp);
|
||||||
// assert_eq!(res.unwrap(), Bytes::from_static(STR.as_ref()));
|
// assert_eq!(_res.unwrap(), Bytes::from_static(STR.as_ref()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
Reference in New Issue
Block a user