1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-07-12 13:33:41 +02:00

Compare commits

...

19 Commits

Author SHA1 Message Date
095ad328ee prepare release 2018-05-10 15:45:06 -07:00
a38afa0cec --no-count for tarpaulin 2018-05-10 13:05:56 -07:00
9619698543 doc string 2018-05-10 13:04:56 -07:00
4b1a471b35 add more examples for extractor config 2018-05-10 13:03:43 -07:00
b6039b0bff add doc string 2018-05-10 11:04:03 -07:00
d8fa43034f export ExtractorConfig type 2018-05-10 11:00:22 -07:00
92f993e054 Fix client request timeout handling 2018-05-10 09:37:38 -07:00
c172deb0f3 Merge pull request #219 from benjamingroeber/improve-readme
correct order of format arguments in readme example
2018-05-10 09:15:50 -07:00
dee6aed010 Merge branch 'master' into improve-readme 2018-05-10 09:15:44 -07:00
76f021a6e3 add tests for ErrorXXX helpers 2018-05-10 09:13:26 -07:00
2f244ea028 fix order of name and id in readme example 2018-05-10 18:12:59 +02:00
5f5ddc8f01 Merge pull request #218 from Dowwie/master
added error response functions for 501,502,503,504
2018-05-10 08:59:23 -07:00
8b473745cb added error response functions for 501,502,503,504 2018-05-10 11:26:38 -04:00
18575ee1ee Add Router::with_async() method for async handler registration 2018-05-09 16:27:31 -07:00
e58b38fd13 deprecate WsWrite from top level mod 2018-05-09 06:12:16 -07:00
b043c34632 bump version 2018-05-09 06:05:44 -07:00
b748bf3b0d make api public 2018-05-09 06:05:16 -07:00
be12d5e6fc make WsWriter trait optional 2018-05-09 05:48:06 -07:00
7c4941f868 update migration doc 2018-05-08 18:48:09 -07:00
17 changed files with 585 additions and 43 deletions

View File

@ -38,7 +38,7 @@ script:
- |
if [[ "$TRAVIS_RUST_VERSION" == "beta" ]]; then
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)
echo "Uploaded code coverage"
fi

View File

@ -1,5 +1,19 @@
# Changes
## 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

View File

@ -1,6 +1,6 @@
[package]
name = "actix-web"
version = "0.6.1"
version = "0.6.3"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust."
readme = "README.md"

View File

@ -1,5 +1,7 @@
## 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::CloseCode::Status` and `ws::CloseCode::Empty` have been removed.

View File

@ -36,7 +36,7 @@ extern crate actix_web;
use actix_web::{http, server, App, Path};
fn index(info: Path<(u32, String)>) -> String {
format!("Hello {}! id:{}", info.0, info.1)
format!("Hello {}! id:{}", info.1, info.0)
}
fn main() {

View File

@ -1,8 +1,15 @@
extern crate version_check;
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() {
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) => (),
None => (),
};

View File

