From f8af3ef7f4be60ae35800adeedc8dcf8597e1f2a Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sun, 22 Apr 2018 15:28:04 -0700 Subject: [PATCH] refactor keep-alive --- src/httprequest.rs | 39 +++++++++-------------- src/server/encoding.rs | 5 +-- src/server/h1.rs | 70 +++++++++++++++++++++++++++++------------- src/server/h1writer.rs | 4 +-- 4 files changed, 67 insertions(+), 51 deletions(-) diff --git a/src/httprequest.rs b/src/httprequest.rs index e917b5c82..ee2bd5a79 100644 --- a/src/httprequest.rs +++ b/src/httprequest.rs @@ -37,6 +37,7 @@ pub struct HttpInnerMessage { pub addr: Option, pub payload: Option, pub info: Option>, + pub keep_alive: bool, resource: RouterResource, } @@ -56,11 +57,12 @@ impl Default for HttpInnerMessage { params: Params::new(), query: Params::new(), query_loaded: false, - cookies: None, addr: None, + cookies: None, payload: None, extensions: Extensions::new(), info: None, + keep_alive: true, resource: RouterResource::Notset, } } @@ -70,20 +72,7 @@ impl HttpInnerMessage { /// Checks if a connection should be kept alive. #[inline] pub fn keep_alive(&self) -> bool { - if let Some(conn) = self.headers.get(header::CONNECTION) { - if let Ok(conn) = conn.to_str() { - if self.version == Version::HTTP_10 && conn.contains("keep-alive") { - true - } else { - self.version == Version::HTTP_11 - && !(conn.contains("close") || conn.contains("upgrade")) - } - } else { - false - } - } else { - self.version != Version::HTTP_10 - } + self.keep_alive } #[inline] @@ -91,12 +80,12 @@ impl HttpInnerMessage { self.headers.clear(); self.extensions.clear(); self.params.clear(); - self.query.clear(); - self.query_loaded = false; - self.cookies = None; self.addr = None; self.info = None; + self.query_loaded = false; + self.cookies = None; self.payload = None; + self.keep_alive = true; self.resource = RouterResource::Notset; } } @@ -126,10 +115,11 @@ impl HttpRequest<()> { params: Params::new(), query: Params::new(), query_loaded: false, + extensions: Extensions::new(), cookies: None, addr: None, - extensions: Extensions::new(), info: None, + keep_alive: true, resource: RouterResource::Notset, }), None, @@ -377,13 +367,13 @@ impl HttpRequest { /// To get client connection information `connection_info()` method should /// be used. #[inline] - pub fn peer_addr(&self) -> Option<&SocketAddr> { - self.as_ref().addr.as_ref() + pub fn peer_addr(&self) -> Option { + self.as_ref().addr } #[inline] pub(crate) fn set_peer_addr(&mut self, addr: Option) { - self.as_mut().addr = addr + self.as_mut().addr = addr; } /// Get a reference to the Params object. @@ -392,6 +382,7 @@ impl HttpRequest { if !self.as_ref().query_loaded { let params: &mut Params = unsafe { mem::transmute(&mut self.as_mut().query) }; + params.clear(); self.as_mut().query_loaded = true; for (key, val) in form_urlencoded::parse(self.query_string().as_ref()) { params.add(key, val); @@ -425,9 +416,9 @@ impl HttpRequest { } } } - msg.cookies = Some(cookies) + msg.cookies = Some(cookies); } - Ok(self.as_ref().cookies.as_ref().unwrap()) + Ok(&self.as_ref().cookies.as_ref().unwrap()) } /// Return request cookie. diff --git a/src/server/encoding.rs b/src/server/encoding.rs index b9da1defe..7c886fe5c 100644 --- a/src/server/encoding.rs +++ b/src/server/encoding.rs @@ -9,7 +9,7 @@ use bytes::{BufMut, Bytes, BytesMut}; use flate2::Compression; use flate2::read::GzDecoder; use flate2::write::{DeflateDecoder, DeflateEncoder, GzEncoder}; -use http::header::{HeaderMap, HeaderValue, ACCEPT_ENCODING, CONNECTION, +use http::header::{HeaderMap, HeaderValue, ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_LENGTH, TRANSFER_ENCODING}; use http::{HttpTryFrom, Method, Version}; @@ -459,9 +459,6 @@ impl ContentEncoder { if resp.upgrade() { if version == Version::HTTP_2 { error!("Connection upgrade is forbidden for HTTP/2"); - } else { - resp.headers_mut() - .insert(CONNECTION, HeaderValue::from_static("upgrade")); } if encoding != ContentEncoding::Identity { encoding = ContentEncoding::Identity; diff --git a/src/server/h1.rs b/src/server/h1.rs index c60762b6e..ec0b1938a 100644 --- a/src/server/h1.rs +++ b/src/server/h1.rs @@ -510,9 +510,10 @@ impl Reader { buf: &mut BytesMut, settings: &WorkerSettings ) -> Poll<(HttpRequest, Option), ParseError> { // Parse http message - let mut has_te = false; let mut has_upgrade = false; - let mut has_length = false; + let mut chunked = false; + let mut content_length = None; + let msg = { let bytes_ptr = buf.as_ref().as_ptr() as usize; let mut headers: [httparse::Header; MAX_HEADERS] = @@ -546,10 +547,10 @@ impl Reader { let msg = settings.get_http_message(); { let msg_mut = msg.get_mut(); + msg_mut.keep_alive = version != Version::HTTP_10; + for header in headers[..headers_len].iter() { if let Ok(name) = HeaderName::from_bytes(header.name.as_bytes()) { - has_te = has_te || name == header::TRANSFER_ENCODING; - has_length = has_length || name == header::CONTENT_LENGTH; has_upgrade = has_upgrade || name == header::UPGRADE; let v_start = header.value.as_ptr() as usize - bytes_ptr; let v_end = v_start + header.value.len(); @@ -558,6 +559,47 @@ impl Reader { slice.slice(v_start, v_end), ) }; + match name { + header::CONTENT_LENGTH => { + if let Ok(s) = value.to_str() { + if let Ok(len) = s.parse::() { + content_length = Some(len) + } else { + debug!("illegal Content-Length: {:?}", len); + return Err(ParseError::Header); + } + } else { + debug!("illegal Content-Length: {:?}", len); + return Err(ParseError::Header); + } + }, + // transfer-encoding + header::TRANSFER_ENCODING => { + if let Ok(s) = value.to_str() { + chunked = s.to_lowercase().contains("chunked"); + } else { + return Err(ParseError::Header) + } + }, + // connection keep-alive state + header::CONNECTION => { + msg_mut.keep_alive = if let Ok(conn) = value.to_str() { + if version == Version::HTTP_10 + && conn.contains("keep-alive") + { + true + } else { + version == Version::HTTP_11 + && !(conn.contains("close") + || conn.contains("upgrade")) + } + } else { + false + }; + }, + _ => (), + } + msg_mut.headers.append(name, value); } else { return Err(ParseError::Header); @@ -572,26 +614,12 @@ impl Reader { }; // https://tools.ietf.org/html/rfc7230#section-3.3.3 - let decoder = if has_te && chunked(&msg.get_mut().headers)? { + let decoder = if chunked { // Chunked encoding Some(Decoder::chunked()) - } else if has_length { + } else if let Some(len) = content_length { // Content-Length - let len = msg.get_ref() - .headers - .get(header::CONTENT_LENGTH) - .unwrap(); - if let Ok(s) = len.to_str() { - if let Ok(len) = s.parse::() { - Some(Decoder::length(len)) - } else { - debug!("illegal Content-Length: {:?}", len); - return Err(ParseError::Header); - } - } else { - debug!("illegal Content-Length: {:?}", len); - return Err(ParseError::Header); - } + Some(Decoder::length(len)) } else if has_upgrade || msg.get_ref().method == Method::CONNECT { // upgrade(websocket) or connect Some(Decoder::eof()) diff --git a/src/server/h1writer.rs b/src/server/h1writer.rs index ee2717bba..3d94d44cf 100644 --- a/src/server/h1writer.rs +++ b/src/server/h1writer.rs @@ -2,8 +2,6 @@ use bytes::BufMut; use futures::{Async, Poll}; -use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE}; -use http::{Method, Version}; use std::rc::Rc; use std::{io, mem}; use tokio_io::AsyncWrite; @@ -17,6 +15,8 @@ use body::{Binary, Body}; use header::ContentEncoding; use httprequest::HttpInnerMessage; use httpresponse::HttpResponse; +use http::{Method, Version}; +use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE}; const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific