diff --git a/CHANGES.md b/CHANGES.md index 9f899ea9d..a20713107 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,8 +1,14 @@ # Changes +## [1.0.3] - unreleased + +### Changed + +* Use `encoding_rs` crate instead of unmaintained `encoding` crate + ## [1.0.2] - 2019-06-17 -### Changes +### Changed * Move cors middleware to `actix-cors` crate. @@ -17,7 +23,7 @@ * Add `middleware::identity::RequestIdentity` trait to `get_identity` from `HttpMessage`. -### Changes +### Changed * Move cors middleware to `actix-cors` crate. @@ -47,7 +53,7 @@ * Add macros for head, options, trace, connect and patch http methods -### Changes +### Changed * Drop an unnecessary `Option<_>` indirection around `ServerBuilder` from `HttpServer`. #863 @@ -65,7 +71,7 @@ * Add `Query::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. @@ -80,7 +86,7 @@ * Allow to set/override app data on scope level -### Changes +### Changed * `App::configure` take an `FnOnce` instead of `Fn` * Upgrade actix-net crates diff --git a/Cargo.toml b/Cargo.toml index 9f7f8776a..4f8cd745d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,7 +83,7 @@ awc = { version = "0.2.1", 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" diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 93c352898..891967f10 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -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 diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 2da410130..c3930a7a6 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -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" diff --git a/actix-http/src/httpmessage.rs b/actix-http/src/httpmessage.rs index 1534973a8..05d668c10 100644 --- a/actix-http/src/httpmessage.rs +++ b/actix-http/src/httpmessage.rs @@ -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 { + 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] diff --git a/src/types/form.rs b/src/types/form.rs index 0bc6a0303..32d0edb69 100644 --- a/src/types/form.rs +++ b/src/types/form.rs @@ -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 { stream: Option>, limit: usize, length: Option, - encoding: EncodingRef, + encoding: &'static Encoding, err: Option, fut: Option>>, } @@ -286,13 +284,14 @@ where } }) .and_then(move |body| { - if (encoding as *const Encoding) == UTF_8 { + if encoding == UTF_8 { serde_urlencoded::from_bytes::(&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::(&body) .map_err(|_| UrlencodedError::Parse) } diff --git a/src/types/payload.rs b/src/types/payload.rs index 8e4dd7030..a8e85e4f3 100644 --- a/src/types/payload.rs +++ b/src/types/payload.rs @@ -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"))?) } }), )) diff --git a/src/types/readlines.rs b/src/types/readlines.rs index c23b84434..cea63e43b 100644 --- a/src/types/readlines.rs +++ b/src/types/readlines.rs @@ -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 { buff: BytesMut, limit: usize, checked_buff: bool, - encoding: EncodingRef, + encoding: &'static Encoding, err: Option, } @@ -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)))