@ -49,6 +49,7 @@ use httpresponse::HttpResponse;
impl ResponseError for SendRequestError {
fn error_response(&self) -> HttpResponse {
match *self {
SendRequestError::Timeout => HttpResponse::GatewayTimeout(),
SendRequestError::Connector(_) => HttpResponse::BadGateway(),
_ => HttpResponse::InternalServerError(),
}.into()

View File

@ -194,6 +194,7 @@ impl Future for SendRequest {
self.state = State::Send(pl);
}
State::Send(mut pl) => {
pl.poll_timeout()?;
pl.poll_write().map_err(|e| {
io::Error::new(io::ErrorKind::Other, format!("{}", e).as_str())
})?;
@ -315,7 +316,7 @@ impl Pipeline {
{
Async::NotReady => need_run = true,
Async::Ready(_) => {
let _ = self.poll_timeout().map_err(|e| {
self.poll_timeout().map_err(|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() {
match self.timeout.as_mut().unwrap().poll() {
Ok(Async::Ready(())) => Err(SendRequestError::Timeout),
Ok(Async::NotReady) => Ok(Async::NotReady),
Ok(Async::Ready(())) => return Err(SendRequestError::Timeout),
Ok(Async::NotReady) => (),
Err(_) => unreachable!(),
}
} else {
Ok(Async::NotReady)
}
Ok(())
}
#[inline]

View File

@ -757,6 +757,46 @@ where
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)]
mod tests {
use super::*;
@ -912,4 +952,52 @@ mod tests {
let resp: HttpResponse = err.error_response();
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);
}
}

View File

@ -175,6 +175,9 @@ pub use httprequest::HttpRequest;
pub use httpresponse::HttpResponse;
pub use json::Json;
pub use scope::Scope;
#[doc(hidden)]
#[deprecated(since = "0.6.2", note = "please use `use actix_web::ws::WsWriter`")]
pub use ws::WsWriter;
#[cfg(feature = "openssl")]
@ -210,6 +213,7 @@ pub mod dev {
pub use resource::ResourceHandler;
pub use route::Route;
pub use router::{Resource, ResourceType, Router};
pub use with::ExtractorConfig;
}
pub mod http {

View File

@ -1,9 +1,11 @@
use std::marker::PhantomData;
use std::rc::Rc;
use futures::Future;
use http::{Method, StatusCode};
use smallvec::SmallVec;
use error::Error;
use handler::{AsyncResult, FromRequest, Handler, Responder};
use httprequest::HttpRequest;
use httpresponse::HttpResponse;
@ -183,6 +185,25 @@ impl<S: 'static> ResourceHandler<S> {
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
///
/// This is similar to `App's` middlewares, but

View File

@ -13,7 +13,7 @@ use httpresponse::HttpResponse;
use middleware::{Finished as MiddlewareFinished, Middleware,
Response as MiddlewareResponse, Started as MiddlewareStarted};
use pred::Predicate;
use with::{ExtractorConfig, With, With2, With3};
use with::{ExtractorConfig, With, With2, With3, WithAsync};
/// Resource route definition
///
@ -129,6 +129,34 @@ impl<S: 'static> Route<S> {
/// |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>
where
F: Fn(T) -> R + 'static,
@ -140,6 +168,49 @@ impl<S: 'static> Route<S> {
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.
///
/// ```rust
@ -189,6 +260,7 @@ impl<S: 'static> Route<S> {
(cfg1, cfg2)
}
#[doc(hidden)]
/// Set handler function, use request extractor for all parameters.
pub fn with3<T1, T2, T3, F, R>(
&mut self, handler: F,

View File

@ -9,6 +9,62 @@ use handler::{AsyncResult, AsyncResultItem, FromRequest, Handler, Responder};
use httprequest::HttpRequest;
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>> {
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>
where
F: Fn(T1, T2) -> R,

View File

@ -518,24 +518,22 @@ impl ClientWriter {
fn as_mut(&mut self) -> &mut Inner {
unsafe { &mut *self.inner.get() }
}
}
impl WsWriter for ClientWriter {
/// Send text frame
#[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));
}
/// Send binary frame
#[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));
}
/// Send ping frame
#[inline]
fn ping(&mut self, message: &str) {
pub fn ping(&mut self, message: &str) {
self.write(Frame::message(
Vec::from(message),
OpCode::Ping,
@ -546,7 +544,7 @@ impl WsWriter for ClientWriter {
/// Send pong frame
#[inline]
fn pong(&mut self, message: &str) {
pub fn pong(&mut self, message: &str) {
self.write(Frame::message(
Vec::from(message),
OpCode::Pong,
@ -557,7 +555,39 @@ impl WsWriter for ClientWriter {
/// Send close frame
#[inline]
fn close(&mut self, reason: Option<CloseReason>) {
pub fn close(&mut self, reason: Option<CloseReason>) {
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);
}
}

View File

@ -149,6 +149,46 @@ where
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
#[inline]
pub fn connected(&self) -> bool {
@ -181,42 +221,32 @@ where
{
/// Send text frame
#[inline]
fn text<T: Into<Binary>>(&mut self, text: T) {
self.write(Frame::message(text.into(), OpCode::Text, true, false));
fn send_text<T: Into<Binary>>(&mut self, text: T) {
self.text(text)
}
/// Send binary frame
#[inline]
fn binary<B: Into<Binary>>(&mut self, data: B) {
self.write(Frame::message(data, OpCode::Binary, true, false));
fn send_binary<B: Into<Binary>>(&mut self, data: B) {
self.binary(data)
}
/// Send ping frame
#[inline]
fn ping(&mut self, message: &str) {
self.write(Frame::message(
Vec::from(message),
OpCode::Ping,
true,
false,
));
fn send_ping(&mut self, message: &str) {
self.ping(message)
}
/// Send pong frame
#[inline]
fn pong(&mut self, message: &str) {
self.write(Frame::message(
Vec::from(message),
OpCode::Pong,
true,
false,
));
fn send_pong(&mut self, message: &str) {
self.pong(message)
}
/// Send close frame
#[inline]
fn close(&mut self, reason: Option<CloseReason>) {
self.write(Frame::close(reason, false));
fn send_close(&mut self, reason: Option<CloseReason>) {
self.close(reason)
}
}

View File

@ -343,15 +343,15 @@ where
/// Common writing methods for a websocket.
pub trait WsWriter {
/// 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
fn binary<B: Into<Binary>>(&mut self, data: B);
fn send_binary<B: Into<Binary>>(&mut self, data: B);
/// Send a ping message
fn ping(&mut self, message: &str);
fn send_ping(&mut self, message: &str);
/// Send a pong message
fn pong(&mut self, message: &str);
fn send_pong(&mut self, message: &str);
/// Close the connection
fn close(&mut self, reason: Option<CloseReason>);
fn send_close(&mut self, reason: Option<CloseReason>);
}
#[cfg(test)]

View File

@ -9,6 +9,7 @@ extern crate tokio_core;
extern crate serde_derive;
extern crate serde_json;
use std::io;
use std::time::Duration;
use actix::*;
@ -377,6 +378,83 @@ fn test_path_and_query_extractor2_async4() {
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]
fn test_non_ascii_route() {
let mut srv = test::TestServer::new(|app| {