From 5b65987f6aff806fa0abbb272b1c0c937f64f975 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Mon, 25 Dec 2017 13:40:06 -0800 Subject: [PATCH] write response optimizations --- src/h1writer.rs | 55 +++++++++++++++--------------------- src/helpers.rs | 74 ++++++++++++++++++++++++++----------------------- 2 files changed, 62 insertions(+), 67 deletions(-) diff --git a/src/h1writer.rs b/src/h1writer.rs index 3b2415fda..e33020fba 100644 --- a/src/h1writer.rs +++ b/src/h1writer.rs @@ -1,8 +1,9 @@ use std::io; +use bytes::BufMut; use futures::{Async, Poll}; use tokio_io::AsyncWrite; use http::Version; -use http::header::{HeaderValue, CONNECTION, DATE, CONTENT_LENGTH}; +use http::header::{HeaderValue, CONNECTION, DATE}; use helpers; use body::Body; @@ -124,8 +125,6 @@ impl Writer for H1Writer { fn start(&mut self, req: &mut HttpMessage, msg: &mut HttpResponse) -> Result { - //trace!("Prepare response with status: {:?}", msg.status()); - // prepare task self.flags.insert(Flags::STARTED); self.encoder = PayloadEncoder::new(self.buffer.clone(), req, msg); @@ -157,53 +156,42 @@ impl Writer for H1Writer { buffer.reserve(256 + msg.headers().len() * AVERAGE_HEADER_SIZE); } - match version { - Version::HTTP_11 => buffer.extend_from_slice(b"HTTP/1.1 "), - Version::HTTP_2 => buffer.extend_from_slice(b"HTTP/2.0 "), - Version::HTTP_10 => buffer.extend_from_slice(b"HTTP/1.0 "), - Version::HTTP_09 => buffer.extend_from_slice(b"HTTP/0.9 "), - } - helpers::convert_u16(msg.status().as_u16(), &mut buffer); - buffer.extend_from_slice(b" "); + // status line + helpers::write_status_line(version, msg.status().as_u16(), &mut buffer); buffer.extend_from_slice(msg.reason().as_bytes()); - buffer.extend_from_slice(b"\r\n"); - - for (key, value) in msg.headers() { - buffer.extend_from_slice(key.as_str().as_bytes()); - buffer.extend_from_slice(b": "); - buffer.extend_from_slice(value.as_ref()); - buffer.extend_from_slice(b"\r\n"); - } match body { Body::Empty => { - buffer.extend_from_slice(CONTENT_LENGTH.as_str().as_bytes()); - buffer.extend_from_slice(b": 0\r\n"); + buffer.extend_from_slice(b"\r\ncontent-length: 0\r\n"); } Body::Binary(ref bytes) => { - buffer.extend_from_slice(CONTENT_LENGTH.as_str().as_bytes()); - buffer.extend_from_slice(b": "); + buffer.extend_from_slice(b"\r\ncontent-length: "); helpers::convert_usize(bytes.len(), &mut buffer); - buffer.extend_from_slice(b"\r\n"); } - _ => () + _ => buffer.extend_from_slice(b"\r\n"), } - + + // write headers + for (key, value) in msg.headers() { + let v = value.as_ref(); + let k = key.as_str().as_bytes(); + buffer.reserve(k.len() + v.len() + 4); + buffer.put_slice(k); + buffer.put_slice(b": "); + buffer.put_slice(v); + buffer.put_slice(b"\r\n"); + } + // using helpers::date is quite a lot faster if !msg.headers().contains_key(DATE) { - buffer.reserve(helpers::DATE_VALUE_LENGTH + 8); - buffer.extend_from_slice(b"Date: "); helpers::date(&mut buffer); + } else { + // msg eof buffer.extend_from_slice(b"\r\n"); } - - // msg eof - buffer.extend_from_slice(b"\r\n"); self.headers_size = buffer.len() as u32; } - // trace!("Response: {:?}", msg); - if let Body::Binary(bytes) = body { self.encoder.write(bytes.as_ref())?; return Ok(WriterState::Done) @@ -218,6 +206,7 @@ impl Writer for H1Writer { if self.flags.contains(Flags::STARTED) { // TODO: add warning, write after EOF self.encoder.write(payload)?; + return Ok(WriterState::Done) } else { // might be response to EXCEPT self.encoder.get_mut().extend_from_slice(payload) diff --git a/src/helpers.rs b/src/helpers.rs index 30b41c8dd..147ab457b 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -6,6 +6,7 @@ use std::ops::{Deref, DerefMut}; use std::collections::VecDeque; use time; use bytes::BytesMut; +use http::Version; use httprequest::HttpMessage; @@ -14,7 +15,11 @@ pub const DATE_VALUE_LENGTH: usize = 29; pub fn date(dst: &mut BytesMut) { CACHED.with(|cache| { - dst.extend_from_slice(cache.borrow().buffer()); + 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); }) } @@ -234,26 +239,31 @@ const DEC_DIGITS_LUT: &[u8] = 6061626364656667686970717273747576777879\ 8081828384858687888990919293949596979899"; -pub(crate) fn convert_u16(mut n: u16, bytes: &mut BytesMut) { - let mut buf: [u8; 39] = unsafe { mem::uninitialized() }; - let mut curr: isize = 39; +pub(crate) fn write_status_line(version: Version, mut n: u16, bytes: &mut BytesMut) { + let mut buf: [u8; 14] = [b'H', b'T', b'T', b'P', b'/', b'1', b'.', b'1', + b' ', b' ', b' ', b' ', b' ', b' ']; + match version { + Version::HTTP_2 => buf[5] = b'2', + Version::HTTP_10 => buf[7] = b'0', + Version::HTTP_09 => {buf[5] = b'0'; buf[7] = b'9';}, + _ => (), + } + + let mut curr: isize = 13; 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::() >= 2 { - // eagerly decode 4 characters at a time - while n >= 10_000 { - let rem = (n % 10_000) as isize; - n /= 10_000; + // 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); - } + 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 @@ -278,32 +288,28 @@ pub(crate) fn convert_u16(mut n: u16, bytes: &mut BytesMut) { } } - unsafe { - bytes.extend_from_slice( - slice::from_raw_parts(buf_ptr.offset(curr), buf.len() - curr as usize)); - } + bytes.extend_from_slice(&buf); } pub(crate) fn convert_usize(mut n: usize, bytes: &mut BytesMut) { let mut curr: isize = 39; - let mut buf: [u8; 39] = unsafe { mem::uninitialized() }; + let mut buf: [u8; 41] = unsafe { mem::uninitialized() }; + buf[39] = b'\r'; + buf[40] = b'\n'; 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::() >= 2 { - // eagerly decode 4 characters at a time - while n >= 10_000 { - let rem = (n % 10_000) as isize; - n /= 10_000; + // 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); - } + 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 @@ -330,7 +336,7 @@ pub(crate) fn convert_usize(mut n: usize, bytes: &mut BytesMut) { unsafe { bytes.extend_from_slice( - slice::from_raw_parts(buf_ptr.offset(curr), buf.len() - curr as usize)); + slice::from_raw_parts(buf_ptr.offset(curr), 41 - curr as usize)); } }