mirror of
https://github.com/actix/actix-web.git
synced 2024-12-19 14:49:01 +00:00
This commit is contained in:
commit
454e9f8ce1
23 changed files with 466 additions and 265 deletions
18
CHANGES.md
18
CHANGES.md
|
@ -25,6 +25,8 @@
|
||||||
|
|
||||||
* `HttpRequest::url_for_static()` for a named route with no variables segments
|
* `HttpRequest::url_for_static()` for a named route with no variables segments
|
||||||
|
|
||||||
|
* Propagation of the application's default resource to scopes that haven't set a default resource.
|
||||||
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
@ -39,7 +41,7 @@
|
||||||
|
|
||||||
* Added header `User-Agent: Actix-web/<current_version>` to default headers when building a request
|
* Added header `User-Agent: Actix-web/<current_version>` to default headers when building a request
|
||||||
|
|
||||||
* port `Extensions` type from http create, we dont need `Send + Sync`
|
* port `Extensions` type from http create, we don't need `Send + Sync`
|
||||||
|
|
||||||
* `HttpRequest::query()` returns `&HashMap<String, String>`
|
* `HttpRequest::query()` returns `&HashMap<String, String>`
|
||||||
|
|
||||||
|
@ -50,6 +52,16 @@
|
||||||
|
|
||||||
* Remove `HttpMessage::range()`
|
* Remove `HttpMessage::range()`
|
||||||
|
|
||||||
|
## [0.6.14] - 2018-06-21
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Allow to disable masking for websockets client
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* SendRequest execution fails with the "internal error: entered unreachable code" #329
|
||||||
|
|
||||||
|
|
||||||
## [0.6.13] - 2018-06-11
|
## [0.6.13] - 2018-06-11
|
||||||
|
|
||||||
|
@ -88,7 +100,7 @@
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
* Allow to use path without traling slashes for scope registration #241
|
* Allow to use path without trailing slashes for scope registration #241
|
||||||
|
|
||||||
* Allow to set encoding for exact NamedFile #239
|
* Allow to set encoding for exact NamedFile #239
|
||||||
|
|
||||||
|
@ -449,7 +461,7 @@
|
||||||
|
|
||||||
* Server multi-threading
|
* Server multi-threading
|
||||||
|
|
||||||
* Gracefull shutdown support
|
* Graceful shutdown support
|
||||||
|
|
||||||
|
|
||||||
## 0.2.1 (2017-11-03)
|
## 0.2.1 (2017-11-03)
|
||||||
|
|
|
@ -50,7 +50,8 @@ flate2-rust = ["flate2/rust_backend"]
|
||||||
features = ["tls", "alpn", "session", "brotli", "flate2-c"]
|
features = ["tls", "alpn", "session", "brotli", "flate2-c"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix = "0.6.1"
|
# actix = "0.6.1"
|
||||||
|
actix = { git="https://github.com/actix/actix.git" }
|
||||||
|
|
||||||
base64 = "0.9"
|
base64 = "0.9"
|
||||||
bitflags = "1.0"
|
bitflags = "1.0"
|
||||||
|
|
32
MIGRATION.md
32
MIGRATION.md
|
@ -23,6 +23,38 @@
|
||||||
* Renamed `client::ClientConnectorError::Connector` to
|
* Renamed `client::ClientConnectorError::Connector` to
|
||||||
`client::ClientConnectorError::Resolver`
|
`client::ClientConnectorError::Resolver`
|
||||||
|
|
||||||
|
* `Route::with()` does not return `ExtractorConfig`, to configure
|
||||||
|
extractor use `Route::with_config()`
|
||||||
|
|
||||||
|
instead of
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let app = App::new().resource("/index.html", |r| {
|
||||||
|
r.method(http::Method::GET)
|
||||||
|
.with(index)
|
||||||
|
.limit(4096); // <- limit size of the payload
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
use
|
||||||
|
|
||||||
|
```rust
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let app = App::new().resource("/index.html", |r| {
|
||||||
|
r.method(http::Method::GET)
|
||||||
|
.with_config(index, |cfg| { // <- register handler
|
||||||
|
cfg.limit(4096); // <- limit size of the payload
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* `Route::with_async()` does not return `ExtractorConfig`, to configure
|
||||||
|
extractor use `Route::with_async_config()`
|
||||||
|
|
||||||
|
|
||||||
## 0.6
|
## 0.6
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ pub struct HttpApplication<S = ()> {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub struct Inner<S> {
|
pub struct Inner<S> {
|
||||||
prefix: usize,
|
prefix: usize,
|
||||||
default: ResourceHandler<S>,
|
default: Rc<RefCell<ResourceHandler<S>>>,
|
||||||
encoding: ContentEncoding,
|
encoding: ContentEncoding,
|
||||||
resources: Vec<ResourceHandler<S>>,
|
resources: Vec<ResourceHandler<S>>,
|
||||||
handlers: Vec<PrefixHandlerType<S>>,
|
handlers: Vec<PrefixHandlerType<S>>,
|
||||||
|
@ -51,7 +51,7 @@ impl<S: 'static> PipelineHandler<S> for Inner<S> {
|
||||||
match htype {
|
match htype {
|
||||||
HandlerType::Normal(idx) => match self.resources[idx].handle(req) {
|
HandlerType::Normal(idx) => match self.resources[idx].handle(req) {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(req) => match self.default.handle(req) {
|
Err(req) => match self.default.borrow_mut().handle(req) {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(_) => AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND)),
|
Err(_) => AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND)),
|
||||||
},
|
},
|
||||||
|
@ -60,7 +60,7 @@ impl<S: 'static> PipelineHandler<S> for Inner<S> {
|
||||||
PrefixHandlerType::Handler(_, ref mut hnd) => hnd.handle(req),
|
PrefixHandlerType::Handler(_, ref mut hnd) => hnd.handle(req),
|
||||||
PrefixHandlerType::Scope(_, ref mut hnd, _) => hnd.handle(req),
|
PrefixHandlerType::Scope(_, ref mut hnd, _) => hnd.handle(req),
|
||||||
},
|
},
|
||||||
HandlerType::Default => match self.default.handle(req) {
|
HandlerType::Default => match self.default.borrow_mut().handle(req) {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(_) => AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND)),
|
Err(_) => AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND)),
|
||||||
},
|
},
|
||||||
|
@ -172,7 +172,7 @@ struct ApplicationParts<S> {
|
||||||
state: S,
|
state: S,
|
||||||
prefix: String,
|
prefix: String,
|
||||||
settings: ServerSettings,
|
settings: ServerSettings,
|
||||||
default: ResourceHandler<S>,
|
default: Rc<RefCell<ResourceHandler<S>>>,
|
||||||
resources: Vec<(Resource, Option<ResourceHandler<S>>)>,
|
resources: Vec<(Resource, Option<ResourceHandler<S>>)>,
|
||||||
handlers: Vec<PrefixHandlerType<S>>,
|
handlers: Vec<PrefixHandlerType<S>>,
|
||||||
external: HashMap<String, Resource>,
|
external: HashMap<String, Resource>,
|
||||||
|
@ -223,7 +223,7 @@ where
|
||||||
state,
|
state,
|
||||||
prefix: "/".to_owned(),
|
prefix: "/".to_owned(),
|
||||||
settings: ServerSettings::default(),
|
settings: ServerSettings::default(),
|
||||||
default: ResourceHandler::default_not_found(),
|
default: Rc::new(RefCell::new(ResourceHandler::default_not_found())),
|
||||||
resources: Vec::new(),
|
resources: Vec::new(),
|
||||||
handlers: Vec::new(),
|
handlers: Vec::new(),
|
||||||
external: HashMap::new(),
|
external: HashMap::new(),
|
||||||
|
@ -335,33 +335,34 @@ where
|
||||||
T: FromRequest<S> + 'static,
|
T: FromRequest<S> + 'static,
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
let parts = self.parts.as_mut().expect("Use after finish");
|
let parts: &mut ApplicationParts<S> = self.parts.as_mut().expect("Use after finish");
|
||||||
|
|
||||||
// get resource handler
|
let out = {
|
||||||
let mut found = false;
|
// get resource handler
|
||||||
for &mut (ref pattern, ref handler) in &mut parts.resources {
|
let mut iterator = parts.resources.iter_mut();
|
||||||
if handler.is_some() && pattern.pattern() == path {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
loop {
|
||||||
let mut handler = ResourceHandler::default();
|
if let Some(&mut (ref pattern, ref mut handler)) = iterator.next() {
|
||||||
handler.method(method).with(f);
|
if let Some(ref mut handler) = *handler {
|
||||||
let pattern = Resource::new(handler.get_name(), path);
|
if pattern.pattern() == path {
|
||||||
parts.resources.push((pattern, Some(handler)));
|
handler.method(method).with(f);
|
||||||
} else {
|
break None;
|
||||||
for &mut (ref pattern, ref mut handler) in &mut parts.resources {
|
}
|
||||||
if let Some(ref mut handler) = *handler {
|
|
||||||
if pattern.pattern() == path {
|
|
||||||
handler.method(method).with(f);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
let mut handler = ResourceHandler::default();
|
||||||
|
handler.method(method).with(f);
|
||||||
|
let pattern = Resource::new(handler.get_name(), path);
|
||||||
|
break Some((pattern, Some(handler)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(out) = out {
|
||||||
|
parts.resources.push(out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -473,7 +474,7 @@ where
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
let parts = self.parts.as_mut().expect("Use after finish");
|
let parts = self.parts.as_mut().expect("Use after finish");
|
||||||
f(&mut parts.default);
|
f(&mut parts.default.borrow_mut());
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -614,7 +615,7 @@ where
|
||||||
|
|
||||||
/// Finish application configuration and create `HttpHandler` object.
|
/// Finish application configuration and create `HttpHandler` object.
|
||||||
pub fn finish(&mut self) -> HttpApplication<S> {
|
pub fn finish(&mut self) -> HttpApplication<S> {
|
||||||
let parts = self.parts.take().expect("Use after finish");
|
let mut parts = self.parts.take().expect("Use after finish");
|
||||||
let prefix = parts.prefix.trim().trim_right_matches('/');
|
let prefix = parts.prefix.trim().trim_right_matches('/');
|
||||||
let (prefix, prefix_len) = if prefix.is_empty() {
|
let (prefix, prefix_len) = if prefix.is_empty() {
|
||||||
("/".to_owned(), 0)
|
("/".to_owned(), 0)
|
||||||
|
@ -627,11 +628,19 @@ where
|
||||||
resources.push((pattern, None));
|
resources.push((pattern, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for ref mut handler in parts.handlers.iter_mut() {
|
||||||
|
if let PrefixHandlerType::Scope(_, ref mut route_handler, _) = handler {
|
||||||
|
if !route_handler.has_default_resource() {
|
||||||
|
route_handler.default_resource(Rc::clone(&parts.default));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let (router, resources) = Router::new(&prefix, parts.settings, resources);
|
let (router, resources) = Router::new(&prefix, parts.settings, resources);
|
||||||
|
|
||||||
let inner = Rc::new(RefCell::new(Inner {
|
let inner = Rc::new(RefCell::new(Inner {
|
||||||
prefix: prefix_len,
|
prefix: prefix_len,
|
||||||
default: parts.default,
|
default: Rc::clone(&parts.default),
|
||||||
encoding: parts.encoding,
|
encoding: parts.encoding,
|
||||||
handlers: parts.handlers,
|
handlers: parts.handlers,
|
||||||
resources,
|
resources,
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::{fmt, io, mem, time};
|
||||||
use actix::resolver::{Connect as ResolveConnect, Resolver, ResolverError};
|
use actix::resolver::{Connect as ResolveConnect, Resolver, ResolverError};
|
||||||
use actix::{
|
use actix::{
|
||||||
fut, Actor, ActorContext, ActorFuture, ActorResponse, Addr, AsyncContext, Context,
|
fut, Actor, ActorContext, ActorFuture, ActorResponse, Addr, AsyncContext, Context,
|
||||||
ContextFutureSpawner, Handler, Message, Recipient, StreamHandler, Supervised,
|
ContextFutureSpawner, Handler, Message, Recipient, StreamHandler2, Supervised,
|
||||||
SystemService, WrapFuture,
|
SystemService, WrapFuture,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -220,7 +220,7 @@ impl Actor for ClientConnector {
|
||||||
self.resolver = Some(Resolver::from_registry())
|
self.resolver = Some(Resolver::from_registry())
|
||||||
}
|
}
|
||||||
self.collect_periodic(ctx);
|
self.collect_periodic(ctx);
|
||||||
ctx.add_stream(self.acq_rx.take().unwrap());
|
ctx.add_stream2(self.acq_rx.take().unwrap());
|
||||||
ctx.spawn(Maintenance);
|
ctx.spawn(Maintenance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -767,7 +767,7 @@ impl Handler<Connect> for ClientConnector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StreamHandler<AcquiredConnOperation, ()> for ClientConnector {
|
impl StreamHandler2<AcquiredConnOperation, ()> for ClientConnector {
|
||||||
fn handle(
|
fn handle(
|
||||||
&mut self, msg: Result<Option<AcquiredConnOperation>, ()>,
|
&mut self, msg: Result<Option<AcquiredConnOperation>, ()>,
|
||||||
ctx: &mut Context<Self>,
|
ctx: &mut Context<Self>,
|
||||||
|
|
|
@ -392,7 +392,7 @@ impl Pipeline {
|
||||||
match self.timeout.as_mut().unwrap().poll() {
|
match self.timeout.as_mut().unwrap().poll() {
|
||||||
Ok(Async::Ready(())) => return Err(SendRequestError::Timeout),
|
Ok(Async::Ready(())) => return Err(SendRequestError::Timeout),
|
||||||
Ok(Async::NotReady) => (),
|
Ok(Async::NotReady) => (),
|
||||||
Err(_) => return Err(SendRequestError::Timeout),
|
Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
12
src/error.rs
12
src/error.rs
|
@ -24,7 +24,7 @@ pub use cookie::ParseError as CookieParseError;
|
||||||
|
|
||||||
use handler::Responder;
|
use handler::Responder;
|
||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
use httpresponse::{HttpResponse, InnerHttpResponse};
|
use httpresponse::{HttpResponse, HttpResponseParts};
|
||||||
|
|
||||||
/// A specialized [`Result`](https://doc.rust-lang.org/std/result/enum.Result.html)
|
/// A specialized [`Result`](https://doc.rust-lang.org/std/result/enum.Result.html)
|
||||||
/// for actix web operations
|
/// for actix web operations
|
||||||
|
@ -654,7 +654,7 @@ pub struct InternalError<T> {
|
||||||
|
|
||||||
enum InternalErrorType {
|
enum InternalErrorType {
|
||||||
Status(StatusCode),
|
Status(StatusCode),
|
||||||
Response(Mutex<Option<Box<InnerHttpResponse>>>),
|
Response(Box<Mutex<Option<HttpResponseParts>>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> InternalError<T> {
|
impl<T> InternalError<T> {
|
||||||
|
@ -669,12 +669,10 @@ impl<T> InternalError<T> {
|
||||||
|
|
||||||
/// Create `InternalError` with predefined `HttpResponse`.
|
/// Create `InternalError` with predefined `HttpResponse`.
|
||||||
pub fn from_response(cause: T, response: HttpResponse) -> Self {
|
pub fn from_response(cause: T, response: HttpResponse) -> Self {
|
||||||
let mut resp = response.into_inner();
|
let resp = response.into_parts();
|
||||||
resp.drop_unsupported_body();
|
|
||||||
|
|
||||||
InternalError {
|
InternalError {
|
||||||
cause,
|
cause,
|
||||||
status: InternalErrorType::Response(Mutex::new(Some(resp))),
|
status: InternalErrorType::Response(Box::new(Mutex::new(Some(resp)))),
|
||||||
backtrace: Backtrace::new(),
|
backtrace: Backtrace::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -716,7 +714,7 @@ where
|
||||||
InternalErrorType::Status(st) => HttpResponse::new(st),
|
InternalErrorType::Status(st) => HttpResponse::new(st),
|
||||||
InternalErrorType::Response(ref resp) => {
|
InternalErrorType::Response(ref resp) => {
|
||||||
if let Some(resp) = resp.lock().unwrap().take() {
|
if let Some(resp) = resp.lock().unwrap().take() {
|
||||||
HttpResponse::from_inner(resp)
|
HttpResponse::from_parts(resp)
|
||||||
} else {
|
} else {
|
||||||
HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR)
|
HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::{fmt, str};
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::{fmt, str};
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use encoding::all::UTF_8;
|
use encoding::all::UTF_8;
|
||||||
|
@ -312,8 +312,10 @@ impl<T: fmt::Display> fmt::Display for Form<T> {
|
||||||
/// let app = App::new().resource(
|
/// let app = App::new().resource(
|
||||||
/// "/index.html",
|
/// "/index.html",
|
||||||
/// |r| {
|
/// |r| {
|
||||||
/// r.method(http::Method::GET).with(index).limit(4096);
|
/// r.method(http::Method::GET)
|
||||||
/// }, // <- change form extractor configuration
|
/// // register form handler and change form extractor configuration
|
||||||
|
/// .with_config(index, |cfg| {cfg.limit(4096);})
|
||||||
|
/// },
|
||||||
/// );
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -408,8 +410,9 @@ impl<S: 'static> FromRequest<S> for Bytes {
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().resource("/index.html", |r| {
|
/// let app = App::new().resource("/index.html", |r| {
|
||||||
/// r.method(http::Method::GET)
|
/// r.method(http::Method::GET)
|
||||||
/// .with(index) // <- register handler with extractor params
|
/// .with_config(index, |cfg| { // <- register handler with extractor params
|
||||||
/// .limit(4096); // <- limit size of the payload
|
/// cfg.limit(4096); // <- limit size of the payload
|
||||||
|
/// })
|
||||||
/// });
|
/// });
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use futures::future::{err, ok, Future};
|
use futures::future::{err, ok, Future};
|
||||||
use futures::{Async, Poll};
|
use futures::{Async, Poll};
|
||||||
|
@ -8,6 +10,7 @@ use error::Error;
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
use httpresponse::HttpResponse;
|
use httpresponse::HttpResponse;
|
||||||
|
use resource::ResourceHandler;
|
||||||
|
|
||||||
/// Trait defines object that could be registered as route handler
|
/// Trait defines object that could be registered as route handler
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
|
@ -403,6 +406,14 @@ where
|
||||||
// /// Trait defines object that could be registered as resource route
|
// /// Trait defines object that could be registered as resource route
|
||||||
pub(crate) trait RouteHandler<S>: 'static {
|
pub(crate) trait RouteHandler<S>: 'static {
|
||||||
fn handle(&mut self, req: HttpRequest<S>) -> AsyncResult<HttpResponse>;
|
fn handle(&mut self, req: HttpRequest<S>) -> AsyncResult<HttpResponse>;
|
||||||
|
|
||||||
|
fn has_default_resource(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_resource(&mut self, default: Rc<RefCell<ResourceHandler<S>>>) {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Route handler wrapper for Handler
|
/// Route handler wrapper for Handler
|
||||||
|
|
|
@ -299,12 +299,15 @@ impl HttpResponse {
|
||||||
self.get_mut().write_capacity = cap;
|
self.get_mut().write_capacity = cap;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn into_inner(mut self) -> Box<InnerHttpResponse> {
|
pub(crate) fn into_parts(mut self) -> HttpResponseParts {
|
||||||
self.0.take().unwrap()
|
self.0.take().unwrap().into_parts()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_inner(inner: Box<InnerHttpResponse>) -> HttpResponse {
|
pub(crate) fn from_parts(parts: HttpResponseParts) -> HttpResponse {
|
||||||
HttpResponse(Some(inner), HttpResponsePool::pool())
|
HttpResponse(
|
||||||
|
Some(Box::new(InnerHttpResponse::from_parts(parts))),
|
||||||
|
HttpResponsePool::pool(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -880,12 +883,12 @@ impl<'a, S> From<&'a HttpRequest<S>> for HttpResponseBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct InnerHttpResponse {
|
struct InnerHttpResponse {
|
||||||
version: Option<Version>,
|
version: Option<Version>,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
status: StatusCode,
|
status: StatusCode,
|
||||||
reason: Option<&'static str>,
|
reason: Option<&'static str>,
|
||||||
pub(crate) body: Body,
|
body: Body,
|
||||||
chunked: Option<bool>,
|
chunked: Option<bool>,
|
||||||
encoding: Option<ContentEncoding>,
|
encoding: Option<ContentEncoding>,
|
||||||
connection_type: Option<ConnectionType>,
|
connection_type: Option<ConnectionType>,
|
||||||
|
@ -894,10 +897,16 @@ pub(crate) struct InnerHttpResponse {
|
||||||
error: Option<Error>,
|
error: Option<Error>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is here only because `failure::Fail: Send + Sync` which looks insane to me
|
pub(crate) struct HttpResponseParts {
|
||||||
unsafe impl Sync for InnerHttpResponse {}
|
version: Option<Version>,
|
||||||
/// This is here only because `failure::Fail: Send + Sync` which looks insane to me
|
headers: HeaderMap,
|
||||||
unsafe impl Send for InnerHttpResponse {}
|
status: StatusCode,
|
||||||
|
reason: Option<&'static str>,
|
||||||
|
body: Option<Bytes>,
|
||||||
|
encoding: Option<ContentEncoding>,
|
||||||
|
connection_type: Option<ConnectionType>,
|
||||||
|
error: Option<Error>,
|
||||||
|
}
|
||||||
|
|
||||||
impl InnerHttpResponse {
|
impl InnerHttpResponse {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -918,21 +927,52 @@ impl InnerHttpResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is for failure, we can not have Send + Sync on Streaming and Actor response
|
/// This is for failure, we can not have Send + Sync on Streaming and Actor response
|
||||||
pub(crate) fn drop_unsupported_body(&mut self) {
|
fn into_parts(mut self) -> HttpResponseParts {
|
||||||
let body = mem::replace(&mut self.body, Body::Empty);
|
let body = match mem::replace(&mut self.body, Body::Empty) {
|
||||||
match body {
|
Body::Empty => None,
|
||||||
Body::Empty => (),
|
Body::Binary(mut bin) => Some(bin.take()),
|
||||||
Body::Binary(mut bin) => {
|
|
||||||
self.body = Body::Binary(bin.take().into());
|
|
||||||
}
|
|
||||||
Body::Streaming(_) | Body::Actor(_) => {
|
Body::Streaming(_) | Body::Actor(_) => {
|
||||||
error!("Streaming or Actor body is not support by error response");
|
error!("Streaming or Actor body is not support by error response");
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
HttpResponseParts {
|
||||||
|
body,
|
||||||
|
version: self.version,
|
||||||
|
headers: self.headers,
|
||||||
|
status: self.status,
|
||||||
|
reason: self.reason,
|
||||||
|
encoding: self.encoding,
|
||||||
|
connection_type: self.connection_type,
|
||||||
|
error: self.error,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_parts(parts: HttpResponseParts) -> InnerHttpResponse {
|
||||||
|
let body = if let Some(ref body) = parts.body {
|
||||||
|
Body::Binary(body.clone().into())
|
||||||
|
} else {
|
||||||
|
Body::Empty
|
||||||
|
};
|
||||||
|
|
||||||
|
InnerHttpResponse {
|
||||||
|
body,
|
||||||
|
status: parts.status,
|
||||||
|
version: parts.version,
|
||||||
|
headers: parts.headers,
|
||||||
|
reason: parts.reason,
|
||||||
|
chunked: None,
|
||||||
|
encoding: parts.encoding,
|
||||||
|
connection_type: parts.connection_type,
|
||||||
|
response_size: 0,
|
||||||
|
write_capacity: MAX_WRITE_BUFFER_SIZE,
|
||||||
|
error: parts.error,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Internal use only! unsafe
|
/// Internal use only!
|
||||||
pub(crate) struct HttpResponsePool(VecDeque<Box<InnerHttpResponse>>);
|
pub(crate) struct HttpResponsePool(VecDeque<Box<InnerHttpResponse>>);
|
||||||
|
|
||||||
thread_local!(static POOL: Rc<UnsafeCell<HttpResponsePool>> = HttpResponsePool::pool());
|
thread_local!(static POOL: Rc<UnsafeCell<HttpResponsePool>> = HttpResponsePool::pool());
|
||||||
|
|
17
src/json.rs
17
src/json.rs
|
@ -171,12 +171,13 @@ where
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().resource("/index.html", |r| {
|
/// let app = App::new().resource("/index.html", |r| {
|
||||||
/// r.method(http::Method::POST)
|
/// r.method(http::Method::POST)
|
||||||
/// .with(index)
|
/// .with_config(index, |cfg| {
|
||||||
/// .limit(4096) // <- change json extractor configuration
|
/// cfg.limit(4096) // <- change json extractor configuration
|
||||||
/// .error_handler(|err, req| { // <- create custom error response
|
/// .error_handler(|err, req| { // <- create custom error response
|
||||||
/// error::InternalError::from_response(
|
/// error::InternalError::from_response(
|
||||||
/// err, HttpResponse::Conflict().finish()).into()
|
/// err, HttpResponse::Conflict().finish()).into()
|
||||||
/// });
|
/// });
|
||||||
|
/// })
|
||||||
/// });
|
/// });
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -326,7 +327,7 @@ mod tests {
|
||||||
use http::header;
|
use http::header;
|
||||||
|
|
||||||
use handler::Handler;
|
use handler::Handler;
|
||||||
use with::{ExtractorConfig, With};
|
use with::With;
|
||||||
|
|
||||||
impl PartialEq for JsonPayloadError {
|
impl PartialEq for JsonPayloadError {
|
||||||
fn eq(&self, other: &JsonPayloadError) -> bool {
|
fn eq(&self, other: &JsonPayloadError) -> bool {
|
||||||
|
@ -409,7 +410,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_with_json() {
|
fn test_with_json() {
|
||||||
let mut cfg = ExtractorConfig::<_, Json<MyObject>>::default();
|
let mut cfg = JsonConfig::default();
|
||||||
cfg.limit(4096);
|
cfg.limit(4096);
|
||||||
let mut handler = With::new(|data: Json<MyObject>| data, cfg);
|
let mut handler = With::new(|data: Json<MyObject>| data, cfg);
|
||||||
|
|
||||||
|
|
|
@ -246,7 +246,6 @@ 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,5 +1,5 @@
|
||||||
//! Multipart requests support
|
//! Multipart requests support
|
||||||
use std::cell::RefCell;
|
use std::cell::{RefCell, UnsafeCell};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::{cmp, fmt};
|
use std::{cmp, fmt};
|
||||||
|
@ -590,7 +590,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PayloadRef<S> {
|
struct PayloadRef<S> {
|
||||||
payload: Rc<PayloadHelper<S>>,
|
payload: Rc<UnsafeCell<PayloadHelper<S>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> PayloadRef<S>
|
impl<S> PayloadRef<S>
|
||||||
|
@ -599,7 +599,7 @@ where
|
||||||
{
|
{
|
||||||
fn new(payload: PayloadHelper<S>) -> PayloadRef<S> {
|
fn new(payload: PayloadHelper<S>) -> PayloadRef<S> {
|
||||||
PayloadRef {
|
PayloadRef {
|
||||||
payload: Rc::new(payload),
|
payload: Rc::new(payload.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -609,7 +609,7 @@ where
|
||||||
{
|
{
|
||||||
if s.current() {
|
if s.current() {
|
||||||
let payload: &mut PayloadHelper<S> =
|
let payload: &mut PayloadHelper<S> =
|
||||||
unsafe { &mut *(self.payload.as_ref() as *const _ as *mut _) };
|
unsafe { &mut *self.payload.get() };
|
||||||
Some(payload)
|
Some(payload)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
97
src/route.rs
97
src/route.rs
|
@ -17,7 +17,7 @@ use middleware::{
|
||||||
Started as MiddlewareStarted,
|
Started as MiddlewareStarted,
|
||||||
};
|
};
|
||||||
use pred::Predicate;
|
use pred::Predicate;
|
||||||
use with::{ExtractorConfig, With, WithAsync};
|
use with::{With, WithAsync};
|
||||||
|
|
||||||
/// Resource route definition
|
/// Resource route definition
|
||||||
///
|
///
|
||||||
|
@ -164,15 +164,49 @@ impl<S: 'static> Route<S> {
|
||||||
/// ); // <- use `with` extractor
|
/// ); // <- 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)
|
||||||
where
|
where
|
||||||
F: Fn(T) -> R + 'static,
|
F: Fn(T) -> R + 'static,
|
||||||
R: Responder + 'static,
|
R: Responder + 'static,
|
||||||
T: FromRequest<S> + 'static,
|
T: FromRequest<S> + 'static,
|
||||||
{
|
{
|
||||||
let cfg = ExtractorConfig::<S, T>::default();
|
self.h(With::new(handler, <T::Config as Default>::default()));
|
||||||
self.h(With::new(handler, cfg.clone()));
|
}
|
||||||
cfg
|
|
||||||
|
/// Set handler function. Same as `.with()` but it allows to configure
|
||||||
|
/// extractor.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # extern crate bytes;
|
||||||
|
/// # extern crate actix_web;
|
||||||
|
/// # extern crate futures;
|
||||||
|
/// #[macro_use] extern crate serde_derive;
|
||||||
|
/// use actix_web::{http, App, Path, Result};
|
||||||
|
///
|
||||||
|
/// /// extract text data from request
|
||||||
|
/// fn index(body: String) -> Result<String> {
|
||||||
|
/// Ok(format!("Body {}!", body))
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let app = App::new().resource("/index.html", |r| {
|
||||||
|
/// r.method(http::Method::GET)
|
||||||
|
/// .with_config(index, |cfg| { // <- register handler
|
||||||
|
/// cfg.limit(4096); // <- limit size of the payload
|
||||||
|
/// })
|
||||||
|
/// });
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn with_config<T, F, R, C>(&mut self, handler: F, cfg_f: C)
|
||||||
|
where
|
||||||
|
F: Fn(T) -> R + 'static,
|
||||||
|
R: Responder + 'static,
|
||||||
|
T: FromRequest<S> + 'static,
|
||||||
|
C: FnOnce(&mut T::Config),
|
||||||
|
{
|
||||||
|
let mut cfg = <T::Config as Default>::default();
|
||||||
|
cfg_f(&mut cfg);
|
||||||
|
self.h(With::new(handler, cfg));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set async handler function, use request extractor for parameters.
|
/// Set async handler function, use request extractor for parameters.
|
||||||
|
@ -204,7 +238,7 @@ impl<S: 'static> Route<S> {
|
||||||
/// ); // <- use `with` extractor
|
/// ); // <- use `with` extractor
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn with_async<T, F, R, I, E>(&mut self, handler: F) -> ExtractorConfig<S, T>
|
pub fn with_async<T, F, R, I, E>(&mut self, handler: F)
|
||||||
where
|
where
|
||||||
F: Fn(T) -> R + 'static,
|
F: Fn(T) -> R + 'static,
|
||||||
R: Future<Item = I, Error = E> + 'static,
|
R: Future<Item = I, Error = E> + 'static,
|
||||||
|
@ -212,9 +246,52 @@ impl<S: 'static> Route<S> {
|
||||||
E: Into<Error> + 'static,
|
E: Into<Error> + 'static,
|
||||||
T: FromRequest<S> + 'static,
|
T: FromRequest<S> + 'static,
|
||||||
{
|
{
|
||||||
let cfg = ExtractorConfig::<S, T>::default();
|
self.h(WithAsync::new(handler, <T::Config as Default>::default()));
|
||||||
self.h(WithAsync::new(handler, cfg.clone()));
|
}
|
||||||
cfg
|
|
||||||
|
/// Set async handler function, use request extractor for parameters.
|
||||||
|
/// This method allows to configure extractor.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # extern crate bytes;
|
||||||
|
/// # extern crate actix_web;
|
||||||
|
/// # extern crate futures;
|
||||||
|
/// #[macro_use] extern crate serde_derive;
|
||||||
|
/// use actix_web::{http, App, Error, Form};
|
||||||
|
/// use futures::Future;
|
||||||
|
///
|
||||||
|
/// #[derive(Deserialize)]
|
||||||
|
/// struct Info {
|
||||||
|
/// username: String,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// /// extract path info using serde
|
||||||
|
/// fn index(info: Form<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_config(index, |cfg| {
|
||||||
|
/// cfg.limit(4096);
|
||||||
|
/// }),
|
||||||
|
/// ); // <- use `with` extractor
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn with_async_config<T, F, R, I, E, C>(&mut self, handler: F, cfg: C)
|
||||||
|
where
|
||||||
|
F: Fn(T) -> R + 'static,
|
||||||
|
R: Future<Item = I, Error = E> + 'static,
|
||||||
|
I: Responder + 'static,
|
||||||
|
E: Into<Error> + 'static,
|
||||||
|
T: FromRequest<S> + 'static,
|
||||||
|
C: FnOnce(&mut T::Config),
|
||||||
|
{
|
||||||
|
let mut extractor_cfg = <T::Config as Default>::default();
|
||||||
|
cfg(&mut extractor_cfg);
|
||||||
|
self.h(WithAsync::new(handler, extractor_cfg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,7 +318,7 @@ impl<S: 'static> InnerHandler<S> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn handle(&self, req: HttpRequest<S>) -> AsyncResult<HttpResponse> {
|
pub fn handle(&self, req: HttpRequest<S>) -> AsyncResult<HttpResponse> {
|
||||||
// reason: handler is unique per thread, handler get called from async code only
|
// reason: handler is unique per thread, handler get called from sync code only
|
||||||
let h = unsafe { &mut *self.0.as_ref().get() };
|
let h = unsafe { &mut *self.0.as_ref().get() };
|
||||||
h.handle(req)
|
h.handle(req)
|
||||||
}
|
}
|
||||||
|
|
|
@ -309,7 +309,7 @@ impl Resource {
|
||||||
params.set_tail(len as u16);
|
params.set_tail(len as u16);
|
||||||
for (idx, segment) in segments.iter().enumerate() {
|
for (idx, segment) in segments.iter().enumerate() {
|
||||||
// reason: Router is part of App, which is unique per thread
|
// reason: Router is part of App, which is unique per thread
|
||||||
// app is alive during whole life of tthread
|
// app is alive during whole life of a thread
|
||||||
let name = unsafe { &*(names[idx].as_str() as *const _) };
|
let name = unsafe { &*(names[idx].as_str() as *const _) };
|
||||||
params.add(name, *segment);
|
params.add(name, *segment);
|
||||||
}
|
}
|
||||||
|
@ -378,7 +378,7 @@ impl Resource {
|
||||||
params.set_tail(tail_len as u16);
|
params.set_tail(tail_len as u16);
|
||||||
for (idx, segment) in segments.iter().enumerate() {
|
for (idx, segment) in segments.iter().enumerate() {
|
||||||
// reason: Router is part of App, which is unique per thread
|
// reason: Router is part of App, which is unique per thread
|
||||||
// app is alive during whole life of tthread
|
// app is alive during whole life of a thread
|
||||||
let name = unsafe { &*(names[idx].as_str() as *const _) };
|
let name = unsafe { &*(names[idx].as_str() as *const _) };
|
||||||
params.add(name, *segment);
|
params.add(name, *segment);
|
||||||
}
|
}
|
||||||
|
|
31
src/scope.rs
31
src/scope.rs
|
@ -405,6 +405,14 @@ impl<S: 'static> RouteHandler<S> for Scope<S> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn has_default_resource(&self) -> bool {
|
||||||
|
self.default.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_resource(&mut self, default: ScopeResource<S>) {
|
||||||
|
self.default = Some(default);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Wrapper<S: 'static> {
|
struct Wrapper<S: 'static> {
|
||||||
|
@ -1188,4 +1196,27 @@ mod tests {
|
||||||
let resp = app.run(req);
|
let resp = app.run(req);
|
||||||
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_default_resource_propagation() {
|
||||||
|
let mut app = App::new()
|
||||||
|
.scope("/app1", |scope| {
|
||||||
|
scope.default_resource(|r| r.f(|_| HttpResponse::BadRequest()))
|
||||||
|
})
|
||||||
|
.scope("/app2", |scope| scope)
|
||||||
|
.default_resource(|r| r.f(|_| HttpResponse::MethodNotAllowed()))
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
let req = TestRequest::with_uri("/non-exist").finish();
|
||||||
|
let resp = app.run(req);
|
||||||
|
assert_eq!(resp.as_msg().status(), StatusCode::METHOD_NOT_ALLOWED);
|
||||||
|
|
||||||
|
let req = TestRequest::with_uri("/app1/non-exist").finish();
|
||||||
|
let resp = app.run(req);
|
||||||
|
assert_eq!(resp.as_msg().status(), StatusCode::BAD_REQUEST);
|
||||||
|
|
||||||
|
let req = TestRequest::with_uri("/app2/non-exist").finish();
|
||||||
|
let resp = app.run(req);
|
||||||
|
assert_eq!(resp.as_msg().status(), StatusCode::METHOD_NOT_ALLOWED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,12 +73,11 @@ impl<T: AsyncWrite, H: 'static> H1Writer<T, H> {
|
||||||
self.flags.contains(Flags::KEEPALIVE) && !self.flags.contains(Flags::UPGRADE)
|
self.flags.contains(Flags::KEEPALIVE) && !self.flags.contains(Flags::UPGRADE)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_data(&mut self, data: &[u8]) -> io::Result<usize> {
|
fn write_data(stream: &mut T, data: &[u8]) -> io::Result<usize> {
|
||||||
let mut written = 0;
|
let mut written = 0;
|
||||||
while written < data.len() {
|
while written < data.len() {
|
||||||
match self.stream.write(&data[written..]) {
|
match stream.write(&data[written..]) {
|
||||||
Ok(0) => {
|
Ok(0) => {
|
||||||
self.disconnected();
|
|
||||||
return Err(io::Error::new(io::ErrorKind::WriteZero, ""));
|
return Err(io::Error::new(io::ErrorKind::WriteZero, ""));
|
||||||
}
|
}
|
||||||
Ok(n) => {
|
Ok(n) => {
|
||||||
|
@ -243,7 +242,16 @@ impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
|
||||||
if self.flags.contains(Flags::UPGRADE) {
|
if self.flags.contains(Flags::UPGRADE) {
|
||||||
if self.buffer.is_empty() {
|
if self.buffer.is_empty() {
|
||||||
let pl: &[u8] = payload.as_ref();
|
let pl: &[u8] = payload.as_ref();
|
||||||
let n = self.write_data(pl)?;
|
let n = match Self::write_data(&mut self.stream, pl) {
|
||||||
|
Err(err) => {
|
||||||
|
if err.kind() == io::ErrorKind::WriteZero {
|
||||||
|
self.disconnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
Ok(val) => val,
|
||||||
|
};
|
||||||
if n < pl.len() {
|
if n < pl.len() {
|
||||||
self.buffer.extend_from_slice(&pl[n..]);
|
self.buffer.extend_from_slice(&pl[n..]);
|
||||||
return Ok(WriterState::Done);
|
return Ok(WriterState::Done);
|
||||||
|
@ -284,9 +292,18 @@ impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn poll_completed(&mut self, shutdown: bool) -> Poll<(), io::Error> {
|
fn poll_completed(&mut self, shutdown: bool) -> Poll<(), io::Error> {
|
||||||
if !self.buffer.is_empty() {
|
if !self.buffer.is_empty() {
|
||||||
let buf: &[u8] =
|
let written = {
|
||||||
unsafe { &mut *(self.buffer.as_ref() as *const _ as *mut _) };
|
match Self::write_data(&mut self.stream, self.buffer.as_ref()) {
|
||||||
let written = self.write_data(buf)?;
|
Err(err) => {
|
||||||
|
if err.kind() == io::ErrorKind::WriteZero {
|
||||||
|
self.disconnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
Ok(val) => val,
|
||||||
|
}
|
||||||
|
};
|
||||||
let _ = self.buffer.split_to(written);
|
let _ = self.buffer.split_to(written);
|
||||||
if shutdown && !self.buffer.is_empty()
|
if shutdown && !self.buffer.is_empty()
|
||||||
|| (self.buffer.len() > self.buffer_capacity)
|
|| (self.buffer.len() > self.buffer_capacity)
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::{io, net, thread};
|
||||||
|
|
||||||
use actix::{
|
use actix::{
|
||||||
fut, signal, Actor, ActorFuture, Addr, Arbiter, AsyncContext, Context, Handler,
|
fut, signal, Actor, ActorFuture, Addr, Arbiter, AsyncContext, Context, Handler,
|
||||||
Response, StreamHandler, System, WrapFuture,
|
Response, StreamHandler2, System, WrapFuture,
|
||||||
};
|
};
|
||||||
|
|
||||||
use futures::sync::mpsc;
|
use futures::sync::mpsc;
|
||||||
|
@ -449,7 +449,7 @@ impl<H: IntoHttpHandler> HttpServer<H> {
|
||||||
// start http server actor
|
// start http server actor
|
||||||
let signals = self.subscribe_to_signals();
|
let signals = self.subscribe_to_signals();
|
||||||
let addr = Actor::create(move |ctx| {
|
let addr = Actor::create(move |ctx| {
|
||||||
ctx.add_stream(rx);
|
ctx.add_stream2(rx);
|
||||||
self
|
self
|
||||||
});
|
});
|
||||||
if let Some(signals) = signals {
|
if let Some(signals) = signals {
|
||||||
|
@ -611,7 +611,7 @@ impl<H: IntoHttpHandler> Handler<signal::Signal> for HttpServer<H> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Commands from accept threads
|
/// Commands from accept threads
|
||||||
impl<H: IntoHttpHandler> StreamHandler<ServerCommand, ()> for HttpServer<H> {
|
impl<H: IntoHttpHandler> StreamHandler2<ServerCommand, ()> for HttpServer<H> {
|
||||||
fn handle(&mut self, msg: Result<Option<ServerCommand>, ()>, _: &mut Context<Self>) {
|
fn handle(&mut self, msg: Result<Option<ServerCommand>, ()>, _: &mut Context<Self>) {
|
||||||
if let Ok(Some(ServerCommand::WorkerDied(idx, socks))) = msg {
|
if let Ok(Some(ServerCommand::WorkerDied(idx, socks))) = msg {
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
|
|
193
src/with.rs
193
src/with.rs
|
@ -1,7 +1,6 @@
|
||||||
use futures::{Async, Future, Poll};
|
use futures::{Async, Future, Poll};
|
||||||
use std::cell::UnsafeCell;
|
use std::cell::UnsafeCell;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use error::Error;
|
use error::Error;
|
||||||
|
@ -9,110 +8,24 @@ use handler::{AsyncResult, AsyncResultItem, FromRequest, Handler, Responder};
|
||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
use httpresponse::HttpResponse;
|
use httpresponse::HttpResponse;
|
||||||
|
|
||||||
/// Extractor configuration
|
pub(crate) struct With<T, S, F, R>
|
||||||
///
|
where
|
||||||
/// `Route::with()` and `Route::with_async()` returns instance
|
F: Fn(T) -> R,
|
||||||
/// of the `ExtractorConfig` type. It could be used for extractor configuration.
|
T: FromRequest<S>,
|
||||||
///
|
S: 'static,
|
||||||
/// In this example `Form<FormData>` configured.
|
{
|
||||||
///
|
hnd: Rc<WithHnd<T, S, F, R>>,
|
||||||
/// ```rust
|
cfg: Rc<T::Config>,
|
||||||
/// # extern crate actix_web;
|
|
||||||
/// #[macro_use] extern crate serde_derive;
|
|
||||||
/// use actix_web::{http, App, Form, Result};
|
|
||||||
///
|
|
||||||
/// #[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::{http, App, Form, Path, Result};
|
|
||||||
///
|
|
||||||
/// #[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>> {
|
|
||||||
cfg: Rc<UnsafeCell<T::Config>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: 'static, T: FromRequest<S>> Default for ExtractorConfig<S, T> {
|
pub struct WithHnd<T, S, F, R>
|
||||||
fn default() -> Self {
|
|
||||||
ExtractorConfig {
|
|
||||||
cfg: Rc::new(UnsafeCell::new(T::Config::default())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: 'static, T: FromRequest<S>> ExtractorConfig<S, T> {
|
|
||||||
pub(crate) fn clone(&self) -> Self {
|
|
||||||
ExtractorConfig {
|
|
||||||
cfg: Rc::clone(&self.cfg),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: 'static, T: FromRequest<S>> AsRef<T::Config> for ExtractorConfig<S, T> {
|
|
||||||
fn as_ref(&self) -> &T::Config {
|
|
||||||
unsafe { &*self.cfg.get() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: 'static, T: FromRequest<S>> Deref for ExtractorConfig<S, T> {
|
|
||||||
type Target = T::Config;
|
|
||||||
|
|
||||||
fn deref(&self) -> &T::Config {
|
|
||||||
unsafe { &*self.cfg.get() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: 'static, T: FromRequest<S>> DerefMut for ExtractorConfig<S, T> {
|
|
||||||
fn deref_mut(&mut self) -> &mut T::Config {
|
|
||||||
unsafe { &mut *self.cfg.get() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct With<T, S, F, R>
|
|
||||||
where
|
where
|
||||||
F: Fn(T) -> R,
|
F: Fn(T) -> R,
|
||||||
T: FromRequest<S>,
|
T: FromRequest<S>,
|
||||||
S: 'static,
|
S: 'static,
|
||||||
{
|
{
|
||||||
hnd: Rc<UnsafeCell<F>>,
|
hnd: Rc<UnsafeCell<F>>,
|
||||||
cfg: ExtractorConfig<S, T>,
|
_t: PhantomData<T>,
|
||||||
_s: PhantomData<S>,
|
_s: PhantomData<S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,11 +35,14 @@ where
|
||||||
T: FromRequest<S>,
|
T: FromRequest<S>,
|
||||||
S: 'static,
|
S: 'static,
|
||||||
{
|
{
|
||||||
pub fn new(f: F, cfg: ExtractorConfig<S, T>) -> Self {
|
pub fn new(f: F, cfg: T::Config) -> Self {
|
||||||
With {
|
With {
|
||||||
cfg,
|
cfg: Rc::new(cfg),
|
||||||
hnd: Rc::new(UnsafeCell::new(f)),
|
hnd: Rc::new(WithHnd {
|
||||||
_s: PhantomData,
|
hnd: Rc::new(UnsafeCell::new(f)),
|
||||||
|
_t: PhantomData,
|
||||||
|
_s: PhantomData,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,8 +82,8 @@ where
|
||||||
S: 'static,
|
S: 'static,
|
||||||
{
|
{
|
||||||
started: bool,
|
started: bool,
|
||||||
hnd: Rc<UnsafeCell<F>>,
|
hnd: Rc<WithHnd<T, S, F, R>>,
|
||||||
cfg: ExtractorConfig<S, T>,
|
cfg: Rc<T::Config>,
|
||||||
req: HttpRequest<S>,
|
req: HttpRequest<S>,
|
||||||
fut1: Option<Box<Future<Item = T, Error = Error>>>,
|
fut1: Option<Box<Future<Item = T, Error = Error>>>,
|
||||||
fut2: Option<Box<Future<Item = HttpResponse, Error = Error>>>,
|
fut2: Option<Box<Future<Item = HttpResponse, Error = Error>>>,
|
||||||
|
@ -206,24 +122,32 @@ where
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let hnd: &mut F = unsafe { &mut *self.hnd.get() };
|
let fut = {
|
||||||
let item = match (*hnd)(item).respond_to(&self.req) {
|
// clone handler, inicrease ref counter
|
||||||
Ok(item) => item.into(),
|
let h = self.hnd.as_ref().hnd.clone();
|
||||||
Err(e) => return Err(e.into()),
|
// Enforce invariants before entering unsafe code.
|
||||||
};
|
// Only two references could exists With struct owns one, and line above
|
||||||
|
if Rc::weak_count(&h) != 0 || Rc::strong_count(&h) != 2 {
|
||||||
match item.into() {
|
panic!("Multiple copies of handler are in use")
|
||||||
AsyncResultItem::Err(err) => Err(err),
|
|
||||||
AsyncResultItem::Ok(resp) => Ok(Async::Ready(resp)),
|
|
||||||
AsyncResultItem::Future(fut) => {
|
|
||||||
self.fut2 = Some(fut);
|
|
||||||
self.poll()
|
|
||||||
}
|
}
|
||||||
}
|
let hnd: &mut F = unsafe { &mut *h.as_ref().get() };
|
||||||
|
let item = match (*hnd)(item).respond_to(&self.req) {
|
||||||
|
Ok(item) => item.into(),
|
||||||
|
Err(e) => return Err(e.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
match item.into() {
|
||||||
|
AsyncResultItem::Err(err) => return Err(err),
|
||||||
|
AsyncResultItem::Ok(resp) => return Ok(Async::Ready(resp)),
|
||||||
|
AsyncResultItem::Future(fut) => fut,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.fut2 = Some(fut);
|
||||||
|
self.poll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WithAsync<T, S, F, R, I, E>
|
pub(crate) struct WithAsync<T, S, F, R, I, E>
|
||||||
where
|
where
|
||||||
F: Fn(T) -> R,
|
F: Fn(T) -> R,
|
||||||
R: Future<Item = I, Error = E>,
|
R: Future<Item = I, Error = E>,
|
||||||
|
@ -232,9 +156,8 @@ where
|
||||||
T: FromRequest<S>,
|
T: FromRequest<S>,
|
||||||
S: 'static,
|
S: 'static,
|
||||||
{
|
{
|
||||||
hnd: Rc<UnsafeCell<F>>,
|
hnd: Rc<WithHnd<T, S, F, R>>,
|
||||||
cfg: ExtractorConfig<S, T>,
|
cfg: Rc<T::Config>,
|
||||||
_s: PhantomData<S>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, S, F, R, I, E> WithAsync<T, S, F, R, I, E>
|
impl<T, S, F, R, I, E> WithAsync<T, S, F, R, I, E>
|
||||||
|
@ -246,11 +169,14 @@ where
|
||||||
T: FromRequest<S>,
|
T: FromRequest<S>,
|
||||||
S: 'static,
|
S: 'static,
|
||||||
{
|
{
|
||||||
pub fn new(f: F, cfg: ExtractorConfig<S, T>) -> Self {
|
pub fn new(f: F, cfg: T::Config) -> Self {
|
||||||
WithAsync {
|
WithAsync {
|
||||||
cfg,
|
cfg: Rc::new(cfg),
|
||||||
hnd: Rc::new(UnsafeCell::new(f)),
|
hnd: Rc::new(WithHnd {
|
||||||
_s: PhantomData,
|
hnd: Rc::new(UnsafeCell::new(f)),
|
||||||
|
_s: PhantomData,
|
||||||
|
_t: PhantomData,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,7 +197,7 @@ where
|
||||||
req,
|
req,
|
||||||
started: false,
|
started: false,
|
||||||
hnd: Rc::clone(&self.hnd),
|
hnd: Rc::clone(&self.hnd),
|
||||||
cfg: self.cfg.clone(),
|
cfg: Rc::clone(&self.cfg),
|
||||||
fut1: None,
|
fut1: None,
|
||||||
fut2: None,
|
fut2: None,
|
||||||
fut3: None,
|
fut3: None,
|
||||||
|
@ -295,8 +221,8 @@ where
|
||||||
S: 'static,
|
S: 'static,
|
||||||
{
|
{
|
||||||
started: bool,
|
started: bool,
|
||||||
hnd: Rc<UnsafeCell<F>>,
|
hnd: Rc<WithHnd<T, S, F, R>>,
|
||||||
cfg: ExtractorConfig<S, T>,
|
cfg: Rc<T::Config>,
|
||||||
req: HttpRequest<S>,
|
req: HttpRequest<S>,
|
||||||
fut1: Option<Box<Future<Item = T, Error = Error>>>,
|
fut1: Option<Box<Future<Item = T, Error = Error>>>,
|
||||||
fut2: Option<R>,
|
fut2: Option<R>,
|
||||||
|
@ -356,8 +282,17 @@ where
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let hnd: &mut F = unsafe { &mut *self.hnd.get() };
|
self.fut2 = {
|
||||||
self.fut2 = Some((*hnd)(item));
|
// clone handler, inicrease ref counter
|
||||||
|
let h = self.hnd.as_ref().hnd.clone();
|
||||||
|
// Enforce invariants before entering unsafe code.
|
||||||
|
// Only two references could exists With struct owns one, and line above
|
||||||
|
if Rc::weak_count(&h) != 0 || Rc::strong_count(&h) != 2 {
|
||||||
|
panic!("Multiple copies of handler are in use")
|
||||||
|
}
|
||||||
|
let hnd: &mut F = unsafe { &mut *h.as_ref().get() };
|
||||||
|
Some((*hnd)(item))
|
||||||
|
};
|
||||||
self.poll()
|
self.poll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,6 +127,7 @@ pub struct Client {
|
||||||
protocols: Option<String>,
|
protocols: Option<String>,
|
||||||
conn: Addr<ClientConnector>,
|
conn: Addr<ClientConnector>,
|
||||||
max_size: usize,
|
max_size: usize,
|
||||||
|
no_masking: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
|
@ -144,6 +145,7 @@ impl Client {
|
||||||
origin: None,
|
origin: None,
|
||||||
protocols: None,
|
protocols: None,
|
||||||
max_size: 65_536,
|
max_size: 65_536,
|
||||||
|
no_masking: false,
|
||||||
conn,
|
conn,
|
||||||
};
|
};
|
||||||
cl.request.uri(uri.as_ref());
|
cl.request.uri(uri.as_ref());
|
||||||
|
@ -198,6 +200,12 @@ impl Client {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Disable payload masking. By default ws client masks frame payload.
|
||||||
|
pub fn no_masking(mut self) -> Self {
|
||||||
|
self.no_masking = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Set request header
|
/// Set request header
|
||||||
pub fn header<K, V>(mut self, key: K, value: V) -> Self
|
pub fn header<K, V>(mut self, key: K, value: V) -> Self
|
||||||
where
|
where
|
||||||
|
@ -260,7 +268,7 @@ impl Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
// start handshake
|
// start handshake
|
||||||
ClientHandshake::new(request, self.max_size)
|
ClientHandshake::new(request, self.max_size, self.no_masking)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -281,10 +289,13 @@ pub struct ClientHandshake {
|
||||||
key: String,
|
key: String,
|
||||||
error: Option<ClientError>,
|
error: Option<ClientError>,
|
||||||
max_size: usize,
|
max_size: usize,
|
||||||
|
no_masking: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientHandshake {
|
impl ClientHandshake {
|
||||||
fn new(mut request: ClientRequest, max_size: usize) -> ClientHandshake {
|
fn new(
|
||||||
|
mut request: ClientRequest, max_size: usize, no_masking: bool,
|
||||||
|
) -> ClientHandshake {
|
||||||
// Generate a random key for the `Sec-WebSocket-Key` header.
|
// Generate a random key for the `Sec-WebSocket-Key` header.
|
||||||
// a base64-encoded (see Section 4 of [RFC4648]) value that,
|
// a base64-encoded (see Section 4 of [RFC4648]) value that,
|
||||||
// when decoded, is 16 bytes in length (RFC 6455)
|
// when decoded, is 16 bytes in length (RFC 6455)
|
||||||
|
@ -304,6 +315,7 @@ impl ClientHandshake {
|
||||||
ClientHandshake {
|
ClientHandshake {
|
||||||
key,
|
key,
|
||||||
max_size,
|
max_size,
|
||||||
|
no_masking,
|
||||||
request: Some(request.send()),
|
request: Some(request.send()),
|
||||||
tx: Some(tx),
|
tx: Some(tx),
|
||||||
error: None,
|
error: None,
|
||||||
|
@ -317,6 +329,7 @@ impl ClientHandshake {
|
||||||
tx: None,
|
tx: None,
|
||||||
error: Some(err),
|
error: Some(err),
|
||||||
max_size: 0,
|
max_size: 0,
|
||||||
|
no_masking: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -427,6 +440,7 @@ impl Future for ClientHandshake {
|
||||||
ClientReader {
|
ClientReader {
|
||||||
inner: Rc::clone(&inner),
|
inner: Rc::clone(&inner),
|
||||||
max_size: self.max_size,
|
max_size: self.max_size,
|
||||||
|
no_masking: self.no_masking,
|
||||||
},
|
},
|
||||||
ClientWriter { inner },
|
ClientWriter { inner },
|
||||||
)))
|
)))
|
||||||
|
@ -437,6 +451,7 @@ impl Future for ClientHandshake {
|
||||||
pub struct ClientReader {
|
pub struct ClientReader {
|
||||||
inner: Rc<RefCell<Inner>>,
|
inner: Rc<RefCell<Inner>>,
|
||||||
max_size: usize,
|
max_size: usize,
|
||||||
|
no_masking: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for ClientReader {
|
impl fmt::Debug for ClientReader {
|
||||||
|
@ -451,13 +466,14 @@ impl Stream for ClientReader {
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||||
let max_size = self.max_size;
|
let max_size = self.max_size;
|
||||||
|
let no_masking = self.no_masking;
|
||||||
let mut inner = self.inner.borrow_mut();
|
let mut inner = self.inner.borrow_mut();
|
||||||
if inner.closed {
|
if inner.closed {
|
||||||
return Ok(Async::Ready(None));
|
return Ok(Async::Ready(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
// read
|
// read
|
||||||
match Frame::parse(&mut inner.rx, false, max_size) {
|
match Frame::parse(&mut inner.rx, no_masking, max_size) {
|
||||||
Ok(Async::Ready(Some(frame))) => {
|
Ok(Async::Ready(Some(frame))) => {
|
||||||
let (_finished, opcode, payload) = frame.unpack();
|
let (_finished, opcode, payload) = frame.unpack();
|
||||||
|
|
||||||
|
|
|
@ -25,12 +25,12 @@
|
||||||
//!
|
//!
|
||||||
//! // Handler for ws::Message messages
|
//! // Handler for ws::Message messages
|
||||||
//! impl StreamHandler<ws::Message, ws::ProtocolError> for Ws {
|
//! impl StreamHandler<ws::Message, ws::ProtocolError> for Ws {
|
||||||
//! fn handle(&mut self, msg: Result<Option<ws::Message>, ws::ProtocolError>, ctx: &mut Self::Context) {
|
//! fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) {
|
||||||
//! match msg {
|
//! match msg {
|
||||||
//! Ok(Some(ws::Message::Ping(msg))) => ctx.pong(&msg),
|
//! ws::Message::Ping(msg) => ctx.pong(&msg),
|
||||||
//! Ok(Some(ws::Message::Text(text))) => ctx.text(text),
|
//! ws::Message::Text(text) => ctx.text(text),
|
||||||
//! Ok(Some(ws::Message::Binary(bin))) => ctx.binary(bin),
|
//! ws::Message::Binary(bin) => ctx.binary(bin),
|
||||||
//! _ => ctx.stop(),
|
//! _ => (),
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
|
|
|
@ -42,6 +42,28 @@ fn test_path_extractor() {
|
||||||
assert_eq!(bytes, Bytes::from_static(b"Welcome test!"));
|
assert_eq!(bytes, Bytes::from_static(b"Welcome test!"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_async_handler() {
|
||||||
|
let mut srv = test::TestServer::new(|app| {
|
||||||
|
app.resource("/{username}/index.html", |r| {
|
||||||
|
r.route().with(|p: Path<PParam>| {
|
||||||
|
Delay::new(Instant::now() + Duration::from_millis(10))
|
||||||
|
.and_then(move |_| Ok(format!("Welcome {}!", p.username)))
|
||||||
|
.responder()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// client request
|
||||||
|
let request = srv.get().uri(srv.url("/test/index.html")).finish().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 test!"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_query_extractor() {
|
fn test_query_extractor() {
|
||||||
let mut srv = test::TestServer::new(|app| {
|
let mut srv = test::TestServer::new(|app| {
|
||||||
|
@ -130,14 +152,17 @@ fn test_form_extractor() {
|
||||||
fn test_form_extractor2() {
|
fn test_form_extractor2() {
|
||||||
let mut srv = test::TestServer::new(|app| {
|
let mut srv = test::TestServer::new(|app| {
|
||||||
app.resource("/{username}/index.html", |r| {
|
app.resource("/{username}/index.html", |r| {
|
||||||
r.route()
|
r.route().with_config(
|
||||||
.with(|form: Form<FormData>| format!("{}", form.username))
|
|form: Form<FormData>| format!("{}", form.username),
|
||||||
.error_handler(|err, _| {
|
|cfg| {
|
||||||
error::InternalError::from_response(
|
cfg.error_handler(|err, _| {
|
||||||
err,
|
error::InternalError::from_response(
|
||||||
HttpResponse::Conflict().finish(),
|
err,
|
||||||
).into()
|
HttpResponse::Conflict().finish(),
|
||||||
});
|
).into()
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -23,16 +23,13 @@ impl Actor for Ws {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StreamHandler<ws::Message, ws::ProtocolError> for Ws {
|
impl StreamHandler<ws::Message, ws::ProtocolError> for Ws {
|
||||||
fn handle(
|
fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) {
|
||||||
&mut self, msg: Result<Option<ws::Message>, ws::ProtocolError>,
|
|
||||||
ctx: &mut Self::Context,
|
|
||||||
) {
|
|
||||||
match msg {
|
match msg {
|
||||||
Ok(Some(ws::Message::Ping(msg))) => ctx.pong(&msg),
|
ws::Message::Ping(msg) => ctx.pong(&msg),
|
||||||
Ok(Some(ws::Message::Text(text))) => ctx.text(text),
|
ws::Message::Text(text) => ctx.text(text),
|
||||||
Ok(Some(ws::Message::Binary(bin))) => ctx.binary(bin),
|
ws::Message::Binary(bin) => ctx.binary(bin),
|
||||||
Ok(Some(ws::Message::Close(reason))) => ctx.close(reason),
|
ws::Message::Close(reason) => ctx.close(reason),
|
||||||
_ => ctx.stop(),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,16 +153,13 @@ impl Ws2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StreamHandler<ws::Message, ws::ProtocolError> for Ws2 {
|
impl StreamHandler<ws::Message, ws::ProtocolError> for Ws2 {
|
||||||
fn handle(
|
fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) {
|
||||||
&mut self, msg: Result<Option<ws::Message>, ws::ProtocolError>,
|
|
||||||
ctx: &mut Self::Context,
|
|
||||||
) {
|
|
||||||
match msg {
|
match msg {
|
||||||
Ok(Some(ws::Message::Ping(msg))) => ctx.pong(&msg),
|
ws::Message::Ping(msg) => ctx.pong(&msg),
|
||||||
Ok(Some(ws::Message::Text(text))) => ctx.text(text),
|
ws::Message::Text(text) => ctx.text(text),
|
||||||
Ok(Some(ws::Message::Binary(bin))) => ctx.binary(bin),
|
ws::Message::Binary(bin) => ctx.binary(bin),
|
||||||
Ok(Some(ws::Message::Close(reason))) => ctx.close(reason),
|
ws::Message::Close(reason) => ctx.close(reason),
|
||||||
_ => ctx.stop(),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue