From e0c8da567c1b6eb258a3caf895338187b9f54725 Mon Sep 17 00:00:00 2001
From: Nikolay Kim <fafhrd91@gmail.com>
Date: Sun, 18 Mar 2018 11:05:44 -0700
Subject: [PATCH] various optimizations

---
 CHANGES.md             |  5 +++
 Cargo.toml             |  2 +-
 src/body.rs            |  3 +-
 src/header/mod.rs      |  2 +
 src/helpers.rs         | 84 +++---------------------------------------
 src/server/encoding.rs | 11 ++++--
 src/server/h1.rs       | 25 +++++++------
 src/server/h1writer.rs | 44 +++++++++++++++-------
 src/server/h2.rs       | 21 ++++++-----
 src/server/h2writer.rs | 19 +++++++---
 src/server/settings.rs | 68 +++++++++++++++++++++++++++++++++-
 src/server/srv.rs      | 13 +------
 src/server/worker.rs   |  3 +-
 13 files changed, 159 insertions(+), 141 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index e3a6e17d..6c4a4aa9 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,10 @@
 # Changes
 
+## 0.4.10 (2018-03-xx)
+
+..
+
+
 ## 0.4.9 (2018-03-16)
 
 * Allow to disable http/2 support
diff --git a/Cargo.toml b/Cargo.toml
index a6f5b6e2..552b1b17 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "actix-web"
-version = "0.4.9"
+version = "0.4.10"
 authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
 description = "Actix web is a simple, pragmatic, extremely fast, web framework for Rust."
 readme = "README.md"
