diff --git a/.travis.yml b/.travis.yml index feae30596..fd7dfd4e5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,7 +26,7 @@ script: - | if [[ "$TRAVIS_RUST_VERSION" == "nightly" ]]; then RUSTFLAGS="--cfg procmacro2_semver_exempt" cargo install -f cargo-tarpaulin - RUST_BACKTRACE=1 cargo tarpaulin --out Xml + RUST_BACKTRACE=1 cargo tarpaulin --features="ssl" --out Xml bash <(curl -s https://codecov.io/bash) echo "Uploaded code coverage" fi diff --git a/Cargo.toml b/Cargo.toml index b3adfa823..af19cacaf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,8 +6,8 @@ description = "Actix http" readme = "README.md" keywords = ["http", "web", "framework", "async", "futures"] homepage = "https://actix.rs" -repository = "https://github.com/actix/actix-web.git" -documentation = "https://actix.rs/api/actix-web/stable/actix_web/" +repository = "https://github.com/actix/actix-http.git" +documentation = "https://actix.rs/api/actix-http/stable/actix_http/" categories = ["network-programming", "asynchronous", "web-programming::http-server", "web-programming::websocket"] @@ -20,7 +20,7 @@ features = ["session"] [badges] travis-ci = { repository = "actix/actix-web", branch = "master" } -appveyor = { repository = "fafhrd91/actix-web-hdy9d" } +appveyor = { repository = "actix/actix-http-hdy9d" } codecov = { repository = "actix/actix-web", branch = "master", service = "github" } [lib] diff --git a/README.md b/README.md index be8160968..4f9e44b90 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,13 @@ -# Actix http [![Build Status](https://travis-ci.org/fafhrd91/actix-http.svg?branch=master)](https://travis-ci.org/fafhrd91/actix-http) [![Build status](https://ci.appveyor.com/api/projects/status/bwq6923pblqg55gk/branch/master?svg=true)](https://ci.appveyor.com/project/fafhrd91/actix-http/branch/master) [![codecov](https://codecov.io/gh/fafhrd91/actix-http/branch/master/graph/badge.svg)](https://codecov.io/gh/fafhrd91/actix-http) [![crates.io](https://meritbadge.herokuapp.com/actix-web)](https://crates.io/crates/actix-web) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +# Actix http [![Build Status](https://travis-ci.org/actix/actix-http.svg?branch=master)](https://travis-ci.org/actix/actix-http) [![Build status](https://ci.appveyor.com/api/projects/status/bwq6923pblqg55gk/branch/master?svg=true)](https://ci.appveyor.com/project/actix/actix-http/branch/master) [![codecov](https://codecov.io/gh/actix/actix-http/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-http) [![crates.io](https://meritbadge.herokuapp.com/actix-web)](https://crates.io/crates/actix-web) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) Actix http ## Documentation & community resources * [User Guide](https://actix.rs/docs/) -* [API Documentation (Development)](https://actix.rs/actix-http/actix_http/) -* [API Documentation (Releases)](https://actix.rs/api/actix-http/stable/actix_http/) +* [API Documentation](https://docs.rs/actix-http/) * [Chat on gitter](https://gitter.im/actix/actix) -* Cargo package: [actix-http](https://crates.io/crates/actix-web) +* Cargo package: [actix-http](https://crates.io/crates/actix-http) * Minimum supported Rust version: 1.26 or later ## Example diff --git a/src/body.rs b/src/body.rs index 4b71e9bb9..12e2d0345 100644 --- a/src/body.rs +++ b/src/body.rs @@ -158,7 +158,7 @@ impl From<&'static str> for Body { impl From<&'static [u8]> for Body { fn from(s: &'static [u8]) -> Body { - Body::Bytes(Bytes::from_static(s.as_ref())) + Body::Bytes(Bytes::from_static(s)) } } diff --git a/src/client/connector.rs b/src/client/connector.rs index b573181ba..5b5356c75 100644 --- a/src/client/connector.rs +++ b/src/client/connector.rs @@ -47,9 +47,7 @@ impl Default for Connector { ssl.build() } #[cfg(not(feature = "ssl"))] - { - () - } + {} }; Connector { diff --git a/src/client/error.rs b/src/client/error.rs index e27a83d85..6c91ff976 100644 --- a/src/client/error.rs +++ b/src/client/error.rs @@ -65,12 +65,8 @@ impl From> for ConnectorError { fn from(err: HandshakeError) -> ConnectorError { match err { HandshakeError::SetupFailure(stack) => SslError::from(stack).into(), - HandshakeError::Failure(stream) => { - SslError::from(stream.into_error()).into() - } - HandshakeError::WouldBlock(stream) => { - SslError::from(stream.into_error()).into() - } + HandshakeError::Failure(stream) => stream.into_error().into(), + HandshakeError::WouldBlock(stream) => stream.into_error().into(), } } } diff --git a/src/client/h1proto.rs b/src/client/h1proto.rs index ed3c66d62..86fc10b84 100644 --- a/src/client/h1proto.rs +++ b/src/client/h1proto.rs @@ -26,9 +26,9 @@ where B: MessageBody, { let io = H1Connection { + created, + pool, io: Some(io), - created: created, - pool: pool, }; let len = body.length(); diff --git a/src/client/h2proto.rs b/src/client/h2proto.rs index fe42b909c..e3d5be0b0 100644 --- a/src/client/h2proto.rs +++ b/src/client/h2proto.rs @@ -91,25 +91,25 @@ impl Future for SendBody { type Error = SendRequestError; fn poll(&mut self) -> Poll { - if self.buf.is_none() { - match self.body.poll_next() { - Ok(Async::Ready(Some(buf))) => { - self.send.reserve_capacity(buf.len()); - self.buf = Some(buf); - } - Ok(Async::Ready(None)) => { - if let Err(e) = self.send.send_data(Bytes::new(), true) { - return Err(e.into()); - } - self.send.reserve_capacity(0); - return Ok(Async::Ready(())); - } - Ok(Async::NotReady) => return Ok(Async::NotReady), - Err(e) => return Err(e.into()), - } - } - loop { + if self.buf.is_none() { + match self.body.poll_next() { + Ok(Async::Ready(Some(buf))) => { + self.send.reserve_capacity(buf.len()); + self.buf = Some(buf); + } + Ok(Async::Ready(None)) => { + if let Err(e) = self.send.send_data(Bytes::new(), true) { + return Err(e.into()); + } + self.send.reserve_capacity(0); + return Ok(Async::Ready(())); + } + Ok(Async::NotReady) => return Ok(Async::NotReady), + Err(e) => return Err(e.into()), + } + } + match self.send.poll_capacity() { Ok(Async::NotReady) => return Ok(Async::NotReady), Ok(Async::Ready(None)) => return Ok(Async::Ready(())), @@ -125,7 +125,7 @@ impl Future for SendBody { self.send.reserve_capacity(buf.len()); self.buf = Some(buf); } - return self.poll(); + continue; } } Err(e) => return Err(e.into()), diff --git a/src/client/pool.rs b/src/client/pool.rs index 089c2627a..425e89395 100644 --- a/src/client/pool.rs +++ b/src/client/pool.rs @@ -248,7 +248,7 @@ where } match self.fut.poll() { - Err(err) => Err(err.into()), + Err(err) => Err(err), Ok(Async::Ready((_, io, proto))) => { let _ = self.inner.take(); if proto == Protocol::Http1 { @@ -259,7 +259,7 @@ where ))) } else { self.h2 = Some(handshake(io)); - return self.poll(); + self.poll() } } Ok(Async::NotReady) => Ok(Async::NotReady), diff --git a/src/client/response.rs b/src/client/response.rs index 005c0875b..9010a3c56 100644 --- a/src/client/response.rs +++ b/src/client/response.rs @@ -13,6 +13,7 @@ use crate::message::{Head, ResponseHead}; use super::h1proto::Payload; /// Client Response +#[derive(Default)] pub struct ClientResponse { pub(crate) head: ResponseHead, pub(crate) payload: RefCell>, diff --git a/src/error.rs b/src/error.rs index 5470534f7..43bf7cfb8 100644 --- a/src/error.rs +++ b/src/error.rs @@ -837,7 +837,7 @@ mod tests { match ParseError::from($from) { e @ $error => { let desc = format!("{}", e); - assert_eq!(desc, $from.description().to_owned()); + assert_eq!(desc, format!("IO error: {}", $from.description())); } _ => unreachable!("{:?}", $from), } @@ -846,7 +846,7 @@ mod tests { #[test] fn test_from() { - // from_and_cause!(io::Error::new(io::ErrorKind::Other, "other") => ParseError::Io(..)); + from_and_cause!(io::Error::new(io::ErrorKind::Other, "other") => ParseError::Io(..)); from!(httparse::Error::HeaderName => ParseError::Header); from!(httparse::Error::HeaderName => ParseError::Header); from!(httparse::Error::HeaderValue => ParseError::Header); diff --git a/src/h1/decoder.rs b/src/h1/decoder.rs index 7c17e909a..480864787 100644 --- a/src/h1/decoder.rs +++ b/src/h1/decoder.rs @@ -273,16 +273,14 @@ impl MessageType for ClientResponse { // message payload let decoder = if let PayloadLength::Payload(pl) = len { pl + } else if status == StatusCode::SWITCHING_PROTOCOLS { + // switching protocol or connect + PayloadType::Stream(PayloadDecoder::eof()) + } else if src.len() >= MAX_BUFFER_SIZE { + error!("MAX_BUFFER_SIZE unprocessed data reached, closing"); + return Err(ParseError::TooLarge); } else { - if status == StatusCode::SWITCHING_PROTOCOLS { - // switching protocol or connect - PayloadType::Stream(PayloadDecoder::eof()) - } else if src.len() >= MAX_BUFFER_SIZE { - error!("MAX_BUFFER_SIZE unprocessed data reached, closing"); - return Err(ParseError::TooLarge); - } else { - PayloadType::None - } + PayloadType::None }; msg.head.status = status; @@ -670,71 +668,6 @@ mod tests { }}; } - struct Buffer { - buf: Bytes, - err: Option, - } - - impl Buffer { - fn new(data: &'static str) -> Buffer { - Buffer { - buf: Bytes::from(data), - err: None, - } - } - } - - impl AsyncRead for Buffer {} - impl io::Read for Buffer { - fn read(&mut self, dst: &mut [u8]) -> Result { - if self.buf.is_empty() { - if self.err.is_some() { - Err(self.err.take().unwrap()) - } else { - Err(io::Error::new(io::ErrorKind::WouldBlock, "")) - } - } else { - let size = cmp::min(self.buf.len(), dst.len()); - let b = self.buf.split_to(size); - dst[..size].copy_from_slice(&b); - Ok(size) - } - } - } - - impl io::Write for Buffer { - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - } - impl AsyncWrite for Buffer { - fn shutdown(&mut self) -> Poll<(), io::Error> { - Ok(Async::Ready(())) - } - fn write_buf(&mut self, _: &mut B) -> Poll { - Ok(Async::NotReady) - } - } - - // #[test] - // fn test_req_parse_err() { - // let mut sys = System::new("test"); - // let _ = sys.block_on(future::lazy(|| { - // let buf = Buffer::new("GET /test HTTP/1\r\n\r\n"); - // let readbuf = BytesMut::new(); - - // let mut h1 = Dispatcher::new(buf, |req| ok(Response::Ok().finish())); - // assert!(h1.poll_io().is_ok()); - // assert!(h1.poll_io().is_ok()); - // assert!(h1.flags.contains(Flags::READ_DISCONNECTED)); - // assert_eq!(h1.tasks.len(), 1); - // future::ok::<_, ()>(()) - // })); - // } - #[test] fn test_parse() { let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n\r\n"); diff --git a/src/h1/dispatcher.rs b/src/h1/dispatcher.rs index 569b1deeb..f66955af1 100644 --- a/src/h1/dispatcher.rs +++ b/src/h1/dispatcher.rs @@ -312,7 +312,7 @@ where Message::Item(req) => { match self.framed.get_codec().message_type() { MessageType::Payload => { - let (ps, pl) = Payload::new(false); + let (ps, pl) = Payload::create(false); *req.inner.payload.borrow_mut() = Some(pl); self.payload = Some(ps); } @@ -417,10 +417,10 @@ where // start shutdown timer if let Some(deadline) = self.config.client_disconnect_timer() { - self.ka_timer.as_mut().map(|timer| { + if let Some(timer) = self.ka_timer.as_mut() { timer.reset(deadline); let _ = timer.poll(); - }); + } } else { return Ok(()); } @@ -439,17 +439,14 @@ where self.state = State::None; } } else if let Some(deadline) = self.config.keep_alive_expire() { - self.ka_timer.as_mut().map(|timer| { + if let Some(timer) = self.ka_timer.as_mut() { timer.reset(deadline); let _ = timer.poll(); - }); + } } - } else { - let expire = self.ka_expire; - self.ka_timer.as_mut().map(|timer| { - timer.reset(expire); - let _ = timer.poll(); - }); + } else if let Some(timer) = self.ka_timer.as_mut() { + timer.reset(self.ka_expire); + let _ = timer.poll(); } } Async::NotReady => (), @@ -526,3 +523,90 @@ where } } } + +#[cfg(test)] +mod tests { + use std::{cmp, io}; + + use actix_codec::{AsyncRead, AsyncWrite}; + use actix_service::IntoService; + use bytes::{Buf, Bytes, BytesMut}; + use futures::future::{lazy, ok}; + + use super::*; + use crate::error::Error; + + struct Buffer { + buf: Bytes, + err: Option, + } + + impl Buffer { + fn new(data: &'static str) -> Buffer { + Buffer { + buf: Bytes::from(data), + err: None, + } + } + } + + impl AsyncRead for Buffer {} + impl io::Read for Buffer { + fn read(&mut self, dst: &mut [u8]) -> Result { + if self.buf.is_empty() { + if self.err.is_some() { + Err(self.err.take().unwrap()) + } else { + Err(io::Error::new(io::ErrorKind::WouldBlock, "")) + } + } else { + let size = cmp::min(self.buf.len(), dst.len()); + let b = self.buf.split_to(size); + dst[..size].copy_from_slice(&b); + Ok(size) + } + } + } + + impl io::Write for Buffer { + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + } + impl AsyncWrite for Buffer { + fn shutdown(&mut self) -> Poll<(), io::Error> { + Ok(Async::Ready(())) + } + fn write_buf(&mut self, _: &mut B) -> Poll { + Ok(Async::NotReady) + } + } + + #[test] + fn test_req_parse_err() { + let mut sys = actix_rt::System::new("test"); + let _ = sys.block_on(lazy(|| { + let buf = Buffer::new("GET /test HTTP/1\r\n\r\n"); + let readbuf = BytesMut::new(); + + let mut h1 = Dispatcher::new( + buf, + ServiceConfig::default(), + (|req| ok::<_, Error>(Response::Ok().finish())).into_service(), + ); + assert!(h1.poll().is_ok()); + assert!(h1.poll().is_ok()); + assert!(h1 + .inner + .as_ref() + .unwrap() + .flags + .contains(Flags::DISCONNECTED)); + // assert_eq!(h1.tasks.len(), 1); + ok::<_, ()>(()) + })); + } +} diff --git a/src/h1/encoder.rs b/src/h1/encoder.rs index 92456520a..32c8f9c48 100644 --- a/src/h1/encoder.rs +++ b/src/h1/encoder.rs @@ -104,10 +104,10 @@ pub(crate) trait MessageType: Sized { let mut remaining = dst.remaining_mut(); let mut buf = unsafe { &mut *(dst.bytes_mut() as *mut [u8]) }; for (key, value) in self.headers() { - match key { - &CONNECTION => continue, - &TRANSFER_ENCODING | &CONTENT_LENGTH if skip_len => continue, - &DATE => { + match *key { + CONNECTION => continue, + TRANSFER_ENCODING | CONTENT_LENGTH if skip_len => continue, + DATE => { has_date = true; } _ => (), diff --git a/src/h1/service.rs b/src/h1/service.rs index 7c2589cc8..d8f63a323 100644 --- a/src/h1/service.rs +++ b/src/h1/service.rs @@ -272,6 +272,7 @@ where } /// `NewService` implementation for `OneRequestService` service +#[derive(Default)] pub struct OneRequest { config: ServiceConfig, _t: PhantomData, diff --git a/src/lib.rs b/src/lib.rs index 0dbaee6aa..442637251 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,6 +59,12 @@ //! * `session` - enables session support, includes `ring` crate as //! dependency //! +#![allow( + clippy::type_complexity, + clippy::new_without_default, + clippy::new_without_default_derive +)] + #[macro_use] extern crate log; diff --git a/src/payload.rs b/src/payload.rs index ea266f70b..6665a0e41 100644 --- a/src/payload.rs +++ b/src/payload.rs @@ -42,7 +42,7 @@ impl Payload { /// * `PayloadSender` - *Sender* side of the stream /// /// * `Payload` - *Receiver* side of the stream - pub fn new(eof: bool) -> (PayloadSender, Payload) { + pub fn create(eof: bool) -> (PayloadSender, Payload) { let shared = Rc::new(RefCell::new(Inner::new(eof))); ( @@ -540,7 +540,7 @@ mod tests { Runtime::new() .unwrap() .block_on(lazy(|| { - let (_, payload) = Payload::new(false); + let (_, payload) = Payload::create(false); let mut payload = PayloadBuffer::new(payload); assert_eq!(payload.len, 0); @@ -557,7 +557,7 @@ mod tests { Runtime::new() .unwrap() .block_on(lazy(|| { - let (mut sender, payload) = Payload::new(false); + let (mut sender, payload) = Payload::create(false); let mut payload = PayloadBuffer::new(payload); assert_eq!(Async::NotReady, payload.readany().ok().unwrap()); @@ -582,7 +582,7 @@ mod tests { Runtime::new() .unwrap() .block_on(lazy(|| { - let (mut sender, payload) = Payload::new(false); + let (mut sender, payload) = Payload::create(false); let mut payload = PayloadBuffer::new(payload); assert_eq!(Async::NotReady, payload.readany().ok().unwrap()); @@ -600,7 +600,7 @@ mod tests { Runtime::new() .unwrap() .block_on(lazy(|| { - let (mut sender, payload) = Payload::new(false); + let (mut sender, payload) = Payload::create(false); let mut payload = PayloadBuffer::new(payload); sender.feed_data(Bytes::from("line1")); @@ -629,7 +629,7 @@ mod tests { Runtime::new() .unwrap() .block_on(lazy(|| { - let (mut sender, payload) = Payload::new(false); + let (mut sender, payload) = Payload::create(false); let mut payload = PayloadBuffer::new(payload); assert_eq!(Async::NotReady, payload.read_exact(2).ok().unwrap()); @@ -663,7 +663,7 @@ mod tests { Runtime::new() .unwrap() .block_on(lazy(|| { - let (mut sender, payload) = Payload::new(false); + let (mut sender, payload) = Payload::create(false); let mut payload = PayloadBuffer::new(payload); assert_eq!(Async::NotReady, payload.read_until(b"ne").ok().unwrap()); @@ -697,7 +697,7 @@ mod tests { Runtime::new() .unwrap() .block_on(lazy(|| { - let (_, mut payload) = Payload::new(false); + let (_, mut payload) = Payload::create(false); payload.unread_data(Bytes::from("data")); assert!(!payload.is_empty()); diff --git a/src/response.rs b/src/response.rs index 5e1c0d076..a4b65f2b4 100644 --- a/src/response.rs +++ b/src/response.rs @@ -109,7 +109,7 @@ impl Response { /// Constructs a response with body #[inline] pub fn with_body(status: StatusCode, body: B) -> Response { - ResponsePool::with_body(status, body.into()) + ResponsePool::with_body(status, body) } /// The source `error` for this response @@ -644,7 +644,7 @@ impl ResponseBuilder { } #[inline] -#[cfg_attr(feature = "cargo-clippy", allow(borrowed_box))] +#[allow(clippy::borrowed_box)] fn parts<'a>( parts: &'a mut Option>, err: &Option, diff --git a/src/service.rs b/src/service.rs index f98234e76..57828187d 100644 --- a/src/service.rs +++ b/src/service.rs @@ -85,7 +85,7 @@ where fn poll(&mut self) -> Poll { if let Some(res) = self.res.take() { - if let Err(_) = self.framed.as_mut().unwrap().force_send(res) { + if self.framed.as_mut().unwrap().force_send(res).is_err() { return Err((self.err.take().unwrap(), self.framed.take().unwrap())); } } @@ -232,6 +232,6 @@ where break; } } - return Ok(Async::Ready(self.framed.take().unwrap())); + Ok(Async::Ready(self.framed.take().unwrap())) } } diff --git a/src/test.rs b/src/test.rs index 4b7e30ac3..a26f31e59 100644 --- a/src/test.rs +++ b/src/test.rs @@ -144,9 +144,8 @@ impl TestRequest { uri, version, headers, - _cookies: _, payload, - prefix: _, + .. } = self; let mut req = Request::new(); diff --git a/src/ws/mask.rs b/src/ws/mask.rs index e9bfb3d56..157375417 100644 --- a/src/ws/mask.rs +++ b/src/ws/mask.rs @@ -1,5 +1,5 @@ //! This is code from [Tungstenite project](https://github.com/snapview/tungstenite-rs) -#![cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))] +#![allow(clippy::cast_ptr_alignment)] use std::ptr::copy_nonoverlapping; use std::slice; @@ -19,7 +19,7 @@ impl<'a> ShortSlice<'a> { /// Faster version of `apply_mask()` which operates on 8-byte blocks. #[inline] -#[cfg_attr(feature = "cargo-clippy", allow(cast_lossless))] +#[allow(clippy::cast_lossless)] pub(crate) fn apply_mask(buf: &mut [u8], mask_u32: u32) { // Extend the mask to 64 bits let mut mask_u64 = ((mask_u32 as u64) << 32) | (mask_u32 as u64); @@ -50,7 +50,7 @@ pub(crate) fn apply_mask(buf: &mut [u8], mask_u32: u32) { // TODO: copy_nonoverlapping here compiles to call memcpy. While it is not so // inefficient, it could be done better. The compiler does not understand that // a `ShortSlice` must be smaller than a u64. -#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))] +#[allow(clippy::needless_pass_by_value)] fn xor_short(buf: ShortSlice, mask: u64) { // Unsafe: we know that a `ShortSlice` fits in a u64 unsafe {