1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-08-26 23:24:47 +02:00

Compare commits

..

12 Commits

Author SHA1 Message Date
Nikolay Kim
1fcf1d4a49 prepare release 2018-06-21 09:47:46 +06:00
Nikolay Kim
4012606910 SendRequest execution fails with the entered unreachable code #329 2018-06-21 09:47:28 +06:00
Nikolay Kim
e975124630 Allow to disable masking for websockets client 2018-06-21 09:38:59 +06:00
Nikolay Kim
6862aa6ee7 prepare release 2018-06-13 05:04:59 -07:00
Nikolay Kim
8a22558f25 http/2 end-of-frame is not set if body is empty bytes #307 2018-06-12 14:47:45 -07:00
Nikolay Kim
b5b9f9656e do not allow stream or actor responses for internal error #301 2018-06-11 19:44:11 -07:00
Nikolay Kim
2fffc55d34 update changelog 2018-06-11 18:53:36 -07:00
Nikolay Kim
7d39f1582e InternalError can trigger memory unsafety #301 2018-06-11 18:52:54 -07:00
Nikolay Kim
75ed053a35 bump version 2018-06-11 12:32:31 -07:00
Nikolay Kim
cfedf5fff4 Merge branch '0.6' of github.com:actix/actix-web into 0.6 2018-06-11 12:32:05 -07:00
Nikolay Kim
be73a36339 use custom resolver 2018-06-11 12:28:43 -07:00
Nikolay Kim
1ad8ba2604 Fix docs.rs build 2018-06-11 12:25:20 -07:00
9 changed files with 137 additions and 64 deletions

View File

@@ -1,5 +1,27 @@
# Changes # Changes
## [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-13
### Fixed
* http/2 end-of-frame is not set if body is empty bytes #307
* InternalError can trigger memory unsafety #301
* Fix docs.rs build
## [0.6.12] - 2018-06-08 ## [0.6.12] - 2018-06-08
### Added ### Added

View File

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

View File

@@ -865,7 +865,7 @@ impl fut::ActorFuture for Maintenance {
let conn = AcquiredConn(key.clone(), Some(Rc::clone(&act.pool))); let conn = AcquiredConn(key.clone(), Some(Rc::clone(&act.pool)));
fut::WrapFuture::<ClientConnector>::actfuture( fut::WrapFuture::<ClientConnector>::actfuture(
Connector::from_registry().send( act.resolver.send(
ResolveConnect::host_and_port(&conn.0.host, conn.0.port) ResolveConnect::host_and_port(&conn.0.host, conn.0.port)
.timeout(waiter.conn_timeout), .timeout(waiter.conn_timeout),
), ),

View File

@@ -380,7 +380,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(_) => unreachable!(), Err(e) => return Err(e.into()),
} }
} }
Ok(()) Ok(())

View File