diff --git a/src/body.rs b/src/body.rs
index fe630343..cdf8c039 100644
--- a/src/body.rs
+++ b/src/body.rs
@@ -235,9 +235,10 @@ impl<'a> From<&'a Arc<Vec<u8>>> for Binary {
 }
 
 impl AsRef<[u8]> for Binary {
+    #[inline]
     fn as_ref(&self) -> &[u8] {
         match *self {
-            Binary::Bytes(ref bytes) => bytes.as_ref(),
+            Binary::Bytes(ref bytes) => &bytes[..],
             Binary::Slice(slice) => slice,
             Binary::SharedString(ref s) => s.as_bytes(),
             Binary::ArcSharedString(ref s) => s.as_bytes(),
diff --git a/src/header/mod.rs b/src/header/mod.rs
index 29fa9e13..3698ca81 100644
--- a/src/header/mod.rs
+++ b/src/header/mod.rs
@@ -149,6 +149,8 @@ impl ContentEncoding {
             ContentEncoding::Identity | ContentEncoding::Auto => "identity",
         }
     }
+
+    #[inline]
     /// default quality value
     pub fn quality(&self) -> f64 {
         match *self {
diff --git a/src/helpers.rs b/src/helpers.rs
index 5f54f48f..623869d0 100644
--- a/src/helpers.rs
+++ b/src/helpers.rs
@@ -1,71 +1,13 @@
-use std::{str, mem, ptr, slice};
+use std::{mem, ptr, slice};
 use std::cell::RefCell;
-use std::fmt::{self, Write};
 use std::rc::Rc;
 use std::ops::{Deref, DerefMut};
 use std::collections::VecDeque;
-use time;
 use bytes::{BufMut, BytesMut};
 use http::Version;
 
 use httprequest::HttpInnerMessage;
 
-// "Sun, 06 Nov 1994 08:49:37 GMT".len()
-pub(crate) const DATE_VALUE_LENGTH: usize = 29;
-
-pub(crate) fn date(dst: &mut BytesMut) {
-    CACHED.with(|cache| {
-        let mut buf: [u8; 39] = unsafe { mem::uninitialized() };
-        buf[..6].copy_from_slice(b"date: ");
-        buf[6..35].copy_from_slice(cache.borrow().buffer());
-        buf[35..].copy_from_slice(b"\r\n\r\n");
-        dst.extend_from_slice(&buf);
-    })
-}
-
-pub(crate) fn date_value(dst: &mut BytesMut) {
-    CACHED.with(|cache| {
-        dst.extend_from_slice(cache.borrow().buffer());
-    })
-}
-
-pub(crate) 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(())
-    }
-}
-
 /// Internal use only! unsafe
 pub(crate) struct SharedMessagePool(RefCell<VecDeque<Rc<HttpInnerMessage>>>);
 
@@ -202,7 +144,7 @@ pub(crate) fn write_status_line(version: Version, mut n: u16, bytes: &mut BytesM
         }
     }
 
-    bytes.extend_from_slice(&buf);
+    bytes.put_slice(&buf);
     if four {
         bytes.put(b' ');
     }
@@ -214,7 +156,7 @@ pub(crate) fn write_content_length(mut n: usize, bytes: &mut BytesMut) {
                                  b'n',b't',b'-',b'l',b'e',b'n',b'g',
                                  b't',b'h',b':',b' ',b'0',b'\r',b'\n'];
         buf[18] = (n as u8) + b'0';
-        bytes.extend_from_slice(&buf);
+        bytes.put_slice(&buf);
     } else if n < 100 {
         let mut buf: [u8; 22] = [b'\r',b'\n',b'c',b'o',b'n',b't',b'e',
                                  b'n',b't',b'-',b'l',b'e',b'n',b'g',
@@ -224,7 +166,7 @@ pub(crate) fn write_content_length(mut n: usize, bytes: &mut BytesMut) {
             ptr::copy_nonoverlapping(
                 DEC_DIGITS_LUT.as_ptr().offset(d1 as isize), buf.as_mut_ptr().offset(18), 2);
         }
-        bytes.extend_from_slice(&buf);
+        bytes.put_slice(&buf);
     } else if n < 1000 {
         let mut buf: [u8; 23] = [b'\r',b'\n',b'c',b'o',b'n',b't',b'e',
                                  b'n',b't',b'-',b'l',b'e',b'n',b'g',
@@ -238,9 +180,9 @@ pub(crate) fn write_content_length(mut n: usize, bytes: &mut BytesMut) {
         // decode last 1
         buf[18] = (n as u8) + b'0';
 
-        bytes.extend_from_slice(&buf);
+        bytes.put_slice(&buf);
     } else {
-        bytes.extend_from_slice(b"\r\ncontent-length: ");
+        bytes.put_slice(b"\r\ncontent-length: ");
         convert_usize(n, bytes);
     }
 }
@@ -299,20 +241,6 @@ pub(crate) fn convert_usize(mut n: usize, bytes: &mut BytesMut) {
 mod tests {
     use super::*;
 
-    #[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();
-        date(&mut buf1);
-        let mut buf2 = BytesMut::new();
-        date(&mut buf2);
-        assert_eq!(buf1, buf2);
-    }
-
     #[test]
     fn test_write_content_length() {
         let mut bytes = BytesMut::new();
diff --git a/src/server/encoding.rs b/src/server/encoding.rs
index 6ba232b0..bd6921d2 100644
--- a/src/server/encoding.rs
+++ b/src/server/encoding.rs
@@ -368,6 +368,7 @@ impl ContentEncoder {
                       response_encoding: ContentEncoding) -> ContentEncoder
     {
         let version = resp.version().unwrap_or_else(|| req.version);
+        let is_head = req.method == Method::HEAD;
         let mut body = resp.replace_body(Body::Empty);
         let has_body = match body {
             Body::Empty => false,
@@ -410,7 +411,9 @@ impl ContentEncoder {
                 TransferEncoding::length(0, buf)
             },
             Body::Binary(ref mut bytes) => {
-                if encoding.is_compression() {
+                if !(encoding == ContentEncoding::Identity
+                     || encoding == ContentEncoding::Auto)
+                {
                     let tmp = SharedBytes::default();
                     let transfer = TransferEncoding::eof(tmp.clone());
                     let mut enc = match encoding {
@@ -431,13 +434,13 @@ impl ContentEncoder {
                     *bytes = Binary::from(tmp.take());
                     encoding = ContentEncoding::Identity;
                 }
-                if req.method == Method::HEAD {
+                if is_head {
                     let mut b = BytesMut::new();
                     let _ = write!(b, "{}", bytes.len());
                     resp.headers_mut().insert(
                         CONTENT_LENGTH, HeaderValue::try_from(b.freeze()).unwrap());
                 } else {
-                    resp.headers_mut().remove(CONTENT_LENGTH);
+                    // resp.headers_mut().remove(CONTENT_LENGTH);
                 }
                 TransferEncoding::eof(buf)
             }
@@ -460,7 +463,7 @@ impl ContentEncoder {
             }
         };
         //
-        if req.method == Method::HEAD {
+        if is_head {
             transfer.kind = TransferEncodingKind::Length(0);
         } else {
             resp.replace_body(body);
diff --git a/src/server/h1.rs b/src/server/h1.rs
index b7055ea0..38d4d434 100644
--- a/src/server/h1.rs
+++ b/src/server/h1.rs
@@ -51,7 +51,7 @@ pub(crate) struct Http1<T: IoStream, H: 'static> {
     flags: Flags,
     settings: Rc<WorkerSettings<H>>,
     addr: Option<SocketAddr>,
-    stream: H1Writer<T>,
+    stream: H1Writer<T, H>,
     reader: Reader,
     read_buf: BytesMut,
     tasks: VecDeque<Entry>,
@@ -72,7 +72,7 @@ impl<T, H> Http1<T, H>
     {
         let bytes = settings.get_shared_bytes();
         Http1{ flags: Flags::KEEPALIVE,
-               stream: H1Writer::new(stream, bytes),
+               stream: H1Writer::new(stream, bytes, Rc::clone(&settings)),
                reader: Reader::new(),
                tasks: VecDeque::new(),
                keepalive_timer: None,
@@ -353,7 +353,7 @@ impl Reader {
             PayloadStatus::Read
         }
     }
-    
+
     #[inline]
     fn decode(&mut self, buf: &mut BytesMut, payload: &mut PayloadInfo)
               -> Result<Decoding, ReaderError>
@@ -502,10 +502,12 @@ impl Reader {
                     httparse::Status::Complete(len) => {
                         let method = Method::try_from(req.method.unwrap())
                             .map_err(|_| ParseError::Method)?;
-                        let path = req.path.unwrap();
-                        let path_start = path.as_ptr() as usize - bytes_ptr;
-                        let path_end = path_start + path.len();
-                        let path = (path_start, path_end);
+                        //let path = req.path.unwrap();
+                        //let path_start = path.as_ptr() as usize - bytes_ptr;
+                        //let path_end = path_start + path.len();
+                        //let path = (path_start, path_end);
+                        let path = Uri::try_from(req.path.unwrap()).unwrap();
+                            //.map_err(|_| ParseError::Uri)?;
 
                         let version = if req.version.unwrap() == 1 {
                             Version::HTTP_11
@@ -525,9 +527,7 @@ impl Reader {
             {
                 let msg_mut = msg.get_mut();
                 for header in headers[..headers_len].iter() {
-                    let n_start = header.name.as_ptr() as usize - bytes_ptr;
-                    let n_end = n_start + header.name.len();
-                    if let Ok(name) = HeaderName::try_from(slice.slice(n_start, n_end)) {
+                    if let Ok(name) = HeaderName::try_from(header.name) {
                         let v_start = header.value.as_ptr() as usize - bytes_ptr;
                         let v_end = v_start + header.value.len();
                         let value = unsafe {
@@ -539,8 +539,9 @@ impl Reader {
                     }
                 }
 
-                msg_mut.uri = Uri::from_shared(
-                    slice.slice(path.0, path.1)).map_err(ParseError::Uri)?;
+                msg_mut.uri = path;
+                //msg_mut.uri = Uri::from_shared(
+                //slice.slice(path.0, path.1)).map_err(ParseError::Uri)?;
                 msg_mut.method = method;
                 msg_mut.version = version;
             }
diff --git a/src/server/h1writer.rs b/src/server/h1writer.rs
index a48c71b0..46c3cf1a 100644
--- a/src/server/h1writer.rs
+++ b/src/server/h1writer.rs
@@ -1,11 +1,12 @@
 #![cfg_attr(feature = "cargo-clippy", allow(redundant_field_names))]
 
 use std::{io, mem};
+use std::rc::Rc;
 use bytes::BufMut;
 use futures::{Async, Poll};
 use tokio_io::AsyncWrite;
 use http::{Method, Version};
-use http::header::{HeaderValue, CONNECTION, DATE};
+use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE};
 
 use helpers;
 use body::{Body, Binary};
@@ -15,6 +16,7 @@ use httpresponse::HttpResponse;
 use super::{Writer, WriterState, MAX_WRITE_BUFFER_SIZE};
 use super::shared::SharedBytes;
 use super::encoding::ContentEncoder;
+use super::settings::WorkerSettings;
 
 const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific
 
@@ -27,7 +29,7 @@ bitflags! {
     }
 }
 
-pub(crate) struct H1Writer<T: AsyncWrite> {
+pub(crate) struct H1Writer<T: AsyncWrite, H: 'static> {
     flags: Flags,
     stream: T,
     encoder: ContentEncoder,
@@ -35,11 +37,14 @@ pub(crate) struct H1Writer<T: AsyncWrite> {
     headers_size: u32,
     buffer: SharedBytes,
     buffer_capacity: usize,
+    settings: Rc<WorkerSettings<H>>,
 }
 
-impl<T: AsyncWrite> H1Writer<T> {
+impl<T: AsyncWrite, H: 'static> H1Writer<T, H> {
 
-    pub fn new(stream: T, buf: SharedBytes) -> H1Writer<T> {
+    pub fn new(stream: T, buf: SharedBytes, settings: Rc<WorkerSettings<H>>)
+               -> H1Writer<T, H>
+    {
         H1Writer {
             flags: Flags::empty(),
             encoder: ContentEncoder::empty(buf.clone()),
@@ -48,6 +53,7 @@ impl<T: AsyncWrite> H1Writer<T> {
             buffer: buf,
             buffer_capacity: 0,
             stream,
+            settings,
         }
     }
 
@@ -87,7 +93,7 @@ impl<T: AsyncWrite> H1Writer<T> {
     }
 }
 
-impl<T: AsyncWrite> Writer for H1Writer<T> {
+impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
 
     #[inline]
     fn written(&self) -> u64 {
@@ -126,11 +132,14 @@ impl<T: AsyncWrite> Writer for H1Writer<T> {
         // render message
         {
             let mut buffer = self.buffer.get_mut();
-            if let Body::Binary(ref bytes) = body {
-                buffer.reserve(256 + msg.headers().len() * AVERAGE_HEADER_SIZE + bytes.len());
+            let mut is_bin = if let Body::Binary(ref bytes) = body {
+                buffer.reserve(
+                    256 + msg.headers().len() * AVERAGE_HEADER_SIZE + bytes.len());
+                true
             } else {
                 buffer.reserve(256 + msg.headers().len() * AVERAGE_HEADER_SIZE);
-            }
+                false
+            };
 
             // status line
             helpers::write_status_line(version, msg.status().as_u16(), &mut buffer);
@@ -139,21 +148,28 @@ impl<T: AsyncWrite> Writer for H1Writer<T> {
             match body {
                 Body::Empty =>
                     if req.method != Method::HEAD {
-                        SharedBytes::extend_from_slice_(buffer, b"\r\ncontent-length: 0\r\n");
+                        SharedBytes::put_slice(
+                            buffer, b"\r\ncontent-length: 0\r\n");
                     } else {
-                        SharedBytes::extend_from_slice_(buffer, b"\r\n");
+                        SharedBytes::put_slice(buffer, b"\r\n");
                     },
                 Body::Binary(ref bytes) =>
                     helpers::write_content_length(bytes.len(), &mut buffer),
                 _ =>
-                    SharedBytes::extend_from_slice_(buffer, b"\r\n"),
+                    SharedBytes::put_slice(buffer, b"\r\n"),
             }
 
             // write headers
             let mut pos = 0;
+            let mut has_date = false;
             let mut remaining = buffer.remaining_mut();
             let mut buf: &mut [u8] = unsafe{ mem::transmute(buffer.bytes_mut()) };
             for (key, value) in msg.headers() {
+                if is_bin && key == CONTENT_LENGTH {
+                    is_bin = false;
+                    continue
+                }
+                has_date = has_date || key == DATE;
                 let v = value.as_ref();
                 let k = key.as_str().as_bytes();
                 let len = k.len() + v.len() + 4;
@@ -182,9 +198,9 @@ impl<T: AsyncWrite> Writer for H1Writer<T> {
             }
             unsafe{buffer.advance_mut(pos)};
 
-            // using helpers::date is quite a lot faster
-            if !msg.headers().contains_key(DATE) {
-                helpers::date(&mut buffer);
+            // optimized date header
+            if !has_date {
+                self.settings.set_date(&mut buffer);
             } else {
                 // msg eof
                 SharedBytes::extend_from_slice_(buffer, b"\r\n");
diff --git a/src/server/h2.rs b/src/server/h2.rs
index 6cc682a1..0219d84b 100644
--- a/src/server/h2.rs
+++ b/src/server/h2.rs
@@ -43,7 +43,7 @@ struct Http2<T, H>
     settings: Rc<WorkerSettings<H>>,
     addr: Option<SocketAddr>,
     state: State<IoWrapper<T>>,
-    tasks: VecDeque<Entry>,
+    tasks: VecDeque<Entry<H>>,
     keepalive_timer: Option<Timeout>,
 }
 
@@ -274,20 +274,20 @@ bitflags! {
     }
 }
 
-struct Entry {
+struct Entry<H: 'static> {
     task: Box<HttpHandlerTask>,
     payload: PayloadType,
     recv: RecvStream,
-    stream: H2Writer,
+    stream: H2Writer<H>,
     flags: EntryFlags,
 }
 
-impl Entry {
-    fn new<H>(parts: Parts,
-              recv: RecvStream,
-              resp: SendResponse<Bytes>,
-              addr: Option<SocketAddr>,
-              settings: &Rc<WorkerSettings<H>>) -> Entry
+impl<H: 'static> Entry<H> {
+    fn new(parts: Parts,
+           recv: RecvStream,
+           resp: SendResponse<Bytes>,
+           addr: Option<SocketAddr>,
+           settings: &Rc<WorkerSettings<H>>) -> Entry<H>
         where H: HttpHandler + 'static
     {
         // Payload and Content-Encoding
@@ -320,7 +320,8 @@ impl Entry {
 
         Entry {task: task.unwrap_or_else(|| Pipeline::error(HttpNotFound)),
                payload: psender,
-               stream: H2Writer::new(resp, settings.get_shared_bytes()),
+               stream: H2Writer::new(
+                   resp, settings.get_shared_bytes(), Rc::clone(settings)),
                flags: EntryFlags::empty(),
                recv,
         }
diff --git a/src/server/h2writer.rs b/src/server/h2writer.rs
index ed97682e..738a0593 100644
--- a/src/server/h2writer.rs
+++ b/src/server/h2writer.rs
@@ -1,6 +1,7 @@
 #![cfg_attr(feature = "cargo-clippy", allow(redundant_field_names))]
 
 use std::{io, cmp};
+use std::rc::Rc;
 use bytes::{Bytes, BytesMut};
 use futures::{Async, Poll};
 use http2::{Reason, SendStream};
@@ -15,6 +16,7 @@ use httprequest::HttpInnerMessage;
 use httpresponse::HttpResponse;
 use super::encoding::ContentEncoder;
 use super::shared::SharedBytes;
+use super::settings::WorkerSettings;
 use super::{Writer, WriterState, MAX_WRITE_BUFFER_SIZE};
 
 const CHUNK_SIZE: usize = 16_384;
@@ -28,7 +30,7 @@ bitflags! {
     }
 }
 
-pub(crate) struct H2Writer {
+pub(crate) struct H2Writer<H: 'static> {
     respond: SendResponse<Bytes>,
     stream: Option<SendStream<Bytes>>,
     encoder: ContentEncoder,
@@ -36,13 +38,17 @@ pub(crate) struct H2Writer {
     written: u64,
     buffer: SharedBytes,
     buffer_capacity: usize,
+    settings: Rc<WorkerSettings<H>>,
 }
 
-impl H2Writer {
+impl<H: 'static> H2Writer<H> {
 
-    pub fn new(respond: SendResponse<Bytes>, buf: SharedBytes) -> H2Writer {
+    pub fn new(respond: SendResponse<Bytes>,
+               buf: SharedBytes, settings: Rc<WorkerSettings<H>>) -> H2Writer<H>
+    {
         H2Writer {
             respond,
+            settings,
             stream: None,
             encoder: ContentEncoder::empty(buf.clone()),
             flags: Flags::empty(),
@@ -59,7 +65,7 @@ impl H2Writer {
     }
 }
 
-impl Writer for H2Writer {
+impl<H: 'static> Writer for H2Writer<H> {
 
     fn written(&self) -> u64 {
         self.written
@@ -84,7 +90,7 @@ impl Writer for H2Writer {
         // using helpers::date is quite a lot faster
         if !msg.headers().contains_key(DATE) {
             let mut bytes = BytesMut::with_capacity(29);
-            helpers::date_value(&mut bytes);
+            self.settings.set_date(&mut bytes);
             msg.headers_mut().insert(DATE, HeaderValue::try_from(bytes.freeze()).unwrap());
         }
 
@@ -95,7 +101,8 @@ impl Writer for H2Writer {
                 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());
+                    CONTENT_LENGTH,
+                    HeaderValue::try_from(val.split_to(l-2).freeze()).unwrap());
             }
             Body::Empty => {
                 msg.headers_mut().insert(CONTENT_LENGTH, HeaderValue::from_static("0"));
diff --git a/src/server/settings.rs b/src/server/settings.rs
index 82b190b8..7f901a71 100644
--- a/src/server/settings.rs
+++ b/src/server/settings.rs
@@ -1,7 +1,10 @@
-use std::{fmt, net};
+use std::{fmt, mem, net};
+use std::fmt::Write;
 use std::rc::Rc;
 use std::sync::Arc;
 use std::cell::{Cell, RefCell, RefMut, UnsafeCell};
+use time;
+use bytes::BytesMut;
 use futures_cpupool::{Builder, CpuPool};
 
 use helpers;
@@ -95,6 +98,8 @@ impl ServerSettings {
     }
 }
 
+// "Sun, 06 Nov 1994 08:49:37 GMT".len()
+const DATE_VALUE_LENGTH: usize = 29;
 
 pub(crate) struct WorkerSettings<H> {
     h: RefCell<Vec<H>>,
@@ -104,6 +109,7 @@ pub(crate) struct WorkerSettings<H> {
     messages: Rc<helpers::SharedMessagePool>,
     channels: Cell<usize>,
     node: Box<Node<()>>,
+    date: UnsafeCell<Date>,
 }
 
 impl<H> WorkerSettings<H> {
@@ -121,6 +127,7 @@ impl<H> WorkerSettings<H> {
             messages: Rc::new(helpers::SharedMessagePool::new()),
             channels: Cell::new(0),
             node: Box::new(Node::head()),
+            date: UnsafeCell::new(Date::new()),
         }
     }
 
@@ -164,4 +171,63 @@ impl<H> WorkerSettings<H> {
             error!("Number of removed channels is bigger than added channel. Bug in actix-web");
         }
     }
+
+    pub fn update_date(&self) {
+        unsafe{&mut *self.date.get()}.update();
+    }
+
+    pub fn set_date(&self, dst: &mut BytesMut) {
+        let mut buf: [u8; 39] = unsafe { mem::uninitialized() };
+        buf[..6].copy_from_slice(b"date: ");
+        buf[6..35].copy_from_slice(&(unsafe{&*self.date.get()}.bytes));
+        buf[35..].copy_from_slice(b"\r\n\r\n");
+        dst.extend_from_slice(&buf);
+    }
+}
+
+struct Date {
+    bytes: [u8; DATE_VALUE_LENGTH],
+    pos: usize,
+}
+
+impl Date {
+    fn new() -> Date {
+        let mut date = Date{bytes: [0; DATE_VALUE_LENGTH], pos: 0};
+        date.update();
+        date
+    }
+    fn update(&mut self) {
+        self.pos = 0;
+        write!(self, "{}", time::at_utc(time::get_time()).rfc822()).unwrap();
+    }
+}
+
+impl fmt::Write for Date {
+    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(())
+    }
+}
+
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_date_len() {
+        assert_eq!(DATE_VALUE_LENGTH, "Sun, 06 Nov 1994 08:49:37 GMT".len());
+    }
+
+    #[test]
+    fn test_date() {
+        let settings = WorkerSettings::<()>::new(Vec::new(), KeepAlive::Os);
+        let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
+        settings.set_date(&mut buf1);
+        let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
+        settings.set_date(&mut buf2);
+        assert_eq!(buf1, buf2);
+    }
 }
diff --git a/src/server/srv.rs b/src/server/srv.rs
index 69ff9012..bb065c47 100644
--- a/src/server/srv.rs
+++ b/src/server/srv.rs
@@ -18,7 +18,6 @@ use native_tls::TlsAcceptor;
 #[cfg(feature="alpn")]
 use openssl::ssl::{AlpnError, SslAcceptorBuilder};
 
-use helpers;
 use super::{IntoHttpHandler, IoStream, KeepAlive};
 use super::{PauseServer, ResumeServer, StopServer};
 use super::channel::{HttpChannel, WrapperStream};
@@ -58,13 +57,8 @@ enum ServerCommand {
     WorkerDied(usize, Info),
 }
 
-impl<H> Actor for HttpServer<H> where H: IntoHttpHandler
-{
+impl<H> Actor for HttpServer<H> where H: IntoHttpHandler {
     type Context = Context<Self>;
-
-    fn started(&mut self, ctx: &mut Self::Context) {
-        self.update_time(ctx);
-    }
 }
 
 impl<H> HttpServer<H> where H: IntoHttpHandler + 'static
@@ -95,11 +89,6 @@ impl<H> HttpServer<H> where H: IntoHttpHandler + 'static
         }
     }
 
-    fn update_time(&self, ctx: &mut Context<Self>) {
-        helpers::update_date();
-        ctx.run_later(Duration::new(1, 0), |slf, ctx| slf.update_time(ctx));
-    }
-
     /// Set number of workers to start.
     ///
     /// By default http server uses number of available logical cpu as threads count.
diff --git a/src/server/worker.rs b/src/server/worker.rs
index a8ca3b2a..3fe9cec1 100644
--- a/src/server/worker.rs
+++ b/src/server/worker.rs
@@ -22,7 +22,6 @@ use tokio_openssl::SslAcceptorExt;
 use actix::*;
 use actix::msgs::StopArbiter;
 
-use helpers;
 use server::{HttpHandler, KeepAlive};
 use server::channel::HttpChannel;
 use server::settings::WorkerSettings;
@@ -76,7 +75,7 @@ impl<H: HttpHandler + 'static> Worker<H> {
     }
 
     fn update_time(&self, ctx: &mut Context<Self>) {
-        helpers::update_date();
+        self.settings.update_date();
         ctx.run_later(time::Duration::new(1, 0), |slf, ctx| slf.update_time(ctx));
     }