From 8760e39281bed39906c989ba275eb8a63d785b67 Mon Sep 17 00:00:00 2001 From: tomaka Date: Mon, 7 Aug 2017 13:57:59 +0200 Subject: [PATCH 01/50] Add new dependencies --- Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 3b7ed1161..95a8a685e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,8 @@ brotli2 = { version = "0.2.1", optional = true } chrono = "0.2.0" filetime = "0.1.10" flate2 = { version = "0.2.14", optional = true } +httparse = "1.2.3" +mio = "0.6.10" multipart = { version = "0.5.1", default-features = false, features = ["server"] } rand = "0.3.11" rustc-serialize = "0.3" From f0a4be94231ad1cbabe0baebd981d776a2dc8585 Mon Sep 17 00:00:00 2001 From: tomaka Date: Mon, 7 Aug 2017 14:00:52 +0200 Subject: [PATCH 02/50] Move Server to its own module --- src/lib.rs | 169 +-------------------------------------------- src/server.rs | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+), 167 deletions(-) create mode 100644 src/server.rs diff --git a/src/lib.rs b/src/lib.rs index 4fc246191..5e6230ca9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,21 +70,18 @@ pub use assets::extension_to_mime; pub use assets::match_assets; pub use log::log; pub use response::{Response, ResponseBody}; +pub use server::Server; pub use tiny_http::ReadWrite; -use std::error::Error; use std::io::Cursor; use std::io::Result as IoResult; use std::io::Read; use std::marker::PhantomData; use std::net::SocketAddr; use std::net::ToSocketAddrs; -use std::panic; -use std::panic::AssertUnwindSafe; use std::slice::Iter as SliceIter; use std::sync::Arc; use std::sync::Mutex; -use std::thread; use std::ascii::AsciiExt; pub mod cgi; @@ -99,6 +96,7 @@ mod find_route; mod log; mod response; mod router; +mod server; #[doc(hidden)] pub mod try_or_400; @@ -206,169 +204,6 @@ pub fn start_server(addr: A, handler: F) -> ! panic!("The server socket closed unexpectedly") } -/// A listening server. -/// -/// This struct is the more manual server creation API of rouille and can be used as an alternative -/// to the `start_server` function. -/// -/// The `start_server` function is just a shortcut for `Server::new` followed with `run`. See the -/// documentation of the `start_server` function for more details about the handler. -/// -/// # Example -/// -/// ```no_run -/// use rouille::Server; -/// use rouille::Response; -/// -/// let server = Server::new("localhost:0", |request| { -/// Response::text("hello world") -/// }).unwrap(); -/// println!("Listening on {:?}", server.server_addr()); -/// server.run(); -/// ``` -pub struct Server { - server: tiny_http::Server, - handler: Arc>, -} - -impl Server where F: Send + Sync + 'static + Fn(&Request) -> Response { - /// Builds a new `Server` object. - /// - /// After this function returns, the HTTP server is listening. - /// - /// Returns an error if there was an error while creating the listening socket, for example if - /// the port is already in use. - pub fn new(addr: A, handler: F) -> Result, Box> - where A: ToSocketAddrs - { - let server = try!(tiny_http::Server::http(addr)); - - Ok(Server { - server: server, - handler: Arc::new(AssertUnwindSafe(handler)), // TODO: using AssertUnwindSafe here is wrong, but unwind safety has some usability problems in Rust in general - }) - } - - /// Returns the address of the listening socket. - #[inline] - pub fn server_addr(&self) -> SocketAddr { - self.server.server_addr() - } - - /// Runs the server forever, or until the listening socket is somehow force-closed by the - /// operating system. - #[inline] - pub fn run(self) { - for request in self.server.incoming_requests() { - self.process(request); - } - } - - /// Processes all the client requests waiting to be processed, then returns. - /// - /// This function executes very quickly, as each client requests that needs to be processed - /// is processed in a separate thread. - #[inline] - pub fn poll(&self) { - while let Ok(Some(request)) = self.server.try_recv() { - self.process(request); - } - } - - // Internal function, called when we got a request from tiny-http that needs to be processed. - fn process(&self, request: tiny_http::Request) { - // We spawn a thread so that requests are processed in parallel. - let handler = self.handler.clone(); - thread::spawn(move || { - // Small helper struct that makes it possible to put - // a `tiny_http::Request` inside a `Box`. - struct RequestRead(Arc>>); - impl Read for RequestRead { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> IoResult { - self.0.lock().unwrap().as_mut().unwrap().as_reader().read(buf) - } - } - - // Building the `Request` object. - let tiny_http_request; - let rouille_request = { - let url = request.url().to_owned(); - let method = request.method().as_str().to_owned(); - let headers = request.headers().iter().map(|h| (h.field.to_string(), h.value.clone().into())).collect(); - let remote_addr = request.remote_addr().clone(); - - tiny_http_request = Arc::new(Mutex::new(Some(request))); - - Request { - url: url, - method: method, - headers: headers, - https: false, - data: Arc::new(Mutex::new(Some(Box::new(RequestRead(tiny_http_request.clone())) as Box<_>))), - remote_addr: remote_addr, - } - }; - - // Calling the handler ; this most likely takes a lot of time. - // If the handler panics, we build a dummy response. - let mut rouille_response = { - // We don't use the `rouille_request` anymore after the panic, so it's ok to assert - // it's unwind safe. - let rouille_request = AssertUnwindSafe(rouille_request); - let res = panic::catch_unwind(move || { - let rouille_request = rouille_request; - handler(&rouille_request) - }); - - match res { - Ok(r) => r, - Err(_) => { - Response::html("

Internal Server Error

\ -

An internal error has occurred on the server.

