From d9187713b2eaa628eb34f68c8a7201a6cf8e010d Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Wed, 15 Apr 2015 11:07:23 -0700 Subject: [PATCH] fix(server): handle keep-alive closing Closes #437 --- src/http.rs | 34 ++++++++++++++++++++++++---------- src/server/mod.rs | 8 ++++++-- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/http.rs b/src/http.rs index b05c0e6b4e..2fad1ee98c 100644 --- a/src/http.rs +++ b/src/http.rs @@ -12,7 +12,7 @@ use method::Method; use status::StatusCode; use uri::RequestUri; use version::HttpVersion::{self, Http10, Http11}; -use HttpError:: HttpTooLargeError; +use HttpError::{HttpIoError, HttpTooLargeError}; use {HttpError, HttpResult}; use self::HttpReader::{SizedReader, ChunkedReader, EofReader, EmptyReader}; @@ -353,6 +353,12 @@ fn parse, I>(rdr: &mut BufReader) -> HttpResu _partial => () } match try!(rdr.read_into_buf()) { + 0 if rdr.get_buf().len() == 0 => { + return Err(HttpIoError(io::Error::new( + io::ErrorKind::ConnectionAborted, + "Connection closed" + ))) + }, 0 => return Err(HttpTooLargeError), _ => () } @@ -417,6 +423,7 @@ impl<'a> TryParse for httparse::Response<'a> { } /// An Incoming Message head. Includes request/status line, and headers. +#[derive(Debug)] pub struct Incoming { /// HTTP version of the message. pub version: HttpVersion, @@ -440,8 +447,10 @@ pub struct RawStatus(pub u16, pub Cow<'static, str>); mod tests { use std::io::{self, Write}; - use super::{read_chunk_size}; + use buffer::BufReader; + use mock::MockStream; + use super::{read_chunk_size, parse_request}; #[test] fn test_write_chunked() { @@ -509,25 +518,30 @@ mod tests { #[test] fn test_parse_incoming() { - use buffer::BufReader; - use mock::MockStream; - - use super::parse_request; let mut raw = MockStream::with_input(b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n"); let mut buf = BufReader::new(&mut raw); parse_request(&mut buf).unwrap(); } + #[test] + fn test_parse_tcp_closed() { + use std::io::ErrorKind; + use error::HttpError::HttpIoError; + + let mut empty = MockStream::new(); + let mut buf = BufReader::new(&mut empty); + match parse_request(&mut buf) { + Err(HttpIoError(ref e)) if e.kind() == ErrorKind::ConnectionAborted => (), + other => panic!("unexpected result: {:?}", other) + } + } + #[cfg(feature = "nightly")] use test::Bencher; #[cfg(feature = "nightly")] #[bench] fn bench_parse_incoming(b: &mut Bencher) { - use buffer::BufReader; - use mock::MockStream; - - use super::parse_request; let mut raw = MockStream::with_input(b"GET /echo HTTP/1.1\r\nHost: hyper.rs\r\n\r\n"); let mut buf = BufReader::new(&mut raw); b.iter(|| { diff --git a/src/server/mod.rs b/src/server/mod.rs index 839509272b..a8e3347759 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,5 +1,5 @@ //! HTTP Server -use std::io::{BufWriter, Write}; +use std::io::{ErrorKind, BufWriter, Write}; use std::marker::PhantomData; use std::net::{SocketAddr, ToSocketAddrs}; use std::path::Path; @@ -134,7 +134,11 @@ where S: NetworkStream + Clone, H: Handler { while keep_alive { let req = match Request::new(&mut rdr, addr) { Ok(req) => req, - Err(e@HttpIoError(_)) => { + Err(HttpIoError(ref e)) if e.kind() == ErrorKind::ConnectionAborted => { + trace!("tcp closed, cancelling keep-alive loop"); + break; + } + Err(HttpIoError(e)) => { debug!("ioerror in keepalive loop = {:?}", e); break; }