|
2 | 2 | // SPDX-License-Identifier: Apache-2.0
|
3 | 3 |
|
4 | 4 | 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}; |
7 | 6 | use std::os::unix::net::{UnixListener, UnixStream};
|
8 | 7 | use std::path::Path;
|
9 | 8 |
|
@@ -278,6 +277,26 @@ impl HttpServer {
|
278 | 277 | })
|
279 | 278 | }
|
280 | 279 |
|
| 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 | + |
281 | 300 | /// Starts the HTTP Server.
|
282 | 301 | pub fn start_server(&mut self) -> Result<()> {
|
283 | 302 | // Add the socket on which we listen for new connections to the
|
@@ -656,6 +675,51 @@ mod tests {
|
656 | 675 | assert!(socket.read(&mut buf[..]).unwrap() > 0);
|
657 | 676 | }
|
658 | 677 |
|
| 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 | + |
659 | 723 | #[test]
|
660 | 724 | fn test_wait_concurrent_connections() {
|
661 | 725 | let path_to_socket = get_temp_socket_file();
|
|
0 commit comments