") - .with_status_code(500) - } - } - }; - - // writing the response - let (res_data, res_len) = rouille_response.data.into_reader_and_size(); - let mut response = tiny_http::Response::empty(rouille_response.status_code) - .with_data(res_data, res_len); - - let mut upgrade_header = "".into(); - - for (key, value) in rouille_response.headers { - if key.eq_ignore_ascii_case("Content-Length") { - continue; - } - - if key.eq_ignore_ascii_case("Upgrade") { - upgrade_header = value; - continue; - } - - if let Ok(header) = tiny_http::Header::from_bytes(key.as_bytes(), value.as_bytes()) { - response.add_header(header); - } else { - // TODO: ? - } - } - - if let Some(ref mut upgrade) = rouille_response.upgrade { - let trq = tiny_http_request.lock().unwrap().take().unwrap(); - let socket = trq.upgrade(&upgrade_header, response); - upgrade.build(socket); - - } else { - // We don't really care if we fail to send the response to the client, as there's - // nothing we can do anyway. - let _ = tiny_http_request.lock().unwrap().take().unwrap().respond(response); - } - }); - } -} - /// Trait for objects that can take ownership of a raw connection to the client data. /// /// The purpose of this trait is to be used with the `Connection: Upgrade` header, hence its name. diff --git a/src/server.rs b/src/server.rs new file mode 100644 index 000000000..c859fa709 --- /dev/null +++ b/src/server.rs @@ -0,0 +1,187 @@ +// Copyright (c) 2016 The Rouille developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use std::error::Error; +use std::io::Result as IoResult; +use std::io::Read; +use std::net::SocketAddr; +use std::net::ToSocketAddrs; +use std::panic; +use std::panic::AssertUnwindSafe; +use std::sync::Arc; +use std::sync::Mutex; +use std::thread; +use std::ascii::AsciiExt; +use tiny_http; + +use Request; +use Response; + +/// A listening server. +/// +/// This struct is the more manual server creation API of rouille and can be used as an alternative +/// to the `start_server` function. +/// +/// The `start_server` function is just a shortcut for `Server::new` followed with `run`. See the +/// documentation of the `start_server` function for more details about the handler. +/// +/// # Example +/// +/// ```no_run +/// use rouille::Server; +/// use rouille::Response; +/// +/// let server = Server::new("localhost:0", |request| { +/// Response::text("hello world") +/// }).unwrap(); +/// println!("Listening on {:?}", server.server_addr()); +/// server.run(); +/// ``` +pub struct Server { + server: tiny_http::Server, + handler: Arc>, +} + +impl Server where F: Send + Sync + 'static + Fn(&Request) -> Response { + /// Builds a new `Server` object. + /// + /// After this function returns, the HTTP server is listening. + /// + /// Returns an error if there was an error while creating the listening socket, for example if + /// the port is already in use. + pub fn new
(addr: A, handler: F) -> Result, Box> + where A: ToSocketAddrs + { + let server = try!(tiny_http::Server::http(addr)); + + Ok(Server { + server: server, + handler: Arc::new(AssertUnwindSafe(handler)), // TODO: using AssertUnwindSafe here is wrong, but unwind safety has some usability problems in Rust in general + }) + } + + /// Returns the address of the listening socket. + #[inline] + pub fn server_addr(&self) -> SocketAddr { + self.server.server_addr() + } + + /// Runs the server forever, or until the listening socket is somehow force-closed by the + /// operating system. + #[inline] + pub fn run(self) { + for request in self.server.incoming_requests() { + self.process(request); + } + } + + /// Processes all the client requests waiting to be processed, then returns. + /// + /// This function executes very quickly, as each client requests that needs to be processed + /// is processed in a separate thread. + #[inline] + pub fn poll(&self) { + while let Ok(Some(request)) = self.server.try_recv() { + self.process(request); + } + } + + // Internal function, called when we got a request from tiny-http that needs to be processed. + fn process(&self, request: tiny_http::Request) { + // We spawn a thread so that requests are processed in parallel. + let handler = self.handler.clone(); + thread::spawn(move || { + // Small helper struct that makes it possible to put + // a `tiny_http::Request` inside a `Box`. + struct RequestRead(Arc>>); + impl Read for RequestRead { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> IoResult { + self.0.lock().unwrap().as_mut().unwrap().as_reader().read(buf) + } + } + + // Building the `Request` object. + let tiny_http_request; + let rouille_request = { + let url = request.url().to_owned(); + let method = request.method().as_str().to_owned(); + let headers = request.headers().iter().map(|h| (h.field.to_string(), h.value.clone().into())).collect(); + let remote_addr = request.remote_addr().clone(); + + tiny_http_request = Arc::new(Mutex::new(Some(request))); + + Request { + url: url, + method: method, + headers: headers, + https: false, + data: Arc::new(Mutex::new(Some(Box::new(RequestRead(tiny_http_request.clone())) as Box<_>))), + remote_addr: remote_addr, + } + }; + + // Calling the handler ; this most likely takes a lot of time. + // If the handler panics, we build a dummy response. + let mut rouille_response = { + // We don't use the `rouille_request` anymore after the panic, so it's ok to assert + // it's unwind safe. + let rouille_request = AssertUnwindSafe(rouille_request); + let res = panic::catch_unwind(move || { + let rouille_request = rouille_request; + handler(&rouille_request) + }); + + match res { + Ok(r) => r, + Err(_) => { + Response::html("

Internal Server Error

\ +

An internal error has occurred on the server.

") + .with_status_code(500) + } + } + }; + + // writing the response + let (res_data, res_len) = rouille_response.data.into_reader_and_size(); + let mut response = tiny_http::Response::empty(rouille_response.status_code) + .with_data(res_data, res_len); + + let mut upgrade_header = "".into(); + + for (key, value) in rouille_response.headers { + if key.eq_ignore_ascii_case("Content-Length") { + continue; + } + + if key.eq_ignore_ascii_case("Upgrade") { + upgrade_header = value; + continue; + } + + if let Ok(header) = tiny_http::Header::from_bytes(key.as_bytes(), value.as_bytes()) { + response.add_header(header); + } else { + // TODO: ? + } + } + + if let Some(ref mut upgrade) = rouille_response.upgrade { + let trq = tiny_http_request.lock().unwrap().take().unwrap(); + let socket = trq.upgrade(&upgrade_header, response); + upgrade.build(socket); + + } else { + // We don't really care if we fail to send the response to the client, as there's + // nothing we can do anyway. + let _ = tiny_http_request.lock().unwrap().take().unwrap().respond(response); + } + }); + } +} From d1119353b5015d376f807fb79ebb402a4d169e0e Mon Sep 17 00:00:00 2001 From: tomaka Date: Mon, 7 Aug 2017 19:38:28 +0200 Subject: [PATCH 03/50] First draft for using mio --- Cargo.toml | 2 + src/lib.rs | 5 + src/server.rs | 294 +++++++++++++++++++++++++++++++++++++++--- src/socket_handler.rs | 282 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 565 insertions(+), 18 deletions(-) create mode 100644 src/socket_handler.rs diff --git a/Cargo.toml b/Cargo.toml index 95a8a685e..42f93e634 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,9 +23,11 @@ flate2 = { version = "0.2.14", optional = true } httparse = "1.2.3" mio = "0.6.10" multipart = { version = "0.5.1", default-features = false, features = ["server"] } +num_cpus = "1.6.2" rand = "0.3.11" rustc-serialize = "0.3" sha1 = "0.2.0" +slab = "0.4.0" term = "0.2" time = "0.1.31" tiny_http = "0.5.6" diff --git a/src/lib.rs b/src/lib.rs index 5e6230ca9..538bfce87 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,10 +58,14 @@ extern crate chrono; extern crate filetime; #[cfg(feature = "flate2")] extern crate flate2; +extern crate httparse; +extern crate mio; extern crate multipart; +extern crate num_cpus; extern crate rand; extern crate rustc_serialize; extern crate sha1; +extern crate slab; extern crate time; extern crate tiny_http; extern crate url; @@ -97,6 +101,7 @@ mod log; mod response; mod router; mod server; +mod socket_handler; #[doc(hidden)] pub mod try_or_400; diff --git a/src/server.rs b/src/server.rs index c859fa709..5f16eccff 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2016 The Rouille developers +// Copyright (c) 2017 The Rouille developers // Licensed under the Apache License, Version 2.0 // or the MIT @@ -8,17 +8,22 @@ // according to those terms. use std::error::Error; -use std::io::Result as IoResult; +use std::io::ErrorKind; use std::io::Read; +use std::io::Write; use std::net::SocketAddr; use std::net::ToSocketAddrs; -use std::panic; use std::panic::AssertUnwindSafe; use std::sync::Arc; use std::sync::Mutex; use std::thread; -use std::ascii::AsciiExt; -use tiny_http; +use mio::{Events, Poll, Ready, PollOpt}; +use mio::tcp::{TcpListener, TcpStream}; +use num_cpus; +use slab::Slab; + +use socket_handler::SocketHandler; +use socket_handler::Update as SocketHandlerUpdate; use Request; use Response; @@ -44,8 +49,27 @@ use Response; /// server.run(); /// ``` pub struct Server { - server: tiny_http::Server, - handler: Arc>, + inner: Arc>, + local_events: Mutex, +} + +// Data shared between threads. +struct ThreadsShare { + // The main poll event. + poll: Poll, + // Storage for all the objects registered towards the `Poll`. + sockets: Mutex>, + // The function that handles requests. + handler: AssertUnwindSafe, +} + +enum Socket { + Listener(TcpListener), + Stream { + stream: TcpStream, + handler: SocketHandler, + update: SocketHandlerUpdate, + }, } impl Server where F: Send + Sync + 'static + Fn(&Request) -> Response { @@ -56,28 +80,79 @@ impl Server where F: Send + Sync + 'static + Fn(&Request) -> Response { /// Returns an error if there was an error while creating the listening socket, for example if /// the port is already in use. pub fn new
(addr: A, handler: F) -> Result, Box> - where A: ToSocketAddrs + where A: ToSocketAddrs, + F: Fn(&Request) -> Response + Send + 'static { - let server = try!(tiny_http::Server::http(addr)); + let server = Server::init(handler)?; + + for addr in addr.to_socket_addrs()? { + server.add_listener(&addr)?; + } + + Ok(server) + } + + // Builds a new `Server` but without any listener. + fn init(handler: F) -> Result, Box> + where F: Fn(&Request) -> Response + Send + 'static + { + let share = Arc::new(ThreadsShare { + poll: Poll::new()?, + sockets: Mutex::new(Slab::new()), + handler: AssertUnwindSafe(handler), // TODO: using AssertUnwindSafe here is wrong, but unwind safety has some usability problems in Rust in general + }); + + for _ in 0 .. num_cpus::get() - 1 { + let share = share.clone(); + thread::spawn(move || { + // Each thread has its own local MIO events. + let mut events = Events::with_capacity(128); + + // TODO: The docs say that two events can be generated, one for read and one for + // write, presumably even if we pass one_shot(). Is this code ready for this + // situation? + + loop { + one_poll(&share, &mut events); + } + }); + } Ok(Server { - server: server, - handler: Arc::new(AssertUnwindSafe(handler)), // TODO: using AssertUnwindSafe here is wrong, but unwind safety has some usability problems in Rust in general + inner: share, + local_events: Mutex::new(Events::with_capacity(128)), }) } + // Adds a new listening addr to the server. + fn add_listener(&self, addr: &SocketAddr) -> Result<(), Box> { + let listener = TcpListener::bind(addr)?; + + let mut slab = self.inner.sockets.lock().unwrap(); + let entry = slab.vacant_entry(); + + self.inner.poll.register(&listener, entry.key().into(), + Ready::readable(), PollOpt::edge() | PollOpt::oneshot())?; + + entry.insert(Socket::Listener(listener)); + + Ok(()) + } + /// Returns the address of the listening socket. #[inline] pub fn server_addr(&self) -> SocketAddr { - self.server.server_addr() + unimplemented!() // FIXME: restore? + //self.server.server_addr() } /// Runs the server forever, or until the listening socket is somehow force-closed by the /// operating system. #[inline] pub fn run(self) { - for request in self.server.incoming_requests() { - self.process(request); + let mut local_events = self.local_events.lock().unwrap(); + loop { + one_poll(&self.inner, &mut local_events); } } @@ -87,12 +162,11 @@ impl Server where F: Send + Sync + 'static + Fn(&Request) -> Response { /// is processed in a separate thread. #[inline] pub fn poll(&self) { - while let Ok(Some(request)) = self.server.try_recv() { - self.process(request); - } + let mut local_events = self.local_events.lock().unwrap(); + one_poll(&self.inner, &mut local_events); } - // Internal function, called when we got a request from tiny-http that needs to be processed. + /*// Internal function, called when we got a request from tiny-http that needs to be processed. fn process(&self, request: tiny_http::Request) { // We spawn a thread so that requests are processed in parallel. let handler = self.handler.clone(); @@ -183,5 +257,189 @@ impl Server where F: Send + Sync + 'static + Fn(&Request) -> Response { let _ = tiny_http_request.lock().unwrap().take().unwrap().respond(response); } }); + }*/ +} + +fn one_poll(share: &Arc>, events: &mut Events) + where F: Fn(&Request) -> Response + Send + Sync + 'static +{ + share.poll.poll(events, None).expect("Error with the system selector"); + + for event in events.iter() { + // We handle reading before writing, as handling reading can generate data to write. + + if event.readiness().is_readable() { + let socket = { + let mut slab = share.sockets.lock().unwrap(); + slab.remove(event.token().into()) + }; + + handle_read(share, socket); + } + + if event.readiness().is_writable() { + let socket = { + let mut slab = share.sockets.lock().unwrap(); + slab.remove(event.token().into()) + }; + + handle_write(share, socket); + } + } +} + +fn handle_read(share: &Arc>, socket: Socket) + where F: Fn(&Request) -> Response + Send + Sync + 'static +{ + match socket { + Socket::Listener(listener) => { + // Call `accept` repeatidely and register the newly-created sockets, + // until `WouldBlock` is returned. + loop { + match listener.accept() { + Ok((stream, client_addr)) => { + let mut slab = share.sockets.lock().unwrap(); + let entry = slab.vacant_entry(); + share.poll.register(&stream, entry.key().into(), Ready::readable(), + PollOpt::edge() | PollOpt::oneshot()) + .expect("Error while registering TCP stream"); + let share = share.clone(); + entry.insert(Socket::Stream { + stream: stream, + handler: SocketHandler::new(client_addr, move |rq| (share.handler)(&rq)), + update: SocketHandlerUpdate::empty(), + }); + }, + Err(ref e) if e.kind() == ErrorKind::WouldBlock => break, + Err(_) => { + // Handle errors with the listener by returning without re-registering it. + // This drops the listener. + return; + }, + }; + }; + + // Re-register the listener for the next time. + let mut slab = share.sockets.lock().unwrap(); + let entry = slab.vacant_entry(); + share.poll.reregister(&listener, entry.key().into(), Ready::readable(), + PollOpt::edge() | PollOpt::oneshot()) + .expect("Error while reregistering TCP listener"); + entry.insert(Socket::Listener(listener)); + }, + + Socket::Stream { mut stream, mut handler, mut update } => { + // Read into `update.pending_read_buffer` until `WouldBlock` is returned. + loop { + let old_pr_len = update.pending_read_buffer.len(); + update.pending_read_buffer.resize(old_pr_len + 256, 0); + + match stream.read(&mut update.pending_read_buffer[old_pr_len..]) { + Ok(0) => { + update.pending_read_buffer.resize(old_pr_len, 0); + break; + }, + Ok(n) => { + update.pending_read_buffer.resize(old_pr_len + n, 0); + }, + Err(ref e) if e.kind() == ErrorKind::Interrupted => { + update.pending_read_buffer.resize(old_pr_len, 0); + }, + Err(ref e) if e.kind() == ErrorKind::WouldBlock => { + update.pending_read_buffer.resize(old_pr_len, 0); + break; + }, + Err(e) => { + panic!("Error while accepting from the TCP listener: {}", e); + }, + }; + } + + // Dispatch to handler. + handler.update(&mut update); + update.new_data_start = update.pending_read_buffer.len(); + + // Re-register stream for next time. + let mut ready = Ready::empty(); + if update.accepts_read { + ready = ready | Ready::readable(); + } + if !update.pending_write_buffer.is_empty() { + ready = ready | Ready::writable(); + } + + let mut slab = share.sockets.lock().unwrap(); + let entry = slab.vacant_entry(); + + let mut insert_entry = false; + + if let Some(registration) = update.registration.take() { + share.poll.register(&*registration, entry.key().into(), + Ready::readable() | Ready::writable(), + PollOpt::edge() | PollOpt::oneshot()) + .expect("Error while registering registration"); + insert_entry = true; + } + + if !ready.is_empty() { + share.poll.reregister(&stream, entry.key().into(), ready, + PollOpt::edge() | PollOpt::oneshot()) + .expect("Error while reregistering TCP stream"); + insert_entry = true; + } + + if insert_entry { + entry.insert(Socket::Stream { stream, handler, update }); + } + }, + } +} + +fn handle_write(share: &ThreadsShare, socket: Socket) { + // Write events can't happen for listeners. + let (mut stream, handler, mut update) = match socket { + Socket::Listener(_) => unreachable!(), + Socket::Stream { stream, handler, update } => (stream, handler, update), + }; + + // Write from `update.pending_write_buffer` to `stream`. + while !update.pending_write_buffer.is_empty() { + match stream.write(&update.pending_write_buffer) { + Ok(0) => { + let _ = stream.flush(); + break; + }, + Ok(n) => { + // TODO: more efficient + update.pending_write_buffer = update.pending_write_buffer[n..].to_owned(); + }, + Err(ref e) if e.kind() == ErrorKind::Interrupted => {}, + Err(ref e) if e.kind() == ErrorKind::WouldBlock => { + let _ = stream.flush(); + break; + }, + Err(_) => { + // Handle errors with the stream by returning without re-registering it. This + // drops the stream. + return; + }, + }; + }; + + // Re-register the stream for the next event. + let mut ready = Ready::empty(); + if update.accepts_read { + ready = ready | Ready::readable(); + } + if !update.pending_write_buffer.is_empty() { + ready = ready | Ready::writable(); + } + if !ready.is_empty() { + let mut slab = share.sockets.lock().unwrap(); + let entry = slab.vacant_entry(); + share.poll.reregister(&stream, entry.key().into(), ready, + PollOpt::edge() | PollOpt::oneshot()) + .expect("Error while reregistering TCP stream"); + entry.insert(Socket::Stream { stream, handler, update }); } } diff --git a/src/socket_handler.rs b/src/socket_handler.rs new file mode 100644 index 000000000..3bf282ee5 --- /dev/null +++ b/src/socket_handler.rs @@ -0,0 +1,282 @@ +// Copyright (c) 2017 The Rouille developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use std::io::Cursor; +use std::io::ErrorKind; +use std::io::Read; +use std::io::Write; +use std::mem; +use std::net::SocketAddr; +use std::sync::Arc; +use std::sync::Mutex; +use std::sync::mpsc::{channel, Receiver}; +use std::str; +use std::thread; +use httparse; +use mio::Ready; +use mio::Registration; + +use Request; +use Response; + +/// Handles the processing of a client connection. +pub struct SocketHandler { + // The handler is a state machine. + state: SocketHandlerState, + + // Address of the client. Will be extracted at some point during the handling. + client_addr: Option, + + // Object that handles the request and returns a response. + handler: Option Response + Send + 'static>>, +} + +enum SocketHandlerState { + Poisonned, + WaitingForRqLine, + WaitingForHeaders { + method: String, + path: String, + version: HttpVersion, + }, + ExecutingHandler { + response_getter: Receiver, + registration: Arc, + }, + SendingResponse { + data: Box, + }, + Closed, +} + +/// Represents the communication between the `SocketHandler` and the outside. +/// +/// The "outside" is supposed to fill `pending_read_buffer` with incoming data, and remove data +/// from `pending_write_buffer`, then call `update`. +pub struct Update { + /// Filled by the handler user. Contains the data that comes from the client. + pub pending_read_buffer: Vec, + + /// Offset within `pending_read_buffer` where new data is available. Everything before this + /// offset was already in `pending_read_buffer` the last time `update` returned. + pub new_data_start: usize, + + /// Set to false by the socket handler when it will no longer process incoming data. If both + /// `accepts_read` is false and `pending_write_buffer` is empty, then you can drop the socket. + pub accepts_read: bool, + + /// Filled by `SocketHandler::update()`. Contains the data that must be sent back to the + /// client. + pub pending_write_buffer: Vec, + + /// When set by the socket handler, it means that the user must call `update` when the + /// `Registration` becomes ready. The user must then set it to 0. The registration is only ever + /// used once. + pub registration: Option>, +} + +impl Update { + pub fn empty() -> Update { + // TODO: don't create two Vecs for each socket + Update { + pending_read_buffer: Vec::new(), + new_data_start: 0, + accepts_read: true, + pending_write_buffer: Vec::new(), + registration: None, + } + } +} + +impl SocketHandler { + pub fn new(client_addr: SocketAddr, handler: F) -> SocketHandler + where F: FnMut(Request) -> Response + Send + 'static + { + SocketHandler { + state: SocketHandlerState::WaitingForRqLine, + client_addr: Some(client_addr), + handler: Some(Box::new(handler)), + } + } + + pub fn update(&mut self, update: &mut Update) { + loop { + match mem::replace(&mut self.state, SocketHandlerState::Poisonned) { + SocketHandlerState::Poisonned => { + panic!("Poisonned request handler"); + }, + + SocketHandlerState::WaitingForRqLine => { + let off = update.new_data_start.saturating_sub(1); + if let Some(rn) = update.pending_read_buffer[off..].windows(2).position(|w| w == b"\r\n") { + let (method, path, version) = { + let (method, path, version) = parse_request_line(&update.pending_read_buffer[..rn]).unwrap(); // TODO: error + (method.to_owned(), path.to_owned(), version) + }; + // TODO: don't reallocate a Vec + update.pending_read_buffer = update.pending_read_buffer[rn + 2..].to_owned(); + self.state = SocketHandlerState::WaitingForHeaders { method, path, version }; + } else { + self.state = SocketHandlerState::WaitingForRqLine; + break; + } + }, + + SocketHandlerState::WaitingForHeaders { method, path, version } => { + let off = update.new_data_start.saturating_sub(3); + if let Some(rnrn) = update.pending_read_buffer[off..].windows(4).position(|w| w == b"\r\n\r\n") { + { + let mut headers = [httparse::EMPTY_HEADER; 32]; + httparse::parse_headers(&update.pending_read_buffer, &mut headers).unwrap(); // TODO: + println!("{:?}", headers); + } + + // TODO: don't reallocate a Vec + update.pending_read_buffer = update.pending_read_buffer[off + rnrn + 4..].to_owned(); + + // TODO: yeah, don't spawn threads left and right + let (registration, set_ready) = Registration::new2(); + let registration = Arc::new(registration); + let mut handler = self.handler.take().unwrap(); + let remote_addr = self.client_addr.take().unwrap(); + let (tx, rx) = channel(); + thread::spawn(move || { + let request = Request { + method: method, + url: path, + headers: Vec::new(), + https: false, + data: Arc::new(Mutex::new(None)), // FIXME: + remote_addr: remote_addr, + }; + + let response = handler(request); + let _ = tx.send(response); + ::std::thread::sleep(::std::time::Duration::from_millis(500)); // TODO: remove + let _ = set_ready.set_readiness(Ready::readable()); + }); + + update.registration = Some(registration.clone()); + self.state = SocketHandlerState::ExecutingHandler { + response_getter: rx, + registration: registration, + }; + break; + + } else { + self.state = SocketHandlerState::WaitingForHeaders { method, path, version }; + break; + } + }, + + SocketHandlerState::ExecutingHandler { response_getter, registration } => { + // TODO: write incoming data to request's reader + if let Ok(response) = response_getter.try_recv() { + assert!(response.upgrade.is_none()); + + let mut headers_data = Vec::new(); + write!(headers_data, "HTTP/1.1 {} Ok\r\n", response.status_code).unwrap(); + for (header, value) in response.headers { + write!(headers_data, "{}: {}\r\n", header, value).unwrap(); + } + write!(headers_data, "\r\n").unwrap(); + + let (body_data, _) = response.data.into_reader_and_size(); + let full_data = Cursor::new(headers_data).chain(body_data); + + self.state = SocketHandlerState::SendingResponse { + data: Box::new(full_data) + }; + + } else { + self.state = SocketHandlerState::ExecutingHandler { response_getter, registration }; + break; + } + }, + + SocketHandlerState::SendingResponse { mut data } => { + let old_pw_len = update.pending_write_buffer.len(); + update.pending_write_buffer.resize(old_pw_len + 256, 0); + + match data.read(&mut update.pending_write_buffer[old_pw_len..]) { + Ok(0) => { + update.pending_write_buffer.resize(old_pw_len, 0); + self.state = SocketHandlerState::WaitingForRqLine; + break; + }, + Ok(n) => { + update.pending_write_buffer.resize(old_pw_len + n, 0); + self.state = SocketHandlerState::SendingResponse { data }; + break; + }, + Err(ref e) if e.kind() == ErrorKind::Interrupted => { + update.pending_write_buffer.resize(old_pw_len, 0); + self.state = SocketHandlerState::SendingResponse { data }; + }, + Err(e) => { + update.pending_write_buffer.resize(old_pw_len, 0); + panic!("{:?}", e); // FIXME: + }, + }; + }, + + SocketHandlerState::Closed => { + debug_assert!(!update.accepts_read); + self.state = SocketHandlerState::Closed; + break; + }, + } + } + } +} + +/// HTTP version (usually 1.0 or 1.1). +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct HttpVersion(pub u8, pub u8); + +/// Parses a "HTTP/1.1" string. +// TODO: handle [u8] correctly +fn parse_http_version(version: &str) -> Result { + let mut elems = version.splitn(2, '/'); + + elems.next(); + let vers = match elems.next() { + Some(v) => v, + None => return Err(()), + }; + + let mut elems = vers.splitn(2, '.'); + let major = elems.next().and_then(|n| n.parse().ok()); + let minor = elems.next().and_then(|n| n.parse().ok()); + + match (major, minor) { + (Some(ma), Some(mi)) => Ok(HttpVersion(ma, mi)), + _ => return Err(()), + } +} + +/// Parses the request line of the request. +/// eg. GET / HTTP/1.1 +// TODO: handle [u8] correctly +fn parse_request_line(line: &[u8]) -> Result<(&str, &str, HttpVersion), ()> { + let line = str::from_utf8(line).unwrap(); // TODO: + let mut words = line.split(' '); + + let method = words.next(); + let path = words.next(); + let version = words.next(); + + let (method, path, version) = match (method, path, version) { + (Some(m), Some(p), Some(v)) => (m, p, v), + _ => return Err(()) + }; + + let version = parse_http_version(version)?; + Ok((method, path, version)) +} From beae9225090fac40e2a39771df1ab873b0159064 Mon Sep 17 00:00:00 2001 From: tomaka Date: Mon, 7 Aug 2017 19:45:02 +0200 Subject: [PATCH 04/50] Parse request headers --- src/socket_handler.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/socket_handler.rs b/src/socket_handler.rs index 3bf282ee5..8d7d9882c 100644 --- a/src/socket_handler.rs +++ b/src/socket_handler.rs @@ -131,11 +131,16 @@ impl SocketHandler { SocketHandlerState::WaitingForHeaders { method, path, version } => { let off = update.new_data_start.saturating_sub(3); if let Some(rnrn) = update.pending_read_buffer[off..].windows(4).position(|w| w == b"\r\n\r\n") { - { + let headers = { + let mut out_headers = Vec::new(); let mut headers = [httparse::EMPTY_HEADER; 32]; - httparse::parse_headers(&update.pending_read_buffer, &mut headers).unwrap(); // TODO: - println!("{:?}", headers); - } + let (_, parsed_headers) = httparse::parse_headers(&update.pending_read_buffer, &mut headers).unwrap().unwrap(); // TODO: + for parsed in parsed_headers { + out_headers.push((parsed.name.to_owned(), String::from_utf8_lossy(parsed.value).into())); // TODO: wrong + } + println!("{:?}", out_headers); + out_headers + }; // TODO: don't reallocate a Vec update.pending_read_buffer = update.pending_read_buffer[off + rnrn + 4..].to_owned(); @@ -150,7 +155,7 @@ impl SocketHandler { let request = Request { method: method, url: path, - headers: Vec::new(), + headers: headers, https: false, data: Arc::new(Mutex::new(None)), // FIXME: remote_addr: remote_addr, @@ -178,7 +183,7 @@ impl SocketHandler { SocketHandlerState::ExecutingHandler { response_getter, registration } => { // TODO: write incoming data to request's reader if let Ok(response) = response_getter.try_recv() { - assert!(response.upgrade.is_none()); + assert!(response.upgrade.is_none()); // TODO: let mut headers_data = Vec::new(); write!(headers_data, "HTTP/1.1 {} Ok\r\n", response.status_code).unwrap(); From 36588ba1f84c944e1e6acdfa6d2961c2c35c12e0 Mon Sep 17 00:00:00 2001 From: tomaka Date: Mon, 7 Aug 2017 19:45:24 +0200 Subject: [PATCH 05/50] Remove old code --- src/server.rs | 93 --------------------------------------------------- 1 file changed, 93 deletions(-) diff --git a/src/server.rs b/src/server.rs index 5f16eccff..dd5d4e6fd 100644 --- a/src/server.rs +++ b/src/server.rs @@ -165,99 +165,6 @@ impl Server where F: Send + Sync + 'static + Fn(&Request) -> Response { let mut local_events = self.local_events.lock().unwrap(); one_poll(&self.inner, &mut local_events); } - - /*// Internal function, called when we got a request from tiny-http that needs to be processed. - fn process(&self, request: tiny_http::Request) { - // We spawn a thread so that requests are processed in parallel. - let handler = self.handler.clone(); - thread::spawn(move || { - // Small helper struct that makes it possible to put - // a `tiny_http::Request` inside a `Box`. - struct RequestRead(Arc>>); - impl Read for RequestRead { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> IoResult { - self.0.lock().unwrap().as_mut().unwrap().as_reader().read(buf) - } - } - - // Building the `Request` object. - let tiny_http_request; - let rouille_request = { - let url = request.url().to_owned(); - let method = request.method().as_str().to_owned(); - let headers = request.headers().iter().map(|h| (h.field.to_string(), h.value.clone().into())).collect(); - let remote_addr = request.remote_addr().clone(); - - tiny_http_request = Arc::new(Mutex::new(Some(request))); - - Request { - url: url, - method: method, - headers: headers, - https: false, - data: Arc::new(Mutex::new(Some(Box::new(RequestRead(tiny_http_request.clone())) as Box<_>))), - remote_addr: remote_addr, - } - }; - - // Calling the handler ; this most likely takes a lot of time. - // If the handler panics, we build a dummy response. - let mut rouille_response = { - // We don't use the `rouille_request` anymore after the panic, so it's ok to assert - // it's unwind safe. - let rouille_request = AssertUnwindSafe(rouille_request); - let res = panic::catch_unwind(move || { - let rouille_request = rouille_request; - handler(&rouille_request) - }); - - match res { - Ok(r) => r, - Err(_) => { - Response::html("

Internal Server Error

\ -

An internal error has occurred on the server.

") - .with_status_code(500) - } - } - }; - - // writing the response - let (res_data, res_len) = rouille_response.data.into_reader_and_size(); - let mut response = tiny_http::Response::empty(rouille_response.status_code) - .with_data(res_data, res_len); - - let mut upgrade_header = "".into(); - - for (key, value) in rouille_response.headers { - if key.eq_ignore_ascii_case("Content-Length") { - continue; - } - - if key.eq_ignore_ascii_case("Upgrade") { - upgrade_header = value; - continue; - } - - if let Ok(header) = tiny_http::Header::from_bytes(key.as_bytes(), value.as_bytes()) { - response.add_header(header); - } else { - // TODO: ? - } - } - - if let Some(ref mut upgrade) = rouille_response.upgrade { - let trq = tiny_http_request.lock().unwrap().take().unwrap(); - let socket = trq.upgrade(&upgrade_header, response); - upgrade.build(socket); - - } else { - // We don't really care if we fail to send the response to the client, as there's - // nothing we can do anyway. - let _ = tiny_http_request.lock().unwrap().take().unwrap().respond(response); - } - }); - }*/ } fn one_poll(share: &Arc>, events: &mut Events) From 77dd08f5bfd65d0734046f6adc407cfe97a77cd7 Mon Sep 17 00:00:00 2001 From: tomaka Date: Mon, 7 Aug 2017 20:03:49 +0200 Subject: [PATCH 06/50] Correctly send back response body --- src/server.rs | 2 ++ src/socket_handler.rs | 31 +++++++------------------------ 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/src/server.rs b/src/server.rs index dd5d4e6fd..3b98cb402 100644 --- a/src/server.rs +++ b/src/server.rs @@ -318,7 +318,9 @@ fn handle_write(share: &ThreadsShare, socket: Socket) { }, Ok(n) => { // TODO: more efficient + println!("sent back {:?}", String::from_utf8_lossy(&update.pending_write_buffer[..n])); update.pending_write_buffer = update.pending_write_buffer[n..].to_owned(); + let _ = stream.flush(); }, Err(ref e) if e.kind() == ErrorKind::Interrupted => {}, Err(ref e) if e.kind() == ErrorKind::WouldBlock => { diff --git a/src/socket_handler.rs b/src/socket_handler.rs index 8d7d9882c..79df7a97d 100644 --- a/src/socket_handler.rs +++ b/src/socket_handler.rs @@ -185,14 +185,16 @@ impl SocketHandler { if let Ok(response) = response_getter.try_recv() { assert!(response.upgrade.is_none()); // TODO: + let (body_data, body_size) = response.data.into_reader_and_size(); + let mut headers_data = Vec::new(); write!(headers_data, "HTTP/1.1 {} Ok\r\n", response.status_code).unwrap(); for (header, value) in response.headers { write!(headers_data, "{}: {}\r\n", header, value).unwrap(); } + write!(headers_data, "Content-Length: {}\r\n", body_size.unwrap()).unwrap(); // TODO: don't unwrap body_size write!(headers_data, "\r\n").unwrap(); - let (body_data, _) = response.data.into_reader_and_size(); let full_data = Cursor::new(headers_data).chain(body_data); self.state = SocketHandlerState::SendingResponse { @@ -206,29 +208,10 @@ impl SocketHandler { }, SocketHandlerState::SendingResponse { mut data } => { - let old_pw_len = update.pending_write_buffer.len(); - update.pending_write_buffer.resize(old_pw_len + 256, 0); - - match data.read(&mut update.pending_write_buffer[old_pw_len..]) { - Ok(0) => { - update.pending_write_buffer.resize(old_pw_len, 0); - self.state = SocketHandlerState::WaitingForRqLine; - break; - }, - Ok(n) => { - update.pending_write_buffer.resize(old_pw_len + n, 0); - self.state = SocketHandlerState::SendingResponse { data }; - break; - }, - Err(ref e) if e.kind() == ErrorKind::Interrupted => { - update.pending_write_buffer.resize(old_pw_len, 0); - self.state = SocketHandlerState::SendingResponse { data }; - }, - Err(e) => { - update.pending_write_buffer.resize(old_pw_len, 0); - panic!("{:?}", e); // FIXME: - }, - }; + // TODO: meh, this can block + data.read_to_end(&mut update.pending_write_buffer).unwrap(); + self.state = SocketHandlerState::WaitingForRqLine; + break; }, SocketHandlerState::Closed => { From ec63056e1cf2bd9bd8247e1001214ebc13e5794d Mon Sep 17 00:00:00 2001 From: tomaka Date: Mon, 7 Aug 2017 20:07:54 +0200 Subject: [PATCH 07/50] Fix when multiple requests in a row --- src/socket_handler.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/socket_handler.rs b/src/socket_handler.rs index 79df7a97d..9fd3ed0b9 100644 --- a/src/socket_handler.rs +++ b/src/socket_handler.rs @@ -31,10 +31,10 @@ pub struct SocketHandler { state: SocketHandlerState, // Address of the client. Will be extracted at some point during the handling. - client_addr: Option, + client_addr: SocketAddr, // Object that handles the request and returns a response. - handler: Option Response + Send + 'static>>, + handler: Arc Response + Send + 'static>>, } enum SocketHandlerState { @@ -100,8 +100,8 @@ impl SocketHandler { { SocketHandler { state: SocketHandlerState::WaitingForRqLine, - client_addr: Some(client_addr), - handler: Some(Box::new(handler)), + client_addr: client_addr, + handler: Arc::new(Mutex::new(handler)), } } @@ -148,8 +148,8 @@ impl SocketHandler { // TODO: yeah, don't spawn threads left and right let (registration, set_ready) = Registration::new2(); let registration = Arc::new(registration); - let mut handler = self.handler.take().unwrap(); - let remote_addr = self.client_addr.take().unwrap(); + let handler = self.handler.clone(); + let remote_addr = self.client_addr.clone(); let (tx, rx) = channel(); thread::spawn(move || { let request = Request { @@ -161,8 +161,9 @@ impl SocketHandler { remote_addr: remote_addr, }; - let response = handler(request); - let _ = tx.send(response); + let mut handler = handler.lock().unwrap(); + let response = (&mut *handler)(request); + let _ = tx.send(response); ::std::thread::sleep(::std::time::Duration::from_millis(500)); // TODO: remove let _ = set_ready.set_readiness(Ready::readable()); }); From 0848440761d89d8cd673d11b7cc161db761fc19d Mon Sep 17 00:00:00 2001 From: tomaka Date: Tue, 8 Aug 2017 11:29:13 +0200 Subject: [PATCH 08/50] Make socket_handler extensible for later --- .../http1.rs} | 85 +++++-------------- src/socket_handler/mod.rs | 81 ++++++++++++++++++ 2 files changed, 104 insertions(+), 62 deletions(-) rename src/{socket_handler.rs => socket_handler/http1.rs} (72%) create mode 100644 src/socket_handler/mod.rs diff --git a/src/socket_handler.rs b/src/socket_handler/http1.rs similarity index 72% rename from src/socket_handler.rs rename to src/socket_handler/http1.rs index 9fd3ed0b9..b6d38c1a0 100644 --- a/src/socket_handler.rs +++ b/src/socket_handler/http1.rs @@ -8,7 +8,6 @@ // according to those terms. use std::io::Cursor; -use std::io::ErrorKind; use std::io::Read; use std::io::Write; use std::mem; @@ -22,13 +21,14 @@ use httparse; use mio::Ready; use mio::Registration; +use socket_handler::Update; use Request; use Response; /// Handles the processing of a client connection. -pub struct SocketHandler { +pub struct Http1Handler { // The handler is a state machine. - state: SocketHandlerState, + state: Http1HandlerState, // Address of the client. Will be extracted at some point during the handling. client_addr: SocketAddr, @@ -37,7 +37,7 @@ pub struct SocketHandler { handler: Arc Response + Send + 'static>>, } -enum SocketHandlerState { +enum Http1HandlerState { Poisonned, WaitingForRqLine, WaitingForHeaders { @@ -55,51 +55,12 @@ enum SocketHandlerState { Closed, } -/// Represents the communication between the `SocketHandler` and the outside. -/// -/// The "outside" is supposed to fill `pending_read_buffer` with incoming data, and remove data -/// from `pending_write_buffer`, then call `update`. -pub struct Update { - /// Filled by the handler user. Contains the data that comes from the client. - pub pending_read_buffer: Vec, - - /// Offset within `pending_read_buffer` where new data is available. Everything before this - /// offset was already in `pending_read_buffer` the last time `update` returned. - pub new_data_start: usize, - - /// Set to false by the socket handler when it will no longer process incoming data. If both - /// `accepts_read` is false and `pending_write_buffer` is empty, then you can drop the socket. - pub accepts_read: bool, - - /// Filled by `SocketHandler::update()`. Contains the data that must be sent back to the - /// client. - pub pending_write_buffer: Vec, - - /// When set by the socket handler, it means that the user must call `update` when the - /// `Registration` becomes ready. The user must then set it to 0. The registration is only ever - /// used once. - pub registration: Option>, -} - -impl Update { - pub fn empty() -> Update { - // TODO: don't create two Vecs for each socket - Update { - pending_read_buffer: Vec::new(), - new_data_start: 0, - accepts_read: true, - pending_write_buffer: Vec::new(), - registration: None, - } - } -} - -impl SocketHandler { - pub fn new(client_addr: SocketAddr, handler: F) -> SocketHandler +impl Http1Handler { + pub fn new(client_addr: SocketAddr, handler: F) -> Http1Handler where F: FnMut(Request) -> Response + Send + 'static { - SocketHandler { - state: SocketHandlerState::WaitingForRqLine, + Http1Handler { + state: Http1HandlerState::WaitingForRqLine, client_addr: client_addr, handler: Arc::new(Mutex::new(handler)), } @@ -107,12 +68,12 @@ impl SocketHandler { pub fn update(&mut self, update: &mut Update) { loop { - match mem::replace(&mut self.state, SocketHandlerState::Poisonned) { - SocketHandlerState::Poisonned => { + match mem::replace(&mut self.state, Http1HandlerState::Poisonned) { + Http1HandlerState::Poisonned => { panic!("Poisonned request handler"); }, - SocketHandlerState::WaitingForRqLine => { + Http1HandlerState::WaitingForRqLine => { let off = update.new_data_start.saturating_sub(1); if let Some(rn) = update.pending_read_buffer[off..].windows(2).position(|w| w == b"\r\n") { let (method, path, version) = { @@ -121,14 +82,14 @@ impl SocketHandler { }; // TODO: don't reallocate a Vec update.pending_read_buffer = update.pending_read_buffer[rn + 2..].to_owned(); - self.state = SocketHandlerState::WaitingForHeaders { method, path, version }; + self.state = Http1HandlerState::WaitingForHeaders { method, path, version }; } else { - self.state = SocketHandlerState::WaitingForRqLine; + self.state = Http1HandlerState::WaitingForRqLine; break; } }, - SocketHandlerState::WaitingForHeaders { method, path, version } => { + Http1HandlerState::WaitingForHeaders { method, path, version } => { let off = update.new_data_start.saturating_sub(3); if let Some(rnrn) = update.pending_read_buffer[off..].windows(4).position(|w| w == b"\r\n\r\n") { let headers = { @@ -169,19 +130,19 @@ impl SocketHandler { }); update.registration = Some(registration.clone()); - self.state = SocketHandlerState::ExecutingHandler { + self.state = Http1HandlerState::ExecutingHandler { response_getter: rx, registration: registration, }; break; } else { - self.state = SocketHandlerState::WaitingForHeaders { method, path, version }; + self.state = Http1HandlerState::WaitingForHeaders { method, path, version }; break; } }, - SocketHandlerState::ExecutingHandler { response_getter, registration } => { + Http1HandlerState::ExecutingHandler { response_getter, registration } => { // TODO: write incoming data to request's reader if let Ok(response) = response_getter.try_recv() { assert!(response.upgrade.is_none()); // TODO: @@ -198,26 +159,26 @@ impl SocketHandler { let full_data = Cursor::new(headers_data).chain(body_data); - self.state = SocketHandlerState::SendingResponse { + self.state = Http1HandlerState::SendingResponse { data: Box::new(full_data) }; } else { - self.state = SocketHandlerState::ExecutingHandler { response_getter, registration }; + self.state = Http1HandlerState::ExecutingHandler { response_getter, registration }; break; } }, - SocketHandlerState::SendingResponse { mut data } => { + Http1HandlerState::SendingResponse { mut data } => { // TODO: meh, this can block data.read_to_end(&mut update.pending_write_buffer).unwrap(); - self.state = SocketHandlerState::WaitingForRqLine; + self.state = Http1HandlerState::WaitingForRqLine; break; }, - SocketHandlerState::Closed => { + Http1HandlerState::Closed => { debug_assert!(!update.accepts_read); - self.state = SocketHandlerState::Closed; + self.state = Http1HandlerState::Closed; break; }, } diff --git a/src/socket_handler/mod.rs b/src/socket_handler/mod.rs new file mode 100644 index 000000000..e378fead3 --- /dev/null +++ b/src/socket_handler/mod.rs @@ -0,0 +1,81 @@ +// Copyright (c) 2017 The Rouille developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use std::net::SocketAddr; +use std::sync::Arc; +use mio::Registration; + +use Request; +use Response; + +use self::http1::Http1Handler; + +mod http1; + +/// Parses the data received by a socket and returns the data to send back. +pub struct SocketHandler { + inner: Http1Handler, +} + +impl SocketHandler { + /// Initialization. + pub fn new(client_addr: SocketAddr, handler: F) -> SocketHandler + where F: FnMut(Request) -> Response + Send + 'static + { + SocketHandler { + inner: Http1Handler::new(client_addr, handler) + } + } + + /// Call this function whenever new data is received on the socket, or when the registration + /// wakes up. + pub fn update(&mut self, update: &mut Update) { + self.inner.update(update) + } +} + +/// Represents the communication between the `SocketHandler` and the outside. +/// +/// The "outside" is supposed to fill `pending_read_buffer` with incoming data, and remove data +/// from `pending_write_buffer`, then call `update`. +pub struct Update { + /// Filled by the handler user. Contains the data that comes from the client. + pub pending_read_buffer: Vec, + + /// Offset within `pending_read_buffer` where new data is available. Everything before this + /// offset was already in `pending_read_buffer` the last time `update` returned. + pub new_data_start: usize, + + /// Set to false by the socket handler when it will no longer process incoming data. If both + /// `accepts_read` is false and `pending_write_buffer` is empty, then you can drop the socket. + pub accepts_read: bool, + + /// Filled by `SocketHandler::update()`. Contains the data that must be sent back to the + /// client. + pub pending_write_buffer: Vec, + + /// When set by the socket handler, it means that the user must call `update` when the + /// `Registration` becomes ready. The user must then set it to 0. The registration is only ever + /// used once. + pub registration: Option>, +} + +impl Update { + /// Builds a new empty `Update`. + pub fn empty() -> Update { + // TODO: don't create two Vecs for each socket + Update { + pending_read_buffer: Vec::new(), + new_data_start: 0, + accepts_read: true, + pending_write_buffer: Vec::new(), + registration: None, + } + } +} From bb9b7834b66c480f80ef46963bcb705b5fe54696 Mon Sep 17 00:00:00 2001 From: tomaka Date: Tue, 8 Aug 2017 13:07:43 +0200 Subject: [PATCH 09/50] Correctly print status code and headers --- Cargo.toml | 1 + src/lib.rs | 1 + src/socket_handler/http1.rs | 121 ++++++++++++++++++++++++++++++++---- 3 files changed, 110 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 42f93e634..8405bb58b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ chrono = "0.2.0" filetime = "0.1.10" flate2 = { version = "0.2.14", optional = true } httparse = "1.2.3" +itoa = "0.3" mio = "0.6.10" multipart = { version = "0.5.1", default-features = false, features = ["server"] } num_cpus = "1.6.2" diff --git a/src/lib.rs b/src/lib.rs index 538bfce87..80820bb27 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,6 +59,7 @@ extern crate filetime; #[cfg(feature = "flate2")] extern crate flate2; extern crate httparse; +extern crate itoa; extern crate mio; extern crate multipart; extern crate num_cpus; diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index b6d38c1a0..67622d91d 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -7,9 +7,9 @@ // notice may not be copied, modified, or distributed except // according to those terms. -use std::io::Cursor; +use std::ascii::AsciiExt; +use std::borrow::Cow; use std::io::Read; -use std::io::Write; use std::mem; use std::net::SocketAddr; use std::sync::Arc; @@ -18,6 +18,7 @@ use std::sync::mpsc::{channel, Receiver}; use std::str; use std::thread; use httparse; +use itoa::write as itoa; use mio::Ready; use mio::Registration; @@ -148,19 +149,13 @@ impl Http1Handler { assert!(response.upgrade.is_none()); // TODO: let (body_data, body_size) = response.data.into_reader_and_size(); - - let mut headers_data = Vec::new(); - write!(headers_data, "HTTP/1.1 {} Ok\r\n", response.status_code).unwrap(); - for (header, value) in response.headers { - write!(headers_data, "{}: {}\r\n", header, value).unwrap(); - } - write!(headers_data, "Content-Length: {}\r\n", body_size.unwrap()).unwrap(); // TODO: don't unwrap body_size - write!(headers_data, "\r\n").unwrap(); - - let full_data = Cursor::new(headers_data).chain(body_data); + write_status_and_headers(&mut update.pending_write_buffer, + response.status_code, + &response.headers, + body_size); self.state = Http1HandlerState::SendingResponse { - data: Box::new(full_data) + data: Box::new(body_data) }; } else { @@ -230,3 +225,103 @@ fn parse_request_line(line: &[u8]) -> Result<(&str, &str, HttpVersion), ()> { let version = parse_http_version(version)?; Ok((method, path, version)) } + +// Writes the status line and headers of the response to `out`. +fn write_status_and_headers(mut out: &mut Vec, status_code: u16, + headers: &[(Cow<'static, str>, Cow<'static, str>)], + body_size: Option) +{ + out.extend_from_slice(b"HTTP/1.1 "); + itoa(&mut out, status_code).unwrap(); + out.push(b' '); + out.extend_from_slice(default_reason_phrase(status_code).as_bytes()); + out.extend_from_slice(b"\r\n"); + + let mut found_server_header = false; + let mut found_date_header = false; + for &(ref header, ref value) in headers { + if !found_server_header && header.eq_ignore_ascii_case("Server") { + found_server_header = true; + } + if !found_date_header && header.eq_ignore_ascii_case("Date") { + found_date_header = true; + } + + // Some headers can't be written with the response, as they are too "low-level". + if header.eq_ignore_ascii_case("Content-Length") || + header.eq_ignore_ascii_case("Transfer-Encoding") || + header.eq_ignore_ascii_case("Connection") || + header.eq_ignore_ascii_case("Trailer") + { + continue; + } + + out.extend_from_slice(header.as_bytes()); + out.extend_from_slice(b": "); + out.extend_from_slice(value.as_bytes()); + out.extend_from_slice(b"\r\n"); + } + + if !found_server_header { + out.extend_from_slice(b"Server: rouille\r\n"); + } + if !found_date_header { + out.extend_from_slice(b"Date: TODO\r\n"); // TODO: + } + + out.extend_from_slice(b"Content-Length: "); + itoa(&mut out, body_size.unwrap()).unwrap(); // TODO: don't unwrap body_size + out.extend_from_slice(b"\r\n"); + out.extend_from_slice(b"\r\n"); +} + +// Returns the phrase corresponding to a status code. +fn default_reason_phrase(status_code: u16) -> &'static str { + match status_code { + 100 => "Continue", + 101 => "Switching Protocols", + 102 => "Processing", + 118 => "Connection timed out", + 200 => "OK", + 201 => "Created", + 202 => "Accepted", + 203 => "Non-Authoritative Information", + 204 => "No Content", + 205 => "Reset Content", + 206 => "Partial Content", + 207 => "Multi-Status", + 210 => "Content Different", + 300 => "Multiple Choices", + 301 => "Moved Permanently", + 302 => "Found", + 303 => "See Other", + 304 => "Not Modified", + 305 => "Use Proxy", + 307 => "Temporary Redirect", + 400 => "Bad Request", + 401 => "Unauthorized", + 402 => "Payment Required", + 403 => "Forbidden", + 404 => "Not Found", + 405 => "Method Not Allowed", + 406 => "Not Acceptable", + 407 => "Proxy Authentication Required", + 408 => "Request Time-out", + 409 => "Conflict", + 410 => "Gone", + 411 => "Length Required", + 412 => "Precondition Failed", + 413 => "Request Entity Too Large", + 414 => "Reques-URI Too Large", + 415 => "Unsupported Media Type", + 416 => "Request range not satisfiable", + 417 => "Expectation Failed", + 500 => "Internal Server Error", + 501 => "Not Implemented", + 502 => "Bad Gateway", + 503 => "Service Unavailable", + 504 => "Gateway Time-out", + 505 => "HTTP Version not supported", + _ => "Unknown" + } +} From 4ba773aeb5c8b31a08748faa1382bbd4392d3508 Mon Sep 17 00:00:00 2001 From: tomaka Date: Tue, 8 Aug 2017 13:08:12 +0200 Subject: [PATCH 10/50] Remove the debug sleep --- src/socket_handler/http1.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index 67622d91d..fe6824283 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -125,8 +125,7 @@ impl Http1Handler { let mut handler = handler.lock().unwrap(); let response = (&mut *handler)(request); - let _ = tx.send(response); - ::std::thread::sleep(::std::time::Duration::from_millis(500)); // TODO: remove + let _ = tx.send(response); let _ = set_ready.set_readiness(Ready::readable()); }); From fa5d31e9b401f01f3c07ad55941181c08482f138 Mon Sep 17 00:00:00 2001 From: tomaka Date: Tue, 8 Aug 2017 13:12:40 +0200 Subject: [PATCH 11/50] Minor fixes --- src/socket_handler/http1.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index fe6824283..394a514dc 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -180,11 +180,11 @@ impl Http1Handler { } } -/// HTTP version (usually 1.0 or 1.1). +// HTTP version (usually 1.0 or 1.1). #[derive(Debug, Clone, PartialEq, Eq)] -pub struct HttpVersion(pub u8, pub u8); +struct HttpVersion(pub u8, pub u8); -/// Parses a "HTTP/1.1" string. +// Parses a "HTTP/1.1" string. // TODO: handle [u8] correctly fn parse_http_version(version: &str) -> Result { let mut elems = version.splitn(2, '/'); @@ -205,8 +205,8 @@ fn parse_http_version(version: &str) -> Result { } } -/// Parses the request line of the request. -/// eg. GET / HTTP/1.1 +// Parses the request line of the request. +// eg. GET / HTTP/1.1 // TODO: handle [u8] correctly fn parse_request_line(line: &[u8]) -> Result<(&str, &str, HttpVersion), ()> { let line = str::from_utf8(line).unwrap(); // TODO: From ab9075a7f2c3e9f851786807da7cf12003dcbf95 Mon Sep 17 00:00:00 2001 From: tomaka Date: Tue, 8 Aug 2017 13:16:30 +0200 Subject: [PATCH 12/50] Correct value for Request::https --- src/socket_handler/http1.rs | 12 +++++++++--- src/socket_handler/mod.rs | 9 ++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index 394a514dc..53d3a887e 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -22,6 +22,7 @@ use itoa::write as itoa; use mio::Ready; use mio::Registration; +use socket_handler::Protocol; use socket_handler::Update; use Request; use Response; @@ -31,9 +32,12 @@ pub struct Http1Handler { // The handler is a state machine. state: Http1HandlerState, - // Address of the client. Will be extracted at some point during the handling. + // Address of the client. Passed to the request object. client_addr: SocketAddr, + // Protocol of the original server. Passed to the request object. + original_protocol: Protocol, + // Object that handles the request and returns a response. handler: Arc Response + Send + 'static>>, } @@ -57,12 +61,13 @@ enum Http1HandlerState { } impl Http1Handler { - pub fn new(client_addr: SocketAddr, handler: F) -> Http1Handler + pub fn new(client_addr: SocketAddr, original_protocol: Protocol, handler: F) -> Http1Handler where F: FnMut(Request) -> Response + Send + 'static { Http1Handler { state: Http1HandlerState::WaitingForRqLine, client_addr: client_addr, + original_protocol: original_protocol, handler: Arc::new(Mutex::new(handler)), } } @@ -111,6 +116,7 @@ impl Http1Handler { let (registration, set_ready) = Registration::new2(); let registration = Arc::new(registration); let handler = self.handler.clone(); + let https = self.original_protocol == Protocol::Https; let remote_addr = self.client_addr.clone(); let (tx, rx) = channel(); thread::spawn(move || { @@ -118,7 +124,7 @@ impl Http1Handler { method: method, url: path, headers: headers, - https: false, + https: https, data: Arc::new(Mutex::new(None)), // FIXME: remote_addr: remote_addr, }; diff --git a/src/socket_handler/mod.rs b/src/socket_handler/mod.rs index e378fead3..114d3cb52 100644 --- a/src/socket_handler/mod.rs +++ b/src/socket_handler/mod.rs @@ -29,7 +29,7 @@ impl SocketHandler { where F: FnMut(Request) -> Response + Send + 'static { SocketHandler { - inner: Http1Handler::new(client_addr, handler) + inner: Http1Handler::new(client_addr, Protocol::Http /* TODO: */, handler) } } @@ -40,6 +40,13 @@ impl SocketHandler { } } +/// Protocol that can serve HTTP. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Protocol { + Http, + Https, +} + /// Represents the communication between the `SocketHandler` and the outside. /// /// The "outside" is supposed to fill `pending_read_buffer` with incoming data, and remove data From 66baf16f4022a98b960f90aa88449673caa4986c Mon Sep 17 00:00:00 2001 From: tomaka Date: Tue, 8 Aug 2017 13:17:09 +0200 Subject: [PATCH 13/50] Remove debugging printlns --- src/server.rs | 1 - src/socket_handler/http1.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/server.rs b/src/server.rs index 3b98cb402..bd98d9a48 100644 --- a/src/server.rs +++ b/src/server.rs @@ -318,7 +318,6 @@ fn handle_write(share: &ThreadsShare, socket: Socket) { }, Ok(n) => { // TODO: more efficient - println!("sent back {:?}", String::from_utf8_lossy(&update.pending_write_buffer[..n])); update.pending_write_buffer = update.pending_write_buffer[n..].to_owned(); let _ = stream.flush(); }, diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index 53d3a887e..acc65de7a 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -105,7 +105,6 @@ impl Http1Handler { for parsed in parsed_headers { out_headers.push((parsed.name.to_owned(), String::from_utf8_lossy(parsed.value).into())); // TODO: wrong } - println!("{:?}", out_headers); out_headers }; From f44264929a28358c25fc2165358af2f69e25b761 Mon Sep 17 00:00:00 2001 From: tomaka Date: Tue, 8 Aug 2017 13:30:48 +0200 Subject: [PATCH 14/50] Fix panic when stream errors --- src/server.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/server.rs b/src/server.rs index bd98d9a48..7942562d5 100644 --- a/src/server.rs +++ b/src/server.rs @@ -256,8 +256,10 @@ fn handle_read(share: &Arc>, socket: Socket) update.pending_read_buffer.resize(old_pr_len, 0); break; }, - Err(e) => { - panic!("Error while accepting from the TCP listener: {}", e); + Err(_) => { + // Handle errors with the stream by returning without re-registering it. + // This drops the stream. + return; }, }; } From 66bfe2a9de706f12e3283ffde2ed7b207f1f5878 Mon Sep 17 00:00:00 2001 From: tomaka Date: Tue, 8 Aug 2017 13:33:51 +0200 Subject: [PATCH 15/50] Fix panic when slab doesn't contain entry --- src/server.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/server.rs b/src/server.rs index 7942562d5..1a37ffbe9 100644 --- a/src/server.rs +++ b/src/server.rs @@ -178,6 +178,9 @@ fn one_poll(share: &Arc>, events: &mut Events) if event.readiness().is_readable() { let socket = { let mut slab = share.sockets.lock().unwrap(); + if !slab.contains(event.token().into()) { + continue; + } slab.remove(event.token().into()) }; @@ -187,6 +190,9 @@ fn one_poll(share: &Arc>, events: &mut Events) if event.readiness().is_writable() { let socket = { let mut slab = share.sockets.lock().unwrap(); + if !slab.contains(event.token().into()) { + continue; + } slab.remove(event.token().into()) }; From 2ab9ef2e042fb8ed644cf83bdd6cf0948562b2bd Mon Sep 17 00:00:00 2001 From: tomaka Date: Tue, 8 Aug 2017 13:47:01 +0200 Subject: [PATCH 16/50] Add a tasks pool --- src/server.rs | 7 +- src/socket_handler/http1.rs | 29 +++--- src/socket_handler/mod.rs | 6 +- src/socket_handler/task_pool.rs | 154 ++++++++++++++++++++++++++++++++ 4 files changed, 183 insertions(+), 13 deletions(-) create mode 100644 src/socket_handler/task_pool.rs diff --git a/src/server.rs b/src/server.rs index 1a37ffbe9..42bd183b4 100644 --- a/src/server.rs +++ b/src/server.rs @@ -22,6 +22,7 @@ use mio::tcp::{TcpListener, TcpStream}; use num_cpus; use slab::Slab; +use socket_handler::TaskPool; use socket_handler::SocketHandler; use socket_handler::Update as SocketHandlerUpdate; @@ -61,6 +62,8 @@ struct ThreadsShare { sockets: Mutex>, // The function that handles requests. handler: AssertUnwindSafe, + // Pool used to dispatch tasks. + task_pool: TaskPool, } enum Socket { @@ -100,6 +103,7 @@ impl Server where F: Send + Sync + 'static + Fn(&Request) -> Response { poll: Poll::new()?, sockets: Mutex::new(Slab::new()), handler: AssertUnwindSafe(handler), // TODO: using AssertUnwindSafe here is wrong, but unwind safety has some usability problems in Rust in general + task_pool: TaskPool::new(), }); for _ in 0 .. num_cpus::get() - 1 { @@ -219,7 +223,8 @@ fn handle_read(share: &Arc>, socket: Socket) let share = share.clone(); entry.insert(Socket::Stream { stream: stream, - handler: SocketHandler::new(client_addr, move |rq| (share.handler)(&rq)), + handler: SocketHandler::new(client_addr, share.task_pool.clone(), + move |rq| (share.handler)(&rq)), update: SocketHandlerUpdate::empty(), }); }, diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index acc65de7a..297aa949a 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -16,7 +16,6 @@ use std::sync::Arc; use std::sync::Mutex; use std::sync::mpsc::{channel, Receiver}; use std::str; -use std::thread; use httparse; use itoa::write as itoa; use mio::Ready; @@ -24,6 +23,7 @@ use mio::Registration; use socket_handler::Protocol; use socket_handler::Update; +use socket_handler::task_pool::TaskPool; use Request; use Response; @@ -40,6 +40,9 @@ pub struct Http1Handler { // Object that handles the request and returns a response. handler: Arc Response + Send + 'static>>, + + // The pool where to dispatch the handler. + task_pool: TaskPool, } enum Http1HandlerState { @@ -61,7 +64,8 @@ enum Http1HandlerState { } impl Http1Handler { - pub fn new(client_addr: SocketAddr, original_protocol: Protocol, handler: F) -> Http1Handler + pub fn new(client_addr: SocketAddr, original_protocol: Protocol, task_pool: TaskPool, + handler: F) -> Http1Handler where F: FnMut(Request) -> Response + Send + 'static { Http1Handler { @@ -69,6 +73,7 @@ impl Http1Handler { client_addr: client_addr, original_protocol: original_protocol, handler: Arc::new(Mutex::new(handler)), + task_pool: task_pool, } } @@ -114,25 +119,29 @@ impl Http1Handler { // TODO: yeah, don't spawn threads left and right let (registration, set_ready) = Registration::new2(); let registration = Arc::new(registration); - let handler = self.handler.clone(); + let mut handler = Some(self.handler.clone()); let https = self.original_protocol == Protocol::Https; - let remote_addr = self.client_addr.clone(); + let mut remote_addr = Some(self.client_addr.clone()); + let mut method = Some(method); + let mut path = Some(path); + let mut headers = Some(headers); let (tx, rx) = channel(); - thread::spawn(move || { + self.task_pool.spawn(Box::new(move || { let request = Request { - method: method, - url: path, - headers: headers, + method: method.take().unwrap(), + url: path.take().unwrap(), + headers: headers.take().unwrap(), https: https, data: Arc::new(Mutex::new(None)), // FIXME: - remote_addr: remote_addr, + remote_addr: remote_addr.take().unwrap(), }; + let handler = handler.take().unwrap(); let mut handler = handler.lock().unwrap(); let response = (&mut *handler)(request); let _ = tx.send(response); let _ = set_ready.set_readiness(Ready::readable()); - }); + }) as Box<_>); update.registration = Some(registration.clone()); self.state = Http1HandlerState::ExecutingHandler { diff --git a/src/socket_handler/mod.rs b/src/socket_handler/mod.rs index 114d3cb52..681219442 100644 --- a/src/socket_handler/mod.rs +++ b/src/socket_handler/mod.rs @@ -15,8 +15,10 @@ use Request; use Response; use self::http1::Http1Handler; +pub use self::task_pool::TaskPool; // TODO: shouldn't be pub, but is used by Server, move it somewher else mod http1; +mod task_pool; /// Parses the data received by a socket and returns the data to send back. pub struct SocketHandler { @@ -25,11 +27,11 @@ pub struct SocketHandler { impl SocketHandler { /// Initialization. - pub fn new(client_addr: SocketAddr, handler: F) -> SocketHandler + pub fn new(client_addr: SocketAddr, task_pool: TaskPool, handler: F) -> SocketHandler where F: FnMut(Request) -> Response + Send + 'static { SocketHandler { - inner: Http1Handler::new(client_addr, Protocol::Http /* TODO: */, handler) + inner: Http1Handler::new(client_addr, Protocol::Http /* TODO: */, task_pool, handler) } } diff --git a/src/socket_handler/task_pool.rs b/src/socket_handler/task_pool.rs new file mode 100644 index 000000000..007786649 --- /dev/null +++ b/src/socket_handler/task_pool.rs @@ -0,0 +1,154 @@ +// Copyright 2015 The tiny-http Contributors +// Copyright (c) 2017 The Rouille developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::sync::{Arc, Mutex, Condvar}; +use std::sync::atomic::{Ordering, AtomicUsize}; +use std::collections::VecDeque; +use std::time::Duration; +use std::thread; + +/// Manages a collection of threads. +/// +/// A new thread is created every time all the existing threads are full. +/// Any idle thread will automatically die after a few seconds. +#[derive(Clone)] +pub struct TaskPool { + sharing: Arc, +} + +struct Sharing { + // list of the tasks to be done by worker threads + todo: Mutex>>, + + // condvar that will be notified whenever a task is added to `todo` + condvar: Condvar, + + // number of total worker threads running + active_tasks: AtomicUsize, + + // number of idle worker threads + waiting_tasks: AtomicUsize, +} + +/// Minimum number of active threads. +static MIN_THREADS: usize = 4; + +struct Registration<'a> { + nb: &'a AtomicUsize +} + +impl<'a> Registration<'a> { + fn new(nb: &'a AtomicUsize) -> Registration<'a> { + nb.fetch_add(1, Ordering::Release); + Registration { nb: nb } + } +} + +impl<'a> Drop for Registration<'a> { + fn drop(&mut self) { + self.nb.fetch_sub(1, Ordering::Release); + } +} + +impl TaskPool { + pub fn new() -> TaskPool { + let pool = TaskPool { + sharing: Arc::new(Sharing { + todo: Mutex::new(VecDeque::new()), + condvar: Condvar::new(), + active_tasks: AtomicUsize::new(0), + waiting_tasks: AtomicUsize::new(0), + }), + }; + + for _ in 0..MIN_THREADS { + pool.add_thread(None) + } + + pool + } + + /// Executes a function in a thread. + /// If no thread is available, spawns a new one. + pub fn spawn(&self, code: Box) { + let mut queue = self.sharing.todo.lock().unwrap(); + + if self.sharing.waiting_tasks.load(Ordering::Acquire) == 0 { + self.add_thread(Some(code)); + + } else { + queue.push_back(code); + self.sharing.condvar.notify_one(); + } + } + + fn add_thread(&self, initial_fn: Option>) { + let sharing = self.sharing.clone(); + + thread::spawn(move || { + let sharing = sharing; + let _active_guard = Registration::new(&sharing.active_tasks); + + if initial_fn.is_some() { + let mut f = initial_fn.unwrap(); + f(); + } + + loop { + let mut task: Box = { + let mut todo = sharing.todo.lock().unwrap(); + + let task; + loop { + if let Some(poped_task) = todo.pop_front() { + task = poped_task; + break; + } + let _waiting_guard = Registration::new(&sharing.waiting_tasks); + + let received = if sharing.active_tasks.load(Ordering::Acquire) + <= MIN_THREADS + { + todo = sharing.condvar.wait(todo).unwrap(); + true + + } else { + let (new_lock, waitres) = sharing.condvar + .wait_timeout(todo, Duration::from_millis(5000)) + .unwrap(); + todo = new_lock; + !waitres.timed_out() + }; + + if !received && todo.is_empty() { + return; + } + } + + task + }; + + task(); + } + }); + } +} + +impl Drop for TaskPool { + fn drop(&mut self) { + self.sharing.active_tasks.store(999999999, Ordering::Release); + self.sharing.condvar.notify_all(); + } +} From 761f048435261b37eb414701286ac220a9b97643 Mon Sep 17 00:00:00 2001 From: tomaka Date: Tue, 8 Aug 2017 14:05:01 +0200 Subject: [PATCH 17/50] Modernize task pool --- Cargo.toml | 1 + src/lib.rs | 1 + src/socket_handler/http1.rs | 20 ++--- src/socket_handler/task_pool.rs | 143 ++++++++------------------------ 4 files changed, 44 insertions(+), 121 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8405bb58b..80890c392 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ gzip = ["flate2"] [dependencies] brotli2 = { version = "0.2.1", optional = true } chrono = "0.2.0" +crossbeam = "0.3.0" filetime = "0.1.10" flate2 = { version = "0.2.14", optional = true } httparse = "1.2.3" diff --git a/src/lib.rs b/src/lib.rs index 80820bb27..22084db1c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,6 +55,7 @@ #[cfg(feature = "brotli2")] extern crate brotli2; extern crate chrono; +extern crate crossbeam; extern crate filetime; #[cfg(feature = "flate2")] extern crate flate2; diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index 297aa949a..6a99dcc5a 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -119,29 +119,25 @@ impl Http1Handler { // TODO: yeah, don't spawn threads left and right let (registration, set_ready) = Registration::new2(); let registration = Arc::new(registration); - let mut handler = Some(self.handler.clone()); + let handler = self.handler.clone(); let https = self.original_protocol == Protocol::Https; - let mut remote_addr = Some(self.client_addr.clone()); - let mut method = Some(method); - let mut path = Some(path); - let mut headers = Some(headers); + let remote_addr = self.client_addr.clone(); let (tx, rx) = channel(); - self.task_pool.spawn(Box::new(move || { + self.task_pool.spawn(move || { let request = Request { - method: method.take().unwrap(), - url: path.take().unwrap(), - headers: headers.take().unwrap(), + method: method, + url: path, + headers: headers, https: https, data: Arc::new(Mutex::new(None)), // FIXME: - remote_addr: remote_addr.take().unwrap(), + remote_addr: remote_addr, }; - let handler = handler.take().unwrap(); let mut handler = handler.lock().unwrap(); let response = (&mut *handler)(request); let _ = tx.send(response); let _ = set_ready.set_readiness(Ready::readable()); - }) as Box<_>); + }); update.registration = Some(registration.clone()); self.state = Http1HandlerState::ExecutingHandler { diff --git a/src/socket_handler/task_pool.rs b/src/socket_handler/task_pool.rs index 007786649..e527e17f2 100644 --- a/src/socket_handler/task_pool.rs +++ b/src/socket_handler/task_pool.rs @@ -13,142 +13,67 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::sync::{Arc, Mutex, Condvar}; -use std::sync::atomic::{Ordering, AtomicUsize}; -use std::collections::VecDeque; -use std::time::Duration; +use std::sync::Arc; use std::thread; +use crossbeam::sync::MsQueue; +use num_cpus; /// Manages a collection of threads. -/// -/// A new thread is created every time all the existing threads are full. -/// Any idle thread will automatically die after a few seconds. #[derive(Clone)] pub struct TaskPool { sharing: Arc, } struct Sharing { - // list of the tasks to be done by worker threads - todo: Mutex>>, - - // condvar that will be notified whenever a task is added to `todo` - condvar: Condvar, - - // number of total worker threads running - active_tasks: AtomicUsize, - - // number of idle worker threads - waiting_tasks: AtomicUsize, -} - -/// Minimum number of active threads. -static MIN_THREADS: usize = 4; - -struct Registration<'a> { - nb: &'a AtomicUsize -} - -impl<'a> Registration<'a> { - fn new(nb: &'a AtomicUsize) -> Registration<'a> { - nb.fetch_add(1, Ordering::Release); - Registration { nb: nb } - } -} - -impl<'a> Drop for Registration<'a> { - fn drop(&mut self) { - self.nb.fetch_sub(1, Ordering::Release); - } + // List of the tasks to be done by worker threads. + // + // If the task returns `true` then the worker thread must continue. Otherwise it must stop. + // This feature is necessary in order to be able to stop worker threads. + todo: MsQueue bool + Send>>, } impl TaskPool { + /// Initializes a new task pool. pub fn new() -> TaskPool { let pool = TaskPool { sharing: Arc::new(Sharing { - todo: Mutex::new(VecDeque::new()), - condvar: Condvar::new(), - active_tasks: AtomicUsize::new(0), - waiting_tasks: AtomicUsize::new(0), + todo: MsQueue::new(), }), }; - for _ in 0..MIN_THREADS { - pool.add_thread(None) + for _ in 0..num_cpus::get() { + let sharing = pool.sharing.clone(); + thread::spawn(move || { + loop { + let mut task = sharing.todo.pop(); + if !task() { + break; + } + } + }); } pool } - /// Executes a function in a thread. - /// If no thread is available, spawns a new one. - pub fn spawn(&self, code: Box) { - let mut queue = self.sharing.todo.lock().unwrap(); - - if self.sharing.waiting_tasks.load(Ordering::Acquire) == 0 { - self.add_thread(Some(code)); - - } else { - queue.push_back(code); - self.sharing.condvar.notify_one(); - } - } - - fn add_thread(&self, initial_fn: Option>) { - let sharing = self.sharing.clone(); - - thread::spawn(move || { - let sharing = sharing; - let _active_guard = Registration::new(&sharing.active_tasks); - - if initial_fn.is_some() { - let mut f = initial_fn.unwrap(); - f(); - } - - loop { - let mut task: Box = { - let mut todo = sharing.todo.lock().unwrap(); - - let task; - loop { - if let Some(poped_task) = todo.pop_front() { - task = poped_task; - break; - } - let _waiting_guard = Registration::new(&sharing.waiting_tasks); - - let received = if sharing.active_tasks.load(Ordering::Acquire) - <= MIN_THREADS - { - todo = sharing.condvar.wait(todo).unwrap(); - true - - } else { - let (new_lock, waitres) = sharing.condvar - .wait_timeout(todo, Duration::from_millis(5000)) - .unwrap(); - todo = new_lock; - !waitres.timed_out() - }; - - if !received && todo.is_empty() { - return; - } - } - - task - }; - - task(); - } - }); + /// Executes a function in a worker thread. + #[inline] + pub fn spawn(&self, code: F) + where F: FnOnce() + Send + 'static + { + let mut code = Some(code); + self.sharing.todo.push(Box::new(move || { + let code = code.take().unwrap(); + code(); + true + })); } } impl Drop for TaskPool { fn drop(&mut self) { - self.sharing.active_tasks.store(999999999, Ordering::Release); - self.sharing.condvar.notify_all(); + for _ in 0 .. num_cpus::get() { + self.sharing.todo.push(Box::new(|| false)); + } } } From 685af114759bcb9b06b57f11a55f05b3f50e79ab Mon Sep 17 00:00:00 2001 From: tomaka Date: Tue, 8 Aug 2017 14:09:58 +0200 Subject: [PATCH 18/50] Fix pool being dropped after a connection is closed --- src/socket_handler/task_pool.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/socket_handler/task_pool.rs b/src/socket_handler/task_pool.rs index e527e17f2..0b436d04e 100644 --- a/src/socket_handler/task_pool.rs +++ b/src/socket_handler/task_pool.rs @@ -70,10 +70,10 @@ impl TaskPool { } } -impl Drop for TaskPool { +impl Drop for Sharing { fn drop(&mut self) { for _ in 0 .. num_cpus::get() { - self.sharing.todo.push(Box::new(|| false)); + self.todo.push(Box::new(|| false)); } } } From e83f1d56183542bde1469f8d200b07b6c94ec531 Mon Sep 17 00:00:00 2001 From: tomaka Date: Tue, 8 Aug 2017 14:50:04 +0200 Subject: [PATCH 19/50] Put the method in an ArrayString --- Cargo.toml | 1 + src/lib.rs | 12 +++++++----- src/socket_handler/http1.rs | 6 ++++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 80890c392..9b218189a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ brotli = ["brotli2"] gzip = ["flate2"] [dependencies] +arrayvec = "0.3.23" brotli2 = { version = "0.2.1", optional = true } chrono = "0.2.0" crossbeam = "0.3.0" diff --git a/src/lib.rs b/src/lib.rs index 22084db1c..68c8e9e5e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,6 +52,7 @@ #![deny(unsafe_code)] +extern crate arrayvec; #[cfg(feature = "brotli2")] extern crate brotli2; extern crate chrono; @@ -79,6 +80,7 @@ pub use response::{Response, ResponseBody}; pub use server::Server; pub use tiny_http::ReadWrite; +use arrayvec::ArrayString; use std::io::Cursor; use std::io::Result as IoResult; use std::io::Read; @@ -224,7 +226,7 @@ pub trait Upgrade { /// This can be either a real request (received by the HTTP server) or a mock object created with /// one of the `fake_*` constructors. pub struct Request { - method: String, + method: ArrayString<[u8; 16]>, url: String, headers: Vec<(String, String)>, https: bool, @@ -242,7 +244,7 @@ impl Request { { Request { url: url.into(), - method: method.into(), + method: ArrayString::from(&method.into()).expect("Method too long"), https: false, data: Arc::new(Mutex::new(Some(Box::new(Cursor::new(data)) as Box<_>))), headers: headers, @@ -257,7 +259,7 @@ impl Request { { Request { url: url.into(), - method: method.into(), + method: ArrayString::from(&method.into()).expect("Method too long"), https: false, data: Arc::new(Mutex::new(Some(Box::new(Cursor::new(data)) as Box<_>))), headers: headers, @@ -274,7 +276,7 @@ impl Request { { Request { url: url.into(), - method: method.into(), + method: ArrayString::from(&method.into()).expect("Method too long"), https: true, data: Arc::new(Mutex::new(Some(Box::new(Cursor::new(data)) as Box<_>))), headers: headers, @@ -289,7 +291,7 @@ impl Request { { Request { url: url.into(), - method: method.into(), + method: ArrayString::from(&method.into()).expect("Method too long"), https: true, data: Arc::new(Mutex::new(Some(Box::new(Cursor::new(data)) as Box<_>))), headers: headers, diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index 6a99dcc5a..76101b85b 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -16,6 +16,7 @@ use std::sync::Arc; use std::sync::Mutex; use std::sync::mpsc::{channel, Receiver}; use std::str; +use arrayvec::ArrayString; use httparse; use itoa::write as itoa; use mio::Ready; @@ -49,7 +50,7 @@ enum Http1HandlerState { Poisonned, WaitingForRqLine, WaitingForHeaders { - method: String, + method: ArrayString<[u8; 16]>, path: String, version: HttpVersion, }, @@ -89,7 +90,8 @@ impl Http1Handler { if let Some(rn) = update.pending_read_buffer[off..].windows(2).position(|w| w == b"\r\n") { let (method, path, version) = { let (method, path, version) = parse_request_line(&update.pending_read_buffer[..rn]).unwrap(); // TODO: error - (method.to_owned(), path.to_owned(), version) + let method = ArrayString::from(method).unwrap(); // TODO: error + (method, path.to_owned(), version) }; // TODO: don't reallocate a Vec update.pending_read_buffer = update.pending_read_buffer[rn + 2..].to_owned(); From 1056a5fafebc7365831b6a379a1319b7b93b9b06 Mon Sep 17 00:00:00 2001 From: tomaka Date: Tue, 8 Aug 2017 14:53:11 +0200 Subject: [PATCH 20/50] Preallocate memory for Update --- src/socket_handler/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/socket_handler/mod.rs b/src/socket_handler/mod.rs index 681219442..439cb713a 100644 --- a/src/socket_handler/mod.rs +++ b/src/socket_handler/mod.rs @@ -80,10 +80,10 @@ impl Update { pub fn empty() -> Update { // TODO: don't create two Vecs for each socket Update { - pending_read_buffer: Vec::new(), + pending_read_buffer: Vec::with_capacity(1024), new_data_start: 0, accepts_read: true, - pending_write_buffer: Vec::new(), + pending_write_buffer: Vec::with_capacity(1024), registration: None, } } From 4a77d84d7acffabb380e100f9993eee3955c62db Mon Sep 17 00:00:00 2001 From: tomaka Date: Tue, 8 Aug 2017 15:04:51 +0200 Subject: [PATCH 21/50] Put new_data_start in http1 --- src/server.rs | 1 - src/socket_handler/http1.rs | 40 +++++++++++++++++++++++++++---------- src/socket_handler/mod.rs | 5 ----- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/server.rs b/src/server.rs index 42bd183b4..c57b72719 100644 --- a/src/server.rs +++ b/src/server.rs @@ -277,7 +277,6 @@ fn handle_read(share: &Arc>, socket: Socket) // Dispatch to handler. handler.update(&mut update); - update.new_data_start = update.pending_read_buffer.len(); // Re-register stream for next time. let mut ready = Ready::empty(); diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index 76101b85b..6c44f2837 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -48,8 +48,15 @@ pub struct Http1Handler { enum Http1HandlerState { Poisonned, - WaitingForRqLine, + WaitingForRqLine { + // Offset within `pending_read_buffer` where new data is available. Everything before this + // offset was already in `pending_read_buffer` the last time `update` returned. + new_data_start: usize, + }, WaitingForHeaders { + // Offset within `pending_read_buffer` where new data is available. Everything before this + // offset was already in `pending_read_buffer` the last time `update` returned. + new_data_start: usize, method: ArrayString<[u8; 16]>, path: String, version: HttpVersion, @@ -70,7 +77,7 @@ impl Http1Handler { where F: FnMut(Request) -> Response + Send + 'static { Http1Handler { - state: Http1HandlerState::WaitingForRqLine, + state: Http1HandlerState::WaitingForRqLine { new_data_start: 0 }, client_addr: client_addr, original_protocol: original_protocol, handler: Arc::new(Mutex::new(handler)), @@ -85,8 +92,8 @@ impl Http1Handler { panic!("Poisonned request handler"); }, - Http1HandlerState::WaitingForRqLine => { - let off = update.new_data_start.saturating_sub(1); + Http1HandlerState::WaitingForRqLine { new_data_start } => { + let off = new_data_start.saturating_sub(1); if let Some(rn) = update.pending_read_buffer[off..].windows(2).position(|w| w == b"\r\n") { let (method, path, version) = { let (method, path, version) = parse_request_line(&update.pending_read_buffer[..rn]).unwrap(); // TODO: error @@ -95,15 +102,23 @@ impl Http1Handler { }; // TODO: don't reallocate a Vec update.pending_read_buffer = update.pending_read_buffer[rn + 2..].to_owned(); - self.state = Http1HandlerState::WaitingForHeaders { method, path, version }; + self.state = Http1HandlerState::WaitingForHeaders { + new_data_start: 0, + method, + path, + version + }; + } else { - self.state = Http1HandlerState::WaitingForRqLine; + self.state = Http1HandlerState::WaitingForRqLine { + new_data_start: update.pending_read_buffer.len(), + }; break; } }, - Http1HandlerState::WaitingForHeaders { method, path, version } => { - let off = update.new_data_start.saturating_sub(3); + Http1HandlerState::WaitingForHeaders { new_data_start, method, path, version } => { + let off = new_data_start.saturating_sub(3); if let Some(rnrn) = update.pending_read_buffer[off..].windows(4).position(|w| w == b"\r\n\r\n") { let headers = { let mut out_headers = Vec::new(); @@ -149,7 +164,12 @@ impl Http1Handler { break; } else { - self.state = Http1HandlerState::WaitingForHeaders { method, path, version }; + self.state = Http1HandlerState::WaitingForHeaders { + new_data_start: update.pending_read_buffer.len(), + method, + path, + version + }; break; } }, @@ -178,7 +198,7 @@ impl Http1Handler { Http1HandlerState::SendingResponse { mut data } => { // TODO: meh, this can block data.read_to_end(&mut update.pending_write_buffer).unwrap(); - self.state = Http1HandlerState::WaitingForRqLine; + self.state = Http1HandlerState::WaitingForRqLine { new_data_start: 0 }; break; }, diff --git a/src/socket_handler/mod.rs b/src/socket_handler/mod.rs index 439cb713a..2d6e37df2 100644 --- a/src/socket_handler/mod.rs +++ b/src/socket_handler/mod.rs @@ -57,10 +57,6 @@ pub struct Update { /// Filled by the handler user. Contains the data that comes from the client. pub pending_read_buffer: Vec, - /// Offset within `pending_read_buffer` where new data is available. Everything before this - /// offset was already in `pending_read_buffer` the last time `update` returned. - pub new_data_start: usize, - /// Set to false by the socket handler when it will no longer process incoming data. If both /// `accepts_read` is false and `pending_write_buffer` is empty, then you can drop the socket. pub accepts_read: bool, @@ -81,7 +77,6 @@ impl Update { // TODO: don't create two Vecs for each socket Update { pending_read_buffer: Vec::with_capacity(1024), - new_data_start: 0, accepts_read: true, pending_write_buffer: Vec::with_capacity(1024), registration: None, From d0167d2eb636dfdb5fe02f824fff4e78f6344611 Mon Sep 17 00:00:00 2001 From: tomaka Date: Tue, 8 Aug 2017 15:12:37 +0200 Subject: [PATCH 22/50] Avoid allocating after receiving status line and headers --- src/socket_handler/http1.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index 6c44f2837..9631029e5 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -100,8 +100,14 @@ impl Http1Handler { let method = ArrayString::from(method).unwrap(); // TODO: error (method, path.to_owned(), version) }; - // TODO: don't reallocate a Vec - update.pending_read_buffer = update.pending_read_buffer[rn + 2..].to_owned(); + + // Remove the first `rn + 2` bytes of `update.pending_read_buffer` + let cut_len = update.pending_read_buffer.len() - (rn + 2); + for n in 0 .. cut_len { + update.pending_read_buffer[n] = update.pending_read_buffer[n + rn + 2]; + } + update.pending_read_buffer.resize(cut_len, 0); + self.state = Http1HandlerState::WaitingForHeaders { new_data_start: 0, method, @@ -130,8 +136,12 @@ impl Http1Handler { out_headers }; - // TODO: don't reallocate a Vec - update.pending_read_buffer = update.pending_read_buffer[off + rnrn + 4..].to_owned(); + // Remove the first `off + rnrn + 4` bytes of `update.pending_read_buffer` + let cut_len = update.pending_read_buffer.len() - (off + rnrn + 4); + for n in 0 .. cut_len { + update.pending_read_buffer[n] = update.pending_read_buffer[n + off + rnrn + 4]; + } + update.pending_read_buffer.resize(cut_len, 0); // TODO: yeah, don't spawn threads left and right let (registration, set_ready) = Registration::new2(); From b6d2c836a7dfed89d33e378488780b2016c740b0 Mon Sep 17 00:00:00 2001 From: tomaka Date: Tue, 8 Aug 2017 15:18:06 +0200 Subject: [PATCH 23/50] Some doc for Update --- src/socket_handler/mod.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/socket_handler/mod.rs b/src/socket_handler/mod.rs index 2d6e37df2..88e91cda3 100644 --- a/src/socket_handler/mod.rs +++ b/src/socket_handler/mod.rs @@ -54,15 +54,17 @@ pub enum Protocol { /// The "outside" is supposed to fill `pending_read_buffer` with incoming data, and remove data /// from `pending_write_buffer`, then call `update`. pub struct Update { - /// Filled by the handler user. Contains the data that comes from the client. + /// Filled by the handler user and emptied by `update()`. Contains the data that comes from + /// the client. pub pending_read_buffer: Vec, - /// Set to false by the socket handler when it will no longer process incoming data. If both - /// `accepts_read` is false and `pending_write_buffer` is empty, then you can drop the socket. + /// Set to false by the socket handler when it will no longer process incoming data. If + /// `accepts_read` is false, `pending_write_buffer` is empty, and `registration` is empty, + /// then you can drop the socket. pub accepts_read: bool, - /// Filled by `SocketHandler::update()`. Contains the data that must be sent back to the - /// client. + /// Filled by `SocketHandler::update()` and emptied by the user. Contains the data that must + /// be sent back to the client. pub pending_write_buffer: Vec, /// When set by the socket handler, it means that the user must call `update` when the From b1bf27a552a423d262b790f0d4e49065ad12d786 Mon Sep 17 00:00:00 2001 From: tomaka Date: Tue, 8 Aug 2017 15:32:22 +0200 Subject: [PATCH 24/50] Minor todo --- src/socket_handler/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/socket_handler/mod.rs b/src/socket_handler/mod.rs index 88e91cda3..a526ccb41 100644 --- a/src/socket_handler/mod.rs +++ b/src/socket_handler/mod.rs @@ -56,6 +56,7 @@ pub enum Protocol { pub struct Update { /// Filled by the handler user and emptied by `update()`. Contains the data that comes from /// the client. + // TODO: try VecDeque and check perfs pub pending_read_buffer: Vec, /// Set to false by the socket handler when it will no longer process incoming data. If @@ -65,6 +66,7 @@ pub struct Update { /// Filled by `SocketHandler::update()` and emptied by the user. Contains the data that must /// be sent back to the client. + // TODO: try VecDeque and check perfs pub pending_write_buffer: Vec, /// When set by the socket handler, it means that the user must call `update` when the From 92bf81f9e2be3d2d1e3d9d11c576d1ce8f2e1331 Mon Sep 17 00:00:00 2001 From: tomaka Date: Tue, 8 Aug 2017 16:31:16 +0200 Subject: [PATCH 25/50] Put update in a trait --- src/server.rs | 5 +++-- src/socket_handler/http1.rs | 5 ++++- src/socket_handler/mod.rs | 20 +++++++++++++------- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/server.rs b/src/server.rs index c57b72719..971cb4dbc 100644 --- a/src/server.rs +++ b/src/server.rs @@ -24,6 +24,7 @@ use slab::Slab; use socket_handler::TaskPool; use socket_handler::SocketHandler; +use socket_handler::SocketHandlerDispatch; use socket_handler::Update as SocketHandlerUpdate; use Request; @@ -70,7 +71,7 @@ enum Socket { Listener(TcpListener), Stream { stream: TcpStream, - handler: SocketHandler, + handler: SocketHandlerDispatch, update: SocketHandlerUpdate, }, } @@ -223,7 +224,7 @@ fn handle_read(share: &Arc>, socket: Socket) let share = share.clone(); entry.insert(Socket::Stream { stream: stream, - handler: SocketHandler::new(client_addr, share.task_pool.clone(), + handler: SocketHandlerDispatch::new(client_addr, share.task_pool.clone(), move |rq| (share.handler)(&rq)), update: SocketHandlerUpdate::empty(), }); diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index 9631029e5..b5e646a44 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -23,6 +23,7 @@ use mio::Ready; use mio::Registration; use socket_handler::Protocol; +use socket_handler::SocketHandler; use socket_handler::Update; use socket_handler::task_pool::TaskPool; use Request; @@ -84,8 +85,10 @@ impl Http1Handler { task_pool: task_pool, } } +} - pub fn update(&mut self, update: &mut Update) { +impl SocketHandler for Http1Handler { + fn update(&mut self, update: &mut Update) { loop { match mem::replace(&mut self.state, Http1HandlerState::Poisonned) { Http1HandlerState::Poisonned => { diff --git a/src/socket_handler/mod.rs b/src/socket_handler/mod.rs index a526ccb41..143a79886 100644 --- a/src/socket_handler/mod.rs +++ b/src/socket_handler/mod.rs @@ -21,23 +21,23 @@ mod http1; mod task_pool; /// Parses the data received by a socket and returns the data to send back. -pub struct SocketHandler { +pub struct SocketHandlerDispatch { inner: Http1Handler, } -impl SocketHandler { +impl SocketHandlerDispatch { /// Initialization. - pub fn new(client_addr: SocketAddr, task_pool: TaskPool, handler: F) -> SocketHandler + pub fn new(client_addr: SocketAddr, task_pool: TaskPool, handler: F) -> SocketHandlerDispatch where F: FnMut(Request) -> Response + Send + 'static { - SocketHandler { + SocketHandlerDispatch { inner: Http1Handler::new(client_addr, Protocol::Http /* TODO: */, task_pool, handler) } } +} - /// Call this function whenever new data is received on the socket, or when the registration - /// wakes up. - pub fn update(&mut self, update: &mut Update) { +impl SocketHandler for SocketHandlerDispatch { + fn update(&mut self, update: &mut Update) { self.inner.update(update) } } @@ -49,6 +49,12 @@ pub enum Protocol { Https, } +pub trait SocketHandler { + /// Call this function whenever new data is received on the socket, or when the registration + /// wakes up. + fn update(&mut self, update: &mut Update); +} + /// Represents the communication between the `SocketHandler` and the outside. /// /// The "outside" is supposed to fill `pending_read_buffer` with incoming data, and remove data From d7398c8c21e567fe20435f38200c2c13b59a740a Mon Sep 17 00:00:00 2001 From: tomaka Date: Tue, 8 Aug 2017 16:58:08 +0200 Subject: [PATCH 26/50] Draft for Rustls support --- Cargo.toml | 1 + src/lib.rs | 1 + src/server.rs | 3 +- src/socket_handler/mod.rs | 26 ++++++++++++--- src/socket_handler/rustls.rs | 64 ++++++++++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 src/socket_handler/rustls.rs diff --git a/Cargo.toml b/Cargo.toml index 9b218189a..e78c22436 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ multipart = { version = "0.5.1", default-features = false, features = ["server"] num_cpus = "1.6.2" rand = "0.3.11" rustc-serialize = "0.3" +rustls = "0.9.0" sha1 = "0.2.0" slab = "0.4.0" term = "0.2" diff --git a/src/lib.rs b/src/lib.rs index 68c8e9e5e..ebb7356d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,6 +67,7 @@ extern crate multipart; extern crate num_cpus; extern crate rand; extern crate rustc_serialize; +extern crate rustls; extern crate sha1; extern crate slab; extern crate time; diff --git a/src/server.rs b/src/server.rs index 971cb4dbc..7da2624be 100644 --- a/src/server.rs +++ b/src/server.rs @@ -22,6 +22,7 @@ use mio::tcp::{TcpListener, TcpStream}; use num_cpus; use slab::Slab; +use socket_handler::Protocol; use socket_handler::TaskPool; use socket_handler::SocketHandler; use socket_handler::SocketHandlerDispatch; @@ -224,7 +225,7 @@ fn handle_read(share: &Arc>, socket: Socket) let share = share.clone(); entry.insert(Socket::Stream { stream: stream, - handler: SocketHandlerDispatch::new(client_addr, share.task_pool.clone(), + handler: SocketHandlerDispatch::new(client_addr, Protocol::Http, share.task_pool.clone(), move |rq| (share.handler)(&rq)), update: SocketHandlerUpdate::empty(), }); diff --git a/src/socket_handler/mod.rs b/src/socket_handler/mod.rs index 143a79886..7747e4c9f 100644 --- a/src/socket_handler/mod.rs +++ b/src/socket_handler/mod.rs @@ -15,30 +15,48 @@ use Request; use Response; use self::http1::Http1Handler; +use self::rustls::RustlsHandler; pub use self::task_pool::TaskPool; // TODO: shouldn't be pub, but is used by Server, move it somewher else mod http1; +mod rustls; mod task_pool; /// Parses the data received by a socket and returns the data to send back. pub struct SocketHandlerDispatch { - inner: Http1Handler, + inner: SocketHandlerDispatchInner, +} + +enum SocketHandlerDispatchInner { + Http(Http1Handler), + Https(RustlsHandler), } impl SocketHandlerDispatch { /// Initialization. - pub fn new(client_addr: SocketAddr, task_pool: TaskPool, handler: F) -> SocketHandlerDispatch + pub fn new(client_addr: SocketAddr, protocol: Protocol, task_pool: TaskPool, + handler: F) -> SocketHandlerDispatch where F: FnMut(Request) -> Response + Send + 'static { + let http_handler = Http1Handler::new(client_addr, protocol, task_pool, handler); + + let inner = match protocol { + Protocol::Http => SocketHandlerDispatchInner::Http(http_handler), + Protocol::Https => SocketHandlerDispatchInner::Https(RustlsHandler::new(http_handler)), + }; + SocketHandlerDispatch { - inner: Http1Handler::new(client_addr, Protocol::Http /* TODO: */, task_pool, handler) + inner: inner, } } } impl SocketHandler for SocketHandlerDispatch { fn update(&mut self, update: &mut Update) { - self.inner.update(update) + match self.inner { + SocketHandlerDispatchInner::Http(ref mut http) => http.update(update), + SocketHandlerDispatchInner::Https(ref mut https) => https.update(update), + } } } diff --git a/src/socket_handler/rustls.rs b/src/socket_handler/rustls.rs new file mode 100644 index 000000000..e7caa4aa9 --- /dev/null +++ b/src/socket_handler/rustls.rs @@ -0,0 +1,64 @@ +// Copyright (c) 2017 The Rouille developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use std::io::Read; +use std::io::Write; +use std::sync::Arc; + +use rustls::ServerConfig; +use rustls::ServerSession; +use rustls::Session; + +use socket_handler::SocketHandler; +use socket_handler::Update; + +/// Handles the processing of a client connection through TLS. +pub struct RustlsHandler { + // The inner handler. + handler: H, + // The Rustls session. + session: ServerSession, + // The update object to communicate with the handler. + handler_update: Update, +} + +// TODO: not working since we don't provide any certificate or anything + +impl RustlsHandler { + pub fn new(inner: H) -> RustlsHandler { + let dummy_config = ServerConfig::new(); + + RustlsHandler { + handler: inner, + session: ServerSession::new(&Arc::new(dummy_config)), + handler_update: Update::empty(), + } + } +} + +impl SocketHandler for RustlsHandler + where H: SocketHandler +{ + fn update(&mut self, update: &mut Update) { + let read_num = self.session.read_tls(&mut (&update.pending_read_buffer[..])).unwrap(); + assert_eq!(read_num, update.pending_read_buffer.len()); + update.pending_read_buffer.clear(); + + self.session.process_new_packets().unwrap(); // TODO: propagate error + + self.session.read_to_end(&mut self.handler_update.pending_read_buffer).unwrap(); + + self.handler.update(&mut self.handler_update); + + self.session.write_all(&self.handler_update.pending_write_buffer).unwrap(); + self.handler_update.pending_write_buffer.clear(); + + self.session.write_tls(&mut update.pending_write_buffer).unwrap(); + } +} From 9ada8980cd98691ea7343e32582b487a7787ac61 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Tue, 8 Aug 2017 17:35:02 +0200 Subject: [PATCH 27/50] Rework Update API --- src/server.rs | 43 +++++++++++++++++++++++++----------- src/socket_handler/http1.rs | 42 ++++++++++++++++++++++++++--------- src/socket_handler/mod.rs | 36 +++++++++++++++++++----------- src/socket_handler/rustls.rs | 16 +++++++++++--- 4 files changed, 98 insertions(+), 39 deletions(-) diff --git a/src/server.rs b/src/server.rs index 7da2624be..d351c26e3 100644 --- a/src/server.rs +++ b/src/server.rs @@ -23,6 +23,7 @@ use num_cpus; use slab::Slab; use socket_handler::Protocol; +use socket_handler::RegistrationState; use socket_handler::TaskPool; use socket_handler::SocketHandler; use socket_handler::SocketHandlerDispatch; @@ -72,6 +73,7 @@ enum Socket { Listener(TcpListener), Stream { stream: TcpStream, + read_closed: bool, handler: SocketHandlerDispatch, update: SocketHandlerUpdate, }, @@ -225,6 +227,7 @@ fn handle_read(share: &Arc>, socket: Socket) let share = share.clone(); entry.insert(Socket::Stream { stream: stream, + read_closed: false, handler: SocketHandlerDispatch::new(client_addr, Protocol::Http, share.task_pool.clone(), move |rq| (share.handler)(&rq)), update: SocketHandlerUpdate::empty(), @@ -248,7 +251,7 @@ fn handle_read(share: &Arc>, socket: Socket) entry.insert(Socket::Listener(listener)); }, - Socket::Stream { mut stream, mut handler, mut update } => { + Socket::Stream { mut stream, mut read_closed, mut handler, mut update } => { // Read into `update.pending_read_buffer` until `WouldBlock` is returned. loop { let old_pr_len = update.pending_read_buffer.len(); @@ -278,11 +281,14 @@ fn handle_read(share: &Arc>, socket: Socket) } // Dispatch to handler. - handler.update(&mut update); + let mut update_result = handler.update(&mut update); + if update_result.close_read { + read_closed = true; + } // Re-register stream for next time. let mut ready = Ready::empty(); - if update.accepts_read { + if !read_closed { ready = ready | Ready::readable(); } if !update.pending_write_buffer.is_empty() { @@ -294,11 +300,22 @@ fn handle_read(share: &Arc>, socket: Socket) let mut insert_entry = false; - if let Some(registration) = update.registration.take() { - share.poll.register(&*registration, entry.key().into(), - Ready::readable() | Ready::writable(), - PollOpt::edge() | PollOpt::oneshot()) - .expect("Error while registering registration"); + if let Some((registration, state)) = update_result.registration.take() { + match state { + RegistrationState::FirstTime => { + share.poll.register(&*registration, entry.key().into(), + Ready::readable() | Ready::writable(), + PollOpt::edge() | PollOpt::oneshot()) + .expect("Error while registering registration"); + }, + RegistrationState::Reregister => { + share.poll.reregister(&*registration, entry.key().into(), + Ready::readable() | Ready::writable(), + PollOpt::edge() | PollOpt::oneshot()) + .expect("Error while registering registration"); + }, + } + insert_entry = true; } @@ -310,7 +327,7 @@ fn handle_read(share: &Arc>, socket: Socket) } if insert_entry { - entry.insert(Socket::Stream { stream, handler, update }); + entry.insert(Socket::Stream { stream, read_closed, handler, update }); } }, } @@ -318,9 +335,9 @@ fn handle_read(share: &Arc>, socket: Socket) fn handle_write(share: &ThreadsShare, socket: Socket) { // Write events can't happen for listeners. - let (mut stream, handler, mut update) = match socket { + let (mut stream, read_closed, handler, mut update) = match socket { Socket::Listener(_) => unreachable!(), - Socket::Stream { stream, handler, update } => (stream, handler, update), + Socket::Stream { stream, read_closed, handler, update } => (stream, read_closed, handler, update), }; // Write from `update.pending_write_buffer` to `stream`. @@ -350,7 +367,7 @@ fn handle_write(share: &ThreadsShare, socket: Socket) { // Re-register the stream for the next event. let mut ready = Ready::empty(); - if update.accepts_read { + if !read_closed { ready = ready | Ready::readable(); } if !update.pending_write_buffer.is_empty() { @@ -362,6 +379,6 @@ fn handle_write(share: &ThreadsShare, socket: Socket) { share.poll.reregister(&stream, entry.key().into(), ready, PollOpt::edge() | PollOpt::oneshot()) .expect("Error while reregistering TCP stream"); - entry.insert(Socket::Stream { stream, handler, update }); + entry.insert(Socket::Stream { stream, read_closed, handler, update }); } } diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index b5e646a44..3bc3477e1 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -23,8 +23,10 @@ use mio::Ready; use mio::Registration; use socket_handler::Protocol; +use socket_handler::RegistrationState; use socket_handler::SocketHandler; use socket_handler::Update; +use socket_handler::UpdateResult; use socket_handler::task_pool::TaskPool; use Request; use Response; @@ -88,7 +90,7 @@ impl Http1Handler { } impl SocketHandler for Http1Handler { - fn update(&mut self, update: &mut Update) { + fn update(&mut self, update: &mut Update) -> UpdateResult { loop { match mem::replace(&mut self.state, Http1HandlerState::Poisonned) { Http1HandlerState::Poisonned => { @@ -122,7 +124,11 @@ impl SocketHandler for Http1Handler { self.state = Http1HandlerState::WaitingForRqLine { new_data_start: update.pending_read_buffer.len(), }; - break; + + break UpdateResult { + registration: None, + close_read: false, + }; } }, @@ -169,12 +175,16 @@ impl SocketHandler for Http1Handler { let _ = set_ready.set_readiness(Ready::readable()); }); - update.registration = Some(registration.clone()); self.state = Http1HandlerState::ExecutingHandler { response_getter: rx, - registration: registration, + registration: registration.clone(), + }; + + break UpdateResult { + registration: Some((registration, + RegistrationState::FirstTime)), + close_read: false, }; - break; } else { self.state = Http1HandlerState::WaitingForHeaders { @@ -183,7 +193,11 @@ impl SocketHandler for Http1Handler { path, version }; - break; + + break UpdateResult { + registration: None, + close_read: false, + }; } }, @@ -204,7 +218,10 @@ impl SocketHandler for Http1Handler { } else { self.state = Http1HandlerState::ExecutingHandler { response_getter, registration }; - break; + break UpdateResult { + registration: None, + close_read: false, + }; } }, @@ -212,13 +229,18 @@ impl SocketHandler for Http1Handler { // TODO: meh, this can block data.read_to_end(&mut update.pending_write_buffer).unwrap(); self.state = Http1HandlerState::WaitingForRqLine { new_data_start: 0 }; - break; + break UpdateResult { + registration: None, + close_read: false, + }; }, Http1HandlerState::Closed => { - debug_assert!(!update.accepts_read); self.state = Http1HandlerState::Closed; - break; + break UpdateResult { + registration: None, + close_read: false, + }; }, } } diff --git a/src/socket_handler/mod.rs b/src/socket_handler/mod.rs index 7747e4c9f..0b11622fc 100644 --- a/src/socket_handler/mod.rs +++ b/src/socket_handler/mod.rs @@ -52,7 +52,7 @@ impl SocketHandlerDispatch { } impl SocketHandler for SocketHandlerDispatch { - fn update(&mut self, update: &mut Update) { + fn update(&mut self, update: &mut Update) -> UpdateResult { match self.inner { SocketHandlerDispatchInner::Http(ref mut http) => http.update(update), SocketHandlerDispatchInner::Https(ref mut https) => https.update(update), @@ -70,33 +70,45 @@ pub enum Protocol { pub trait SocketHandler { /// Call this function whenever new data is received on the socket, or when the registration /// wakes up. - fn update(&mut self, update: &mut Update); + fn update(&mut self, update: &mut Update) -> UpdateResult; +} + +#[derive(Debug)] +pub struct UpdateResult { + /// When `Some`, means that the user must call `update` when the `Registration` becomes ready + /// (either for reading or writing). The registration should be registered with `oneshot()`. + pub registration: Option<(Arc, RegistrationState)>, + + /// Set to true if the socket handler will no longer process incoming data. If + /// `close_read` is true, `pending_write_buffer` is empty, and `registration` is empty, + /// then you can drop the socket. + pub close_read: bool, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum RegistrationState { + /// It is the first time this registration is returned. + FirstTime, + /// This registration has been registered before, and `reregister` should be used. + Reregister, } /// Represents the communication between the `SocketHandler` and the outside. /// /// The "outside" is supposed to fill `pending_read_buffer` with incoming data, and remove data /// from `pending_write_buffer`, then call `update`. +#[derive(Debug)] pub struct Update { /// Filled by the handler user and emptied by `update()`. Contains the data that comes from /// the client. // TODO: try VecDeque and check perfs pub pending_read_buffer: Vec, - /// Set to false by the socket handler when it will no longer process incoming data. If - /// `accepts_read` is false, `pending_write_buffer` is empty, and `registration` is empty, - /// then you can drop the socket. - pub accepts_read: bool, - /// Filled by `SocketHandler::update()` and emptied by the user. Contains the data that must /// be sent back to the client. // TODO: try VecDeque and check perfs pub pending_write_buffer: Vec, - /// When set by the socket handler, it means that the user must call `update` when the - /// `Registration` becomes ready. The user must then set it to 0. The registration is only ever - /// used once. - pub registration: Option>, } impl Update { @@ -105,9 +117,7 @@ impl Update { // TODO: don't create two Vecs for each socket Update { pending_read_buffer: Vec::with_capacity(1024), - accepts_read: true, pending_write_buffer: Vec::with_capacity(1024), - registration: None, } } } diff --git a/src/socket_handler/rustls.rs b/src/socket_handler/rustls.rs index e7caa4aa9..1ef869542 100644 --- a/src/socket_handler/rustls.rs +++ b/src/socket_handler/rustls.rs @@ -17,6 +17,7 @@ use rustls::Session; use socket_handler::SocketHandler; use socket_handler::Update; +use socket_handler::UpdateResult; /// Handles the processing of a client connection through TLS. pub struct RustlsHandler { @@ -45,20 +46,29 @@ impl RustlsHandler { impl SocketHandler for RustlsHandler where H: SocketHandler { - fn update(&mut self, update: &mut Update) { + fn update(&mut self, update: &mut Update) -> UpdateResult { let read_num = self.session.read_tls(&mut (&update.pending_read_buffer[..])).unwrap(); assert_eq!(read_num, update.pending_read_buffer.len()); update.pending_read_buffer.clear(); - self.session.process_new_packets().unwrap(); // TODO: propagate error + if let Err(_) = self.session.process_new_packets() { + // Drop the socket. + update.pending_write_buffer.clear(); + return UpdateResult { + registration: None, + close_read: true, + }; + } self.session.read_to_end(&mut self.handler_update.pending_read_buffer).unwrap(); - self.handler.update(&mut self.handler_update); + let result = self.handler.update(&mut self.handler_update); self.session.write_all(&self.handler_update.pending_write_buffer).unwrap(); self.handler_update.pending_write_buffer.clear(); self.session.write_tls(&mut update.pending_write_buffer).unwrap(); + + result } } From 3f6958375d05644869c6da589b0c505d7ee58f9e Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Tue, 8 Aug 2017 18:13:27 +0200 Subject: [PATCH 28/50] Better handler dispatching --- src/socket_handler/http1.rs | 151 +++++++++++++++++++++++------------- 1 file changed, 95 insertions(+), 56 deletions(-) diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index 3bc3477e1..02325c54d 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -14,13 +14,15 @@ use std::mem; use std::net::SocketAddr; use std::sync::Arc; use std::sync::Mutex; -use std::sync::mpsc::{channel, Receiver}; +use std::sync::mpsc::{channel, Sender, Receiver}; +use std::sync::mpsc::TryRecvError; use std::str; use arrayvec::ArrayString; use httparse; use itoa::write as itoa; use mio::Ready; use mio::Registration; +use mio::SetReadiness; use socket_handler::Protocol; use socket_handler::RegistrationState; @@ -65,12 +67,13 @@ enum Http1HandlerState { version: HttpVersion, }, ExecutingHandler { - response_getter: Receiver, + // Contains blocks of output data streamed by the handler. Closed when the handler doesn't + // have any more data to send. + response_getter: Receiver>, + // Registration that is triggered by the background thread whenever somed ata is available + // in `response_getter`. registration: Arc, }, - SendingResponse { - data: Box, - }, Closed, } @@ -152,34 +155,23 @@ impl SocketHandler for Http1Handler { } update.pending_read_buffer.resize(cut_len, 0); + // We now create a new task for our task pool in which the request is + // built, the handler is called, and the response is sent as Vecs through + // a channel. + let (data_out_tx, data_out_rx) = channel(); + // TODO: yeah, don't spawn threads left and right let (registration, set_ready) = Registration::new2(); - let registration = Arc::new(registration); - let handler = self.handler.clone(); - let https = self.original_protocol == Protocol::Https; - let remote_addr = self.client_addr.clone(); - let (tx, rx) = channel(); - self.task_pool.spawn(move || { - let request = Request { - method: method, - url: path, - headers: headers, - https: https, - data: Arc::new(Mutex::new(None)), // FIXME: - remote_addr: remote_addr, - }; - let mut handler = handler.lock().unwrap(); - let response = (&mut *handler)(request); - let _ = tx.send(response); - let _ = set_ready.set_readiness(Ready::readable()); - }); + spawn_handler_task(&self.task_pool, self.handler.clone(), method, path, + headers, self.original_protocol, + self.client_addr.clone(), data_out_tx, set_ready); + let registration = Arc::new(registration); self.state = Http1HandlerState::ExecutingHandler { - response_getter: rx, + response_getter: data_out_rx, registration: registration.clone(), }; - break UpdateResult { registration: Some((registration, RegistrationState::FirstTime)), @@ -202,39 +194,37 @@ impl SocketHandler for Http1Handler { }, Http1HandlerState::ExecutingHandler { response_getter, registration } => { - // TODO: write incoming data to request's reader - if let Ok(response) = response_getter.try_recv() { - assert!(response.upgrade.is_none()); // TODO: - - let (body_data, body_size) = response.data.into_reader_and_size(); - write_status_and_headers(&mut update.pending_write_buffer, - response.status_code, - &response.headers, - body_size); - - self.state = Http1HandlerState::SendingResponse { - data: Box::new(body_data) - }; - - } else { - self.state = Http1HandlerState::ExecutingHandler { response_getter, registration }; - break UpdateResult { - registration: None, - close_read: false, - }; + match response_getter.try_recv() { + Ok(mut data) => { + // Got some data. + update.pending_write_buffer.append(&mut data); + self.state = Http1HandlerState::ExecutingHandler { + response_getter: response_getter, + registration: registration, + }; + }, + Err(TryRecvError::Disconnected) => { + // The handler has finished streaming the response. + self.state = Http1HandlerState::WaitingForRqLine { new_data_start: 0 }; + break UpdateResult { + registration: None, + close_read: false, + }; + }, + Err(TryRecvError::Empty) => { + // Spurious wakeup. + self.state = Http1HandlerState::ExecutingHandler { + response_getter: response_getter, + registration: registration.clone() + }; + break UpdateResult { + registration: Some((registration, RegistrationState::Reregister)), + close_read: false, + }; + }, } }, - Http1HandlerState::SendingResponse { mut data } => { - // TODO: meh, this can block - data.read_to_end(&mut update.pending_write_buffer).unwrap(); - self.state = Http1HandlerState::WaitingForRqLine { new_data_start: 0 }; - break UpdateResult { - registration: None, - close_read: false, - }; - }, - Http1HandlerState::Closed => { self.state = Http1HandlerState::Closed; break UpdateResult { @@ -247,6 +237,55 @@ impl SocketHandler for Http1Handler { } } +fn spawn_handler_task(task_pool: &TaskPool, handler: Arc Response + Send + 'static>>, + method: ArrayString<[u8; 16]>, path: String, + headers: Vec<(String, String)>, original_protocol: Protocol, + remote_addr: SocketAddr, data_out_tx: Sender>, + set_ready: SetReadiness) +{ + let https = original_protocol == Protocol::Https; + + task_pool.spawn(move || { + let request = Request { + method: method, + url: path, + headers: headers, + https: https, + data: Arc::new(Mutex::new(None)), // FIXME: + remote_addr: remote_addr, + }; + + let mut handler = handler.lock().unwrap(); + let response = (&mut *handler)(request); + assert!(response.upgrade.is_none()); // TODO: unimplemented + + let (mut body_data, body_size) = response.data.into_reader_and_size(); + + let mut out_buffer = Vec::new(); + write_status_and_headers(&mut out_buffer, + response.status_code, + &response.headers, + body_size); + + match data_out_tx.send(out_buffer) { + Ok(_) => (), + Err(_) => return, + }; + + let _ = set_ready.set_readiness(Ready::readable()); + + // TODO: streaming output + let mut out_data = Vec::new(); + body_data.read_to_end(&mut out_data).unwrap(); // TODO: what if error? + + match data_out_tx.send(out_data) { + Ok(_) => (), + Err(_) => return, + }; + let _ = set_ready.set_readiness(Ready::readable()); + }); +} + // HTTP version (usually 1.0 or 1.1). #[derive(Debug, Clone, PartialEq, Eq)] struct HttpVersion(pub u8, pub u8); From 467259f1c17680f5ac6584c4e20d09ffa58cbaed Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Tue, 8 Aug 2017 18:16:01 +0200 Subject: [PATCH 29/50] Minor code style --- src/socket_handler/http1.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index 02325c54d..a65b72602 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -159,10 +159,7 @@ impl SocketHandler for Http1Handler { // built, the handler is called, and the response is sent as Vecs through // a channel. let (data_out_tx, data_out_rx) = channel(); - - // TODO: yeah, don't spawn threads left and right let (registration, set_ready) = Registration::new2(); - spawn_handler_task(&self.task_pool, self.handler.clone(), method, path, headers, self.original_protocol, self.client_addr.clone(), data_out_tx, set_ready); @@ -237,7 +234,9 @@ impl SocketHandler for Http1Handler { } } -fn spawn_handler_task(task_pool: &TaskPool, handler: Arc Response + Send + 'static>>, +// Starts the task of handling a request. +fn spawn_handler_task(task_pool: &TaskPool, + handler: Arc Response + Send + 'static>>, method: ArrayString<[u8; 16]>, path: String, headers: Vec<(String, String)>, original_protocol: Protocol, remote_addr: SocketAddr, data_out_tx: Sender>, From f17f50f7ac6a22df7bfffc8ba36b014b33d59540 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Tue, 8 Aug 2017 18:20:34 +0200 Subject: [PATCH 30/50] Streaming data send back --- src/socket_handler/http1.rs | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index a65b72602..11e929512 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -9,6 +9,7 @@ use std::ascii::AsciiExt; use std::borrow::Cow; +use std::io::ErrorKind; use std::io::Read; use std::mem; use std::net::SocketAddr; @@ -273,15 +274,25 @@ fn spawn_handler_task(task_pool: &TaskPool, let _ = set_ready.set_readiness(Ready::readable()); - // TODO: streaming output - let mut out_data = Vec::new(); - body_data.read_to_end(&mut out_data).unwrap(); // TODO: what if error? + loop { + let mut out_data = vec![0; 256]; + match body_data.read(&mut out_data) { + Ok(0) => break, + Ok(n) => out_data.truncate(n), + Err(ref e) if e.kind() == ErrorKind::Interrupted => {}, + Err(_) => { + // Handle errors by silently stopping the stream. + // TODO: better way? + return; + }, + }; - match data_out_tx.send(out_data) { - Ok(_) => (), - Err(_) => return, - }; - let _ = set_ready.set_readiness(Ready::readable()); + match data_out_tx.send(out_data) { + Ok(_) => (), + Err(_) => return, + }; + let _ = set_ready.set_readiness(Ready::readable()); + } }); } From 814b05376cd350fee594c21786e39fa8df3653b7 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Tue, 8 Aug 2017 18:31:19 +0200 Subject: [PATCH 31/50] More documentation --- src/socket_handler/http1.rs | 55 +++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index 11e929512..c32518195 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -39,46 +39,65 @@ pub struct Http1Handler { // The handler is a state machine. state: Http1HandlerState, - // Address of the client. Passed to the request object. + // Address of the client. Necessary for the request objects. client_addr: SocketAddr, - // Protocol of the original server. Passed to the request object. + // Protocol of the original server. Necessary for the request objects. original_protocol: Protocol, // Object that handles the request and returns a response. handler: Arc Response + Send + 'static>>, - + // The pool where to dispatch the handler. task_pool: TaskPool, } +// Current status of the handler. enum Http1HandlerState { + // A panic happened during the processing. In this state any call to `update` will panic. Poisonned, + + // The `pending_read_buffer` doesn't have enough bytes to contain the initial request line. WaitingForRqLine { // Offset within `pending_read_buffer` where new data is available. Everything before this // offset was already in `pending_read_buffer` the last time `update` returned. new_data_start: usize, }, + + // The request line has been parsed (its informations are inside the variant), but the + // `pending_read_buffer` doesn't have enough bytes to contain the headers. WaitingForHeaders { // Offset within `pending_read_buffer` where new data is available. Everything before this // offset was already in `pending_read_buffer` the last time `update` returned. new_data_start: usize, + // HTTP method (eg. GET, POST, ...) parsed from the request line. method: ArrayString<[u8; 16]>, + // URL requested by the HTTP client parsed from the request line. path: String, + // HTTP version parsed from the request line. version: HttpVersion, }, + + // The handler is currently being executed in the task pool and is streaming data. ExecutingHandler { // Contains blocks of output data streamed by the handler. Closed when the handler doesn't // have any more data to send. response_getter: Receiver>, - // Registration that is triggered by the background thread whenever somed ata is available + // Registration that is triggered by the background thread whenever some data is available // in `response_getter`. registration: Arc, }, + + // Happens after a request with `Connection: close`. The connection is considered as closed by + // the handler and nothing more will be processed. Closed, } impl Http1Handler { + /// Starts handling a new HTTP client connection. + /// + /// `client_addr` and `original_protocol` are necessary for building the `Request` objects. + /// `task_pool` and `handler` indicate how the requests must be processed. pub fn new(client_addr: SocketAddr, original_protocol: Protocol, task_pool: TaskPool, handler: F) -> Http1Handler where F: FnMut(Request) -> Response + Send + 'static @@ -102,15 +121,19 @@ impl SocketHandler for Http1Handler { }, Http1HandlerState::WaitingForRqLine { new_data_start } => { + // Try to find a \r\n in the buffer. let off = new_data_start.saturating_sub(1); - if let Some(rn) = update.pending_read_buffer[off..].windows(2).position(|w| w == b"\r\n") { + let rn = update.pending_read_buffer[off..].windows(2) + .position(|w| w == b"\r\n"); + if let Some(rn) = rn { + // Found a request line! let (method, path, version) = { - let (method, path, version) = parse_request_line(&update.pending_read_buffer[..rn]).unwrap(); // TODO: error - let method = ArrayString::from(method).unwrap(); // TODO: error + let (method, path, version) = parse_request_line(&update.pending_read_buffer[..rn]).unwrap(); // TODO: handle error + let method = ArrayString::from(method).unwrap(); // TODO: handle error (method, path.to_owned(), version) }; - // Remove the first `rn + 2` bytes of `update.pending_read_buffer` + // Remove the request line from the head of the buffer. let cut_len = update.pending_read_buffer.len() - (rn + 2); for n in 0 .. cut_len { update.pending_read_buffer[n] = update.pending_read_buffer[n + rn + 2]; @@ -125,6 +148,8 @@ impl SocketHandler for Http1Handler { }; } else { + // No full request line in the buffer yet. + // TODO: put a limit on the buffer size self.state = Http1HandlerState::WaitingForRqLine { new_data_start: update.pending_read_buffer.len(), }; @@ -137,8 +162,13 @@ impl SocketHandler for Http1Handler { }, Http1HandlerState::WaitingForHeaders { new_data_start, method, path, version } => { + // Try to find a `\r\n\r\n` in the buffer which would indicate the end of the + // headers. let off = new_data_start.saturating_sub(3); - if let Some(rnrn) = update.pending_read_buffer[off..].windows(4).position(|w| w == b"\r\n\r\n") { + let rnrn = update.pending_read_buffer[off..].windows(4) + .position(|w| w == b"\r\n\r\n"); + if let Some(rnrn) = rnrn { + // Found headers! Parse them. let headers = { let mut out_headers = Vec::new(); let mut headers = [httparse::EMPTY_HEADER; 32]; @@ -149,7 +179,7 @@ impl SocketHandler for Http1Handler { out_headers }; - // Remove the first `off + rnrn + 4` bytes of `update.pending_read_buffer` + // Remove the headers from the head of the buffer. let cut_len = update.pending_read_buffer.len() - (off + rnrn + 4); for n in 0 .. cut_len { update.pending_read_buffer[n] = update.pending_read_buffer[n + off + rnrn + 4]; @@ -177,6 +207,8 @@ impl SocketHandler for Http1Handler { }; } else { + // No full headers in the buffer yet. + // TODO: put a limit on the buffer size self.state = Http1HandlerState::WaitingForHeaders { new_data_start: update.pending_read_buffer.len(), method, @@ -194,7 +226,7 @@ impl SocketHandler for Http1Handler { Http1HandlerState::ExecutingHandler { response_getter, registration } => { match response_getter.try_recv() { Ok(mut data) => { - // Got some data. + // Got some data for the response. update.pending_write_buffer.append(&mut data); self.state = Http1HandlerState::ExecutingHandler { response_getter: response_getter, @@ -203,6 +235,7 @@ impl SocketHandler for Http1Handler { }, Err(TryRecvError::Disconnected) => { // The handler has finished streaming the response. + // TODO: jump to `Closed` instead if `Connection: closed` was set self.state = Http1HandlerState::WaitingForRqLine { new_data_start: 0 }; break UpdateResult { registration: None, From c8a612381a2f790de5b208e3a3ab4a8f8b8041c2 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Tue, 8 Aug 2017 18:45:04 +0200 Subject: [PATCH 32/50] Draft for handling Connection: close --- src/socket_handler/http1.rs | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index c32518195..6f797bd15 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -80,6 +80,8 @@ enum Http1HandlerState { // The handler is currently being executed in the task pool and is streaming data. ExecutingHandler { + // True if `Connection: close` was requested by the client as part of the headers. + connection_close: bool, // Contains blocks of output data streamed by the handler. Closed when the handler doesn't // have any more data to send. response_getter: Receiver>, @@ -197,6 +199,7 @@ impl SocketHandler for Http1Handler { let registration = Arc::new(registration); self.state = Http1HandlerState::ExecutingHandler { + connection_close: false, // TODO: response_getter: data_out_rx, registration: registration.clone(), }; @@ -223,12 +226,15 @@ impl SocketHandler for Http1Handler { } }, - Http1HandlerState::ExecutingHandler { response_getter, registration } => { + Http1HandlerState::ExecutingHandler { connection_close, response_getter, + registration } => + { match response_getter.try_recv() { Ok(mut data) => { // Got some data for the response. update.pending_write_buffer.append(&mut data); self.state = Http1HandlerState::ExecutingHandler { + connection_close: connection_close, response_getter: response_getter, registration: registration, }; @@ -236,15 +242,22 @@ impl SocketHandler for Http1Handler { Err(TryRecvError::Disconnected) => { // The handler has finished streaming the response. // TODO: jump to `Closed` instead if `Connection: closed` was set - self.state = Http1HandlerState::WaitingForRqLine { new_data_start: 0 }; - break UpdateResult { - registration: None, - close_read: false, - }; + if connection_close { + self.state = Http1HandlerState::Closed; + } else { + self.state = Http1HandlerState::WaitingForRqLine { + new_data_start: 0 + }; + break UpdateResult { + registration: None, + close_read: false, + }; + } }, Err(TryRecvError::Empty) => { // Spurious wakeup. self.state = Http1HandlerState::ExecutingHandler { + connection_close: connection_close, response_getter: response_getter, registration: registration.clone() }; @@ -260,7 +273,7 @@ impl SocketHandler for Http1Handler { self.state = Http1HandlerState::Closed; break UpdateResult { registration: None, - close_read: false, + close_read: true, }; }, } From a77c31629018b225786fde5321e8779c463c1757 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Tue, 8 Aug 2017 19:46:23 +0200 Subject: [PATCH 33/50] Drop the Mutex earlier --- src/socket_handler/http1.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index 6f797bd15..44577693c 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -301,8 +301,10 @@ fn spawn_handler_task(task_pool: &TaskPool, remote_addr: remote_addr, }; - let mut handler = handler.lock().unwrap(); - let response = (&mut *handler)(request); + let response = { + let mut handler = handler.lock().unwrap(); + (&mut *handler)(request) + }; assert!(response.upgrade.is_none()); // TODO: unimplemented let (mut body_data, body_size) = response.data.into_reader_and_size(); From 6809a6ffecea350f9d069c1bfc47045766f50432 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Tue, 8 Aug 2017 19:55:06 +0200 Subject: [PATCH 34/50] Minor optimization in data reception --- src/socket_handler/http1.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index 44577693c..fee98be40 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -232,7 +232,11 @@ impl SocketHandler for Http1Handler { match response_getter.try_recv() { Ok(mut data) => { // Got some data for the response. - update.pending_write_buffer.append(&mut data); + if update.pending_write_buffer.is_empty() { + update.pending_write_buffer = data; + } else { + update.pending_write_buffer.append(&mut data); + } self.state = Http1HandlerState::ExecutingHandler { connection_close: connection_close, response_getter: response_getter, From bf7ac3aac399553481660229a106c70ee5da0719 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Tue, 8 Aug 2017 19:55:37 +0200 Subject: [PATCH 35/50] Remove TODO --- src/socket_handler/http1.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index fee98be40..0eaa92c3f 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -245,7 +245,6 @@ impl SocketHandler for Http1Handler { }, Err(TryRecvError::Disconnected) => { // The handler has finished streaming the response. - // TODO: jump to `Closed` instead if `Connection: closed` was set if connection_close { self.state = Http1HandlerState::Closed; } else { From 71532de190faf10921d3841985a0e7b001e65a4e Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Tue, 8 Aug 2017 20:40:59 +0200 Subject: [PATCH 36/50] Optimize writing --- src/server.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/server.rs b/src/server.rs index d351c26e3..6e393fef6 100644 --- a/src/server.rs +++ b/src/server.rs @@ -347,9 +347,12 @@ fn handle_write(share: &ThreadsShare, socket: Socket) { let _ = stream.flush(); break; }, - Ok(n) => { - // TODO: more efficient - update.pending_write_buffer = update.pending_write_buffer[n..].to_owned(); + Ok(written) => { + let cut_len = update.pending_write_buffer.len() - written; + for n in 0 .. cut_len { + update.pending_write_buffer[n] = update.pending_write_buffer[n + written]; + } + update.pending_write_buffer.resize(cut_len, 0); let _ = stream.flush(); }, Err(ref e) if e.kind() == ErrorKind::Interrupted => {}, From 5b12ee287efb7ecf2affb99b940487a41122600c Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Tue, 8 Aug 2017 20:42:02 +0200 Subject: [PATCH 37/50] Don't flush when writing --- src/server.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/server.rs b/src/server.rs index 6e393fef6..9c42fce82 100644 --- a/src/server.rs +++ b/src/server.rs @@ -343,23 +343,16 @@ fn handle_write(share: &ThreadsShare, socket: Socket) { // Write from `update.pending_write_buffer` to `stream`. while !update.pending_write_buffer.is_empty() { match stream.write(&update.pending_write_buffer) { - Ok(0) => { - let _ = stream.flush(); - break; - }, + Ok(0) => break, Ok(written) => { let cut_len = update.pending_write_buffer.len() - written; for n in 0 .. cut_len { update.pending_write_buffer[n] = update.pending_write_buffer[n + written]; } update.pending_write_buffer.resize(cut_len, 0); - let _ = stream.flush(); }, Err(ref e) if e.kind() == ErrorKind::Interrupted => {}, - Err(ref e) if e.kind() == ErrorKind::WouldBlock => { - let _ = stream.flush(); - break; - }, + Err(ref e) if e.kind() == ErrorKind::WouldBlock => break, Err(_) => { // Handle errors with the stream by returning without re-registering it. This // drops the stream. From ae862ca86944f87be2a93cb07b706b539c2795b5 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Tue, 8 Aug 2017 20:50:49 +0200 Subject: [PATCH 38/50] Add write_flush_suggested --- src/server.rs | 20 +++++++++++++++----- src/socket_handler/http1.rs | 6 ++++++ src/socket_handler/mod.rs | 4 ++++ src/socket_handler/rustls.rs | 1 + 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/server.rs b/src/server.rs index 9c42fce82..b060df85f 100644 --- a/src/server.rs +++ b/src/server.rs @@ -74,6 +74,7 @@ enum Socket { Stream { stream: TcpStream, read_closed: bool, + write_flush_suggested: bool, handler: SocketHandlerDispatch, update: SocketHandlerUpdate, }, @@ -228,6 +229,7 @@ fn handle_read(share: &Arc>, socket: Socket) entry.insert(Socket::Stream { stream: stream, read_closed: false, + write_flush_suggested: false, handler: SocketHandlerDispatch::new(client_addr, Protocol::Http, share.task_pool.clone(), move |rq| (share.handler)(&rq)), update: SocketHandlerUpdate::empty(), @@ -251,7 +253,9 @@ fn handle_read(share: &Arc>, socket: Socket) entry.insert(Socket::Listener(listener)); }, - Socket::Stream { mut stream, mut read_closed, mut handler, mut update } => { + Socket::Stream { mut stream, mut read_closed, mut write_flush_suggested, mut handler, + mut update } => + { // Read into `update.pending_read_buffer` until `WouldBlock` is returned. loop { let old_pr_len = update.pending_read_buffer.len(); @@ -285,6 +289,9 @@ fn handle_read(share: &Arc>, socket: Socket) if update_result.close_read { read_closed = true; } + if update_result.write_flush_suggested { + write_flush_suggested = true; + } // Re-register stream for next time. let mut ready = Ready::empty(); @@ -327,7 +334,8 @@ fn handle_read(share: &Arc>, socket: Socket) } if insert_entry { - entry.insert(Socket::Stream { stream, read_closed, handler, update }); + entry.insert(Socket::Stream { stream, read_closed, write_flush_suggested, + handler, update }); } }, } @@ -335,9 +343,10 @@ fn handle_read(share: &Arc>, socket: Socket) fn handle_write(share: &ThreadsShare, socket: Socket) { // Write events can't happen for listeners. - let (mut stream, read_closed, handler, mut update) = match socket { + let (mut stream, read_closed, write_flush_suggested, handler, mut update) = match socket { Socket::Listener(_) => unreachable!(), - Socket::Stream { stream, read_closed, handler, update } => (stream, read_closed, handler, update), + Socket::Stream { stream, read_closed, write_flush_suggested, handler, update } => + (stream, read_closed, write_flush_suggested, handler, update), }; // Write from `update.pending_write_buffer` to `stream`. @@ -375,6 +384,7 @@ fn handle_write(share: &ThreadsShare, socket: Socket) { share.poll.reregister(&stream, entry.key().into(), ready, PollOpt::edge() | PollOpt::oneshot()) .expect("Error while reregistering TCP stream"); - entry.insert(Socket::Stream { stream, read_closed, handler, update }); + entry.insert(Socket::Stream { stream, read_closed, write_flush_suggested, + handler, update }); } } diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index 0eaa92c3f..5752dce7e 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -159,6 +159,7 @@ impl SocketHandler for Http1Handler { break UpdateResult { registration: None, close_read: false, + write_flush_suggested: false, }; } }, @@ -207,6 +208,7 @@ impl SocketHandler for Http1Handler { registration: Some((registration, RegistrationState::FirstTime)), close_read: false, + write_flush_suggested: false, }; } else { @@ -222,6 +224,7 @@ impl SocketHandler for Http1Handler { break UpdateResult { registration: None, close_read: false, + write_flush_suggested: false, }; } }, @@ -254,6 +257,7 @@ impl SocketHandler for Http1Handler { break UpdateResult { registration: None, close_read: false, + write_flush_suggested: true, }; } }, @@ -267,6 +271,7 @@ impl SocketHandler for Http1Handler { break UpdateResult { registration: Some((registration, RegistrationState::Reregister)), close_read: false, + write_flush_suggested: false, }; }, } @@ -277,6 +282,7 @@ impl SocketHandler for Http1Handler { break UpdateResult { registration: None, close_read: true, + write_flush_suggested: true, }; }, } diff --git a/src/socket_handler/mod.rs b/src/socket_handler/mod.rs index 0b11622fc..2f033984b 100644 --- a/src/socket_handler/mod.rs +++ b/src/socket_handler/mod.rs @@ -83,6 +83,10 @@ pub struct UpdateResult { /// `close_read` is true, `pending_write_buffer` is empty, and `registration` is empty, /// then you can drop the socket. pub close_read: bool, + + /// If true, the socket handler suggests to flush the content of the write buffer to the + /// socket. + pub write_flush_suggested: bool, } #[derive(Debug, Copy, Clone, PartialEq, Eq)] diff --git a/src/socket_handler/rustls.rs b/src/socket_handler/rustls.rs index 1ef869542..121f97644 100644 --- a/src/socket_handler/rustls.rs +++ b/src/socket_handler/rustls.rs @@ -57,6 +57,7 @@ impl SocketHandler for RustlsHandler return UpdateResult { registration: None, close_read: true, + write_flush_suggested: false, }; } From 4126b48a972af3e2842f3b70492e27ea797d22ca Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Wed, 9 Aug 2017 11:19:33 +0200 Subject: [PATCH 39/50] Basic client input data handling --- Cargo.toml | 1 + src/lib.rs | 1 + src/socket_handler/http1.rs | 103 +++++++++++++++++--- src/socket_handler/mod.rs | 1 + src/socket_handler/request_body_analyzer.rs | 65 ++++++++++++ 5 files changed, 155 insertions(+), 16 deletions(-) create mode 100644 src/socket_handler/request_body_analyzer.rs diff --git a/Cargo.toml b/Cargo.toml index e78c22436..01a76147c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ gzip = ["flate2"] [dependencies] arrayvec = "0.3.23" +atoi = "0.2.2" brotli2 = { version = "0.2.1", optional = true } chrono = "0.2.0" crossbeam = "0.3.0" diff --git a/src/lib.rs b/src/lib.rs index ebb7356d6..05baac96b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,6 +53,7 @@ #![deny(unsafe_code)] extern crate arrayvec; +extern crate atoi; #[cfg(feature = "brotli2")] extern crate brotli2; extern crate chrono; diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index 5752dce7e..3e9d90e41 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -9,6 +9,9 @@ use std::ascii::AsciiExt; use std::borrow::Cow; +use std::io::copy; +use std::io::Cursor; +use std::io::Error as IoError; use std::io::ErrorKind; use std::io::Read; use std::mem; @@ -30,6 +33,7 @@ use socket_handler::RegistrationState; use socket_handler::SocketHandler; use socket_handler::Update; use socket_handler::UpdateResult; +use socket_handler::request_body_analyzer::RequestBodyAnalyzer; use socket_handler::task_pool::TaskPool; use Request; use Response; @@ -82,12 +86,16 @@ enum Http1HandlerState { ExecutingHandler { // True if `Connection: close` was requested by the client as part of the headers. connection_close: bool, + // Analyzes and decodes the client input. + input_analyzer: RequestBodyAnalyzer, + // Used to send buffers containing the body of the request. `None` if no more data. + input_data: Option>>, // Contains blocks of output data streamed by the handler. Closed when the handler doesn't // have any more data to send. response_getter: Receiver>, // Registration that is triggered by the background thread whenever some data is available // in `response_getter`. - registration: Arc, + registration: (Arc, RegistrationState), }, // Happens after a request with `Connection: close`. The connection is considered as closed by @@ -172,7 +180,7 @@ impl SocketHandler for Http1Handler { .position(|w| w == b"\r\n\r\n"); if let Some(rnrn) = rnrn { // Found headers! Parse them. - let headers = { + let headers: Vec<(String, String)> = { let mut out_headers = Vec::new(); let mut headers = [httparse::EMPTY_HEADER; 32]; let (_, parsed_headers) = httparse::parse_headers(&update.pending_read_buffer, &mut headers).unwrap().unwrap(); // TODO: @@ -189,26 +197,30 @@ impl SocketHandler for Http1Handler { } update.pending_read_buffer.resize(cut_len, 0); + let input_analyzer = { + let iter = headers + .iter() + .map(|&(ref h, ref v)| (h.as_str(), v.as_str())); + RequestBodyAnalyzer::new(iter) + }; + // We now create a new task for our task pool in which the request is // built, the handler is called, and the response is sent as Vecs through // a channel. let (data_out_tx, data_out_rx) = channel(); + let (data_in_tx, data_in_rx) = channel(); let (registration, set_ready) = Registration::new2(); spawn_handler_task(&self.task_pool, self.handler.clone(), method, path, headers, self.original_protocol, - self.client_addr.clone(), data_out_tx, set_ready); + self.client_addr.clone(), data_out_tx, data_in_rx, + set_ready); - let registration = Arc::new(registration); self.state = Http1HandlerState::ExecutingHandler { connection_close: false, // TODO: + input_analyzer: input_analyzer, + input_data: Some(data_in_tx), response_getter: data_out_rx, - registration: registration.clone(), - }; - break UpdateResult { - registration: Some((registration, - RegistrationState::FirstTime)), - close_read: false, - write_flush_suggested: false, + registration: (Arc::new(registration), RegistrationState::FirstTime), }; } else { @@ -229,9 +241,23 @@ impl SocketHandler for Http1Handler { } }, - Http1HandlerState::ExecutingHandler { connection_close, response_getter, + Http1HandlerState::ExecutingHandler { connection_close, mut input_data, + mut input_analyzer, response_getter, registration } => { + { + let analysis = input_analyzer.feed(&mut update.pending_read_buffer); + if analysis.body_data >= 1 { + // TODO: more optimal + let body_data = update.pending_read_buffer[0 .. analysis.body_data].to_owned(); + update.pending_read_buffer = update.pending_read_buffer[analysis.body_data..].to_owned(); + let _ = input_data.as_mut().unwrap().send(body_data); + } + if analysis.finished { + input_data = None; + } + } + match response_getter.try_recv() { Ok(mut data) => { // Got some data for the response. @@ -242,6 +268,8 @@ impl SocketHandler for Http1Handler { } self.state = Http1HandlerState::ExecutingHandler { connection_close: connection_close, + input_data: input_data, + input_analyzer: input_analyzer, response_getter: response_getter, registration: registration, }; @@ -265,11 +293,13 @@ impl SocketHandler for Http1Handler { // Spurious wakeup. self.state = Http1HandlerState::ExecutingHandler { connection_close: connection_close, + input_data: input_data, + input_analyzer: input_analyzer, response_getter: response_getter, - registration: registration.clone() + registration: (registration.0.clone(), RegistrationState::Reregister), }; break UpdateResult { - registration: Some((registration, RegistrationState::Reregister)), + registration: Some(registration), close_read: false, write_flush_suggested: false, }; @@ -296,17 +326,58 @@ fn spawn_handler_task(task_pool: &TaskPool, method: ArrayString<[u8; 16]>, path: String, headers: Vec<(String, String)>, original_protocol: Protocol, remote_addr: SocketAddr, data_out_tx: Sender>, - set_ready: SetReadiness) + data_in_rx: Receiver>, set_ready: SetReadiness) { let https = original_protocol == Protocol::Https; + struct ReadWrapper(Receiver>, Cursor>); + impl Read for ReadWrapper { + fn read(&mut self, buf: &mut [u8]) -> Result { + let initial_buf_len = buf.len() as u64; + let mut total_written = 0; + let mut buf = Cursor::new(buf); + + total_written += copy(&mut self.1, &mut buf).unwrap(); + debug_assert!(total_written <= initial_buf_len); + if total_written == initial_buf_len { + return Ok(total_written as usize); + } + + match self.0.recv() { + Ok(data) => self.1 = Cursor::new(data), + Err(_) => return Ok(total_written as usize), + }; + + total_written += copy(&mut self.1, &mut buf).unwrap(); + debug_assert!(total_written <= initial_buf_len); + if total_written == initial_buf_len { + return Ok(total_written as usize); + } + + loop { + match self.0.try_recv() { + Ok(data) => self.1 = Cursor::new(data), + Err(_) => return Ok(total_written as usize), + }; + + total_written += copy(&mut self.1, &mut buf).unwrap(); + debug_assert!(total_written <= initial_buf_len); + if total_written == initial_buf_len { + return Ok(total_written as usize); + } + } + } + } + + let reader = ReadWrapper(data_in_rx, Cursor::new(Vec::new())); + task_pool.spawn(move || { let request = Request { method: method, url: path, headers: headers, https: https, - data: Arc::new(Mutex::new(None)), // FIXME: + data: Arc::new(Mutex::new(Some(Box::new(reader)))), remote_addr: remote_addr, }; diff --git a/src/socket_handler/mod.rs b/src/socket_handler/mod.rs index 2f033984b..c7b71bad3 100644 --- a/src/socket_handler/mod.rs +++ b/src/socket_handler/mod.rs @@ -19,6 +19,7 @@ use self::rustls::RustlsHandler; pub use self::task_pool::TaskPool; // TODO: shouldn't be pub, but is used by Server, move it somewher else mod http1; +mod request_body_analyzer; mod rustls; mod task_pool; diff --git a/src/socket_handler/request_body_analyzer.rs b/src/socket_handler/request_body_analyzer.rs new file mode 100644 index 000000000..d4a9caca9 --- /dev/null +++ b/src/socket_handler/request_body_analyzer.rs @@ -0,0 +1,65 @@ +// Copyright (c) 2017 The Rouille developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use atoi::atoi; +use std::ascii::AsciiExt; +use std::mem; + +pub struct RequestBodyAnalyzer { + remaining_content_length: u64, +} + +impl RequestBodyAnalyzer { + pub fn new<'a, I>(headers: I) -> RequestBodyAnalyzer + where I: Iterator // TODO: should be [u8] eventually + { + let mut content_length = None; + for (header, value) in headers { + if header.eq_ignore_ascii_case("Content-Length") { + content_length = atoi(value.as_bytes()); + } + } + + RequestBodyAnalyzer { + remaining_content_length: content_length.unwrap_or(0), + } + } + + /// Processes some data. + pub fn feed(&mut self, data: &mut [u8]) -> FeedOutcome { + // The most common case is a request without any data. + if self.remaining_content_length == 0 { + return FeedOutcome { + body_data: 0, + finished: true, + }; + } + + if (data.len() as u64) < self.remaining_content_length { + self.remaining_content_length -= data.len() as u64; + return FeedOutcome { + body_data: data.len(), + finished: self.remaining_content_length == 0, + }; + } + + FeedOutcome { + body_data: mem::replace(&mut self.remaining_content_length, 0) as usize, + finished: true, + } + } +} + +pub struct FeedOutcome { + /// Number of bytes in `data` that contain the body of the request. + pub body_data: usize, + /// True if the request is finished. Calling `feed` again would return a `FeedOutcome` with a + /// `body_data` of 0. + pub finished: bool, +} From ad51efe01f0cd3e07f4c8a7e4c7936fd48d4cba3 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Wed, 9 Aug 2017 12:03:05 +0200 Subject: [PATCH 40/50] More work on HTTPS --- src/server.rs | 83 +++++++++++++++++++++++++-------- src/socket_handler/mod.rs | 26 +++++++---- src/socket_handler/rustls.rs | 89 +++++++++++++++++++++++++++++++++--- 3 files changed, 164 insertions(+), 34 deletions(-) diff --git a/src/server.rs b/src/server.rs index b060df85f..d80f8735e 100644 --- a/src/server.rs +++ b/src/server.rs @@ -22,7 +22,8 @@ use mio::tcp::{TcpListener, TcpStream}; use num_cpus; use slab::Slab; -use socket_handler::Protocol; +pub use socket_handler::RustlsConfig as SslConfig; + use socket_handler::RegistrationState; use socket_handler::TaskPool; use socket_handler::SocketHandler; @@ -70,7 +71,10 @@ struct ThreadsShare { } enum Socket { - Listener(TcpListener), + Listener{ + listener: TcpListener, + https: Option, + }, Stream { stream: TcpStream, read_closed: bool, @@ -91,17 +95,13 @@ impl Server where F: Send + Sync + 'static + Fn(&Request) -> Response { where A: ToSocketAddrs, F: Fn(&Request) -> Response + Send + 'static { - let server = Server::init(handler)?; - - for addr in addr.to_socket_addrs()? { - server.add_listener(&addr)?; - } - + let server = Server::empty(handler)?; + server.add_http_listeners(addr)?; Ok(server) } - // Builds a new `Server` but without any listener. - fn init(handler: F) -> Result, Box> + /// Builds a new `Server` but without any listener. + pub fn empty(handler: F) -> Result, Box> where F: Fn(&Request) -> Response + Send + 'static { let share = Arc::new(ThreadsShare { @@ -133,8 +133,19 @@ impl Server where F: Send + Sync + 'static + Fn(&Request) -> Response { }) } - // Adds a new listening addr to the server. - fn add_listener(&self, addr: &SocketAddr) -> Result<(), Box> { + // Adds new HTTP listening addresses to the server. + pub fn add_http_listeners
(&self, addr: A) -> Result<(), Box> + where A: ToSocketAddrs + { + for addr in addr.to_socket_addrs()? { + // TODO: error recovery? what if the 2nd address returns an error? do we keep the first running? + self.add_http_listener(&addr)?; + } + Ok(()) + } + + /// Adds a new listening addr to the server. + pub fn add_http_listener(&self, addr: &SocketAddr) -> Result<(), Box> { let listener = TcpListener::bind(addr)?; let mut slab = self.inner.sockets.lock().unwrap(); @@ -143,7 +154,29 @@ impl Server where F: Send + Sync + 'static + Fn(&Request) -> Response { self.inner.poll.register(&listener, entry.key().into(), Ready::readable(), PollOpt::edge() | PollOpt::oneshot())?; - entry.insert(Socket::Listener(listener)); + entry.insert(Socket::Listener { + listener: listener, + https: None, + }); + + Ok(()) + } + + /// Adds a new listening addr to the server. + pub fn add_https_listener(&self, addr: &SocketAddr, config: SslConfig) + -> Result<(), Box> { + let listener = TcpListener::bind(addr)?; + + let mut slab = self.inner.sockets.lock().unwrap(); + let entry = slab.vacant_entry(); + + self.inner.poll.register(&listener, entry.key().into(), + Ready::readable(), PollOpt::edge() | PollOpt::oneshot())?; + + entry.insert(Socket::Listener { + listener: listener, + https: Some(config), + }); Ok(()) } @@ -214,7 +247,7 @@ fn handle_read(share: &Arc>, socket: Socket) where F: Fn(&Request) -> Response + Send + Sync + 'static { match socket { - Socket::Listener(listener) => { + Socket::Listener { listener, https } => { // Call `accept` repeatidely and register the newly-created sockets, // until `WouldBlock` is returned. loop { @@ -225,13 +258,22 @@ fn handle_read(share: &Arc>, socket: Socket) share.poll.register(&stream, entry.key().into(), Ready::readable(), PollOpt::edge() | PollOpt::oneshot()) .expect("Error while registering TCP stream"); - let share = share.clone(); + let handler = { + let share = share.clone(); + if let Some(ref https) = https { + SocketHandlerDispatch::https(https.clone(), client_addr, + share.task_pool.clone(), + move |rq| (share.handler)(&rq)) + } else { + SocketHandlerDispatch::http(client_addr, share.task_pool.clone(), + move |rq| (share.handler)(&rq)) + } + }; entry.insert(Socket::Stream { stream: stream, read_closed: false, write_flush_suggested: false, - handler: SocketHandlerDispatch::new(client_addr, Protocol::Http, share.task_pool.clone(), - move |rq| (share.handler)(&rq)), + handler: handler, update: SocketHandlerUpdate::empty(), }); }, @@ -250,7 +292,10 @@ fn handle_read(share: &Arc>, socket: Socket) share.poll.reregister(&listener, entry.key().into(), Ready::readable(), PollOpt::edge() | PollOpt::oneshot()) .expect("Error while reregistering TCP listener"); - entry.insert(Socket::Listener(listener)); + entry.insert(Socket::Listener { + listener: listener, + https: https, + }); }, Socket::Stream { mut stream, mut read_closed, mut write_flush_suggested, mut handler, @@ -344,7 +389,7 @@ fn handle_read(share: &Arc>, socket: Socket) fn handle_write(share: &ThreadsShare, socket: Socket) { // Write events can't happen for listeners. let (mut stream, read_closed, write_flush_suggested, handler, mut update) = match socket { - Socket::Listener(_) => unreachable!(), + Socket::Listener { .. } => unreachable!(), Socket::Stream { stream, read_closed, write_flush_suggested, handler, update } => (stream, read_closed, write_flush_suggested, handler, update), }; diff --git a/src/socket_handler/mod.rs b/src/socket_handler/mod.rs index c7b71bad3..43a4bba2c 100644 --- a/src/socket_handler/mod.rs +++ b/src/socket_handler/mod.rs @@ -16,6 +16,7 @@ use Response; use self::http1::Http1Handler; use self::rustls::RustlsHandler; +pub use self::rustls::RustlsConfig; pub use self::task_pool::TaskPool; // TODO: shouldn't be pub, but is used by Server, move it somewher else mod http1; @@ -34,18 +35,25 @@ enum SocketHandlerDispatchInner { } impl SocketHandlerDispatch { - /// Initialization. - pub fn new(client_addr: SocketAddr, protocol: Protocol, task_pool: TaskPool, - handler: F) -> SocketHandlerDispatch + /// Initialization for HTTP. + pub fn http(client_addr: SocketAddr, task_pool: TaskPool, handler: F) + -> SocketHandlerDispatch where F: FnMut(Request) -> Response + Send + 'static { - let http_handler = Http1Handler::new(client_addr, protocol, task_pool, handler); - - let inner = match protocol { - Protocol::Http => SocketHandlerDispatchInner::Http(http_handler), - Protocol::Https => SocketHandlerDispatchInner::Https(RustlsHandler::new(http_handler)), - }; + let http_handler = Http1Handler::new(client_addr, Protocol::Http, task_pool, handler); + let inner = SocketHandlerDispatchInner::Http(http_handler); + SocketHandlerDispatch { + inner: inner, + } + } + /// Initialization for HTTPS. + pub fn https(config: RustlsConfig, client_addr: SocketAddr, task_pool: TaskPool, handler: F) + -> SocketHandlerDispatch + where F: FnMut(Request) -> Response + Send + 'static + { + let http_handler = Http1Handler::new(client_addr, Protocol::Https, task_pool, handler); + let inner = SocketHandlerDispatchInner::Https(RustlsHandler::new(config, http_handler)); SocketHandlerDispatch { inner: inner, } diff --git a/src/socket_handler/rustls.rs b/src/socket_handler/rustls.rs index 121f97644..9c2576c74 100644 --- a/src/socket_handler/rustls.rs +++ b/src/socket_handler/rustls.rs @@ -7,18 +7,99 @@ // notice may not be copied, modified, or distributed except // according to those terms. +use std::collections::HashMap; +use std::fs::File; +use std::io::BufReader; use std::io::Read; use std::io::Write; +use std::path::Path; use std::sync::Arc; +use std::sync::Mutex; +use rustls::ResolvesServerCert; use rustls::ServerConfig; use rustls::ServerSession; use rustls::Session; +use rustls::SignatureScheme; +use rustls::internal::pemfile; +use rustls::sign::CertChainAndSigner; +use rustls::sign::RSASigner; use socket_handler::SocketHandler; use socket_handler::Update; use socket_handler::UpdateResult; +#[derive(Clone)] +pub struct RustlsConfig { + config: Arc, + inner: RustlsConfigInner, +} + +#[derive(Clone)] +struct RustlsConfigInner { + certificates: Arc>>, +} + +impl ResolvesServerCert for RustlsConfigInner { + fn resolve(&self, server_name: Option<&str>, _: &[SignatureScheme]) + -> Option + { + let server_name = match server_name { + Some(s) => s, + None => return None, + }; + + let certificates = self.certificates.lock().unwrap(); + certificates + .get(server_name) + .map(|v| v.clone()) + } +} + +impl RustlsConfig { + pub fn new() -> RustlsConfig { + let inner = RustlsConfigInner { + certificates: Arc::new(Mutex::new(HashMap::new())), + }; + + let mut config = ServerConfig::new(); + config.cert_resolver = Box::new(inner.clone()); + RustlsConfig { + config: Arc::new(config), + inner: inner, + } + } + + pub fn set_certificate_from_pem(&self, domain_name: S, pub_pem: Pu, priv_pem: Pr) + where S: Into, + Pu: AsRef, + Pr: AsRef + { + // TODO: better error handling + let pub_chain = { + let pub_file = File::open(pub_pem).expect("Failed to open public PEM file"); + let mut pub_file = BufReader::new(pub_file); + pemfile::certs(&mut pub_file).expect("Failed to parse public PEM file") + }; + + let priv_key = { + let priv_file = File::open(priv_pem).expect("Failed to open private PEM file"); + let mut priv_file = BufReader::new(priv_file); + // TODO: PKCS8 + let mut keys = pemfile::rsa_private_keys(&mut priv_file).expect("Failed to parse private PEM file"); + if keys.len() != 1 { + panic!("No private key in PEM file, or multiple keys found"); + } + keys.remove(0) + }; + + let signer = RSASigner::new(&priv_key).expect("Failed to create RSASigner"); + + let mut certificates = self.inner.certificates.lock().unwrap(); + certificates.insert(domain_name.into(), (pub_chain, Arc::new(Box::new(signer) as Box<_>))); + } +} + /// Handles the processing of a client connection through TLS. pub struct RustlsHandler { // The inner handler. @@ -29,15 +110,11 @@ pub struct RustlsHandler { handler_update: Update, } -// TODO: not working since we don't provide any certificate or anything - impl RustlsHandler { - pub fn new(inner: H) -> RustlsHandler { - let dummy_config = ServerConfig::new(); - + pub fn new(config: RustlsConfig, inner: H) -> RustlsHandler { RustlsHandler { handler: inner, - session: ServerSession::new(&Arc::new(dummy_config)), + session: ServerSession::new(&config.config), handler_update: Update::empty(), } } From 190adfa69c56dd2c717d72dfa4f62cb4899e2f40 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Wed, 9 Aug 2017 13:28:37 +0200 Subject: [PATCH 41/50] Rustls now working properly --- src/lib.rs | 1 + src/socket_handler/rustls.rs | 105 +++++++++++++++++++++++++++-------- 2 files changed, 84 insertions(+), 22 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 05baac96b..9be574ea1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,6 +80,7 @@ pub use assets::match_assets; pub use log::log; pub use response::{Response, ResponseBody}; pub use server::Server; +pub use server::SslConfig; pub use tiny_http::ReadWrite; use arrayvec::ArrayString; diff --git a/src/socket_handler/rustls.rs b/src/socket_handler/rustls.rs index 9c2576c74..7ebd7a010 100644 --- a/src/socket_handler/rustls.rs +++ b/src/socket_handler/rustls.rs @@ -8,6 +8,7 @@ // according to those terms. use std::collections::HashMap; +use std::error::Error; use std::fs::File; use std::io::BufReader; use std::io::Read; @@ -16,9 +17,12 @@ use std::path::Path; use std::sync::Arc; use std::sync::Mutex; +use rustls::Certificate; +use rustls::PrivateKey; use rustls::ResolvesServerCert; use rustls::ServerConfig; use rustls::ServerSession; +use rustls::ServerSessionMemoryCache; use rustls::Session; use rustls::SignatureScheme; use rustls::internal::pemfile; @@ -29,6 +33,12 @@ use socket_handler::SocketHandler; use socket_handler::Update; use socket_handler::UpdateResult; +/// Configuration for HTTPS handling. +/// +/// This struct internally contains `Arc`s, which means that you can clone it for a cheap cost. +/// +/// Note that this configuration can be updated at runtime. Certificates can be added or removed +/// while the server is running. This will only affect new HTTP connections though. #[derive(Clone)] pub struct RustlsConfig { config: Arc, @@ -57,46 +67,50 @@ impl ResolvesServerCert for RustlsConfigInner { } impl RustlsConfig { + /// Builds a new configuration. You should do this at initialization only. + /// + /// Once the configuration is created, you should add certificates to it. Otherwise people + /// won't be able to connect to it. pub fn new() -> RustlsConfig { let inner = RustlsConfigInner { certificates: Arc::new(Mutex::new(HashMap::new())), }; let mut config = ServerConfig::new(); + //config.alpn_protocols = vec!["http/1.1".to_owned()]; // TODO: config.cert_resolver = Box::new(inner.clone()); + config.session_storage = Mutex::new(ServerSessionMemoryCache::new(1024)); + RustlsConfig { config: Arc::new(config), inner: inner, } } + /// Removes the certificate of a domain name. + pub fn remove_certificate(&self, domain_name: &str) { + let mut certificates = self.inner.certificates.lock().unwrap(); + certificates.remove(domain_name); + } + + /// Sets the certificate of a domain name. The certificates and private key are parsed from + /// PEM files whose path is passed as parameter. + /// + /// Replaces the existing certificate for this domain name if one has been set earlier. pub fn set_certificate_from_pem(&self, domain_name: S, pub_pem: Pu, priv_pem: Pr) + -> Result<(), Box> where S: Into, Pu: AsRef, Pr: AsRef { - // TODO: better error handling - let pub_chain = { - let pub_file = File::open(pub_pem).expect("Failed to open public PEM file"); - let mut pub_file = BufReader::new(pub_file); - pemfile::certs(&mut pub_file).expect("Failed to parse public PEM file") - }; - - let priv_key = { - let priv_file = File::open(priv_pem).expect("Failed to open private PEM file"); - let mut priv_file = BufReader::new(priv_file); - // TODO: PKCS8 - let mut keys = pemfile::rsa_private_keys(&mut priv_file).expect("Failed to parse private PEM file"); - if keys.len() != 1 { - panic!("No private key in PEM file, or multiple keys found"); - } - keys.remove(0) - }; - - let signer = RSASigner::new(&priv_key).expect("Failed to create RSASigner"); + let pub_chain = load_certificates(pub_pem)?; + let priv_key = load_private_key(priv_pem)?; + let signer = RSASigner::new(&priv_key) + .map_err(|_| String::from("Failed to create RSASigner"))?; let mut certificates = self.inner.certificates.lock().unwrap(); certificates.insert(domain_name.into(), (pub_chain, Arc::new(Box::new(signer) as Box<_>))); + Ok(()) } } @@ -111,6 +125,10 @@ pub struct RustlsHandler { } impl RustlsHandler { + /// Starts handling a TLS connection. + /// + /// This struct only performs the encoding and decoding, while the actual handling is performed + /// by `inner`. pub fn new(config: RustlsConfig, inner: H) -> RustlsHandler { RustlsHandler { handler: inner, @@ -124,12 +142,13 @@ impl SocketHandler for RustlsHandler where H: SocketHandler { fn update(&mut self, update: &mut Update) -> UpdateResult { + // Pass outside data to the ServerSession. let read_num = self.session.read_tls(&mut (&update.pending_read_buffer[..])).unwrap(); assert_eq!(read_num, update.pending_read_buffer.len()); update.pending_read_buffer.clear(); if let Err(_) = self.session.process_new_packets() { - // Drop the socket. + // Drop the socket in case of an error. update.pending_write_buffer.clear(); return UpdateResult { registration: None, @@ -138,15 +157,57 @@ impl SocketHandler for RustlsHandler }; } + // Pass data from the ServerSession to the inner handler. self.session.read_to_end(&mut self.handler_update.pending_read_buffer).unwrap(); - let result = self.handler.update(&mut self.handler_update); + // Pass data from the inner handler to the ServerSession. self.session.write_all(&self.handler_update.pending_write_buffer).unwrap(); self.handler_update.pending_write_buffer.clear(); - self.session.write_tls(&mut update.pending_write_buffer).unwrap(); + // Pass data from the ServerSession to the outside. + while self.session.wants_write() { + self.session.write_tls(&mut update.pending_write_buffer).unwrap(); + } result } } + +// Load certificates chain from a PEM file. +fn load_certificates

(path: P) -> Result, Box> + where P: AsRef +{ + let file = File::open(path)?; + let mut reader = BufReader::new(file); + let certs = pemfile::certs(&mut reader) + .map_err(|_| String::from("Certificates PEM file contains invalid keys"))?; + Ok(certs) +} + +// Load private key from a PEM file. +fn load_private_key

(path: P) -> Result> + where P: AsRef +{ + let path = path.as_ref(); + + let mut rsa_keys = { + let file = File::open(path)?; + let mut reader = BufReader::new(file); + pemfile::rsa_private_keys(&mut reader) + .map_err(|_| String::from("Private key PEM file contains invalid keys"))? + }; + + let mut pkcs8_keys = { + let file = File::open(path)?; + let mut reader = BufReader::new(file); + pemfile::pkcs8_private_keys(&mut reader) + .map_err(|_| String::from("Private key PEM file contains invalid keys"))? + }; + + Ok(if !pkcs8_keys.is_empty() { + pkcs8_keys.remove(0) + } else { + rsa_keys.remove(0) + }) +} From 1c032aa4e3c4c7acd80e753a46e6c6213b0d9f47 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Wed, 9 Aug 2017 13:46:00 +0200 Subject: [PATCH 42/50] Better Rustls error handling --- src/socket_handler/rustls.rs | 46 ++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/src/socket_handler/rustls.rs b/src/socket_handler/rustls.rs index 7ebd7a010..5bfdb545d 100644 --- a/src/socket_handler/rustls.rs +++ b/src/socket_handler/rustls.rs @@ -143,9 +143,19 @@ impl SocketHandler for RustlsHandler { fn update(&mut self, update: &mut Update) -> UpdateResult { // Pass outside data to the ServerSession. - let read_num = self.session.read_tls(&mut (&update.pending_read_buffer[..])).unwrap(); - assert_eq!(read_num, update.pending_read_buffer.len()); - update.pending_read_buffer.clear(); + match self.session.read_tls(&mut (&update.pending_read_buffer[..])) { + Ok(read_num) => { + assert_eq!(read_num, update.pending_read_buffer.len()); + update.pending_read_buffer.clear(); + }, + Err(_) => { + return UpdateResult { + registration: None, + close_read: true, + write_flush_suggested: false, + }; + }, + }; if let Err(_) = self.session.process_new_packets() { // Drop the socket in case of an error. @@ -158,16 +168,38 @@ impl SocketHandler for RustlsHandler } // Pass data from the ServerSession to the inner handler. - self.session.read_to_end(&mut self.handler_update.pending_read_buffer).unwrap(); + if let Err(_) = self.session.read_to_end(&mut self.handler_update.pending_read_buffer) { + return UpdateResult { + registration: None, + close_read: true, + write_flush_suggested: false, + }; + } + + // Call the inner handler. let result = self.handler.update(&mut self.handler_update); // Pass data from the inner handler to the ServerSession. - self.session.write_all(&self.handler_update.pending_write_buffer).unwrap(); - self.handler_update.pending_write_buffer.clear(); + match self.session.write_all(&self.handler_update.pending_write_buffer) { + Ok(_) => self.handler_update.pending_write_buffer.clear(), + Err(_) => { + return UpdateResult { + registration: None, + close_read: true, + write_flush_suggested: false, + }; + } + }; // Pass data from the ServerSession to the outside. while self.session.wants_write() { - self.session.write_tls(&mut update.pending_write_buffer).unwrap(); + if let Err(_) = self.session.write_tls(&mut update.pending_write_buffer) { + return UpdateResult { + registration: None, + close_read: true, + write_flush_suggested: true, + }; + } } result From e9059c8499b13fc71b52d4e20f44a9e4282f5cd0 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Wed, 9 Aug 2017 15:49:49 +0200 Subject: [PATCH 43/50] Add support for chunked transfer encoding decoding --- src/socket_handler/http1.rs | 4 +- src/socket_handler/request_body_analyzer.rs | 177 +++++++++++++++++--- 2 files changed, 159 insertions(+), 22 deletions(-) diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index 3e9d90e41..887f40e9e 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -250,8 +250,10 @@ impl SocketHandler for Http1Handler { if analysis.body_data >= 1 { // TODO: more optimal let body_data = update.pending_read_buffer[0 .. analysis.body_data].to_owned(); - update.pending_read_buffer = update.pending_read_buffer[analysis.body_data..].to_owned(); + update.pending_read_buffer = update.pending_read_buffer[analysis.body_data + analysis.unused_trailing..].to_owned(); let _ = input_data.as_mut().unwrap().send(body_data); + } else { + assert_eq!(analysis.unused_trailing, 0); } if analysis.finished { input_data = None; diff --git a/src/socket_handler/request_body_analyzer.rs b/src/socket_handler/request_body_analyzer.rs index d4a9caca9..7f32796f3 100644 --- a/src/socket_handler/request_body_analyzer.rs +++ b/src/socket_handler/request_body_analyzer.rs @@ -9,57 +9,192 @@ use atoi::atoi; use std::ascii::AsciiExt; +use std::cmp; use std::mem; pub struct RequestBodyAnalyzer { - remaining_content_length: u64, + inner: RequestBodyAnalyzerInner, +} + +enum RequestBodyAnalyzerInner { + ContentLength { + // Remaining body length. + remaining_content_length: u64, + }, + ChunkedTransferEncoding { + // Remaining size of the chunk being read. `None` if we are not in a chunk. + remaining_chunk_size: Option, + }, + EndOfStream, } impl RequestBodyAnalyzer { + /// Reads the request's headers to determine how the body will need to be handled. pub fn new<'a, I>(headers: I) -> RequestBodyAnalyzer where I: Iterator // TODO: should be [u8] eventually { let mut content_length = None; + let mut chunked = false; + for (header, value) in headers { + if header.eq_ignore_ascii_case("Transfer-Encoding") { + if value.eq_ignore_ascii_case("chunked") { + chunked = true; + } + } if header.eq_ignore_ascii_case("Content-Length") { content_length = atoi(value.as_bytes()); } } RequestBodyAnalyzer { - remaining_content_length: content_length.unwrap_or(0), + inner: match (content_length, chunked) { + (_, true) => RequestBodyAnalyzerInner::ChunkedTransferEncoding { + remaining_chunk_size: None, + }, + (Some(len), _) => RequestBodyAnalyzerInner::ContentLength { + remaining_content_length: len, + }, + _ => RequestBodyAnalyzerInner::EndOfStream, // TODO: /!\ + }, } } - /// Processes some data. + /// Processes some data. Call this method with a slice containing data received by the socket. + /// This method will "decode" them in place. The decoding always takes less space than the + /// input, so there's no buffering of any sort. pub fn feed(&mut self, data: &mut [u8]) -> FeedOutcome { - // The most common case is a request without any data. - if self.remaining_content_length == 0 { - return FeedOutcome { - body_data: 0, - finished: true, - }; - } + match self.inner { + RequestBodyAnalyzerInner::ContentLength { ref mut remaining_content_length } => { + if (data.len() as u64) < *remaining_content_length { + *remaining_content_length -= data.len() as u64; + FeedOutcome { + body_data: data.len(), + unused_trailing: 0, + finished: *remaining_content_length == 0, + } - if (data.len() as u64) < self.remaining_content_length { - self.remaining_content_length -= data.len() as u64; - return FeedOutcome { - body_data: data.len(), - finished: self.remaining_content_length == 0, - }; - } + } else { + FeedOutcome { + body_data: mem::replace(&mut *remaining_content_length, 0) as usize, + unused_trailing: 0, + finished: true, + } + } + }, - FeedOutcome { - body_data: mem::replace(&mut self.remaining_content_length, 0) as usize, - finished: true, + RequestBodyAnalyzerInner::ChunkedTransferEncoding { ref mut remaining_chunk_size } => { + // `out_body_data` contains the number of bytes from the start of `data` that are + // already final. + // + // `out_unused_trailing` contains the number of bytes after `out_body_data` that + // are garbage. + // + // Therefore at any point during this algorithm, + // `out_body_data + out_unused_trailing` is the offset of the next byte of input. + // + // Incrementing `out_unused_trailing` means that we skip bytes from the input. + let mut out_body_data = 0; + let mut out_unused_trailing = 0; + + loop { + if remaining_chunk_size.is_none() { + match try_read_chunk_size(&data[out_body_data + out_unused_trailing..]) { + Some((skip, chunk_size)) => { + *remaining_chunk_size = Some(chunk_size); + debug_assert_ne!(skip, 0); + out_unused_trailing += skip; + }, + None => return FeedOutcome { + body_data: out_body_data, + unused_trailing: out_unused_trailing, + finished: false, + }, + } + } + + if *remaining_chunk_size == Some(0) { + return FeedOutcome { + body_data: out_body_data, + unused_trailing: out_unused_trailing, + finished: true, + } + } + + let copy_len = cmp::min(data.len() - out_body_data - out_unused_trailing, + remaining_chunk_size.unwrap()); + if out_unused_trailing != 0 { + for n in 0 .. copy_len { + data[out_body_data + n] = data[out_body_data + out_unused_trailing + n]; + } + } + out_body_data += copy_len; + *remaining_chunk_size.as_mut().unwrap() -= copy_len; + if *remaining_chunk_size == Some(0) { + *remaining_chunk_size = None; + } + } + }, + + RequestBodyAnalyzerInner::EndOfStream => { + FeedOutcome { + body_data: data.len(), + unused_trailing: 0, + finished: false, + } + }, } } } +/// Result of the `feed` method. pub struct FeedOutcome { - /// Number of bytes in `data` that contain the body of the request. + /// Number of bytes from the start of `data` that contain the body of the request. If + /// `finished` is true, then any further byte is part of the next request. If `finished` is + /// false, then any further byte is still part of this request but hasn't been decoded yet. pub body_data: usize, + + /// Number of bytes following `body_data` that are irrelevant and that should be discarded. + pub unused_trailing: usize, + /// True if the request is finished. Calling `feed` again would return a `FeedOutcome` with a /// `body_data` of 0. pub finished: bool, } + +// Tries to read a chunk size from `data`. Returns `None` if not enough data. +// Returns the number of bytes that make up the chunk size, and the chunk size value. +fn try_read_chunk_size(data: &[u8]) -> Option<(usize, usize)> { + let crlf_pos = match data.windows(2).position(|n| n == b"\r\n") { + Some(p) => p, + None => return None, + }; + + let chunk_size = match atoi(&data[..crlf_pos]) { + Some(s) => s, + None => return None, // TODO: error instead + }; + + Some((crlf_pos + 2, chunk_size)) +} + +#[cfg(test)] +mod tests { + use super::RequestBodyAnalyzer; + + #[test] + fn chunked_decode() { + let mut analyzer = { + let headers = vec![("Transfer-Encoding", "chunked")]; + RequestBodyAnalyzer::new(headers.into_iter()) + }; + + let mut buffer = b"6\r\nhello 5\r\nworld0\r\n".to_vec(); + let outcome = analyzer.feed(&mut buffer); + + assert_eq!(outcome.body_data, 11); + assert_eq!(outcome.unused_trailing, 20 - 11); + assert!(outcome.finished); + assert_eq!(&buffer[..11], &b"hello world"[..]); + } +} From 42c7794153834eff6d6733f1dd24716b425232e5 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Wed, 9 Aug 2017 15:53:56 +0200 Subject: [PATCH 44/50] Bugfix and test for chunked decoding --- src/socket_handler/request_body_analyzer.rs | 35 ++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/socket_handler/request_body_analyzer.rs b/src/socket_handler/request_body_analyzer.rs index 7f32796f3..c409a59c9 100644 --- a/src/socket_handler/request_body_analyzer.rs +++ b/src/socket_handler/request_body_analyzer.rs @@ -121,6 +121,15 @@ impl RequestBodyAnalyzer { } } + debug_assert!(out_body_data + out_unused_trailing <= data.len()); + if data.len() == out_body_data + out_unused_trailing { + return FeedOutcome { + body_data: out_body_data, + unused_trailing: out_unused_trailing, + finished: false, + }; + } + let copy_len = cmp::min(data.len() - out_body_data - out_unused_trailing, remaining_chunk_size.unwrap()); if out_unused_trailing != 0 { @@ -183,7 +192,7 @@ mod tests { use super::RequestBodyAnalyzer; #[test] - fn chunked_decode() { + fn chunked_decode_one_buf() { let mut analyzer = { let headers = vec![("Transfer-Encoding", "chunked")]; RequestBodyAnalyzer::new(headers.into_iter()) @@ -197,4 +206,28 @@ mod tests { assert!(outcome.finished); assert_eq!(&buffer[..11], &b"hello world"[..]); } + + #[test] + fn chunked_decode_multi_buf() { + let mut analyzer = { + let headers = vec![("Transfer-Encoding", "chunked")]; + RequestBodyAnalyzer::new(headers.into_iter()) + }; + + let mut buf1 = b"6\r\nhel".to_vec(); + let out1 = analyzer.feed(&mut buf1); + + let mut buf2 = b"lo 5\r\nworld0\r\n".to_vec(); + let out2 = analyzer.feed(&mut buf2); + + assert_eq!(out1.body_data, 3); + assert_eq!(out1.unused_trailing, 6 - 3); + assert!(!out1.finished); + assert_eq!(&buf1[..3], &b"hel"[..]); + + assert_eq!(out2.body_data, 8); + assert_eq!(out2.unused_trailing, 14 - 8); + assert!(out2.finished); + assert_eq!(&buf2[..8], &b"lo world"[..]); + } } From 025c7cbab83d44dfccabfce482689518fb704bc5 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Thu, 10 Aug 2017 08:48:10 +0200 Subject: [PATCH 45/50] Use 17 bytes for the method --- src/lib.rs | 4 +++- src/socket_handler/http1.rs | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9be574ea1..9c2f0e1ee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -229,7 +229,9 @@ pub trait Upgrade { /// This can be either a real request (received by the HTTP server) or a mock object created with /// one of the `fake_*` constructors. pub struct Request { - method: ArrayString<[u8; 16]>, + // The method (`GET`, `POST`, ..). The longest registered method know to the author is + // `UPDATEREDIRECTREF` and is 17 bytes long. + method: ArrayString<[u8; 17]>, url: String, headers: Vec<(String, String)>, https: bool, diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index 887f40e9e..d07e8d82d 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -75,7 +75,7 @@ enum Http1HandlerState { // offset was already in `pending_read_buffer` the last time `update` returned. new_data_start: usize, // HTTP method (eg. GET, POST, ...) parsed from the request line. - method: ArrayString<[u8; 16]>, + method: ArrayString<[u8; 17]>, // URL requested by the HTTP client parsed from the request line. path: String, // HTTP version parsed from the request line. @@ -325,7 +325,7 @@ impl SocketHandler for Http1Handler { // Starts the task of handling a request. fn spawn_handler_task(task_pool: &TaskPool, handler: Arc Response + Send + 'static>>, - method: ArrayString<[u8; 16]>, path: String, + method: ArrayString<[u8; 17]>, path: String, headers: Vec<(String, String)>, original_protocol: Protocol, remote_addr: SocketAddr, data_out_tx: Sender>, data_in_rx: Receiver>, set_ready: SetReadiness) From 98f4bac74a445a55b753b303b2b01eff3831f30f Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Thu, 10 Aug 2017 08:55:16 +0200 Subject: [PATCH 46/50] Handle errors in request line --- src/socket_handler/http1.rs | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index d07e8d82d..11eb750ae 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -137,10 +137,38 @@ impl SocketHandler for Http1Handler { .position(|w| w == b"\r\n"); if let Some(rn) = rn { // Found a request line! - let (method, path, version) = { - let (method, path, version) = parse_request_line(&update.pending_read_buffer[..rn]).unwrap(); // TODO: handle error - let method = ArrayString::from(method).unwrap(); // TODO: handle error - (method, path.to_owned(), version) + let method; + let path; + let version; + { + let (method_raw, path_raw, version_raw) = match parse_request_line(&update.pending_read_buffer[..rn]) { + Ok(v) => v, + Err(_) => { + write_status_and_headers(&mut update.pending_write_buffer, 400, &[], Some(0)); + self.state = Http1HandlerState::Closed; + break UpdateResult { + registration: None, + close_read: true, + write_flush_suggested: true, + }; + }, + }; + + method = match ArrayString::from(method_raw) { + Ok(m) => m, + Err(_) => { + write_status_and_headers(&mut update.pending_write_buffer, 501, &[], Some(0)); + self.state = Http1HandlerState::Closed; + break UpdateResult { + registration: None, + close_read: true, + write_flush_suggested: true, + }; + }, + }; + + path = path_raw.to_owned(); + version = version_raw; }; // Remove the request line from the head of the buffer. From e81bfe3e8f16bea56a25861e8b4df5b4052862b6 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Thu, 10 Aug 2017 08:58:50 +0200 Subject: [PATCH 47/50] Handle buffer too large errors --- src/socket_handler/http1.rs | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index 11eb750ae..a320fc5b5 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -43,6 +43,9 @@ pub struct Http1Handler { // The handler is a state machine. state: Http1HandlerState, + // Maximum number of bytes in the buffer while waiting for the request line or headers. + max_buffer_size: usize, + // Address of the client. Necessary for the request objects. client_addr: SocketAddr, @@ -114,6 +117,7 @@ impl Http1Handler { { Http1Handler { state: Http1HandlerState::WaitingForRqLine { new_data_start: 0 }, + max_buffer_size: 10240, client_addr: client_addr, original_protocol: original_protocol, handler: Arc::new(Mutex::new(handler)), @@ -187,7 +191,19 @@ impl SocketHandler for Http1Handler { } else { // No full request line in the buffer yet. - // TODO: put a limit on the buffer size + + // Handle buffer too large. + if update.pending_read_buffer.len() > self.max_buffer_size { + write_status_and_headers(&mut update.pending_write_buffer, 413, + &[], Some(0)); + self.state = Http1HandlerState::Closed; + break UpdateResult { + registration: None, + close_read: true, + write_flush_suggested: true, + }; + } + self.state = Http1HandlerState::WaitingForRqLine { new_data_start: update.pending_read_buffer.len(), }; @@ -253,7 +269,19 @@ impl SocketHandler for Http1Handler { } else { // No full headers in the buffer yet. - // TODO: put a limit on the buffer size + + // Handle buffer too large. + if update.pending_read_buffer.len() > self.max_buffer_size { + write_status_and_headers(&mut update.pending_write_buffer, 413, + &[], Some(0)); + self.state = Http1HandlerState::Closed; + break UpdateResult { + registration: None, + close_read: true, + write_flush_suggested: true, + }; + } + self.state = Http1HandlerState::WaitingForHeaders { new_data_start: update.pending_read_buffer.len(), method, @@ -338,6 +366,7 @@ impl SocketHandler for Http1Handler { }, Http1HandlerState::Closed => { + update.pending_read_buffer.clear(); self.state = Http1HandlerState::Closed; break UpdateResult { registration: None, From a8ad92d987d90750c78ae1af0042bb9b2aee9800 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Thu, 10 Aug 2017 18:03:39 +0200 Subject: [PATCH 48/50] Fix wrong handling of empty requests --- src/socket_handler/request_body_analyzer.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/socket_handler/request_body_analyzer.rs b/src/socket_handler/request_body_analyzer.rs index c409a59c9..7cc387088 100644 --- a/src/socket_handler/request_body_analyzer.rs +++ b/src/socket_handler/request_body_analyzer.rs @@ -55,7 +55,14 @@ impl RequestBodyAnalyzer { (Some(len), _) => RequestBodyAnalyzerInner::ContentLength { remaining_content_length: len, }, - _ => RequestBodyAnalyzerInner::EndOfStream, // TODO: /!\ + _ => { + // If we have neither a Content-Length nor a Transfer-Encoding, + // assuming that we have no data. + // TODO: could also be multipart/byteranges + RequestBodyAnalyzerInner::ContentLength { + remaining_content_length: 0, + } + }, }, } } From ab22b0a2be053aa2903113e8a300006dfce47d98 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Thu, 10 Aug 2017 18:48:04 +0200 Subject: [PATCH 49/50] Add support for chunked encoding --- src/server.rs | 7 ++++- src/socket_handler/http1.rs | 30 ++++++++++++++++++--- src/socket_handler/request_body_analyzer.rs | 1 + 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/server.rs b/src/server.rs index d80f8735e..d40e508fb 100644 --- a/src/server.rs +++ b/src/server.rs @@ -388,7 +388,7 @@ fn handle_read(share: &Arc>, socket: Socket) fn handle_write(share: &ThreadsShare, socket: Socket) { // Write events can't happen for listeners. - let (mut stream, read_closed, write_flush_suggested, handler, mut update) = match socket { + let (mut stream, read_closed, mut write_flush_suggested, handler, mut update) = match socket { Socket::Listener { .. } => unreachable!(), Socket::Stream { stream, read_closed, write_flush_suggested, handler, update } => (stream, read_closed, write_flush_suggested, handler, update), @@ -415,6 +415,11 @@ fn handle_write(share: &ThreadsShare, socket: Socket) { }; }; + if write_flush_suggested { + let _ = stream.flush(); + write_flush_suggested = false; + } + // Re-register the stream for the next event. let mut ready = Ready::empty(); if !read_closed { diff --git a/src/socket_handler/http1.rs b/src/socket_handler/http1.rs index a320fc5b5..f4c458313 100644 --- a/src/socket_handler/http1.rs +++ b/src/socket_handler/http1.rs @@ -14,6 +14,7 @@ use std::io::Cursor; use std::io::Error as IoError; use std::io::ErrorKind; use std::io::Read; +use std::io::Write; use std::mem; use std::net::SocketAddr; use std::sync::Arc; @@ -462,7 +463,7 @@ fn spawn_handler_task(task_pool: &TaskPool, let _ = set_ready.set_readiness(Ready::readable()); loop { - let mut out_data = vec![0; 256]; + let mut out_data = vec![0; 1024]; match body_data.read(&mut out_data) { Ok(0) => break, Ok(n) => out_data.truncate(n), @@ -474,12 +475,29 @@ fn spawn_handler_task(task_pool: &TaskPool, }, }; + // Encoding as chunks if relevant. + // TODO: more optimized + if body_size.is_none() { + let len = out_data.len(); + let data = mem::replace(&mut out_data, Vec::with_capacity(len + 7)); + write!(&mut out_data, "{:x}", len).unwrap(); + out_data.extend_from_slice(b"\r\n"); + out_data.extend(data); + out_data.extend_from_slice(b"\r\n"); + } + match data_out_tx.send(out_data) { Ok(_) => (), Err(_) => return, }; let _ = set_ready.set_readiness(Ready::readable()); } + + if body_size.is_none() { + let _ = data_out_tx.send(b"0\r\n\r\n".to_vec()); + } + + let _ = set_ready.set_readiness(Ready::readable()); }); } @@ -571,9 +589,13 @@ fn write_status_and_headers(mut out: &mut Vec, status_code: u16, out.extend_from_slice(b"Date: TODO\r\n"); // TODO: } - out.extend_from_slice(b"Content-Length: "); - itoa(&mut out, body_size.unwrap()).unwrap(); // TODO: don't unwrap body_size - out.extend_from_slice(b"\r\n"); + if let Some(body_size) = body_size { + out.extend_from_slice(b"Content-Length: "); + itoa(&mut out, body_size).unwrap(); + out.extend_from_slice(b"\r\n"); + } else { + out.extend_from_slice(b"Transfer-Encoding: chunked\r\n"); + } out.extend_from_slice(b"\r\n"); } diff --git a/src/socket_handler/request_body_analyzer.rs b/src/socket_handler/request_body_analyzer.rs index 7cc387088..b739e348d 100644 --- a/src/socket_handler/request_body_analyzer.rs +++ b/src/socket_handler/request_body_analyzer.rs @@ -144,6 +144,7 @@ impl RequestBodyAnalyzer { data[out_body_data + n] = data[out_body_data + out_unused_trailing + n]; } } + // FIXME: wrong because ignores trailing \r\n at end of chunks out_body_data += copy_len; *remaining_chunk_size.as_mut().unwrap() -= copy_len; if *remaining_chunk_size == Some(0) { From 2a705f552bf9a79d5ff86db6500e18724a918ac4 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Thu, 10 Aug 2017 19:15:57 +0200 Subject: [PATCH 50/50] Remove unused EndOfStream --- src/socket_handler/request_body_analyzer.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/socket_handler/request_body_analyzer.rs b/src/socket_handler/request_body_analyzer.rs index b739e348d..6d1f6bbc1 100644 --- a/src/socket_handler/request_body_analyzer.rs +++ b/src/socket_handler/request_body_analyzer.rs @@ -25,7 +25,6 @@ enum RequestBodyAnalyzerInner { // Remaining size of the chunk being read. `None` if we are not in a chunk. remaining_chunk_size: Option, }, - EndOfStream, } impl RequestBodyAnalyzer { @@ -152,14 +151,6 @@ impl RequestBodyAnalyzer { } } }, - - RequestBodyAnalyzerInner::EndOfStream => { - FeedOutcome { - body_data: data.len(), - unused_trailing: 0, - finished: false, - } - }, } } }