1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-07-07 03:10:17 +02:00

Compare commits

..

13 Commits

Author SHA1 Message Date
a3a78ac6fb Do not set Content-Length header, let actix-http set it #930 2019-06-28 11:42:20 +06:00
596483ff55 prepare actix-web-actors release 2019-06-28 10:54:23 +06:00
768859513a Expose the max limit for payload sizes in Websocket Actors. #925 (#933)
* Expose the max limit for payload sizes in Websocket Actors.

* Revert to previous not-formatted code.

* Implement WebsocketContext::with_codec and make Codec Copy and Clone.

* Fix formatting.

* Fix formatting.
2019-06-28 10:49:03 +06:00
44bb79cd07 Call req.path() on Json extractor error only (#945)
* Call req.path() on Json extractor error only

* Cleanup len parse code
2019-06-28 10:44:53 +06:00
af9fb5d190 Support asynchronous data factories #850 2019-06-28 10:43:52 +06:00
c0c71f82c0 Fixes typo. (#940)
Small typo fix.
2019-06-25 23:23:36 +06:00
b948f74b54 Extractor configuration Migration (#937)
added guide for Extractor configuration in MIGRATION.md
2019-06-24 07:16:04 +06:00
1a24ff8717 Add builder function for HTTP 429 Too Many Requests status (#931) 2019-06-21 13:06:29 +06:00
47fab0e393 Bump derive_more crate version to 0.15.0 in actix-cors (#927) 2019-06-19 16:41:42 +06:00
313ac48765 Use encoding_rs crate instead of unmaintained encoding crate (#922)
* Use encoding_rs crate instead of unmaintained encoding crate

* Update changelog
2019-06-18 12:43:25 +06:00
d7780d53c9 Fix typo in actix_web::web::Data::get_ref docstring (#921) 2019-06-18 07:27:23 +06:00
ad0e6f73b3 update version 2019-06-17 12:35:00 +06:00
546a8a58db remove cors and identity middlewares 2019-06-17 12:33:00 +06:00
26 changed files with 297 additions and 154 deletions

View File

@ -1,14 +1,34 @@
# Changes
## [1.0.3] - unreleased
### Added
* Support asynchronous data factories #850
### Changed
* Use `encoding_rs` crate instead of unmaintained `encoding` crate
## [1.0.2] - 2019-06-17
### Changed
* Move cors middleware to `actix-cors` crate.
* Move identity middleware to `actix-identity` crate.
## [1.0.1] - 2019-06-17
### Add
### Added
* Add support for PathConfig #903
* Add `middleware::identity::RequestIdentity` trait to `get_identity` from `HttpMessage`.
### Changes
### Changed
* Move cors middleware to `actix-cors` crate.
@ -27,7 +47,7 @@
## [1.0.0] - 2019-06-05
### Add
### Added
* Add `Scope::configure()` method.
@ -38,7 +58,7 @@
* Add macros for head, options, trace, connect and patch http methods
### Changes
### Changed
* Drop an unnecessary `Option<_>` indirection around `ServerBuilder` from `HttpServer`. #863
@ -56,7 +76,7 @@
* Add `Query<T>::from_query()` to extract parameters from a query string. #846
* `QueryConfig`, similar to `JsonConfig` for customizing error handling of query extractors.
### Changes
### Changed
* `JsonConfig` is now `Send + Sync`, this implies that `error_handler` must be `Send + Sync` too.
@ -71,7 +91,7 @@
* Allow to set/override app data on scope level
### Changes
### Changed
* `App::configure` take an `FnOnce` instead of `Fn`
* Upgrade actix-net crates

View File

@ -1,6 +1,6 @@
[package]
name = "actix-web"
version = "1.0.1"
version = "1.0.2"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust."
readme = "README.md"
@ -43,7 +43,7 @@ members = [
]
[features]
default = ["brotli", "flate2-zlib", "client", "fail", "depracated"]
default = ["brotli", "flate2-zlib", "client", "fail"]
# http client
client = ["awc"]
@ -68,9 +68,6 @@ ssl = ["openssl", "actix-server/ssl", "awc/ssl"]
# rustls
rust-tls = ["rustls", "actix-server/rust-tls"]
# deprecated middlewares
depracated = ["actix-cors", "actix-identity"]
[dependencies]
actix-codec = "0.1.2"
actix-service = "0.4.1"
@ -84,13 +81,9 @@ actix-server-config = "0.1.1"
actix-threadpool = "0.1.1"
awc = { version = "0.2.1", optional = true }
# deprecated middlewares
actix-cors = { version = "0.1.0", optional = true }
actix-identity = { version = "0.1.0", optional = true }
bytes = "0.4"
derive_more = "0.15.0"
encoding = "0.2"
encoding_rs = "0.8"
futures = "0.1.25"
hashbrown = "0.5.0"
log = "0.4"
@ -125,7 +118,7 @@ opt-level = 3
codegen-units = 1
[patch.crates-io]
# actix-web = { path = "." }
actix-web = { path = "." }
actix-http = { path = "actix-http" }
actix-http-test = { path = "test-server" }
actix-web-codegen = { path = "actix-web-codegen" }

View File

@ -31,6 +31,64 @@
## 1.0.0
* Extractor configuration. In version 1.0 this is handled with the new `Data` mechanism for both setting and retrieving the configuration
instead of
```rust
#[derive(Default)]
struct ExtractorConfig {
config: String,
}
impl FromRequest for YourExtractor {
type Config = ExtractorConfig;
type Result = Result<YourExtractor, Error>;
fn from_request(req: &HttpRequest, cfg: &Self::Config) -> Self::Result {
println!("use the config: {:?}", cfg.config);
...
}
}
App::new().resource("/route_with_config", |r| {
r.post().with_config(handler_fn, |cfg| {
cfg.0.config = "test".to_string();
})
})
```
use the HttpRequest to get the configuration like any other `Data` with `req.app_data::<C>()` and set it with the `data()` method on the `resource`
```rust
#[derive(Default)]
struct ExtractorConfig {
config: String,
}
impl FromRequest for YourExtractor {
type Error = Error;
type Future = Result<Self, Self::Error>;
type Config = ExtractorConfig;
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
let cfg = req.app_data::<ExtractorConfig>();
println!("config data?: {:?}", cfg.unwrap().role);
...
}
}
App::new().service(
resource("/route_with_config")
.data(ExtractorConfig {
config: "test".to_string(),
})
.route(post().to(handler_fn)),
)
```
* Resource registration. 1.0 version uses generalized resource
registration via `.service()` method.

View File

@ -1,5 +1,9 @@
# Changes
## [0.1.1] - unreleased
* Bump `derive_more` crate version to 0.15.0
## [0.1.0] - 2019-06-15
* Move cors middleware to separate crate

View File

@ -19,5 +19,5 @@ path = "src/lib.rs"
[dependencies]
actix-web = "1.0.0"
actix-service = "0.4.0"
derive_more = "0.14.1"
derive_more = "0.15.0"
futures = "0.1.25"

View File

@ -1,5 +1,10 @@
# Changes
## [0.1.3] - 2019-06-28
* Do not set `Content-Length` header, let actix-http set it #930
## [0.1.2] - 2019-06-13
* Content-Length is 0 for NamedFile HEAD request #914

View File

@ -1,6 +1,6 @@
[package]
name = "actix-files"
version = "0.1.2"
version = "0.1.3"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Static files support for actix web."
readme = "README.md"
@ -18,7 +18,7 @@ name = "actix_files"
path = "src/lib.rs"
[dependencies]
actix-web = { version = "1.0.0", default-features = false }
actix-web = { version = "1.0.2", default-features = false }
actix-http = "0.2.4"
actix-service = "0.4.1"
bitflags = "1"
@ -32,4 +32,4 @@ percent-encoding = "1.0"
v_htmlescape = "0.4"
[dev-dependencies]
actix-web = { version = "1.0.0", features=["ssl"] }
actix-web = { version = "1.0.2", features=["ssl"] }

View File

@ -855,6 +855,8 @@ mod tests {
#[test]
fn test_named_file_content_length_headers() {
use actix_web::body::{MessageBody, ResponseBody};
let mut srv = test::init_service(
App::new().service(Files::new("test", ".").index_file("tests/test.binary")),
);
@ -866,14 +868,13 @@ mod tests {
.to_request();
let response = test::call_service(&mut srv, request);
let contentlength = response
.headers()
.get(header::CONTENT_LENGTH)
.unwrap()
.to_str()
.unwrap();
assert_eq!(contentlength, "11");
// let contentlength = response
// .headers()
// .get(header::CONTENT_LENGTH)
// .unwrap()
// .to_str()
// .unwrap();
// assert_eq!(contentlength, "11");
// Invalid range header
let request = TestRequest::get()
@ -890,14 +891,13 @@ mod tests {
.to_request();
let response = test::call_service(&mut srv, request);
let contentlength = response
.headers()
.get(header::CONTENT_LENGTH)
.unwrap()
.to_str()
.unwrap();
assert_eq!(contentlength, "100");
// let contentlength = response
// .headers()
// .get(header::CONTENT_LENGTH)
// .unwrap()
// .to_str()
// .unwrap();
// assert_eq!(contentlength, "100");
// chunked
let request = TestRequest::get()
@ -939,14 +939,14 @@ mod tests {
.to_request();
let response = test::call_service(&mut srv, request);
let contentlength = response
.headers()
.get(header::CONTENT_LENGTH)
.unwrap()
.to_str()
.unwrap();
assert_eq!(contentlength, "100");
// TODO: fix check
// let contentlength = response
// .headers()
// .get(header::CONTENT_LENGTH)
// .unwrap()
// .to_str()
// .unwrap();
// assert_eq!(contentlength, "100");
}
#[test]

View File

@ -414,8 +414,6 @@ impl Responder for NamedFile {
};
};
resp.header(header::CONTENT_LENGTH, format!("{}", length));
if precondition_failed {
return Ok(resp.status(StatusCode::PRECONDITION_FAILED).finish());
} else if not_modified {

View File

@ -1,5 +1,11 @@
# Changes
## [0.2.5] - unreleased
### Changed
* Use `encoding_rs` crate instead of unmaintained `encoding` crate
## [0.2.4] - 2019-06-16
### Fixed
@ -83,7 +89,7 @@
## [0.1.1] - 2019-04-19
### Changes
### Changed
* Cookie::max_age() accepts value in seconds

View File

@ -58,7 +58,7 @@ byteorder = "1.2"
copyless = "0.1.2"
derive_more = "0.15.0"
either = "1.5.2"
encoding = "0.2"
encoding_rs = "0.8"
futures = "0.1.25"
hashbrown = "0.5.0"
h2 = "0.1.16"

View File

@ -61,6 +61,7 @@ impl Response {
STATIC_RESP!(RangeNotSatisfiable, StatusCode::RANGE_NOT_SATISFIABLE);
STATIC_RESP!(ExpectationFailed, StatusCode::EXPECTATION_FAILED);
STATIC_RESP!(UnprocessableEntity, StatusCode::UNPROCESSABLE_ENTITY);
STATIC_RESP!(TooManyRequests, StatusCode::TOO_MANY_REQUESTS);
STATIC_RESP!(InternalServerError, StatusCode::INTERNAL_SERVER_ERROR);
STATIC_RESP!(NotImplemented, StatusCode::NOT_IMPLEMENTED);

View File

@ -1,9 +1,7 @@
use std::cell::{Ref, RefMut};
use std::str;
use encoding::all::UTF_8;
use encoding::label::encoding_from_whatwg_label;
use encoding::EncodingRef;
use encoding_rs::{Encoding, UTF_8};
use http::header;
use mime::Mime;
@ -59,10 +57,12 @@ pub trait HttpMessage: Sized {
/// Get content type encoding
///
/// UTF-8 is used by default, If request charset is not set.
fn encoding(&self) -> Result<EncodingRef, ContentTypeError> {
fn encoding(&self) -> Result<&'static Encoding, ContentTypeError> {
if let Some(mime_type) = self.mime_type()? {
if let Some(charset) = mime_type.get_param("charset") {
if let Some(enc) = encoding_from_whatwg_label(charset.as_str()) {
if let Some(enc) =
Encoding::for_label_no_replacement(charset.as_str().as_bytes())
{
Ok(enc)
} else {
Err(ContentTypeError::UnknownEncoding)
@ -166,8 +166,7 @@ where
#[cfg(test)]
mod tests {
use bytes::Bytes;
use encoding::all::ISO_8859_2;
use encoding::Encoding;
use encoding_rs::ISO_8859_2;
use mime;
use super::*;
@ -223,7 +222,7 @@ mod tests {
"application/json; charset=ISO-8859-2",
)
.finish();
assert_eq!(ISO_8859_2.name(), req.encoding().unwrap().name());
assert_eq!(ISO_8859_2, req.encoding().unwrap());
}
#[test]

View File

@ -37,7 +37,7 @@ pub enum Frame {
Close(Option<CloseReason>),
}
#[derive(Debug)]
#[derive(Debug, Copy, Clone)]
/// WebSockets protocol codec
pub struct Codec {
max_size: usize,

View File

@ -1,5 +1,9 @@
# Changes
## [1.0.1] - 2019-06-28
* Allow to use custom ws codec with `WebsocketContext` #925
## [1.0.0] - 2019-05-29
* Update actix-http and actix-web

View File

@ -1,6 +1,6 @@
[package]
name = "actix-web-actors"
version = "1.0.0"
version = "1.0.1"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix actors support for actix web framework."
readme = "README.md"
@ -19,7 +19,7 @@ path = "src/lib.rs"
[dependencies]
actix = "0.8.3"
actix-web = "1.0.0"
actix-web = "1.0.2"
actix-http = "0.2.4"
actix-codec = "0.1.2"
bytes = "0.4"

View File

@ -177,9 +177,30 @@ where
inner: ContextParts::new(mb.sender_producer()),
messages: VecDeque::new(),
};
ctx.add_stream(WsStream::new(stream));
ctx.add_stream(WsStream::new(stream, Codec::new()));
WebsocketContextFut::new(ctx, actor, mb)
WebsocketContextFut::new(ctx, actor, mb, Codec::new())
}
#[inline]
/// Create a new Websocket context from a request, an actor, and a codec
pub fn with_codec<S>(
actor: A,
stream: S,
codec: Codec,
) -> impl Stream<Item = Bytes, Error = Error>
where
A: StreamHandler<Message, ProtocolError>,
S: Stream<Item = Bytes, Error = PayloadError> + 'static,
{
let mb = Mailbox::default();
let mut ctx = WebsocketContext {
inner: ContextParts::new(mb.sender_producer()),
messages: VecDeque::new(),
};
ctx.add_stream(WsStream::new(stream, codec));
WebsocketContextFut::new(ctx, actor, mb, codec)
}
/// Create a new Websocket context
@ -197,11 +218,11 @@ where
inner: ContextParts::new(mb.sender_producer()),
messages: VecDeque::new(),
};
ctx.add_stream(WsStream::new(stream));
ctx.add_stream(WsStream::new(stream, Codec::new()));
let act = f(&mut ctx);
WebsocketContextFut::new(ctx, act, mb)
WebsocketContextFut::new(ctx, act, mb, Codec::new())
}
}
@ -288,11 +309,11 @@ impl<A> WebsocketContextFut<A>
where
A: Actor<Context = WebsocketContext<A>>,
{
fn new(ctx: WebsocketContext<A>, act: A, mailbox: Mailbox<A>) -> Self {
fn new(ctx: WebsocketContext<A>, act: A, mailbox: Mailbox<A>, codec: Codec) -> Self {
let fut = ContextFut::new(ctx, act, mailbox);
WebsocketContextFut {
fut,
encoder: Codec::new(),
encoder: codec,
buf: BytesMut::new(),
closed: false,
}
@ -353,10 +374,10 @@ impl<S> WsStream<S>
where
S: Stream<Item = Bytes, Error = PayloadError>,
{
fn new(stream: S) -> Self {
fn new(stream: S, codec: Codec) -> Self {
Self {
stream,
decoder: Codec::new(),
decoder: codec,
buf: BytesMut::new(),
closed: false,
}

View File

@ -8,7 +8,7 @@ use actix_service::boxed::{self, BoxedNewService};
use actix_service::{
apply_transform, IntoNewService, IntoTransform, NewService, Transform,
};
use futures::IntoFuture;
use futures::{Future, IntoFuture};
use crate::app_service::{AppEntry, AppInit, AppRoutingFactory};
use crate::config::{AppConfig, AppConfigInner, ServiceConfig};
@ -23,6 +23,7 @@ use crate::service::{
};
type HttpNewService = BoxedNewService<(), ServiceRequest, ServiceResponse, Error, ()>;
type FnDataFactory = Box<Fn() -> Box<dyn Future<Item = Box<DataFactory>, Error = ()>>>;
/// Application builder - structure that follows the builder pattern
/// for building application instances.
@ -32,6 +33,7 @@ pub struct App<T, B> {
default: Option<Rc<HttpNewService>>,
factory_ref: Rc<RefCell<Option<AppRoutingFactory>>>,
data: Vec<Box<DataFactory>>,
data_factories: Vec<FnDataFactory>,
config: AppConfigInner,
external: Vec<ResourceDef>,
_t: PhantomData<(B)>,
@ -44,6 +46,7 @@ impl App<AppEntry, Body> {
App {
endpoint: AppEntry::new(fref.clone()),
data: Vec::new(),
data_factories: Vec::new(),
services: Vec::new(),
default: None,
factory_ref: fref,
@ -100,6 +103,31 @@ where
self
}
/// Set application data factory. This function is
/// similar to `.data()` but it accepts data factory. Data object get
/// constructed asynchronously during application initialization.
pub fn data_factory<F, Out>(mut self, data: F) -> Self
where
F: Fn() -> Out + 'static,
Out: IntoFuture + 'static,
Out::Error: std::fmt::Debug,
{
self.data_factories.push(Box::new(move || {
Box::new(
data()
.into_future()
.map_err(|e| {
log::error!("Can not construct data instance: {:?}", e);
})
.map(|data| {
let data: Box<dyn DataFactory> = Box::new(Data::new(data));
data
}),
)
}));
self
}
/// Set application data. Application data could be accessed
/// by using `Data<T>` extractor where `T` is data type.
pub fn register_data<U: 'static>(mut self, data: Data<U>) -> Self {
@ -349,6 +377,7 @@ where
App {
endpoint,
data: self.data,
data_factories: self.data_factories,
services: self.services,
default: self.default,
factory_ref: self.factory_ref,
@ -423,6 +452,7 @@ where
fn into_new_service(self) -> AppInit<T, B> {
AppInit {
data: Rc::new(self.data),
data_factories: Rc::new(self.data_factories),
endpoint: self.endpoint,
services: Rc::new(RefCell::new(self.services)),
external: RefCell::new(self.external),
@ -490,24 +520,24 @@ mod tests {
assert_eq!(resp.status(), StatusCode::CREATED);
}
// #[test]
// fn test_data_factory() {
// let mut srv =
// init_service(App::new().data_factory(|| Ok::<_, ()>(10usize)).service(
// web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
// ));
// let req = TestRequest::default().to_request();
// let resp = block_on(srv.call(req)).unwrap();
// assert_eq!(resp.status(), StatusCode::OK);
#[test]
fn test_data_factory() {
let mut srv =
init_service(App::new().data_factory(|| Ok::<_, ()>(10usize)).service(
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
));
let req = TestRequest::default().to_request();
let resp = block_on(srv.call(req)).unwrap();
assert_eq!(resp.status(), StatusCode::OK);
// let mut srv =
// init_service(App::new().data_factory(|| Ok::<_, ()>(10u32)).service(
// web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
// ));
// let req = TestRequest::default().to_request();
// let resp = block_on(srv.call(req)).unwrap();
// assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
// }
let mut srv =
init_service(App::new().data_factory(|| Ok::<_, ()>(10u32)).service(
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
));
let req = TestRequest::default().to_request();
let resp = block_on(srv.call(req)).unwrap();
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
}
fn md<S, B>(
req: ServiceRequest,

View File

@ -25,6 +25,7 @@ type BoxedResponse = Either<
FutureResult<ServiceResponse, Error>,
Box<Future<Item = ServiceResponse, Error = Error>>,
>;
type FnDataFactory = Box<Fn() -> Box<dyn Future<Item = Box<DataFactory>, Error = ()>>>;
/// Service factory to convert `Request` to a `ServiceRequest<S>`.
/// It also executes data factories.
@ -40,6 +41,7 @@ where
{
pub(crate) endpoint: T,
pub(crate) data: Rc<Vec<Box<DataFactory>>>,
pub(crate) data_factories: Rc<Vec<FnDataFactory>>,
pub(crate) config: RefCell<AppConfig>,
pub(crate) services: Rc<RefCell<Vec<Box<ServiceFactory>>>>,
pub(crate) default: Option<Rc<HttpNewService>>,
@ -119,16 +121,12 @@ where
let rmap = Rc::new(rmap);
rmap.finish(rmap.clone());
// create app data container
let mut data = Extensions::new();
for f in self.data.iter() {
f.create(&mut data);
}
AppInitResult {
endpoint: None,
endpoint_fut: self.endpoint.new_service(&()),
data: Rc::new(data),
data: self.data.clone(),
data_factories: Vec::new(),
data_factories_fut: self.data_factories.iter().map(|f| f()).collect(),
config,
rmap,
_t: PhantomData,
@ -144,7 +142,9 @@ where
endpoint_fut: T::Future,
rmap: Rc<ResourceMap>,
config: AppConfig,
data: Rc<Extensions>,
data: Rc<Vec<Box<DataFactory>>>,
data_factories: Vec<Box<DataFactory>>,
data_factories_fut: Vec<Box<dyn Future<Item = Box<DataFactory>, Error = ()>>>,
_t: PhantomData<B>,
}
@ -159,21 +159,43 @@ where
>,
{
type Item = AppInitService<T::Service, B>;
type Error = T::InitError;
type Error = ();
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
// async data factories
let mut idx = 0;
while idx < self.data_factories_fut.len() {
match self.data_factories_fut[idx].poll()? {
Async::Ready(f) => {
self.data_factories.push(f);
self.data_factories_fut.remove(idx);
}
Async::NotReady => idx += 1,
}
}
if self.endpoint.is_none() {
if let Async::Ready(srv) = self.endpoint_fut.poll()? {
self.endpoint = Some(srv);
}
}
if self.endpoint.is_some() {
if self.endpoint.is_some() && self.data_factories_fut.is_empty() {
// create app data container
let mut data = Extensions::new();
for f in self.data.iter() {
f.create(&mut data);
}
for f in &self.data_factories {
f.create(&mut data);
}
Ok(Async::Ready(AppInitService {
service: self.endpoint.take().unwrap(),
rmap: self.rmap.clone(),
config: self.config.clone(),
data: self.data.clone(),
data: Rc::new(data),
pool: HttpRequestPool::create(),
}))
} else {

View File

@ -73,7 +73,7 @@ impl<T> Data<T> {
Data(Arc::new(state))
}
/// Get referecnce to inner app data.
/// Get reference to inner app data.
pub fn get_ref(&self) -> &T {
self.0.as_ref()
}

View File

@ -10,17 +10,3 @@ mod normalize;
pub use self::defaultheaders::DefaultHeaders;
pub use self::logger::Logger;
pub use self::normalize::NormalizePath;
#[cfg(feature = "deprecated")]
#[deprecated(
since = "1.0.1",
note = "please use `actix_cors` instead. support will be removed in actix-web 1.0.2"
)]
pub use actix_cors as cors;
#[cfg(feature = "deprecated")]
#[deprecated(
since = "1.0.1",
note = "please use `actix_identity` instead. support will be removed in actix-web 1.0.2"
)]
pub use actix_identity as identity;

View File

@ -27,7 +27,7 @@ type HttpNewService = BoxedNewService<(), ServiceRequest, ServiceResponse, Error
/// Resource in turn has at least one route.
/// Route consists of an handlers objects and list of guards
/// (objects that implement `Guard` trait).
/// Resources and rouets uses builder-like pattern for configuration.
/// Resources and routes uses builder-like pattern for configuration.
/// During request handling, resource object iterate through all routes
/// and check guards for specific route, if request matches all
/// guards, route considered matched and route handler get called.

View File

@ -5,9 +5,7 @@ use std::{fmt, ops};
use actix_http::{Error, HttpMessage, Payload};
use bytes::BytesMut;
use encoding::all::UTF_8;
use encoding::types::{DecoderTrap, Encoding};
use encoding::EncodingRef;
use encoding_rs::{Encoding, UTF_8};
use futures::{Future, Poll, Stream};
use serde::de::DeserializeOwned;
@ -187,7 +185,7 @@ pub struct UrlEncoded<U> {
stream: Option<Decompress<Payload>>,
limit: usize,
length: Option<usize>,
encoding: EncodingRef,
encoding: &'static Encoding,
err: Option<UrlencodedError>,
fut: Option<Box<Future<Item = U, Error = UrlencodedError>>>,
}
@ -286,13 +284,14 @@ where
}
})
.and_then(move |body| {
if (encoding as *const Encoding) == UTF_8 {
if encoding == UTF_8 {
serde_urlencoded::from_bytes::<U>(&body)
.map_err(|_| UrlencodedError::Parse)
} else {
let body = encoding
.decode(&body, DecoderTrap::Strict)
.map_err(|_| UrlencodedError::Parse)?;
.decode_without_bom_handling_and_without_replacement(&body)
.map(|s| s.into_owned())
.ok_or(UrlencodedError::Parse)?;
serde_urlencoded::from_str::<U>(&body)
.map_err(|_| UrlencodedError::Parse)
}

View File

@ -180,16 +180,14 @@ where
.map(|c| (c.limit, c.ehandler.clone(), c.content_type.clone()))
.unwrap_or((32768, None, None));
let path = req.path().to_string();
Box::new(
JsonBody::new(req, payload, ctype)
.limit(limit)
.map_err(move |e| {
log::debug!(
"Failed to deserialize Json from payload. \
Request path: {:?}",
path
Request path: {}",
req2.path()
);
if let Some(err) = err {
(*err)(e, &req2)
@ -324,14 +322,11 @@ where
};
}
let mut len = None;
if let Some(l) = req.headers().get(&CONTENT_LENGTH) {
if let Ok(s) = l.to_str() {
if let Ok(l) = s.parse::<usize>() {
len = Some(l)
}
}
}
let len = req
.headers()
.get(&CONTENT_LENGTH)
.and_then(|l| l.to_str().ok())
.and_then(|s| s.parse::<usize>().ok());
let payload = Decompress::from_headers(payload.take(), req.headers());
JsonBody {

View File

@ -4,8 +4,7 @@ use std::str;
use actix_http::error::{Error, ErrorBadRequest, PayloadError};
use actix_http::HttpMessage;
use bytes::{Bytes, BytesMut};
use encoding::all::UTF_8;
use encoding::types::{DecoderTrap, Encoding};
use encoding_rs::UTF_8;
use futures::future::{err, Either, FutureResult};
use futures::{Future, Poll, Stream};
use mime::Mime;
@ -208,15 +207,15 @@ impl FromRequest for String {
.limit(limit)
.from_err()
.and_then(move |body| {
let enc: *const Encoding = encoding as *const Encoding;
if enc == UTF_8 {
if encoding == UTF_8 {
Ok(str::from_utf8(body.as_ref())
.map_err(|_| ErrorBadRequest("Can not decode body"))?
.to_owned())
} else {
Ok(encoding
.decode(&body, DecoderTrap::Strict)
.map_err(|_| ErrorBadRequest("Can not decode body"))?)
.decode_without_bom_handling_and_without_replacement(&body)
.map(|s| s.into_owned())
.ok_or_else(|| ErrorBadRequest("Can not decode body"))?)
}
}),
))

View File

@ -1,9 +1,8 @@
use std::borrow::Cow;
use std::str;
use bytes::{Bytes, BytesMut};
use encoding::all::UTF_8;
use encoding::types::{DecoderTrap, Encoding};
use encoding::EncodingRef;
use encoding_rs::{Encoding, UTF_8};
use futures::{Async, Poll, Stream};
use crate::dev::Payload;
@ -16,7 +15,7 @@ pub struct Readlines<T: HttpMessage> {
buff: BytesMut,
limit: usize,
checked_buff: bool,
encoding: EncodingRef,
encoding: &'static Encoding,
err: Option<ReadlinesError>,
}
@ -87,15 +86,17 @@ where
if ind + 1 > self.limit {
return Err(ReadlinesError::LimitOverflow);
}
let enc: *const Encoding = self.encoding as *const Encoding;
let line = if enc == UTF_8 {
let line = if self.encoding == UTF_8 {
str::from_utf8(&self.buff.split_to(ind + 1))
.map_err(|_| ReadlinesError::EncodingError)?
.to_owned()
} else {
self.encoding
.decode(&self.buff.split_to(ind + 1), DecoderTrap::Strict)
.map_err(|_| ReadlinesError::EncodingError)?
.decode_without_bom_handling_and_without_replacement(
&self.buff.split_to(ind + 1),
)
.map(Cow::into_owned)
.ok_or(ReadlinesError::EncodingError)?
};
return Ok(Async::Ready(Some(line)));
}
@ -117,15 +118,17 @@ where
if ind + 1 > self.limit {
return Err(ReadlinesError::LimitOverflow);
}
let enc: *const Encoding = self.encoding as *const Encoding;
let line = if enc == UTF_8 {
let line = if self.encoding == UTF_8 {
str::from_utf8(&bytes.split_to(ind + 1))
.map_err(|_| ReadlinesError::EncodingError)?
.to_owned()
} else {
self.encoding
.decode(&bytes.split_to(ind + 1), DecoderTrap::Strict)
.map_err(|_| ReadlinesError::EncodingError)?
.decode_without_bom_handling_and_without_replacement(
&bytes.split_to(ind + 1),
)
.map(Cow::into_owned)
.ok_or(ReadlinesError::EncodingError)?
};
// extend buffer with rest of the bytes;
self.buff.extend_from_slice(&bytes);
@ -143,15 +146,15 @@ where
if self.buff.len() > self.limit {
return Err(ReadlinesError::LimitOverflow);
}
let enc: *const Encoding = self.encoding as *const Encoding;
let line = if enc == UTF_8 {
let line = if self.encoding == UTF_8 {
str::from_utf8(&self.buff)
.map_err(|_| ReadlinesError::EncodingError)?
.to_owned()
} else {
self.encoding
.decode(&self.buff, DecoderTrap::Strict)
.map_err(|_| ReadlinesError::EncodingError)?
.decode_without_bom_handling_and_without_replacement(&self.buff)
.map(Cow::into_owned)
.ok_or(ReadlinesError::EncodingError)?
};
self.buff.clear();
Ok(Async::Ready(Some(line)))