Completely remove epoll crate dependency
Move epoll.rs from polly to utils crate and use this file, in micro_http, as an epoll wrapper instead of the epoll crate. Signed-off-by: Laura Loghin <lauralg@amazon.com> Signed-off-by: YUAN LYU <lyuyuan92@gmail.com>
This commit is contained in:
committed by
Adrian Catangiu
parent
7289d64e24
commit
548d297be7
14
Cargo.lock
generated
14
Cargo.lock
generated
@@ -4,29 +4,27 @@
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "epoll"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "990bcfe26bea89669ede68c3f970f61d02568dbc8660317c98d805ea4e710685"
|
||||
dependencies = [
|
||||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
|
||||
|
||||
[[package]]
|
||||
name = "micro_http"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"epoll 4.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"epoll",
|
||||
]
|
||||
|
||||
[metadata]
|
||||
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
"checksum epoll 4.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "990bcfe26bea89669ede68c3f970f61d02568dbc8660317c98d805ea4e710685"
|
||||
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
|
||||
|
||||
@@ -109,6 +109,8 @@
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
extern crate utils;
|
||||
|
||||
mod common;
|
||||
mod connection;
|
||||
mod request;
|
||||
|
||||
112
src/server.rs
112
src/server.rs
@@ -1,8 +1,6 @@
|
||||
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
extern crate epoll;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io::{Read, Write};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
@@ -18,6 +16,8 @@ use crate::response::{Response, StatusCode};
|
||||
|
||||
pub use crate::common::ServerError;
|
||||
|
||||
use utils::epoll;
|
||||
|
||||
static SERVER_FULL_ERROR_MESSAGE: &[u8] = b"HTTP/1.1 503\r\n\
|
||||
Server: Firecracker API\r\n\
|
||||
Connection: close\r\n\
|
||||
@@ -205,11 +205,11 @@ impl<T: Read + Write> ClientConnection<T> {
|
||||
/// requests and sends responses for awaiting requests is `requests`.
|
||||
/// It can be called in a loop, which will render the thread that the
|
||||
/// server runs on incapable of performing other operations, or it can
|
||||
/// be used in another `EPOLL` structure, as it provides its `epoll_fd`,
|
||||
/// the file descriptor of the epoll structure used within the server,
|
||||
/// and it can be added to another one using the `EPOLLIN` flag. Whenever
|
||||
/// there is a notification on that fd, `requests` should be
|
||||
/// called once.
|
||||
/// be used in another `EPOLL` structure, as it provides its `epoll`,
|
||||
/// which is a wrapper over the file descriptor of the epoll structure
|
||||
/// used within the server, and it can be added to another one using
|
||||
/// the `EPOLLIN` flag. Whenever there is a notification on that fd,
|
||||
/// `requests` should be called once.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@@ -244,8 +244,8 @@ impl<T: Read + Write> ClientConnection<T> {
|
||||
pub struct HttpServer {
|
||||
/// Socket on which we listen for new connections.
|
||||
socket: UnixListener,
|
||||
/// File descriptor of the server's epoll structure.
|
||||
epoll_fd: RawFd,
|
||||
/// Server's epoll instance.
|
||||
epoll: epoll::Epoll,
|
||||
/// Holds the token-connection pairs of the server.
|
||||
/// Each connection has an associated identification token, which is
|
||||
/// the file descriptor of the underlying stream.
|
||||
@@ -263,10 +263,10 @@ impl HttpServer {
|
||||
/// Returns an `IOError` when binding or `epoll::create` fails.
|
||||
pub fn new<P: AsRef<Path>>(path_to_socket: P) -> Result<Self> {
|
||||
let socket = UnixListener::bind(path_to_socket).map_err(ServerError::IOError)?;
|
||||
let epoll_fd = epoll::create(true).map_err(ServerError::IOError)?;
|
||||
let epoll = epoll::Epoll::new().map_err(ServerError::IOError)?;
|
||||
Ok(Self {
|
||||
socket,
|
||||
epoll_fd,
|
||||
epoll,
|
||||
connections: HashMap::new(),
|
||||
})
|
||||
}
|
||||
@@ -275,7 +275,7 @@ impl HttpServer {
|
||||
pub fn start_server(&mut self) -> Result<()> {
|
||||
// Add the socket on which we listen for new connections to the
|
||||
// `epoll` structure.
|
||||
Self::epoll_add(self.epoll_fd, self.socket.as_raw_fd())
|
||||
Self::epoll_add(&self.epoll, self.socket.as_raw_fd())
|
||||
}
|
||||
|
||||
/// This function is responsible for the data exchange with the clients and should
|
||||
@@ -297,17 +297,15 @@ impl HttpServer {
|
||||
/// on a connection on which it is not possible.
|
||||
pub fn requests(&mut self) -> Result<Vec<ServerRequest>> {
|
||||
let mut parsed_requests: Vec<ServerRequest> = vec![];
|
||||
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); MAX_CONNECTIONS];
|
||||
|
||||
let mut events = vec![epoll::EpollEvent::default(); MAX_CONNECTIONS];
|
||||
// This is a wrapper over the syscall `epoll_wait` and it will block the
|
||||
// current thread until at least one event is received.
|
||||
// The received notifications will then populate the `events` array with
|
||||
// `event_count` elements, where 1 <= event_count <= MAX_CONNECTIONS.
|
||||
let event_count = match epoll::wait(self.epoll_fd, -1, &mut events[..]) {
|
||||
Ok(event_count) => event_count,
|
||||
Err(e) if e.kind() == std::io::ErrorKind::Interrupted => 0,
|
||||
Err(e) => return Err(ServerError::IOError(e)),
|
||||
};
|
||||
let event_count = self
|
||||
.epoll
|
||||
.wait(MAX_CONNECTIONS, -1, &mut events[..])
|
||||
.map_err(ServerError::IOError)?;
|
||||
// We use `take()` on the iterator over `events` as, even though only
|
||||
// `events_count` events have been inserted into `events`, the size of
|
||||
// the array is still `MAX_CONNECTIONS`, so we discard empty elements
|
||||
@@ -316,7 +314,7 @@ impl HttpServer {
|
||||
// Check the file descriptor which produced the notification `e`.
|
||||
// It could be that we have a new connection, or one of our open
|
||||
// connections is ready to exchange data with a client.
|
||||
if e.data as RawFd == self.socket.as_raw_fd() {
|
||||
if e.fd() == self.socket.as_raw_fd() {
|
||||
// We have received a notification on the listener socket, which
|
||||
// means we have a new connection to accept.
|
||||
match self.handle_new_connection() {
|
||||
@@ -339,9 +337,9 @@ impl HttpServer {
|
||||
};
|
||||
} else {
|
||||
// We have a notification on one of our open connections.
|
||||
let fd = e.data as RawFd;
|
||||
let fd = e.fd();
|
||||
let client_connection = self.connections.get_mut(&fd).unwrap();
|
||||
if e.events & epoll::Events::EPOLLIN.bits() != 0 {
|
||||
if e.event_set().contains(epoll::EventSet::IN) {
|
||||
// We have bytes to read from this connection.
|
||||
// If our `read` yields `Request` objects, we wrap them with an ID before
|
||||
// handing them to the user.
|
||||
@@ -349,23 +347,23 @@ impl HttpServer {
|
||||
&mut client_connection
|
||||
.read()?
|
||||
.into_iter()
|
||||
.map(|request| ServerRequest::new(request, fd as u64))
|
||||
.map(|request| ServerRequest::new(request, e.data()))
|
||||
.collect(),
|
||||
);
|
||||
// If the connection was incoming before we read and we now have to write
|
||||
// either an error message or an `expect` response, we change its `epoll`
|
||||
// event set to notify us when the stream is ready for writing.
|
||||
if client_connection.state == ClientConnectionState::AwaitingOutgoing {
|
||||
Self::epoll_mod(self.epoll_fd, fd, epoll::Events::EPOLLOUT)?;
|
||||
Self::epoll_mod(&self.epoll, fd, epoll::EventSet::OUT)?;
|
||||
}
|
||||
} else if e.events & epoll::Events::EPOLLOUT.bits() != 0 {
|
||||
} else if e.event_set().contains(epoll::EventSet::OUT) {
|
||||
// We have bytes to write on this connection.
|
||||
client_connection.write()?;
|
||||
// If the connection was outgoing before we tried to write the responses
|
||||
// and we don't have any more responses to write, we change the `epoll`
|
||||
// event set to notify us when we have bytes to read from the stream.
|
||||
if client_connection.state == ClientConnectionState::AwaitingIncoming {
|
||||
Self::epoll_mod(self.epoll_fd, fd, epoll::Events::EPOLLIN)?;
|
||||
Self::epoll_mod(&self.epoll, fd, epoll::EventSet::IN)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -389,18 +387,21 @@ impl HttpServer {
|
||||
/// The file descriptor of the `epoll` structure can enable the server to become
|
||||
/// a non-blocking structure in an application.
|
||||
///
|
||||
/// Returns the file descriptor of the server's internal `epoll` structure.
|
||||
/// Returns a reference to the instance of the server's internal `epoll` structure.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ## Non-blocking server
|
||||
/// ```
|
||||
/// extern crate epoll;
|
||||
/// extern crate utils;
|
||||
///
|
||||
/// use std::os::unix::io::AsRawFd;
|
||||
///
|
||||
/// use micro_http::{HttpServer, Response, StatusCode};
|
||||
/// use utils::epoll;
|
||||
///
|
||||
/// // Create our epoll manager.
|
||||
/// let epoll_fd = epoll::create(true).unwrap();
|
||||
/// let epoll = epoll::Epoll::new().unwrap();
|
||||
///
|
||||
/// let path_to_socket = "/tmp/epoll_example.sock";
|
||||
/// std::fs::remove_file(path_to_socket).unwrap_or_default();
|
||||
@@ -410,11 +411,10 @@ impl HttpServer {
|
||||
/// server.start_server().unwrap();
|
||||
///
|
||||
/// // Add our server to the `epoll` manager.
|
||||
/// epoll::ctl(
|
||||
/// epoll_fd,
|
||||
/// epoll::ControlOptions::EPOLL_CTL_ADD,
|
||||
/// server.epoll_fd(),
|
||||
/// epoll::Event::new(epoll::Events::EPOLLIN, 1234u64),
|
||||
/// epoll.ctl(
|
||||
/// epoll::ControlOperation::Add,
|
||||
/// server.epoll().as_raw_fd(),
|
||||
/// &epoll::EpollEvent::new(epoll::EventSet::IN, 1234u64),
|
||||
/// )
|
||||
/// .unwrap();
|
||||
///
|
||||
@@ -424,9 +424,9 @@ impl HttpServer {
|
||||
/// // Control loop of the application.
|
||||
/// let mut events = Vec::with_capacity(10);
|
||||
/// loop {
|
||||
/// let num_ev = epoll::wait(epoll_fd, -1, events.as_mut_slice());
|
||||
/// let num_ev = epoll.wait(10, -1, events.as_mut_slice());
|
||||
/// for event in events {
|
||||
/// match event.data {
|
||||
/// match event.data() {
|
||||
/// // The server notification.
|
||||
/// 1234 => {
|
||||
/// let request = server.requests();
|
||||
@@ -442,8 +442,8 @@ impl HttpServer {
|
||||
/// break;
|
||||
/// }
|
||||
/// ```
|
||||
pub fn epoll_fd(&self) -> RawFd {
|
||||
self.epoll_fd
|
||||
pub fn epoll(&self) -> &epoll::Epoll {
|
||||
&self.epoll
|
||||
}
|
||||
|
||||
/// Enqueues the provided responses in the outgoing connection.
|
||||
@@ -468,7 +468,7 @@ impl HttpServer {
|
||||
// `epoll` event set to notify us when the stream is ready for writing.
|
||||
if let ClientConnectionState::AwaitingIncoming = client_connection.state {
|
||||
client_connection.state = ClientConnectionState::AwaitingOutgoing;
|
||||
Self::epoll_mod(self.epoll_fd, response.id as RawFd, epoll::Events::EPOLLOUT)?;
|
||||
Self::epoll_mod(&self.epoll, response.id as RawFd, epoll::EventSet::OUT)?;
|
||||
}
|
||||
client_connection.enqueue_response(response.response);
|
||||
}
|
||||
@@ -499,7 +499,7 @@ impl HttpServer {
|
||||
})
|
||||
.and_then(|stream| {
|
||||
// Add the stream to the `epoll` structure and listen for bytes to be read.
|
||||
Self::epoll_add(self.epoll_fd, stream.as_raw_fd())?;
|
||||
Self::epoll_add(&self.epoll, stream.as_raw_fd())?;
|
||||
// Then add it to our open connections.
|
||||
self.connections.insert(
|
||||
stream.as_raw_fd(),
|
||||
@@ -514,29 +514,25 @@ impl HttpServer {
|
||||
///
|
||||
/// # Errors
|
||||
/// `IOError` is returned when an `EPOLL_CTL_MOD` control operation fails.
|
||||
fn epoll_mod(epoll_fd: RawFd, stream_fd: RawFd, evset: epoll::Events) -> Result<()> {
|
||||
let event = epoll::Event::new(evset, stream_fd as u64);
|
||||
epoll::ctl(
|
||||
epoll_fd,
|
||||
epoll::ControlOptions::EPOLL_CTL_MOD,
|
||||
stream_fd,
|
||||
event,
|
||||
)
|
||||
.map_err(ServerError::IOError)
|
||||
fn epoll_mod(epoll: &epoll::Epoll, stream_fd: RawFd, evset: epoll::EventSet) -> Result<()> {
|
||||
let event = epoll::EpollEvent::new(evset, stream_fd as u64);
|
||||
epoll
|
||||
.ctl(epoll::ControlOperation::Modify, stream_fd, &event)
|
||||
.map_err(ServerError::IOError)
|
||||
}
|
||||
|
||||
/// Adds a stream to the `epoll` notification structure with the `EPOLLIN` event set.
|
||||
///
|
||||
/// # Errors
|
||||
/// `IOError` is returned when an `EPOLL_CTL_ADD` control operation fails.
|
||||
fn epoll_add(epoll_fd: RawFd, stream_fd: RawFd) -> Result<()> {
|
||||
epoll::ctl(
|
||||
epoll_fd,
|
||||
epoll::ControlOptions::EPOLL_CTL_ADD,
|
||||
stream_fd,
|
||||
epoll::Event::new(epoll::Events::EPOLLIN, stream_fd as u64),
|
||||
)
|
||||
.map_err(ServerError::IOError)
|
||||
fn epoll_add(epoll: &epoll::Epoll, stream_fd: RawFd) -> Result<()> {
|
||||
epoll
|
||||
.ctl(
|
||||
epoll::ControlOperation::Add,
|
||||
stream_fd,
|
||||
&epoll::EpollEvent::new(epoll::EventSet::IN, stream_fd as u64),
|
||||
)
|
||||
.map_err(ServerError::IOError)
|
||||
}
|
||||
|
||||
/// Removes a stream to the `epoll` notification structure.
|
||||
@@ -558,7 +554,7 @@ mod tests {
|
||||
use std::io::{Read, Write};
|
||||
use std::os::unix::net::UnixStream;
|
||||
|
||||
use crate::common::Body;
|
||||
use common::Body;
|
||||
|
||||
#[test]
|
||||
fn test_wait_one_connection() {
|
||||
|
||||
Reference in New Issue
Block a user