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 std::io;
use bytes::BufMut;
use futures::{Async, Poll}; use futures::{Async, Poll};
use tokio_io::AsyncWrite; use tokio_io::AsyncWrite;
use http::Version; use http::Version;
use http::header::{HeaderValue, CONNECTION, DATE, CONTENT_LENGTH}; use http::header::{HeaderValue, CONNECTION, DATE};
use helpers; use helpers;
use body::Body; use body::Body;
@ -124,8 +125,6 @@ impl<T: AsyncWrite> Writer for H1Writer<T> {
fn start(&mut self, req: &mut HttpMessage, msg: &mut HttpResponse) fn start(&mut self, req: &mut HttpMessage, msg: &mut HttpResponse)
-> Result<WriterState, io::Error> -> Result<WriterState, io::Error>
{ {
//trace!("Prepare response with status: {:?}", msg.status());
// prepare task // prepare task
self.flags.insert(Flags::STARTED); self.flags.insert(Flags::STARTED);
self.encoder = PayloadEncoder::new(self.buffer.clone(), req, msg); 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); buffer.reserve(256 + msg.headers().len() * AVERAGE_HEADER_SIZE);
} }
match version { // status line
Version::HTTP_11 => buffer.extend_from_slice(b"HTTP/1.1 "), helpers::write_status_line(version, msg.status().as_u16(), &mut buffer);
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" ");
buffer.extend_from_slice(msg.reason().as_bytes()); 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 { match body {
Body::Empty => { Body::Empty => {
buffer.extend_from_slice(CONTENT_LENGTH.as_str().as_bytes()); buffer.extend_from_slice(b"\r\ncontent-length: 0\r\n");
buffer.extend_from_slice(b": 0\r\n");
} }
Body::Binary(ref bytes) => { Body::Binary(ref bytes) => {
buffer.extend_from_slice(CONTENT_LENGTH.as_str().as_bytes()); buffer.extend_from_slice(b"\r\ncontent-length: ");
buffer.extend_from_slice(b": ");
helpers::convert_usize(bytes.len(), &mut buffer); 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 // using helpers::date is quite a lot faster
if !msg.headers().contains_key(DATE) { if !msg.headers().contains_key(DATE) {
buffer.reserve(helpers::DATE_VALUE_LENGTH + 8);
buffer.extend_from_slice(b"Date: ");
helpers::date(&mut buffer); helpers::date(&mut buffer);
} else {
// msg eof
buffer.extend_from_slice(b"\r\n"); buffer.extend_from_slice(b"\r\n");
} }
// msg eof
buffer.extend_from_slice(b"\r\n");
self.headers_size = buffer.len() as u32; self.headers_size = buffer.len() as u32;
} }
// trace!("Response: {:?}", msg);
if let Body::Binary(bytes) = body { if let Body::Binary(bytes) = body {
self.encoder.write(bytes.as_ref())?; self.encoder.write(bytes.as_ref())?;
return Ok(WriterState::Done) return Ok(WriterState::Done)
@ -218,6 +206,7 @@ impl<T: AsyncWrite> Writer for H1Writer<T> {
if self.flags.contains(Flags::STARTED) { if self.flags.contains(Flags::STARTED) {
// TODO: add warning, write after EOF // TODO: add warning, write after EOF
self.encoder.write(payload)?; self.encoder.write(payload)?;
return Ok(WriterState::Done)
} else { } else {
// might be response to EXCEPT // might be response to EXCEPT
self.encoder.get_mut().extend_from_slice(payload) self.encoder.get_mut().extend_from_slice(payload)

View File

@ -6,6 +6,7 @@ use std::ops::{Deref, DerefMut};
use std::collections::VecDeque; use std::collections::VecDeque;
use time; use time;
use bytes::BytesMut; use bytes::BytesMut;
use http::Version;
use httprequest::HttpMessage; use httprequest::HttpMessage;
@ -14,7 +15,11 @@ pub const DATE_VALUE_LENGTH: usize = 29;
pub fn date(dst: &mut BytesMut) { pub fn date(dst: &mut BytesMut) {
CACHED.with(|cache| { 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\ 6061626364656667686970717273747576777879\
8081828384858687888990919293949596979899"; 8081828384858687888990919293949596979899";
pub(crate) fn convert_u16(mut n: u16, bytes: &mut BytesMut) { pub(crate) fn write_status_line(version: Version, mut n: u16, bytes: &mut BytesMut) {
let mut buf: [u8; 39] = unsafe { mem::uninitialized() }; let mut buf: [u8; 14] = [b'H', b'T', b'T', b'P', b'/', b'1', b'.', b'1',
let mut curr: isize = 39; 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 buf_ptr = buf.as_mut_ptr();
let lut_ptr = DEC_DIGITS_LUT.as_ptr(); let lut_ptr = DEC_DIGITS_LUT.as_ptr();
unsafe { unsafe {
// need at least 16 bits for the 4-characters-at-a-time to work. // eagerly decode 4 characters at a time
if mem::size_of::<u16>() >= 2 { while n >= 10_000 {
// eagerly decode 4 characters at a time let rem = (n % 10_000) as isize;
while n >= 10_000 { n /= 10_000;
let rem = (n % 10_000) as isize;
n /= 10_000;
let d1 = (rem / 100) << 1; let d1 = (rem / 100) << 1;
let d2 = (rem % 100) << 1; let d2 = (rem % 100) << 1;
curr -= 4; curr -= 4;
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); 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); 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 // 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(&buf);
bytes.extend_from_slice(
slice::from_raw_parts(buf_ptr.offset(curr), buf.len() - curr as usize));
}
} }
pub(crate) fn convert_usize(mut n: usize, bytes: &mut BytesMut) { pub(crate) fn convert_usize(mut n: usize, bytes: &mut BytesMut) {
let mut curr: isize = 39; 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 buf_ptr = buf.as_mut_ptr();
let lut_ptr = DEC_DIGITS_LUT.as_ptr(); let lut_ptr = DEC_DIGITS_LUT.as_ptr();
unsafe { unsafe {
// need at least 16 bits for the 4-characters-at-a-time to work. // eagerly decode 4 characters at a time
if mem::size_of::<usize>() >= 2 { while n >= 10_000 {
// eagerly decode 4 characters at a time let rem = (n % 10_000) as isize;
while n >= 10_000 { n /= 10_000;
let rem = (n % 10_000) as isize;
n /= 10_000;
let d1 = (rem / 100) << 1; let d1 = (rem / 100) << 1;
let d2 = (rem % 100) << 1; let d2 = (rem % 100) << 1;
curr -= 4; curr -= 4;
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); 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); 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 // 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 { unsafe {
bytes.extend_from_slice( 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));
} }
} }