@@ -1,9 +1,9 @@
//! Error and Result module //! Error and Result module
use std::cell::RefCell;
use std::io::Error as IoError; use std::io::Error as IoError;
use std::str::Utf8Error; use std::str::Utf8Error;
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use std::{fmt, io, result}; use std::sync::Mutex;
use std::{fmt, io, mem, result};
use actix::MailboxError; use actix::MailboxError;
use cookie; use cookie;
@@ -21,9 +21,10 @@ pub use url::ParseError as UrlParseError;
// re-exports // re-exports
pub use cookie::ParseError as CookieParseError; pub use cookie::ParseError as CookieParseError;
use body::Body;
use handler::Responder; use handler::Responder;
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpresponse::HttpResponse; use httpresponse::{HttpResponse, InnerHttpResponse};
/// 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
@@ -35,7 +36,7 @@ pub type Result<T, E = Error> = result::Result<T, E>;
/// General purpose actix web error. /// General purpose actix web error.
/// ///
/// An actix web error is used to carry errors from `failure` or `std::error` /// An actix web error is used to carry errors from `failure` or `std::error`
/// through actix in a convenient way. It can be created through through /// through actix in a convenient way. It can be created through through
/// converting errors with `into()`. /// converting errors with `into()`.
/// ///
@@ -50,7 +51,9 @@ pub struct Error {
impl Error { impl Error {
/// Deprecated way to reference the underlying response error. /// Deprecated way to reference the underlying response error.
#[deprecated(since = "0.6.0", note = "please use `Error::as_response_error()` instead")] #[deprecated(
since = "0.6.0", note = "please use `Error::as_response_error()` instead"
)]
pub fn cause(&self) -> &ResponseError { pub fn cause(&self) -> &ResponseError {
self.cause.as_ref() self.cause.as_ref()
} }
@@ -77,7 +80,8 @@ impl Error {
} }
} }
/// Attempts to downcast this `Error` to a particular `Fail` type by reference. /// Attempts to downcast this `Error` to a particular `Fail` type by
/// reference.
/// ///
/// If the underlying error is not of type `T`, this will return `None`. /// If the underlying error is not of type `T`, this will return `None`.
pub fn downcast_ref<T: Fail>(&self) -> Option<&T> { pub fn downcast_ref<T: Fail>(&self) -> Option<&T> {
@@ -96,14 +100,13 @@ impl Error {
// //
// This currently requires a transmute. This could be avoided if failure // This currently requires a transmute. This could be avoided if failure
// provides a deref: https://github.com/rust-lang-nursery/failure/pull/213 // provides a deref: https://github.com/rust-lang-nursery/failure/pull/213
let compat: Option<&failure::Compat<failure::Error>> = Fail::downcast_ref(self.cause.as_fail()); let compat: Option<&failure::Compat<failure::Error>> =
Fail::downcast_ref(self.cause.as_fail());
if let Some(compat) = compat { if let Some(compat) = compat {
pub struct CompatWrappedError { pub struct CompatWrappedError {
error: failure::Error, error: failure::Error,
} }
let compat: &CompatWrappedError = unsafe { let compat: &CompatWrappedError = unsafe { ::std::mem::transmute(compat) };
::std::mem::transmute(compat)
};
compat.error.downcast_ref() compat.error.downcast_ref()
} else { } else {
None None
@@ -113,8 +116,8 @@ impl Error {
/// Helper trait to downcast a response error into a fail. /// Helper trait to downcast a response error into a fail.
/// ///
/// This is currently not exposed because it's unclear if this is the best way to /// This is currently not exposed because it's unclear if this is the best way
/// achieve the downcasting on `Error` for which this is needed. /// to achieve the downcasting on `Error` for which this is needed.
#[doc(hidden)] #[doc(hidden)]
pub trait InternalResponseErrorAsFail { pub trait InternalResponseErrorAsFail {
#[doc(hidden)] #[doc(hidden)]
@@ -125,8 +128,12 @@ pub trait InternalResponseErrorAsFail {
#[doc(hidden)] #[doc(hidden)]
impl<T: ResponseError> InternalResponseErrorAsFail for T { impl<T: ResponseError> InternalResponseErrorAsFail for T {
fn as_fail(&self) -> &Fail { self } fn as_fail(&self) -> &Fail {
fn as_mut_fail(&mut self) -> &mut Fail { self } self
}
fn as_mut_fail(&mut self) -> &mut Fail {
self
}
} }
/// Error that can be converted to `HttpResponse` /// Error that can be converted to `HttpResponse`
@@ -183,11 +190,9 @@ impl<T: ResponseError> From<T> for Error {
} }
/// Compatibility for `failure::Error` /// Compatibility for `failure::Error`
impl<T> ResponseError for failure::Compat<T> impl<T> ResponseError for failure::Compat<T> where
where T: fmt::Display + fmt::Debug + Sync + Send + 'static
T: fmt::Display + fmt::Debug + Sync + Send + 'static, {}
{
}
impl From<failure::Error> for Error { impl From<failure::Error> for Error {
fn from(err: failure::Error) -> Error { fn from(err: failure::Error) -> Error {
@@ -620,8 +625,8 @@ impl From<UrlParseError> for UrlGenerationError {
/// use actix_web::fs::NamedFile; /// use actix_web::fs::NamedFile;
/// ///
/// fn index(req: HttpRequest) -> Result<fs::NamedFile> { /// fn index(req: HttpRequest) -> Result<fs::NamedFile> {
/// let f = NamedFile::open("test.txt").map_err(error::ErrorBadRequest)?; /// let f = NamedFile::open("test.txt").map_err(error::ErrorBadRequest)?;
/// Ok(f) /// Ok(f)
/// } /// }
/// # fn main() {} /// # fn main() {}
/// ``` /// ```
@@ -631,12 +636,9 @@ pub struct InternalError<T> {
backtrace: Backtrace, backtrace: Backtrace,
} }
unsafe impl<T> Sync for InternalError<T> {}
unsafe impl<T> Send for InternalError<T> {}
enum InternalErrorType { enum InternalErrorType {
Status(StatusCode), Status(StatusCode),
Response(RefCell<Option<HttpResponse>>), Response(Mutex<Option<Box<InnerHttpResponse>>>),
} }
impl<T> InternalError<T> { impl<T> InternalError<T> {
@@ -651,9 +653,21 @@ 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 body = mem::replace(&mut resp.body, Body::Empty);
match body {
Body::Empty => (),
Body::Binary(mut bin) => {
resp.body = Body::Binary(bin.take().into());
}
Body::Streaming(_) | Body::Actor(_) => {
error!("Streaming or Actor body is not support by error response");
}
}
InternalError { InternalError {
cause, cause,
status: InternalErrorType::Response(RefCell::new(Some(response))), status: InternalErrorType::Response(Mutex::new(Some(resp))),
backtrace: Backtrace::new(), backtrace: Backtrace::new(),
} }
} }
@@ -694,8 +708,8 @@ where
match self.status { match self.status {
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.borrow_mut().take() { if let Some(resp) = resp.lock().unwrap().take() {
resp HttpResponse::from_inner(resp)
} else { } else {
HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR) HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR)
} }

View File

@@ -241,6 +241,14 @@ impl HttpResponse {
pub fn set_write_buffer_capacity(&mut self, cap: usize) { pub fn set_write_buffer_capacity(&mut self, cap: usize) {
self.get_mut().write_capacity = cap; self.get_mut().write_capacity = cap;
} }
pub(crate) fn into_inner(mut self) -> Box<InnerHttpResponse> {
self.0.take().unwrap()
}
pub(crate) fn from_inner(inner: Box<InnerHttpResponse>) -> HttpResponse {
HttpResponse(Some(inner), HttpResponsePool::pool())
}
} }
impl fmt::Debug for HttpResponse { impl fmt::Debug for HttpResponse {
@@ -297,11 +305,13 @@ impl HttpResponseBuilder {
/// ///
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_web;
/// use actix_web::{HttpRequest, HttpResponse, Result, http}; /// use actix_web::{http, HttpRequest, HttpResponse, Result};
/// ///
/// fn index(req: HttpRequest) -> Result<HttpResponse> { /// fn index(req: HttpRequest) -> Result<HttpResponse> {
/// Ok(HttpResponse::Ok() /// Ok(HttpResponse::Ok()
/// .set(http::header::IfModifiedSince("Sun, 07 Nov 1994 08:48:37 GMT".parse()?)) /// .set(http::header::IfModifiedSince(
/// "Sun, 07 Nov 1994 08:48:37 GMT".parse()?,
/// ))
/// .finish()) /// .finish())
/// } /// }
/// fn main() {} /// fn main() {}
@@ -455,7 +465,8 @@ impl HttpResponseBuilder {
/// .path("/") /// .path("/")
/// .secure(true) /// .secure(true)
/// .http_only(true) /// .http_only(true)
/// .finish()) /// .finish(),
/// )
/// .finish() /// .finish()
/// } /// }
/// fn main() {} /// fn main() {}
@@ -781,12 +792,12 @@ impl<'a, S> From<&'a HttpRequest<S>> for HttpResponseBuilder {
} }
#[derive(Debug)] #[derive(Debug)]
struct InnerHttpResponse { pub(crate) struct InnerHttpResponse {
version: Option<Version>, version: Option<Version>,
headers: HeaderMap, headers: HeaderMap,
status: StatusCode, status: StatusCode,
reason: Option<&'static str>, reason: Option<&'static str>,
body: Body, pub(crate) body: Body,
chunked: Option<bool>, chunked: Option<bool>,
encoding: Option<ContentEncoding>, encoding: Option<ContentEncoding>,
connection_type: Option<ConnectionType>, connection_type: Option<ConnectionType>,
@@ -795,6 +806,9 @@ struct InnerHttpResponse {
error: Option<Error>, error: Option<Error>,
} }
unsafe impl Sync for InnerHttpResponse {}
unsafe impl Send for InnerHttpResponse {}
impl InnerHttpResponse { impl InnerHttpResponse {
#[inline] #[inline]
fn new(status: StatusCode, body: Body) -> InnerHttpResponse { fn new(status: StatusCode, body: Body) -> InnerHttpResponse {

View File

@@ -6,15 +6,15 @@
//! # use std::thread; //! # use std::thread;
//! //!
//! fn index(info: Path<(String, u32)>) -> String { //! fn index(info: Path<(String, u32)>) -> String {
//! format!("Hello {}! id:{}", info.0, info.1) //! format!("Hello {}! id:{}", info.0, info.1)
//! } //! }
//! //!
//! fn main() { //! fn main() {
//! # thread::spawn(|| { //! # thread::spawn(|| {
//! server::new( //! server::new(|| {
//! || App::new() //! App::new().resource("/{name}/{id}/index.html", |r| r.with(index))
//! .resource("/{name}/{id}/index.html", |r| r.with(index))) //! }).bind("127.0.0.1:8080")
//! .bind("127.0.0.1:8080").unwrap() //! .unwrap()
//! .run(); //! .run();
//! # }); //! # });
//! } //! }
@@ -77,6 +77,7 @@
//! //!
#![cfg_attr(actix_nightly, feature( #![cfg_attr(actix_nightly, feature(
specialization, // for impl ErrorResponse for std::error::Error specialization, // for impl ErrorResponse for std::error::Error
extern_prelude,
))] ))]
#![cfg_attr( #![cfg_attr(
feature = "cargo-clippy", feature = "cargo-clippy",

View File

@@ -89,9 +89,6 @@ impl<H: 'static> Writer for H2Writer<H> {
self.flags.insert(Flags::STARTED); self.flags.insert(Flags::STARTED);
self.encoder = self.encoder =
ContentEncoder::for_server(self.buffer.clone(), req, msg, encoding); ContentEncoder::for_server(self.buffer.clone(), req, msg, encoding);
if let Body::Empty = *msg.body() {
self.flags.insert(Flags::EOF);
}
// http2 specific // http2 specific
msg.headers_mut().remove(CONNECTION); msg.headers_mut().remove(CONNECTION);
@@ -108,15 +105,22 @@ impl<H: 'static> Writer for H2Writer<H> {
let body = msg.replace_body(Body::Empty); let body = msg.replace_body(Body::Empty);
match body { match body {
Body::Binary(ref bytes) => { Body::Binary(ref bytes) => {
let mut val = BytesMut::new(); if bytes.is_empty() {
helpers::convert_usize(bytes.len(), &mut val); msg.headers_mut()
let l = val.len(); .insert(CONTENT_LENGTH, HeaderValue::from_static("0"));
msg.headers_mut().insert( self.flags.insert(Flags::EOF);
CONTENT_LENGTH, } else {
HeaderValue::try_from(val.split_to(l - 2).freeze()).unwrap(), let mut val = BytesMut::new();
); helpers::convert_usize(bytes.len(), &mut val);
let l = val.len();
msg.headers_mut().insert(
CONTENT_LENGTH,
HeaderValue::try_from(val.split_to(l - 2).freeze()).unwrap(),
);
}
} }
Body::Empty => { Body::Empty => {
self.flags.insert(Flags::EOF);
msg.headers_mut() msg.headers_mut()
.insert(CONTENT_LENGTH, HeaderValue::from_static("0")); .insert(CONTENT_LENGTH, HeaderValue::from_static("0"));
} }
@@ -141,14 +145,18 @@ impl<H: 'static> Writer for H2Writer<H> {
trace!("Response: {:?}", msg); trace!("Response: {:?}", msg);
if let Body::Binary(bytes) = body { if let Body::Binary(bytes) = body {
self.flags.insert(Flags::EOF); if bytes.is_empty() {
self.written = bytes.len() as u64; Ok(WriterState::Done)
self.encoder.write(bytes)?; } else {
if let Some(ref mut stream) = self.stream { self.flags.insert(Flags::EOF);
self.flags.insert(Flags::RESERVED); self.written = bytes.len() as u64;
stream.reserve_capacity(cmp::min(self.buffer.len(), CHUNK_SIZE)); self.encoder.write(bytes)?;
if let Some(ref mut stream) = self.stream {
self.flags.insert(Flags::RESERVED);
stream.reserve_capacity(cmp::min(self.buffer.len(), CHUNK_SIZE));
}
Ok(WriterState::Pause)
} }
Ok(WriterState::Pause)
} else { } else {
msg.replace_body(body); msg.replace_body(body);
self.buffer_capacity = msg.write_buffer_capacity(); self.buffer_capacity = msg.write_buffer_capacity();
@@ -177,10 +185,8 @@ impl<H: 'static> Writer for H2Writer<H> {
} }
fn write_eof(&mut self) -> io::Result<WriterState> { fn write_eof(&mut self) -> io::Result<WriterState> {
self.encoder.write_eof()?;
self.flags.insert(Flags::EOF); self.flags.insert(Flags::EOF);
if !self.encoder.is_eof() { if !self.encoder.write_eof()? {
Err(io::Error::new( Err(io::Error::new(
io::ErrorKind::Other, io::ErrorKind::Other,
"Last payload item, but eof is not reached", "Last payload item, but eof is not reached",

View File

@@ -113,6 +113,7 @@ pub struct Client {
protocols: Option<String>, protocols: Option<String>,
conn: Addr<Unsync, ClientConnector>, conn: Addr<Unsync, ClientConnector>,
max_size: usize, max_size: usize,
no_masking: bool,
} }
impl Client { impl Client {
@@ -132,6 +133,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());
@@ -186,6 +188,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
@@ -248,7 +256,7 @@ impl Client {
} }
// start handshake // start handshake
ClientHandshake::new(request, self.max_size) ClientHandshake::new(request, self.max_size, self.no_masking)
} }
} }
} }
@@ -269,10 +277,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)
@@ -292,6 +303,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,
@@ -305,6 +317,7 @@ impl ClientHandshake {
tx: None, tx: None,
error: Some(err), error: Some(err),
max_size: 0, max_size: 0,
no_masking: false,
} }
} }
@@ -415,6 +428,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 },
))) )))
@@ -424,6 +438,7 @@ impl Future for ClientHandshake {
pub struct ClientReader { pub struct ClientReader {
inner: Rc<UnsafeCell<Inner>>, inner: Rc<UnsafeCell<Inner>>,
max_size: usize, max_size: usize,
no_masking: bool,
} }
impl fmt::Debug for ClientReader { impl fmt::Debug for ClientReader {
@@ -445,13 +460,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 inner = self.as_mut(); let inner = self.as_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();