Skip to content

Commit 9b605a8

Browse files
bryteisegeorgepisaltu
authored andcommitted
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 <[email protected]>
1 parent 49240ce commit 9b605a8

File tree

2 files changed

+67
-3
lines changed

2 files changed

+67
-3
lines changed

coverage_config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"coverage_score": 93.1, "exclude_path": "", "crate_features": ""}
1+
{"coverage_score": 93.0, "exclude_path": "", "crate_features": ""}

src/server.rs

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
use std::io::{Read, Write};
5-
use std::os::unix::io::AsRawFd;
6-
use std::os::unix::io::RawFd;
5+
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
76
use std::os::unix::net::{UnixListener, UnixStream};
87
use std::path::Path;
98

@@ -278,6 +277,26 @@ impl HttpServer {
278277
})
279278
}
280279

280+
/// Constructor for `HttpServer`.
281+
///
282+
/// Note that this function requires the socket_fd to be solely owned
283+
/// and not be associated with another File in the caller as it uses
284+
/// the unsafe `UnixListener::from_raw_fd method`.
285+
///
286+
/// Returns the newly formed `HttpServer`.
287+
///
288+
/// # Errors
289+
/// Returns an `IOError` when `epoll::create` fails.
290+
pub fn new_from_fd(socket_fd: RawFd) -> Result<Self> {
291+
let socket = unsafe { UnixListener::from_raw_fd(socket_fd) };
292+
let epoll = epoll::Epoll::new().map_err(ServerError::IOError)?;
293+
Ok(HttpServer {
294+
socket,
295+
epoll,
296+
connections: HashMap::new(),
297+
})
298+
}
299+
281300
/// Starts the HTTP Server.
282301
pub fn start_server(&mut self) -> Result<()> {
283302
// Add the socket on which we listen for new connections to the
@@ -656,6 +675,51 @@ mod tests {
656675
assert!(socket.read(&mut buf[..]).unwrap() > 0);
657676
}
658677

678+
#[test]
679+
fn test_wait_one_fd_connection() {
680+
use std::os::unix::io::IntoRawFd;
681+
let path_to_socket = get_temp_socket_file();
682+
683+
let socket_listener = UnixListener::bind(path_to_socket.as_path()).unwrap();
684+
let socket_fd = socket_listener.into_raw_fd();
685+
686+
let mut server = HttpServer::new_from_fd(socket_fd).unwrap();
687+
server.start_server().unwrap();
688+
689+
// Test one incoming connection.
690+
let mut socket = UnixStream::connect(path_to_socket.as_path()).unwrap();
691+
assert!(server.requests().unwrap().is_empty());
692+
693+
socket
694+
.write_all(
695+
b"PATCH /machine-config HTTP/1.1\r\n\
696+
Content-Length: 13\r\n\
697+
Content-Type: application/json\r\n\r\nwhatever body",
698+
)
699+
.unwrap();
700+
701+
let mut req_vec = server.requests().unwrap();
702+
let server_request = req_vec.remove(0);
703+
704+
server
705+
.respond(server_request.process(|request| {
706+
assert_eq!(
707+
std::str::from_utf8(&request.body.as_ref().unwrap().body).unwrap(),
708+
"whatever body"
709+
);
710+
let mut response = Response::new(Version::Http11, StatusCode::OK);
711+
let response_body = b"response body";
712+
response.set_body(Body::new(response_body.to_vec()));
713+
response
714+
}))
715+
.unwrap();
716+
assert!(server.requests().unwrap().is_empty());
717+
718+
let mut buf: [u8; 1024] = [0; 1024];
719+
assert!(socket.read(&mut buf[..]).unwrap() > 0);
720+
assert!(String::from_utf8_lossy(&buf).contains("response body"));
721+
}
722+
659723
#[test]
660724
fn test_wait_concurrent_connections() {
661725
let path_to_socket = get_temp_socket_file();

0 commit comments

Comments
 (0)