1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-28 01:32:57 +01:00

various optimizations

This commit is contained in:
Nikolay Kim 2017-12-13 16:44:35 -08:00
parent 81f8da03ae
commit 96f598f2c4
13 changed files with 328 additions and 232 deletions

View File

@ -48,6 +48,7 @@ pub enum Binary {
impl Body { impl Body {
/// Does this body streaming. /// Does this body streaming.
#[inline]
pub fn is_streaming(&self) -> bool { pub fn is_streaming(&self) -> bool {
match *self { match *self {
Body::Streaming(_) | Body::StreamingContext Body::Streaming(_) | Body::StreamingContext
@ -57,6 +58,7 @@ impl Body {
} }
/// Is this binary body. /// Is this binary body.
#[inline]
pub fn is_binary(&self) -> bool { pub fn is_binary(&self) -> bool {
match *self { match *self {
Body::Binary(_) => true, Body::Binary(_) => true,
@ -114,10 +116,12 @@ impl<T> From<T> for Body where T: Into<Binary>{
} }
impl Binary { impl Binary {
#[inline]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.len() == 0 self.len() == 0
} }
#[inline]
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
match *self { match *self {
Binary::Bytes(ref bytes) => bytes.len(), Binary::Bytes(ref bytes) => bytes.len(),

View File

@ -1,68 +0,0 @@
use std::cell::RefCell;
use std::fmt::{self, Write};
use std::str;
use time::{self, Duration};
// "Sun, 06 Nov 1994 08:49:37 GMT".len()
pub const DATE_VALUE_LENGTH: usize = 29;
pub fn extend(dst: &mut [u8]) {
CACHED.with(|cache| {
let mut cache = cache.borrow_mut();
let now = time::get_time();
if now > cache.next_update {
cache.update(now);
}
dst.copy_from_slice(cache.buffer());
})
}
struct CachedDate {
bytes: [u8; DATE_VALUE_LENGTH],
pos: usize,
next_update: time::Timespec,
}
thread_local!(static CACHED: RefCell<CachedDate> = RefCell::new(CachedDate {
bytes: [0; DATE_VALUE_LENGTH],
pos: 0,
next_update: time::Timespec::new(0, 0),
}));
impl CachedDate {
fn buffer(&self) -> &[u8] {
&self.bytes[..]
}
fn update(&mut self, now: time::Timespec) {
self.pos = 0;
write!(self, "{}", time::at_utc(now).rfc822()).unwrap();
assert_eq!(self.pos, DATE_VALUE_LENGTH);
self.next_update = now + Duration::seconds(1);
self.next_update.nsec = 0;
}
}
impl fmt::Write for CachedDate {
fn write_str(&mut self, s: &str) -> fmt::Result {
let len = s.len();
self.bytes[self.pos..self.pos + len].copy_from_slice(s.as_bytes());
self.pos += len;
Ok(())
}
}
#[test]
fn test_date_len() {
assert_eq!(DATE_VALUE_LENGTH, "Sun, 06 Nov 1994 08:49:37 GMT".len());
}
#[test]
fn test_date() {
let mut buf1 = [0u8; 29];
extend(&mut buf1);
let mut buf2 = [0u8; 29];
extend(&mut buf2);
assert_eq!(buf1, buf2);
}

View File

@ -13,6 +13,7 @@ use flate2::write::{GzEncoder, DeflateDecoder, DeflateEncoder};
use brotli2::write::{BrotliDecoder, BrotliEncoder}; use brotli2::write::{BrotliDecoder, BrotliEncoder};
use bytes::{Bytes, BytesMut, BufMut, Writer}; use bytes::{Bytes, BytesMut, BufMut, Writer};
use utils;
use body::{Body, Binary}; use body::{Body, Binary};
use error::PayloadError; use error::PayloadError;
use httprequest::HttpMessage; use httprequest::HttpMessage;
@ -35,6 +36,14 @@ pub enum ContentEncoding {
} }
impl ContentEncoding { impl ContentEncoding {
fn is_compression(&self) -> bool {
match *self {
ContentEncoding::Identity | ContentEncoding::Auto => false,
_ => true
}
}
fn as_str(&self) -> &'static str { fn as_str(&self) -> &'static str {
match *self { match *self {
ContentEncoding::Br => "br", ContentEncoding::Br => "br",
@ -270,10 +279,10 @@ impl PayloadWriter for EncodedPayload {
Decoder::Gzip(ref mut decoder) => { Decoder::Gzip(ref mut decoder) => {
if decoder.is_none() { if decoder.is_none() {
let mut buf = BytesMut::new(); let mut buf = BytesMut::new();
buf.extend(data); buf.extend_from_slice(&data);
*(decoder.as_mut()) = Some(GzDecoder::new(Wrapper{buf: buf}).unwrap()); *(decoder.as_mut()) = Some(GzDecoder::new(Wrapper{buf: buf}).unwrap());
} else { } else {
decoder.as_mut().as_mut().unwrap().get_mut().buf.extend(data); decoder.as_mut().as_mut().unwrap().get_mut().buf.extend_from_slice(&data);
} }
loop { loop {
@ -362,8 +371,10 @@ impl PayloadEncoder {
} }
encoding => encoding, encoding => encoding,
}; };
resp.headers_mut().insert( if encoding.is_compression() {
CONTENT_ENCODING, HeaderValue::from_static(encoding.as_str())); resp.headers_mut().insert(
CONTENT_ENCODING, HeaderValue::from_static(encoding.as_str()));
}
encoding encoding
} else { } else {
ContentEncoding::Identity ContentEncoding::Identity
@ -400,15 +411,13 @@ impl PayloadEncoder {
let b = enc.get_mut().take(); let b = enc.get_mut().take();
resp.headers_mut().insert( resp.headers_mut().insert(
CONTENT_LENGTH, CONTENT_LENGTH, utils::convert_into_header(b.len()));
HeaderValue::from_str(&b.len().to_string()).unwrap());
*bytes = Binary::from(b); *bytes = Binary::from(b);
encoding = ContentEncoding::Identity; encoding = ContentEncoding::Identity;
TransferEncoding::eof() TransferEncoding::eof()
} else { } else {
resp.headers_mut().insert( resp.headers_mut().insert(
CONTENT_LENGTH, CONTENT_LENGTH, utils::convert_into_header(bytes.len()));
HeaderValue::from_str(&bytes.len().to_string()).unwrap());
TransferEncoding::eof() TransferEncoding::eof()
} }
} }
@ -491,12 +500,14 @@ impl PayloadEncoder {
self.0.is_eof() self.0.is_eof()
} }
#[inline] #[cfg_attr(feature = "cargo-clippy", allow(inline_always))]
#[inline(always)]
pub fn write(&mut self, payload: &[u8]) -> Result<(), io::Error> { pub fn write(&mut self, payload: &[u8]) -> Result<(), io::Error> {
self.0.write(payload) self.0.write(payload)
} }
#[inline] #[cfg_attr(feature = "cargo-clippy", allow(inline_always))]
#[inline(always)]
pub fn write_eof(&mut self) -> Result<(), io::Error> { pub fn write_eof(&mut self) -> Result<(), io::Error> {
self.0.write_eof() self.0.write_eof()
} }
@ -553,6 +564,7 @@ impl ContentEncoder {
} }
} }
#[cfg_attr(feature = "cargo-clippy", allow(inline_always))]
#[inline(always)] #[inline(always)]
pub fn write_eof(&mut self) -> Result<(), io::Error> { pub fn write_eof(&mut self) -> Result<(), io::Error> {
let encoder = mem::replace(self, ContentEncoder::Identity(TransferEncoding::eof())); let encoder = mem::replace(self, ContentEncoder::Identity(TransferEncoding::eof()));
@ -592,6 +604,7 @@ impl ContentEncoder {
} }
} }
#[cfg_attr(feature = "cargo-clippy", allow(inline_always))]
#[inline(always)] #[inline(always)]
pub fn write(&mut self, data: &[u8]) -> Result<(), io::Error> { pub fn write(&mut self, data: &[u8]) -> Result<(), io::Error> {
match *self { match *self {
@ -692,11 +705,11 @@ impl TransferEncoding {
} }
/// Encode message. Return `EOF` state of encoder /// Encode message. Return `EOF` state of encoder
#[inline(always)] #[inline]
pub fn encode(&mut self, msg: &[u8]) -> bool { pub fn encode(&mut self, msg: &[u8]) -> bool {
match self.kind { match self.kind {
TransferEncodingKind::Eof => { TransferEncodingKind::Eof => {
self.buffer.extend(msg); self.buffer.extend_from_slice(msg);
msg.is_empty() msg.is_empty()
}, },
TransferEncodingKind::Chunked(ref mut eof) => { TransferEncodingKind::Chunked(ref mut eof) => {
@ -706,11 +719,11 @@ impl TransferEncoding {
if msg.is_empty() { if msg.is_empty() {
*eof = true; *eof = true;
self.buffer.extend(b"0\r\n\r\n"); self.buffer.extend_from_slice(b"0\r\n\r\n");
} else { } else {
write!(self.buffer, "{:X}\r\n", msg.len()).unwrap(); write!(self.buffer, "{:X}\r\n", msg.len()).unwrap();
self.buffer.extend(msg); self.buffer.extend_from_slice(msg);
self.buffer.extend(b"\r\n"); self.buffer.extend_from_slice(b"\r\n");
} }
*eof *eof
}, },
@ -720,7 +733,7 @@ impl TransferEncoding {
} }
let max = cmp::min(*remaining, msg.len() as u64); let max = cmp::min(*remaining, msg.len() as u64);
trace!("sized write = {}", max); trace!("sized write = {}", max);
self.buffer.extend(msg[..max as usize].as_ref()); self.buffer.extend_from_slice(msg[..max as usize].as_ref());
*remaining -= max as u64; *remaining -= max as u64;
trace!("encoded {} bytes, remaining = {}", max, remaining); trace!("encoded {} bytes, remaining = {}", max, remaining);
@ -730,14 +743,14 @@ impl TransferEncoding {
} }
/// Encode eof. Return `EOF` state of encoder /// Encode eof. Return `EOF` state of encoder
#[inline(always)] #[inline]
pub fn encode_eof(&mut self) { pub fn encode_eof(&mut self) {
match self.kind { match self.kind {
TransferEncodingKind::Eof | TransferEncodingKind::Length(_) => (), TransferEncodingKind::Eof | TransferEncodingKind::Length(_) => (),
TransferEncodingKind::Chunked(ref mut eof) => { TransferEncodingKind::Chunked(ref mut eof) => {
if !*eof { if !*eof {
*eof = true; *eof = true;
self.buffer.extend(b"0\r\n\r\n"); self.buffer.extend_from_slice(b"0\r\n\r\n");
} }
}, },
} }

View File

@ -88,8 +88,8 @@ impl<T, H> Http1<T, H>
keepalive_timer: None } keepalive_timer: None }
} }
pub fn into_inner(mut self) -> (Rc<Vec<H>>, T, Option<SocketAddr>, Bytes) { pub fn into_inner(self) -> (Rc<Vec<H>>, T, Option<SocketAddr>, Bytes) {
(self.handlers, self.stream.unwrap(), self.addr, self.read_buf.freeze()) (self.handlers, self.stream.into_inner(), self.addr, self.read_buf.freeze())
} }
pub fn poll(&mut self) -> Poll<Http1Result, ()> { pub fn poll(&mut self) -> Poll<Http1Result, ()> {
@ -129,7 +129,7 @@ impl<T, H> Http1<T, H>
} else { } else {
self.flags.remove(Flags::KEEPALIVE); self.flags.remove(Flags::KEEPALIVE);
} }
self.stream = H1Writer::new(self.stream.unwrap()); self.stream.reset();
item.flags.insert(EntryFlags::EOF); item.flags.insert(EntryFlags::EOF);
if ready { if ready {
@ -185,7 +185,7 @@ impl<T, H> Http1<T, H>
// read incoming data // read incoming data
while !self.flags.contains(Flags::ERROR) && !self.flags.contains(Flags::H2) && while !self.flags.contains(Flags::ERROR) && !self.flags.contains(Flags::H2) &&
self.tasks.len() < MAX_PIPELINED_MESSAGES { self.tasks.len() < MAX_PIPELINED_MESSAGES {
match self.reader.parse(self.stream.get_mut(), &mut self.read_buf) { match self.reader.parse(self.stream.get_mut(), &mut self.read_buf) {
Ok(Async::Ready(Item::Http1(mut req))) => { Ok(Async::Ready(Item::Http1(mut req))) => {
not_ready = false; not_ready = false;
@ -252,12 +252,12 @@ impl<T, H> Http1<T, H>
if self.flags.contains(Flags::KEEPALIVE) { if self.flags.contains(Flags::KEEPALIVE) {
if self.keepalive_timer.is_none() { if self.keepalive_timer.is_none() {
trace!("Start keep-alive timer"); trace!("Start keep-alive timer");
let mut timeout = Timeout::new( let mut to = Timeout::new(
Duration::new(KEEPALIVE_PERIOD, 0), Duration::new(KEEPALIVE_PERIOD, 0),
Arbiter::handle()).unwrap(); Arbiter::handle()).unwrap();
// register timeout // register timeout
let _ = timeout.poll(); let _ = to.poll();
self.keepalive_timer = Some(timeout); self.keepalive_timer = Some(to);
} }
} else { } else {
// keep-alive disable, drop connection // keep-alive disable, drop connection
@ -482,8 +482,7 @@ impl Reader {
} }
} }
fn parse_message(buf: &mut BytesMut) -> Result<Message, ParseError> fn parse_message(buf: &mut BytesMut) -> Result<Message, ParseError> {
{
if buf.is_empty() { if buf.is_empty() {
return Ok(Message::NotReady); return Ok(Message::NotReady);
} }

View File

@ -4,7 +4,7 @@ use tokio_io::AsyncWrite;
use http::Version; use http::Version;
use http::header::{HeaderValue, CONNECTION, CONTENT_TYPE, DATE}; use http::header::{HeaderValue, CONNECTION, CONTENT_TYPE, DATE};
use date; use utils;
use body::Body; use body::Body;
use encoding::PayloadEncoder; use encoding::PayloadEncoder;
use httprequest::HttpMessage; use httprequest::HttpMessage;
@ -45,7 +45,7 @@ bitflags! {
pub(crate) struct H1Writer<T: AsyncWrite> { pub(crate) struct H1Writer<T: AsyncWrite> {
flags: Flags, flags: Flags,
stream: Option<T>, stream: T,
encoder: PayloadEncoder, encoder: PayloadEncoder,
written: u64, written: u64,
headers_size: u32, headers_size: u32,
@ -56,7 +56,7 @@ impl<T: AsyncWrite> H1Writer<T> {
pub fn new(stream: T) -> H1Writer<T> { pub fn new(stream: T) -> H1Writer<T> {
H1Writer { H1Writer {
flags: Flags::empty(), flags: Flags::empty(),
stream: Some(stream), stream: stream,
encoder: PayloadEncoder::default(), encoder: PayloadEncoder::default(),
written: 0, written: 0,
headers_size: 0, headers_size: 0,
@ -64,11 +64,16 @@ impl<T: AsyncWrite> H1Writer<T> {
} }
pub fn get_mut(&mut self) -> &mut T { pub fn get_mut(&mut self) -> &mut T {
self.stream.as_mut().unwrap() &mut self.stream
} }
pub fn unwrap(&mut self) -> T { pub fn reset(&mut self) {
self.stream.take().unwrap() self.written = 0;
self.flags = Flags::empty();
}
pub fn into_inner(self) -> T {
self.stream
} }
pub fn disconnected(&mut self) { pub fn disconnected(&mut self) {
@ -82,22 +87,20 @@ impl<T: AsyncWrite> H1Writer<T> {
fn write_to_stream(&mut self) -> Result<WriterState, io::Error> { fn write_to_stream(&mut self) -> Result<WriterState, io::Error> {
let buffer = self.encoder.get_mut(); let buffer = self.encoder.get_mut();
if let Some(ref mut stream) = self.stream { while !buffer.is_empty() {
while !buffer.is_empty() { match self.stream.write(buffer.as_ref()) {
match stream.write(buffer.as_ref()) { Ok(n) => {
Ok(n) => { buffer.split_to(n);
buffer.split_to(n); self.written += n as u64;
self.written += n as u64; },
}, Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { if buffer.len() > MAX_WRITE_BUFFER_SIZE {
if buffer.len() > MAX_WRITE_BUFFER_SIZE { return Ok(WriterState::Pause)
return Ok(WriterState::Pause) } else {
} else { return Ok(WriterState::Done)
return Ok(WriterState::Done)
}
} }
Err(err) => return Err(err),
} }
Err(err) => return Err(err),
} }
} }
Ok(WriterState::Done) Ok(WriterState::Done)
@ -143,50 +146,47 @@ impl<T: AsyncWrite> Writer for H1Writer<T> {
// render message // render message
{ {
let buffer = self.encoder.get_mut(); let mut buffer = self.encoder.get_mut();
if let Body::Binary(ref bytes) = *msg.body() { if let Body::Binary(ref bytes) = *msg.body() {
buffer.reserve(130 + msg.headers().len() * AVERAGE_HEADER_SIZE + bytes.len()); buffer.reserve(150 + msg.headers().len() * AVERAGE_HEADER_SIZE + bytes.len());
} else { } else {
buffer.reserve(130 + msg.headers().len() * AVERAGE_HEADER_SIZE); buffer.reserve(150 + msg.headers().len() * AVERAGE_HEADER_SIZE);
} }
match version { match version {
Version::HTTP_11 => buffer.extend(b"HTTP/1.1 "), Version::HTTP_11 => buffer.extend_from_slice(b"HTTP/1.1 "),
Version::HTTP_2 => buffer.extend(b"HTTP/2.0 "), Version::HTTP_2 => buffer.extend_from_slice(b"HTTP/2.0 "),
Version::HTTP_10 => buffer.extend(b"HTTP/1.0 "), Version::HTTP_10 => buffer.extend_from_slice(b"HTTP/1.0 "),
Version::HTTP_09 => buffer.extend(b"HTTP/0.9 "), Version::HTTP_09 => buffer.extend_from_slice(b"HTTP/0.9 "),
} }
buffer.extend(msg.status().as_u16().to_string().as_bytes()); utils::convert_u16(msg.status().as_u16(), &mut buffer);
buffer.extend(b" "); buffer.extend_from_slice(b" ");
buffer.extend(msg.reason().as_bytes()); buffer.extend_from_slice(msg.reason().as_bytes());
buffer.extend(b"\r\n"); buffer.extend_from_slice(b"\r\n");
for (key, value) in msg.headers() { for (key, value) in msg.headers() {
let t: &[u8] = key.as_ref(); let t: &[u8] = key.as_ref();
buffer.extend(t); buffer.extend_from_slice(t);
buffer.extend(b": "); buffer.extend_from_slice(b": ");
buffer.extend(value.as_ref()); buffer.extend_from_slice(value.as_ref());
buffer.extend(b"\r\n"); buffer.extend_from_slice(b"\r\n");
} }
// using http::h1::date is quite a lot faster than generating // using utils::date is quite a lot faster
// a unique Date header each time like req/s goes up about 10%
if !msg.headers().contains_key(DATE) { if !msg.headers().contains_key(DATE) {
buffer.reserve(date::DATE_VALUE_LENGTH + 8); buffer.reserve(utils::DATE_VALUE_LENGTH + 8);
buffer.extend(b"Date: "); buffer.extend_from_slice(b"Date: ");
let mut bytes = [0u8; 29]; utils::extend(&mut buffer);
date::extend(&mut bytes[..]); buffer.extend_from_slice(b"\r\n");
buffer.extend(&bytes);
buffer.extend(b"\r\n");
} }
// default content-type // default content-type
if !msg.headers().contains_key(CONTENT_TYPE) { if !msg.headers().contains_key(CONTENT_TYPE) {
buffer.extend(b"ContentType: application/octet-stream\r\n".as_ref()); buffer.extend_from_slice(b"ContentType: application/octet-stream\r\n");
} }
// msg eof // msg eof
buffer.extend(b"\r\n"); buffer.extend_from_slice(b"\r\n");
self.headers_size = buffer.len() as u32; self.headers_size = buffer.len() as u32;
} }

View File

@ -1,12 +1,12 @@
use std::{io, cmp}; use std::{io, cmp};
use bytes::Bytes; use bytes::{Bytes, BytesMut};
use futures::{Async, Poll}; use futures::{Async, Poll};
use http2::{Reason, SendStream}; use http2::{Reason, SendStream};
use http2::server::Respond; use http2::server::Respond;
use http::{Version, HttpTryFrom, Response}; use http::{Version, HttpTryFrom, Response};
use http::header::{HeaderValue, CONNECTION, CONTENT_TYPE, TRANSFER_ENCODING, DATE}; use http::header::{HeaderValue, CONNECTION, CONTENT_TYPE, TRANSFER_ENCODING, DATE};
use date; use utils;
use body::Body; use body::Body;
use encoding::PayloadEncoder; use encoding::PayloadEncoder;
use httprequest::HttpMessage; use httprequest::HttpMessage;
@ -124,11 +124,10 @@ impl Writer for H2Writer {
msg.headers_mut().remove(CONNECTION); msg.headers_mut().remove(CONNECTION);
msg.headers_mut().remove(TRANSFER_ENCODING); msg.headers_mut().remove(TRANSFER_ENCODING);
// using http::h1::date is quite a lot faster than generating // using utils::date is quite a lot faster
// a unique Date header each time like req/s goes up about 10%
if !msg.headers().contains_key(DATE) { if !msg.headers().contains_key(DATE) {
let mut bytes = [0u8; 29]; let mut bytes = BytesMut::with_capacity(29);
date::extend(&mut bytes[..]); utils::extend(&mut bytes);
msg.headers_mut().insert(DATE, HeaderValue::try_from(&bytes[..]).unwrap()); msg.headers_mut().insert(DATE, HeaderValue::try_from(&bytes[..]).unwrap());
} }

View File

@ -515,7 +515,7 @@ impl Future for UrlEncoded {
Ok(Async::Ready(m)) Ok(Async::Ready(m))
}, },
Ok(Async::Ready(Some(item))) => { Ok(Async::Ready(Some(item))) => {
self.body.extend(item.0); self.body.extend_from_slice(&item.0);
continue continue
}, },
Err(err) => Err(err), Err(err) => Err(err),

View File

@ -47,8 +47,9 @@ impl HttpResponse {
#[inline] #[inline]
pub fn build(status: StatusCode) -> HttpResponseBuilder { pub fn build(status: StatusCode) -> HttpResponseBuilder {
HttpResponseBuilder { HttpResponseBuilder {
parts: Some(Parts::new(status)), response: Some(HttpResponse::new(status, Body::Empty)),
err: None, err: None,
cookies: None,
} }
} }
@ -57,7 +58,7 @@ impl HttpResponse {
pub fn new(status: StatusCode, body: Body) -> HttpResponse { pub fn new(status: StatusCode, body: Body) -> HttpResponse {
HttpResponse { HttpResponse {
version: None, version: None,
headers: Default::default(), headers: HeaderMap::with_capacity(8),
status: status, status: status,
reason: None, reason: None,
body: body, body: body,
@ -213,49 +214,22 @@ impl fmt::Debug for HttpResponse {
} }
} }
#[derive(Debug)]
struct Parts {
version: Option<Version>,
headers: HeaderMap,
status: StatusCode,
reason: Option<&'static str>,
chunked: bool,
encoding: ContentEncoding,
connection_type: Option<ConnectionType>,
cookies: Option<CookieJar>,
}
impl Parts {
fn new(status: StatusCode) -> Self {
Parts {
version: None,
headers: HeaderMap::with_capacity(8),
status: status,
reason: None,
chunked: false,
encoding: ContentEncoding::Auto,
connection_type: None,
cookies: None,
}
}
}
/// An HTTP response builder /// An HTTP response builder
/// ///
/// This type can be used to construct an instance of `HttpResponse` through a /// This type can be used to construct an instance of `HttpResponse` through a
/// builder-like pattern. /// builder-like pattern.
#[derive(Debug)] #[derive(Debug)]
pub struct HttpResponseBuilder { pub struct HttpResponseBuilder {
parts: Option<Parts>, response: Option<HttpResponse>,
err: Option<HttpError>, err: Option<HttpError>,
cookies: Option<CookieJar>,
} }
impl HttpResponseBuilder { impl HttpResponseBuilder {
/// Set the HTTP version of this response. /// Set the HTTP version of this response.
#[inline] #[inline]
pub fn version(&mut self, version: Version) -> &mut Self { pub fn version(&mut self, version: Version) -> &mut Self {
if let Some(parts) = parts(&mut self.parts, &self.err) { if let Some(parts) = parts(&mut self.response, &self.err) {
parts.version = Some(version); parts.version = Some(version);
} }
self self
@ -264,7 +238,7 @@ impl HttpResponseBuilder {
/// Set the `StatusCode` for this response. /// Set the `StatusCode` for this response.
#[inline] #[inline]
pub fn status(&mut self, status: StatusCode) -> &mut Self { pub fn status(&mut self, status: StatusCode) -> &mut Self {
if let Some(parts) = parts(&mut self.parts, &self.err) { if let Some(parts) = parts(&mut self.response, &self.err) {
parts.status = status; parts.status = status;
} }
self self
@ -276,7 +250,7 @@ impl HttpResponseBuilder {
where HeaderName: HttpTryFrom<K>, where HeaderName: HttpTryFrom<K>,
HeaderValue: HttpTryFrom<V> HeaderValue: HttpTryFrom<V>
{ {
if let Some(parts) = parts(&mut self.parts, &self.err) { if let Some(parts) = parts(&mut self.response, &self.err) {
match HeaderName::try_from(key) { match HeaderName::try_from(key) {
Ok(key) => { Ok(key) => {
match HeaderValue::try_from(value) { match HeaderValue::try_from(value) {
@ -293,7 +267,7 @@ impl HttpResponseBuilder {
/// Set the custom reason for the response. /// Set the custom reason for the response.
#[inline] #[inline]
pub fn reason(&mut self, reason: &'static str) -> &mut Self { pub fn reason(&mut self, reason: &'static str) -> &mut Self {
if let Some(parts) = parts(&mut self.parts, &self.err) { if let Some(parts) = parts(&mut self.response, &self.err) {
parts.reason = Some(reason); parts.reason = Some(reason);
} }
self self
@ -306,7 +280,7 @@ impl HttpResponseBuilder {
/// To enforce specific encoding, use specific ContentEncoding` value. /// To enforce specific encoding, use specific ContentEncoding` value.
#[inline] #[inline]
pub fn content_encoding(&mut self, enc: ContentEncoding) -> &mut Self { pub fn content_encoding(&mut self, enc: ContentEncoding) -> &mut Self {
if let Some(parts) = parts(&mut self.parts, &self.err) { if let Some(parts) = parts(&mut self.response, &self.err) {
parts.encoding = enc; parts.encoding = enc;
} }
self self
@ -315,7 +289,7 @@ impl HttpResponseBuilder {
/// Set connection type /// Set connection type
#[inline] #[inline]
pub fn connection_type(&mut self, conn: ConnectionType) -> &mut Self { pub fn connection_type(&mut self, conn: ConnectionType) -> &mut Self {
if let Some(parts) = parts(&mut self.parts, &self.err) { if let Some(parts) = parts(&mut self.response, &self.err) {
parts.connection_type = Some(conn); parts.connection_type = Some(conn);
} }
self self
@ -336,7 +310,7 @@ impl HttpResponseBuilder {
/// Enables automatic chunked transfer encoding /// Enables automatic chunked transfer encoding
#[inline] #[inline]
pub fn enable_chunked(&mut self) -> &mut Self { pub fn enable_chunked(&mut self) -> &mut Self {
if let Some(parts) = parts(&mut self.parts, &self.err) { if let Some(parts) = parts(&mut self.response, &self.err) {
parts.chunked = true; parts.chunked = true;
} }
self self
@ -347,7 +321,7 @@ impl HttpResponseBuilder {
pub fn content_type<V>(&mut self, value: V) -> &mut Self pub fn content_type<V>(&mut self, value: V) -> &mut Self
where HeaderValue: HttpTryFrom<V> where HeaderValue: HttpTryFrom<V>
{ {
if let Some(parts) = parts(&mut self.parts, &self.err) { if let Some(parts) = parts(&mut self.response, &self.err) {
match HeaderValue::try_from(value) { match HeaderValue::try_from(value) {
Ok(value) => { parts.headers.insert(header::CONTENT_TYPE, value); }, Ok(value) => { parts.headers.insert(header::CONTENT_TYPE, value); },
Err(e) => self.err = Some(e.into()), Err(e) => self.err = Some(e.into()),
@ -358,25 +332,23 @@ impl HttpResponseBuilder {
/// Set a cookie /// Set a cookie
pub fn cookie<'c>(&mut self, cookie: Cookie<'c>) -> &mut Self { pub fn cookie<'c>(&mut self, cookie: Cookie<'c>) -> &mut Self {
if let Some(parts) = parts(&mut self.parts, &self.err) { if self.cookies.is_none() {
if parts.cookies.is_none() { let mut jar = CookieJar::new();
let mut jar = CookieJar::new(); jar.add(cookie.into_owned());
jar.add(cookie.into_owned()); self.cookies = Some(jar)
parts.cookies = Some(jar) } else {
} else { self.cookies.as_mut().unwrap().add(cookie.into_owned());
parts.cookies.as_mut().unwrap().add(cookie.into_owned());
}
} }
self self
} }
/// Remote cookie, cookie has to be cookie from `HttpRequest::cookies()` method. /// Remote cookie, cookie has to be cookie from `HttpRequest::cookies()` method.
pub fn del_cookie<'a>(&mut self, cookie: &Cookie<'a>) -> &mut Self { pub fn del_cookie<'a>(&mut self, cookie: &Cookie<'a>) -> &mut Self {
if let Some(parts) = parts(&mut self.parts, &self.err) { {
if parts.cookies.is_none() { if self.cookies.is_none() {
parts.cookies = Some(CookieJar::new()) self.cookies = Some(CookieJar::new())
} }
let mut jar = parts.cookies.as_mut().unwrap(); let jar = self.cookies.as_mut().unwrap();
let cookie = cookie.clone().into_owned(); let cookie = cookie.clone().into_owned();
jar.add_original(cookie.clone()); jar.add_original(cookie.clone());
jar.remove(cookie); jar.remove(cookie);
@ -397,36 +369,26 @@ impl HttpResponseBuilder {
/// Set a body and generate `HttpResponse`. /// Set a body and generate `HttpResponse`.
/// `HttpResponseBuilder` can not be used after this call. /// `HttpResponseBuilder` can not be used after this call.
pub fn body<B: Into<Body>>(&mut self, body: B) -> Result<HttpResponse, HttpError> { pub fn body<B: Into<Body>>(&mut self, body: B) -> Result<HttpResponse, HttpError> {
let mut parts = self.parts.take().expect("cannot reuse response builder");
if let Some(e) = self.err.take() { if let Some(e) = self.err.take() {
return Err(e) return Err(e)
} }
if let Some(jar) = parts.cookies { let mut response = self.response.take().expect("cannot reuse response builder");
if let Some(ref jar) = self.cookies {
for cookie in jar.delta() { for cookie in jar.delta() {
parts.headers.append( response.headers.append(
header::SET_COOKIE, header::SET_COOKIE,
HeaderValue::from_str(&cookie.to_string())?); HeaderValue::from_str(&cookie.to_string())?);
} }
} }
Ok(HttpResponse { response.body = body.into();
version: parts.version, Ok(response)
headers: parts.headers,
status: parts.status,
reason: parts.reason,
body: body.into(),
chunked: parts.chunked,
encoding: parts.encoding,
connection_type: parts.connection_type,
response_size: 0,
error: None,
})
} }
/// Set a json body and generate `HttpResponse` /// Set a json body and generate `HttpResponse`
pub fn json<T: Serialize>(&mut self, value: T) -> Result<HttpResponse, Error> { pub fn json<T: Serialize>(&mut self, value: T) -> Result<HttpResponse, Error> {
let body = serde_json::to_string(&value)?; let body = serde_json::to_string(&value)?;
let contains = if let Some(parts) = parts(&mut self.parts, &self.err) { let contains = if let Some(parts) = parts(&mut self.response, &self.err) {
parts.headers.contains_key(header::CONTENT_TYPE) parts.headers.contains_key(header::CONTENT_TYPE)
} else { } else {
true true
@ -444,7 +406,8 @@ impl HttpResponseBuilder {
} }
} }
fn parts<'a>(parts: &'a mut Option<Parts>, err: &Option<HttpError>) -> Option<&'a mut Parts> fn parts<'a>(parts: &'a mut Option<HttpResponse>, err: &Option<HttpError>)
-> Option<&'a mut HttpResponse>
{ {
if err.is_some() { if err.is_some() {
return None return None

View File

@ -55,7 +55,7 @@ extern crate tokio_openssl;
mod application; mod application;
mod body; mod body;
mod context; mod context;
mod date; mod utils;
mod encoding; mod encoding;
mod httprequest; mod httprequest;
mod httpresponse; mod httpresponse;

View File

@ -250,7 +250,7 @@ impl Inner {
let mut chunk = self.items.pop_front().unwrap(); let mut chunk = self.items.pop_front().unwrap();
let rem = cmp::min(size - buf.len(), chunk.len()); let rem = cmp::min(size - buf.len(), chunk.len());
self.len -= rem; self.len -= rem;
buf.extend(&chunk.split_to(rem)); buf.extend_from_slice(&chunk.split_to(rem));
if !chunk.is_empty() { if !chunk.is_empty() {
self.items.push_front(chunk); self.items.push_front(chunk);
return Ok(Async::Ready(buf.freeze())) return Ok(Async::Ready(buf.freeze()))
@ -299,12 +299,12 @@ impl Inner {
let mut buf = BytesMut::with_capacity(length); let mut buf = BytesMut::with_capacity(length);
if num > 0 { if num > 0 {
for _ in 0..num { for _ in 0..num {
buf.extend(self.items.pop_front().unwrap()); buf.extend_from_slice(&self.items.pop_front().unwrap());
} }
} }
if offset > 0 { if offset > 0 {
let mut chunk = self.items.pop_front().unwrap(); let mut chunk = self.items.pop_front().unwrap();
buf.extend(chunk.split_to(offset)); buf.extend_from_slice(&chunk.split_to(offset));
if !chunk.is_empty() { if !chunk.is_empty() {
self.items.push_front(chunk) self.items.push_front(chunk)
} }
@ -330,7 +330,7 @@ impl Inner {
if len > 0 { if len > 0 {
let mut buf = BytesMut::with_capacity(len); let mut buf = BytesMut::with_capacity(len);
for item in &self.items { for item in &self.items {
buf.extend(item); buf.extend_from_slice(item);
} }
self.items = VecDeque::new(); self.items = VecDeque::new();
self.len = 0; self.len = 0;

View File

@ -1,7 +1,7 @@
use std::{io, net, thread}; use std::{io, net, thread};
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
//use std::time::Duration; use std::time::Duration;
use std::marker::PhantomData; use std::marker::PhantomData;
use actix::dev::*; use actix::dev::*;
@ -28,6 +28,7 @@ use openssl::pkcs12::ParsedPkcs12;
#[cfg(feature="alpn")] #[cfg(feature="alpn")]
use tokio_openssl::{SslStream, SslAcceptorExt}; use tokio_openssl::{SslStream, SslAcceptorExt};
use utils;
use channel::{HttpChannel, HttpHandler, IntoHttpHandler}; use channel::{HttpChannel, HttpHandler, IntoHttpHandler};
/// Various server settings /// Various server settings
@ -99,10 +100,23 @@ pub struct HttpServer<T, A, H, U>
impl<T: 'static, A: 'static, H, U: 'static> Actor for HttpServer<T, A, H, U> { impl<T: 'static, A: 'static, H, U: 'static> Actor for HttpServer<T, A, H, U> {
type Context = Context<Self>; type Context = Context<Self>;
fn started(&mut self, ctx: &mut Self::Context) {
self.update_time(ctx);
}
}
impl<T: 'static, A: 'static, H, U: 'static> HttpServer<T, A, H, U> {
fn update_time(&self, ctx: &mut Context<Self>) {
utils::update_date();
ctx.run_later(Duration::new(1, 0), |slf, ctx| slf.update_time(ctx));
}
} }
impl<T, A, H, U, V> HttpServer<T, A, H, U> impl<T, A, H, U, V> HttpServer<T, A, H, U>
where H: HttpHandler, where A: 'static,
T: AsyncRead + AsyncWrite + 'static,
H: HttpHandler,
U: IntoIterator<Item=V> + 'static, U: IntoIterator<Item=V> + 'static,
V: IntoHttpHandler<Handler=H>, V: IntoHttpHandler<Handler=H>,
{ {
@ -126,15 +140,7 @@ impl<T, A, H, U, V> HttpServer<T, A, H, U>
self.threads = num; self.threads = num;
self self
} }
}
impl<T, A, H, U, V> HttpServer<T, A, H, U>
where T: AsyncRead + AsyncWrite + 'static,
A: 'static,
H: HttpHandler,
U: IntoIterator<Item=V> + 'static,
V: IntoHttpHandler<Handler=H>,
{
/// Start listening for incomming connections from a stream. /// Start listening for incomming connections from a stream.
/// ///
/// This method uses only one thread for handling incoming connections. /// This method uses only one thread for handling incoming connections.
@ -387,11 +393,18 @@ struct Worker<H> {
handler: StreamHandlerType, handler: StreamHandlerType,
} }
impl<H: 'static> Worker<H> {
fn update_time(&self, ctx: &mut Context<Self>) {
utils::update_date();
ctx.run_later(Duration::new(1, 0), |slf, ctx| slf.update_time(ctx));
}
}
impl<H: 'static> Actor for Worker<H> { impl<H: 'static> Actor for Worker<H> {
type Context = Context<Self>; type Context = Context<Self>;
fn started(&mut self, ctx: &mut Self::Context) { fn started(&mut self, ctx: &mut Self::Context) {
self.update_time(ctx);
} }
} }

173
src/utils.rs Normal file
View File

@ -0,0 +1,173 @@
use std::{str, mem, ptr, slice};
use std::cell::RefCell;
use std::fmt::{self, Write};
use time;
use bytes::BytesMut;
use http::header::HeaderValue;
// "Sun, 06 Nov 1994 08:49:37 GMT".len()
pub const DATE_VALUE_LENGTH: usize = 29;
pub fn extend(dst: &mut BytesMut) {
CACHED.with(|cache| {
dst.extend_from_slice(cache.borrow().buffer());
})
}
pub fn update_date() {
CACHED.with(|cache| {
cache.borrow_mut().update();
});
}
struct CachedDate {
bytes: [u8; DATE_VALUE_LENGTH],
pos: usize,
}
thread_local!(static CACHED: RefCell<CachedDate> = RefCell::new(CachedDate {
bytes: [0; DATE_VALUE_LENGTH],
pos: 0,
}));
impl CachedDate {
fn buffer(&self) -> &[u8] {
&self.bytes[..]
}
fn update(&mut self) {
self.pos = 0;
write!(self, "{}", time::at_utc(time::get_time()).rfc822()).unwrap();
assert_eq!(self.pos, DATE_VALUE_LENGTH);
}
}
impl fmt::Write for CachedDate {
fn write_str(&mut self, s: &str) -> fmt::Result {
let len = s.len();
self.bytes[self.pos..self.pos + len].copy_from_slice(s.as_bytes());
self.pos += len;
Ok(())
}
}
const DEC_DIGITS_LUT: &[u8] =
b"0001020304050607080910111213141516171819\
2021222324252627282930313233343536373839\
4041424344454647484950515253545556575859\
6061626364656667686970717273747576777879\
8081828384858687888990919293949596979899";
pub(crate) fn convert_u16(mut n: u16, bytes: &mut BytesMut) {
let mut buf: [u8; 39] = unsafe { mem::uninitialized() };
let mut curr = buf.len() as isize;
let buf_ptr = buf.as_mut_ptr();
let lut_ptr = DEC_DIGITS_LUT.as_ptr();
unsafe {
// need at least 16 bits for the 4-characters-at-a-time to work.
if mem::size_of::<u16>() >= 2 {
// eagerly decode 4 characters at a time
while n >= 10_000 {
let rem = (n % 10_000) as isize;
n /= 10_000;
let d1 = (rem / 100) << 1;
let d2 = (rem % 100) << 1;
curr -= 4;
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2);
}
}
// if we reach here numbers are <= 9999, so at most 4 chars long
let mut n = n as isize; // possibly reduce 64bit math
// decode 2 more chars, if > 2 chars
if n >= 100 {
let d1 = (n % 100) << 1;
n /= 100;
curr -= 2;
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
}
// decode last 1 or 2 chars
if n < 10 {
curr -= 1;
*buf_ptr.offset(curr) = (n as u8) + b'0';
} else {
let d1 = n << 1;
curr -= 2;
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
}
}
unsafe {
bytes.extend_from_slice(
slice::from_raw_parts(buf_ptr.offset(curr), buf.len() - curr as usize));
}
}
pub(crate) fn convert_into_header(mut n: usize) -> HeaderValue {
let mut curr: isize = 39;
let mut buf: [u8; 39] = unsafe { mem::uninitialized() };
let buf_ptr = buf.as_mut_ptr();
let lut_ptr = DEC_DIGITS_LUT.as_ptr();
unsafe {
// need at least 16 bits for the 4-characters-at-a-time to work.
if mem::size_of::<usize>() >= 2 {
// eagerly decode 4 characters at a time
while n >= 10_000 {
let rem = (n % 10_000) as isize;
n /= 10_000;
let d1 = (rem / 100) << 1;
let d2 = (rem % 100) << 1;
curr -= 4;
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2);
}
}
// if we reach here numbers are <= 9999, so at most 4 chars long
let mut n = n as isize; // possibly reduce 64bit math
// decode 2 more chars, if > 2 chars
if n >= 100 {
let d1 = (n % 100) << 1;
n /= 100;
curr -= 2;
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
}
// decode last 1 or 2 chars
if n < 10 {
curr -= 1;
*buf_ptr.offset(curr) = (n as u8) + b'0';
} else {
let d1 = n << 1;
curr -= 2;
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
}
}
unsafe {
HeaderValue::from_bytes(
slice::from_raw_parts(buf_ptr.offset(curr), buf.len() - curr as usize)).unwrap()
}
}
#[test]
fn test_date_len() {
assert_eq!(DATE_VALUE_LENGTH, "Sun, 06 Nov 1994 08:49:37 GMT".len());
}
#[test]
fn test_date() {
let mut buf1 = BytesMut::new();
extend(&mut buf1);
let mut buf2 = BytesMut::new();
extend(&mut buf2);
assert_eq!(buf1, buf2);
}

View File

@ -201,7 +201,7 @@ impl Stream for WsStream {
loop { loop {
match self.rx.readany() { match self.rx.readany() {
Ok(Async::Ready(Some(chunk))) => { Ok(Async::Ready(Some(chunk))) => {
self.buf.extend(chunk.0) self.buf.extend_from_slice(&chunk.0)
} }
Ok(Async::Ready(None)) => { Ok(Async::Ready(None)) => {
done = true; done = true;