From b1f33e29ec79a766e369bc3d32305175fa5152a4 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sat, 16 Dec 2017 07:29:15 -0800 Subject: [PATCH] simplify content-length calculation --- README.md | 2 +- src/encoding.rs | 19 +++++-------------- src/h1.rs | 6 +----- src/h1writer.rs | 36 +++++++++++++++++++++++++----------- src/h2writer.rs | 19 ++++++++++++++++--- src/helpers.rs | 7 +++---- src/ws.rs | 2 +- 7 files changed, 52 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index fcb40d8f..473b517d 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Each result is best of five runs. All measurements are req/sec. Name | 1 thread | 1 pipeline | 3 thread | 3 pipeline | 8 thread | 8 pipeline ---- | -------- | ---------- | -------- | ---------- | -------- | ---------- -Actix | 91.200 | 912.000 | 122.100 | 2.083.000 | 107.400 | 2.650.000 +Actix | 91.200 | 950.000 | 122.100 | 2.083.000 | 107.400 | 2.730.000 Gotham | 61.000 | 178.000 | | | | Iron | | | | | 94.500 | 78.000 Rocket | | | | | 95.500 | failed diff --git a/src/encoding.rs b/src/encoding.rs index b632a1a3..85cd7fd6 100644 --- a/src/encoding.rs +++ b/src/encoding.rs @@ -13,10 +13,9 @@ use flate2::write::{GzEncoder, DeflateDecoder, DeflateEncoder}; use brotli2::write::{BrotliDecoder, BrotliEncoder}; use bytes::{Bytes, BytesMut, BufMut, Writer}; -use helpers; -use helpers::SharedBytes; use body::{Body, Binary}; use error::PayloadError; +use helpers::SharedBytes; use httprequest::HttpMessage; use httpresponse::HttpResponse; use payload::{PayloadSender, PayloadWriter}; @@ -390,7 +389,7 @@ impl PayloadEncoder { if resp.chunked() { error!("Chunked transfer is enabled but body is set to Empty"); } - resp.headers_mut().insert(CONTENT_LENGTH, HeaderValue::from_static("0")); + resp.headers_mut().remove(CONTENT_LENGTH); TransferEncoding::eof(buf) }, Body::Binary(ref mut bytes) => { @@ -409,18 +408,12 @@ impl PayloadEncoder { // TODO return error! let _ = enc.write(bytes.as_ref()); let _ = enc.write_eof(); - let b = enc.get_mut().take(); - resp.headers_mut().insert( - CONTENT_LENGTH, helpers::convert_into_header(b.len())); - *bytes = Binary::from(b); + *bytes = Binary::from(enc.get_mut().take()); encoding = ContentEncoding::Identity; - TransferEncoding::eof(buf) - } else { - resp.headers_mut().insert( - CONTENT_LENGTH, helpers::convert_into_header(bytes.len())); - TransferEncoding::eof(buf) } + resp.headers_mut().remove(CONTENT_LENGTH); + TransferEncoding::eof(buf) } Body::Streaming(_) | Body::StreamingContext => { if resp.chunked() { @@ -734,11 +727,9 @@ impl TransferEncoding { return *remaining == 0 } let max = cmp::min(*remaining, msg.len() as u64); - trace!("sized write = {}", max); self.buffer.get_mut().extend_from_slice(msg[..max as usize].as_ref()); *remaining -= max as u64; - trace!("encoded {} bytes, remaining = {}", max, remaining); *remaining == 0 }, } diff --git a/src/h1.rs b/src/h1.rs index fa1a1f2e..78e060e1 100644 --- a/src/h1.rs +++ b/src/h1.rs @@ -435,7 +435,7 @@ impl Reader { let read = if buf.is_empty() { match self.read_from_io(io, buf) { Ok(Async::Ready(0)) => { - debug!("Ignored premature client disconnection"); + // debug!("Ignored premature client disconnection"); return Err(ReaderError::Disconnect); }, Ok(Async::Ready(_)) => (), @@ -713,7 +713,6 @@ impl Decoder { pub fn decode(&mut self, body: &mut BytesMut) -> Poll, io::Error> { match self.kind { Kind::Length(ref mut remaining) => { - trace!("Sized read, remaining={:?}", remaining); if *remaining == 0 { Ok(Async::Ready(None)) } else { @@ -794,7 +793,6 @@ impl ChunkedState { } } fn read_size(rdr: &mut BytesMut, size: &mut u64) -> Poll { - trace!("Read chunk hex size"); let radix = 16; match byte!(rdr) { b @ b'0'...b'9' => { @@ -833,14 +831,12 @@ impl ChunkedState { } } fn read_extension(rdr: &mut BytesMut) -> Poll { - trace!("read_extension"); match byte!(rdr) { b'\r' => Ok(Async::Ready(ChunkedState::SizeLf)), _ => Ok(Async::Ready(ChunkedState::Extension)), // no supported extensions } } fn read_size_lf(rdr: &mut BytesMut, size: &mut u64) -> Poll { - trace!("Chunk size is {:?}", size); match byte!(rdr) { b'\n' if *size > 0 => Ok(Async::Ready(ChunkedState::Body)), b'\n' if *size == 0 => Ok(Async::Ready(ChunkedState::EndCr)), diff --git a/src/h1writer.rs b/src/h1writer.rs index dd868d7d..3b2415fd 100644 --- a/src/h1writer.rs +++ b/src/h1writer.rs @@ -2,7 +2,7 @@ use std::io; use futures::{Async, Poll}; use tokio_io::AsyncWrite; use http::Version; -use http::header::{HeaderValue, CONNECTION, DATE}; +use http::header::{HeaderValue, CONNECTION, DATE, CONTENT_LENGTH}; use helpers; use body::Body; @@ -124,7 +124,7 @@ impl Writer for H1Writer { fn start(&mut self, req: &mut HttpMessage, msg: &mut HttpResponse) -> Result { - trace!("Prepare response with status: {:?}", msg.status()); + //trace!("Prepare response with status: {:?}", msg.status()); // prepare task self.flags.insert(Flags::STARTED); @@ -146,11 +146,12 @@ impl Writer for H1Writer { } else if version >= Version::HTTP_11 { msg.headers_mut().insert(CONNECTION, HeaderValue::from_static("close")); } + let body = msg.replace_body(Body::Empty); // render message { let mut buffer = self.encoder.get_mut(); - if let Body::Binary(ref bytes) = *msg.body() { + if let Body::Binary(ref bytes) = body { buffer.reserve(256 + msg.headers().len() * AVERAGE_HEADER_SIZE + bytes.len()); } else { buffer.reserve(256 + msg.headers().len() * AVERAGE_HEADER_SIZE); @@ -174,6 +175,20 @@ impl Writer for H1Writer { 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"); + } + Body::Binary(ref bytes) => { + buffer.extend_from_slice(CONTENT_LENGTH.as_str().as_bytes()); + buffer.extend_from_slice(b": "); + helpers::convert_usize(bytes.len(), &mut buffer); + buffer.extend_from_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); @@ -187,14 +202,13 @@ impl Writer for H1Writer { self.headers_size = buffer.len() as u32; } - trace!("Response: {:?}", msg); + // trace!("Response: {:?}", msg); - if msg.body().is_binary() { - let body = msg.replace_body(Body::Empty); - if let Body::Binary(bytes) = body { - self.encoder.write(bytes.as_ref())?; - return Ok(WriterState::Done) - } + if let Body::Binary(bytes) = body { + self.encoder.write(bytes.as_ref())?; + return Ok(WriterState::Done) + } else { + msg.replace_body(body); } Ok(WriterState::Done) } @@ -221,7 +235,7 @@ impl Writer for H1Writer { self.encoder.write_eof()?; if !self.encoder.is_eof() { - //debug!("last payload item, but it is not EOF "); + // debug!("last payload item, but it is not EOF "); Err(io::Error::new(io::ErrorKind::Other, "Last payload item, but eof is not reached")) } else if self.encoder.len() > MAX_WRITE_BUFFER_SIZE { diff --git a/src/h2writer.rs b/src/h2writer.rs index 5874d660..d3091c6f 100644 --- a/src/h2writer.rs +++ b/src/h2writer.rs @@ -4,7 +4,7 @@ use futures::{Async, Poll}; use http2::{Reason, SendStream}; use http2::server::Respond; use http::{Version, HttpTryFrom, Response}; -use http::header::{HeaderValue, CONNECTION, TRANSFER_ENCODING, DATE}; +use http::header::{HeaderValue, CONNECTION, TRANSFER_ENCODING, DATE, CONTENT_LENGTH}; use helpers; use body::Body; @@ -114,7 +114,7 @@ impl Writer for H2Writer { fn start(&mut self, req: &mut HttpMessage, msg: &mut HttpResponse) -> Result { - trace!("Prepare response with status: {:?}", msg.status()); + // trace!("Prepare response with status: {:?}", msg.status()); // prepare response self.flags.insert(Flags::STARTED); @@ -142,6 +142,19 @@ impl Writer for H2Writer { resp.headers_mut().insert(key, value.clone()); } + match *msg.body() { + Body::Binary(ref bytes) => { + let mut val = BytesMut::new(); + helpers::convert_usize(bytes.len(), &mut val); + resp.headers_mut().insert( + CONTENT_LENGTH, HeaderValue::try_from(val.freeze()).unwrap()); + } + Body::Empty => { + resp.headers_mut().insert(CONTENT_LENGTH, HeaderValue::from_static("0")); + }, + _ => (), + } + match self.respond.send_response(resp, self.flags.contains(Flags::EOF)) { Ok(stream) => self.stream = Some(stream), @@ -149,7 +162,7 @@ impl Writer for H2Writer { return Err(io::Error::new(io::ErrorKind::Other, "err")), } - trace!("Response: {:?}", msg); + // trace!("Response: {:?}", msg); if msg.body().is_binary() { if let Body::Binary(bytes) = msg.replace_body(Body::Empty) { diff --git a/src/helpers.rs b/src/helpers.rs index 1873df9f..30b41c8d 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -6,7 +6,6 @@ use std::ops::{Deref, DerefMut}; use std::collections::VecDeque; use time; use bytes::BytesMut; -use http::header::HeaderValue; use httprequest::HttpMessage; @@ -285,7 +284,7 @@ pub(crate) fn convert_u16(mut n: u16, bytes: &mut BytesMut) { } } -pub(crate) fn convert_into_header(mut n: usize) -> HeaderValue { +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 buf_ptr = buf.as_mut_ptr(); @@ -330,8 +329,8 @@ pub(crate) fn convert_into_header(mut n: usize) -> HeaderValue { } unsafe { - HeaderValue::from_bytes( - slice::from_raw_parts(buf_ptr.offset(curr), buf.len() - curr as usize)).unwrap() + bytes.extend_from_slice( + slice::from_raw_parts(buf_ptr.offset(curr), buf.len() - curr as usize)); } } diff --git a/src/ws.rs b/src/ws.rs index d30e525a..324a304a 100644 --- a/src/ws.rs +++ b/src/ws.rs @@ -220,7 +220,7 @@ impl Stream for WsStream { loop { match wsframe::Frame::parse(&mut self.buf) { Ok(Some(frame)) => { - trace!("WsFrame {}", frame); + // trace!("WsFrame {}", frame); let (_finished, opcode, payload) = frame.unpack(); match opcode {