1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-30 18:34:36 +01:00

write response optimizations

This commit is contained in:
Nikolay Kim 2017-12-25 13:40:06 -08:00
parent 89c9dfb5bc
commit 5b65987f6a
2 changed files with 62 additions and 67 deletions

View File

@ -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<T: AsyncWrite> Writer for H1Writer<T> {
fn start(&mut self, req: &mut HttpMessage, msg: &mut HttpResponse)
-> Result<WriterState, io::Error>
{
//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<T: AsyncWrite> Writer for H1Writer<T> {
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<T: AsyncWrite> Writer for H1Writer<T> {
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)

View File

@ -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::<u16>() >= 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::<usize>() >= 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));
}
}