From 9b605a8b61df602cda62076d30d9f70307ea336f Mon Sep 17 00:00:00 2001 From: William Douglas Date: Tue, 13 Apr 2021 14:27:31 +0000 Subject: [PATCH] Enable creating a socket from an existing fd Allow the creation of the http server's socket from an existing fd. This is done to enable another process to initiate the creation of a server by preparing the socket so it can immediately be used rather than polling to connect to the path. Note, the coverage file change due to what appears to be a rounding error when calculating coverage. Signed-off-by: William Douglas --- coverage_config.json | 2 +- src/server.rs | 68 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/coverage_config.json b/coverage_config.json index ade818e..f0b9678 100644 --- a/coverage_config.json +++ b/coverage_config.json @@ -1 +1 @@ -{"coverage_score": 93.1, "exclude_path": "", "crate_features": ""} +{"coverage_score": 93.0, "exclude_path": "", "crate_features": ""} diff --git a/src/server.rs b/src/server.rs index ebb7ef1..82fba6d 100644 --- a/src/server.rs +++ b/src/server.rs @@ -2,8 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use std::io::{Read, Write}; -use std::os::unix::io::AsRawFd; -use std::os::unix::io::RawFd; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::os::unix::net::{UnixListener, UnixStream}; use std::path::Path; @@ -278,6 +277,26 @@ impl HttpServer { }) } + /// Constructor for `HttpServer`. + /// + /// Note that this function requires the socket_fd to be solely owned + /// and not be associated with another File in the caller as it uses + /// the unsafe `UnixListener::from_raw_fd method`. + /// + /// Returns the newly formed `HttpServer`. + /// + /// # Errors + /// Returns an `IOError` when `epoll::create` fails. + pub fn new_from_fd(socket_fd: RawFd) -> Result { + let socket = unsafe { UnixListener::from_raw_fd(socket_fd) }; + let epoll = epoll::Epoll::new().map_err(ServerError::IOError)?; + Ok(HttpServer { + socket, + epoll, + connections: HashMap::new(), + }) + } + /// Starts the HTTP Server. pub fn start_server(&mut self) -> Result<()> { // Add the socket on which we listen for new connections to the @@ -656,6 +675,51 @@ mod tests { assert!(socket.read(&mut buf[..]).unwrap() > 0); } + #[test] + fn test_wait_one_fd_connection() { + use std::os::unix::io::IntoRawFd; + let path_to_socket = get_temp_socket_file(); + + let socket_listener = UnixListener::bind(path_to_socket.as_path()).unwrap(); + let socket_fd = socket_listener.into_raw_fd(); + + let mut server = HttpServer::new_from_fd(socket_fd).unwrap(); + server.start_server().unwrap(); + + // Test one incoming connection. + let mut socket = UnixStream::connect(path_to_socket.as_path()).unwrap(); + assert!(server.requests().unwrap().is_empty()); + + socket + .write_all( + b"PATCH /machine-config HTTP/1.1\r\n\ + Content-Length: 13\r\n\ + Content-Type: application/json\r\n\r\nwhatever body", + ) + .unwrap(); + + let mut req_vec = server.requests().unwrap(); + let server_request = req_vec.remove(0); + + server + .respond(server_request.process(|request| { + assert_eq!( + std::str::from_utf8(&request.body.as_ref().unwrap().body).unwrap(), + "whatever body" + ); + let mut response = Response::new(Version::Http11, StatusCode::OK); + let response_body = b"response body"; + response.set_body(Body::new(response_body.to_vec())); + response + })) + .unwrap(); + assert!(server.requests().unwrap().is_empty()); + + let mut buf: [u8; 1024] = [0; 1024]; + assert!(socket.read(&mut buf[..]).unwrap() > 0); + assert!(String::from_utf8_lossy(&buf).contains("response body")); + } + #[test] fn test_wait_concurrent_connections() { let path_to_socket = get_temp_socket_file();