micro_http: some doc and code corrections
Signed-off-by: karthik nedunchezhiyan <karthik1705.n@gmail.com> Signed-off-by: YUAN LYU <lyuyuan92@gmail.com>
This commit is contained in:
committed by
Adrian Catangiu
parent
4ee7142098
commit
569230220f
@@ -21,25 +21,32 @@ pub enum Header {
|
||||
}
|
||||
|
||||
impl Header {
|
||||
/// Returns a byte slice representation of the object.
|
||||
pub fn raw(&self) -> &'static [u8] {
|
||||
match self {
|
||||
Header::ContentLength => b"Content-Length",
|
||||
Header::ContentType => b"Content-Type",
|
||||
Header::Expect => b"Expect",
|
||||
Header::TransferEncoding => b"Transfer-Encoding",
|
||||
Header::Server => b"Server",
|
||||
Self::ContentLength => b"Content-Length",
|
||||
Self::ContentType => b"Content-Type",
|
||||
Self::Expect => b"Expect",
|
||||
Self::TransferEncoding => b"Transfer-Encoding",
|
||||
Self::Server => b"Server",
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a byte slice into a Header structure. Header must be ASCII, so also
|
||||
/// UTF-8 valid.
|
||||
///
|
||||
/// # Errors
|
||||
/// `InvalidRequest` is returned if slice contains invalid utf8 characters.
|
||||
/// `InvalidHeader` is returned if unsupported header found.
|
||||
fn try_from(string: &[u8]) -> Result<Self, RequestError> {
|
||||
if let Ok(mut utf8_string) = String::from_utf8(string.to_vec()) {
|
||||
utf8_string.make_ascii_lowercase();
|
||||
match utf8_string.trim() {
|
||||
"content-length" => Ok(Header::ContentLength),
|
||||
"content-type" => Ok(Header::ContentType),
|
||||
"expect" => Ok(Header::Expect),
|
||||
"transfer-encoding" => Ok(Header::TransferEncoding),
|
||||
"server" => Ok(Header::Server),
|
||||
"content-length" => Ok(Self::ContentLength),
|
||||
"content-type" => Ok(Self::ContentType),
|
||||
"expect" => Ok(Self::Expect),
|
||||
"transfer-encoding" => Ok(Self::TransferEncoding),
|
||||
"server" => Ok(Self::Server),
|
||||
_ => Err(RequestError::InvalidHeader),
|
||||
}
|
||||
} else {
|
||||
@@ -75,16 +82,18 @@ pub struct Headers {
|
||||
chunked: bool,
|
||||
}
|
||||
|
||||
impl Headers {
|
||||
impl Default for Headers {
|
||||
/// By default Requests are created with no headers.
|
||||
pub fn default() -> Headers {
|
||||
Headers {
|
||||
content_length: 0,
|
||||
expect: false,
|
||||
chunked: false,
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
content_length: Default::default(),
|
||||
expect: Default::default(),
|
||||
chunked: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Headers {
|
||||
/// Expects one header line and parses it, updating the header structure or returning an
|
||||
/// error if the header is invalid.
|
||||
///
|
||||
@@ -94,6 +103,17 @@ impl Headers {
|
||||
/// `InvalidHeader` is returned when the parsed header is formatted incorrectly or suggests
|
||||
/// that the client is using HTTP features that we do not support in this implementation,
|
||||
/// which invalidates the request.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// extern crate micro_http;
|
||||
/// use micro_http::Headers;
|
||||
///
|
||||
/// let mut request_header = Headers::default();
|
||||
/// assert!(request_header.parse_header_line(b"Content-Length: 24").is_ok());
|
||||
/// assert!(request_header.parse_header_line(b"Content-Length: 24: 2").is_err());
|
||||
/// ```
|
||||
pub fn parse_header_line(&mut self, header_line: &[u8]) -> Result<(), RequestError> {
|
||||
// Headers must be ASCII, so also UTF-8 valid.
|
||||
match std::str::from_utf8(header_line) {
|
||||
@@ -104,17 +124,13 @@ impl Headers {
|
||||
}
|
||||
if let Ok(head) = Header::try_from(entry[0].as_bytes()) {
|
||||
match head {
|
||||
Header::ContentLength => {
|
||||
let try_numeric: Result<i32, std::num::ParseIntError> =
|
||||
std::str::FromStr::from_str(entry[1].trim());
|
||||
match try_numeric {
|
||||
Ok(content_length) if content_length >= 0 => {
|
||||
self.content_length = content_length;
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(RequestError::InvalidHeader),
|
||||
Header::ContentLength => match entry[1].trim().parse::<i32>() {
|
||||
Ok(content_length) => {
|
||||
self.content_length = content_length;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Err(_) => Err(RequestError::InvalidHeader),
|
||||
},
|
||||
Header::ContentType => {
|
||||
match MediaType::try_from(entry[1].trim().as_bytes()) {
|
||||
Ok(_) => Ok(()),
|
||||
@@ -165,7 +181,7 @@ impl Headers {
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn new(content_length: i32, expect: bool, chunked: bool) -> Self {
|
||||
Headers {
|
||||
Self {
|
||||
content_length,
|
||||
expect,
|
||||
chunked,
|
||||
@@ -196,7 +212,7 @@ impl Headers {
|
||||
pub fn try_from(bytes: &[u8]) -> Result<Headers, RequestError> {
|
||||
// Headers must be ASCII, so also UTF-8 valid.
|
||||
if let Ok(text) = std::str::from_utf8(bytes) {
|
||||
let mut headers = Headers::default();
|
||||
let mut headers = Self::default();
|
||||
|
||||
let header_lines = text.split("\r\n");
|
||||
for header_line in header_lines {
|
||||
@@ -224,30 +240,57 @@ pub enum MediaType {
|
||||
}
|
||||
|
||||
impl Default for MediaType {
|
||||
/// Default value for MediaType is application/json
|
||||
fn default() -> Self {
|
||||
MediaType::ApplicationJson
|
||||
Self::ApplicationJson
|
||||
}
|
||||
}
|
||||
|
||||
impl MediaType {
|
||||
fn try_from(bytes: &[u8]) -> Result<Self, RequestError> {
|
||||
/// Parses a byte slice into a MediaType structure for a HTTP request. MediaType
|
||||
/// must be ASCII, so also UTF-8 valid.
|
||||
///
|
||||
/// # Errors
|
||||
/// The function returns `InvalidRequest` when parsing the byte stream fails or
|
||||
/// unsupported MediaType found.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// extern crate micro_http;
|
||||
/// use micro_http::MediaType;
|
||||
///
|
||||
/// assert!(MediaType::try_from(b"application/json").is_ok());
|
||||
/// assert!(MediaType::try_from(b"application/json2").is_err());
|
||||
/// ```
|
||||
pub fn try_from(bytes: &[u8]) -> Result<Self, RequestError> {
|
||||
if bytes.is_empty() {
|
||||
return Err(RequestError::InvalidRequest);
|
||||
}
|
||||
let utf8_slice =
|
||||
String::from_utf8(bytes.to_vec()).map_err(|_| RequestError::InvalidRequest)?;
|
||||
match utf8_slice.as_str().trim() {
|
||||
"text/plain" => Ok(MediaType::PlainText),
|
||||
"application/json" => Ok(MediaType::ApplicationJson),
|
||||
"text/plain" => Ok(Self::PlainText),
|
||||
"application/json" => Ok(Self::ApplicationJson),
|
||||
_ => Err(RequestError::InvalidRequest),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a static string representation of the object.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// extern crate micro_http;
|
||||
/// use micro_http::MediaType;
|
||||
///
|
||||
/// let media_type = MediaType::ApplicationJson;
|
||||
/// assert_eq!(media_type.as_str(), "application/json");
|
||||
/// ```
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
MediaType::PlainText => "text/plain",
|
||||
MediaType::ApplicationJson => "application/json",
|
||||
Self::PlainText => "text/plain",
|
||||
Self::ApplicationJson => "application/json",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,12 +33,12 @@ pub enum RequestError {
|
||||
impl Display for RequestError {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
|
||||
match self {
|
||||
RequestError::InvalidHttpMethod(inner) => write!(f, "Invalid HTTP Method: {}", inner),
|
||||
RequestError::InvalidUri(inner) => write!(f, "Invalid URI: {}", inner),
|
||||
RequestError::InvalidHttpVersion(inner) => write!(f, "Invalid HTTP Version: {}", inner),
|
||||
RequestError::UnsupportedHeader => write!(f, "Unsupported header."),
|
||||
RequestError::InvalidHeader => write!(f, "Invalid header."),
|
||||
RequestError::InvalidRequest => write!(f, "Invalid request."),
|
||||
Self::InvalidHttpMethod(inner) => write!(f, "Invalid HTTP Method: {}", inner),
|
||||
Self::InvalidUri(inner) => write!(f, "Invalid URI: {}", inner),
|
||||
Self::InvalidHttpVersion(inner) => write!(f, "Invalid HTTP Version: {}", inner),
|
||||
Self::UnsupportedHeader => write!(f, "Unsupported header."),
|
||||
Self::InvalidHeader => write!(f, "Invalid header."),
|
||||
Self::InvalidRequest => write!(f, "Invalid request."),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,10 +59,10 @@ pub enum ConnectionError {
|
||||
impl Display for ConnectionError {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
|
||||
match self {
|
||||
ConnectionError::ParseError(inner) => write!(f, "Parsing error: {}", inner),
|
||||
ConnectionError::StreamError(inner) => write!(f, "Stream error: {}", inner),
|
||||
ConnectionError::ConnectionClosed => write!(f, "Connection closed."),
|
||||
ConnectionError::InvalidWrite => write!(f, "Invalid write attempt."),
|
||||
Self::ParseError(inner) => write!(f, "Parsing error: {}", inner),
|
||||
Self::StreamError(inner) => write!(f, "Stream error: {}", inner),
|
||||
Self::ConnectionClosed => write!(f, "Connection closed."),
|
||||
Self::InvalidWrite => write!(f, "Invalid write attempt."),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,9 +96,9 @@ pub enum ServerError {
|
||||
impl Display for ServerError {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
|
||||
match self {
|
||||
ServerError::IOError(inner) => write!(f, "IO error: {}", inner),
|
||||
ServerError::ConnectionError(inner) => write!(f, "Connection error: {}", inner),
|
||||
ServerError::ServerFull => write!(f, "Server is full."),
|
||||
Self::IOError(inner) => write!(f, "IO error: {}", inner),
|
||||
Self::ConnectionError(inner) => write!(f, "Connection error: {}", inner),
|
||||
Self::ServerFull => write!(f, "Server is full."),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,7 +122,7 @@ pub struct Body {
|
||||
impl Body {
|
||||
/// Creates a new `Body` from a `String` input.
|
||||
pub fn new<T: Into<Vec<u8>>>(body: T) -> Self {
|
||||
Body { body: body.into() }
|
||||
Self { body: body.into() }
|
||||
}
|
||||
|
||||
/// Returns the body as an `u8 slice`.
|
||||
@@ -159,12 +159,12 @@ impl Method {
|
||||
/// an error, but when using the input b"GET", it returns Method::Get.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns `RequestError` if the method specified by `bytes` is unsupported.
|
||||
/// `InvalidHttpMethod` is returned if the specified HTTP method is unsupported.
|
||||
pub fn try_from(bytes: &[u8]) -> Result<Self, RequestError> {
|
||||
match bytes {
|
||||
b"GET" => Ok(Method::Get),
|
||||
b"PUT" => Ok(Method::Put),
|
||||
b"PATCH" => Ok(Method::Patch),
|
||||
b"GET" => Ok(Self::Get),
|
||||
b"PUT" => Ok(Self::Put),
|
||||
b"PATCH" => Ok(Self::Patch),
|
||||
_ => Err(RequestError::InvalidHttpMethod("Unsupported HTTP method.")),
|
||||
}
|
||||
}
|
||||
@@ -172,9 +172,9 @@ impl Method {
|
||||
/// Returns an `u8 slice` corresponding to the Method.
|
||||
pub fn raw(self) -> &'static [u8] {
|
||||
match self {
|
||||
Method::Get => b"GET",
|
||||
Method::Put => b"PUT",
|
||||
Method::Patch => b"PATCH",
|
||||
Self::Get => b"GET",
|
||||
Self::Put => b"PUT",
|
||||
Self::Patch => b"PATCH",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,12 +208,19 @@ pub enum Version {
|
||||
Http11,
|
||||
}
|
||||
|
||||
impl Default for Version {
|
||||
/// Returns the default HTTP version = HTTP/1.1.
|
||||
fn default() -> Self {
|
||||
Self::Http11
|
||||
}
|
||||
}
|
||||
|
||||
impl Version {
|
||||
/// HTTP Version as an `u8 slice`.
|
||||
pub fn raw(self) -> &'static [u8] {
|
||||
match self {
|
||||
Version::Http10 => b"HTTP/1.0",
|
||||
Version::Http11 => b"HTTP/1.1",
|
||||
Self::Http10 => b"HTTP/1.0",
|
||||
Self::Http11 => b"HTTP/1.1",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,21 +230,16 @@ impl Version {
|
||||
/// The version is case sensitive and the accepted input is upper case.
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns a `RequestError` when the version is not supported.
|
||||
/// Returns a `InvalidHttpVersion` when the HTTP version is not supported.
|
||||
pub fn try_from(bytes: &[u8]) -> Result<Self, RequestError> {
|
||||
match bytes {
|
||||
b"HTTP/1.0" => Ok(Version::Http10),
|
||||
b"HTTP/1.1" => Ok(Version::Http11),
|
||||
b"HTTP/1.0" => Ok(Self::Http10),
|
||||
b"HTTP/1.1" => Ok(Self::Http11),
|
||||
_ => Err(RequestError::InvalidHttpVersion(
|
||||
"Unsupported HTTP version.",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the default HTTP version = HTTP/1.1.
|
||||
pub fn default() -> Self {
|
||||
Version::Http11
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -55,7 +55,7 @@ pub struct HttpConnection<T> {
|
||||
impl<T: Read + Write> HttpConnection<T> {
|
||||
/// Creates an empty connection.
|
||||
pub fn new(stream: T) -> Self {
|
||||
HttpConnection {
|
||||
Self {
|
||||
pending_request: None,
|
||||
stream,
|
||||
state: ConnectionState::WaitingForRequestLine,
|
||||
@@ -114,8 +114,12 @@ impl<T: Read + Write> HttpConnection<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// Reads a maximum of `BUFFER_SIZE` bytes from the stream into `buffer`.
|
||||
// The return value represents the end index of what we have just appended.
|
||||
/// Reads a maximum of 1024 bytes from the stream into `buffer`.
|
||||
/// The return value represents the end index of what we have just appended.
|
||||
///
|
||||
/// # Errors
|
||||
/// `StreamError` is returned if any error occurred while reading the stream.
|
||||
/// `ConnectionClosed` is returned if the client closed the connection.
|
||||
fn read_bytes(&mut self) -> Result<usize, ConnectionError> {
|
||||
loop {
|
||||
// Append new bytes to what we already have in the buffer.
|
||||
@@ -129,8 +133,11 @@ impl<T: Read + Write> HttpConnection<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// Parses bytes in `buffer` for a valid request line.
|
||||
// Returns `false` if there are no more bytes to be parsed in the buffer.
|
||||
/// Parses bytes in `buffer` for a valid request line.
|
||||
/// Returns `false` if there are no more bytes to be parsed in the buffer.
|
||||
///
|
||||
/// # Errors
|
||||
/// `ParseError` is returned if unable to parse request line or line longer than BUFFER_SIZE.
|
||||
fn parse_request_line(
|
||||
&mut self,
|
||||
start: &mut usize,
|
||||
@@ -141,13 +148,12 @@ impl<T: Read + Write> HttpConnection<T> {
|
||||
let line = &self.buffer[*start..(*start + line_end_index)];
|
||||
|
||||
*start = *start + line_end_index + CRLF_LEN;
|
||||
let request_line =
|
||||
RequestLine::try_from(line).map_err(ConnectionError::ParseError)?;
|
||||
|
||||
// Form the request with a valid request line, which is the bare minimum
|
||||
// for a valid request.
|
||||
self.pending_request = Some(Request {
|
||||
request_line,
|
||||
request_line: RequestLine::try_from(line)
|
||||
.map_err(ConnectionError::ParseError)?,
|
||||
headers: Headers::default(),
|
||||
body: None,
|
||||
});
|
||||
@@ -170,19 +176,23 @@ impl<T: Read + Write> HttpConnection<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// Parses bytes in `buffer` for header fields.
|
||||
// Returns `false` if there are no more bytes to be parsed in the buffer.
|
||||
/// Parses bytes in `buffer` for header fields.
|
||||
/// Returns `false` if there are no more bytes to be parsed in the buffer.
|
||||
///
|
||||
/// # Errors
|
||||
/// `ParseError` is returned if unable to parse header or line longer than BUFFER_SIZE.
|
||||
fn parse_headers(
|
||||
&mut self,
|
||||
line_start_index: &mut usize,
|
||||
end_cursor: usize,
|
||||
) -> Result<bool, ConnectionError> {
|
||||
match find(&self.buffer[*line_start_index..end_cursor], &[CR, LF]) {
|
||||
// We have found the end of the headers.
|
||||
// `line_start_index` points to the end of the most recently found CR LF
|
||||
// sequence. That means that if we found the next CR LF sequence at this index,
|
||||
// they are, in fact, a CR LF CR LF sequence, which marks the end of the header
|
||||
// fields, per HTTP specification.
|
||||
|
||||
// We have found the end of the header.
|
||||
Some(0) => {
|
||||
// If our current state is `WaitingForHeaders`, it means that we already have
|
||||
// a valid request formed from a request line, so it's safe to unwrap.
|
||||
@@ -243,8 +253,11 @@ impl<T: Read + Write> HttpConnection<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// Parses bytes in `buffer` to be put into the request body, if there should be one.
|
||||
// Returns `false` if there are no more bytes to be parsed in the buffer.
|
||||
/// Parses bytes in `buffer` to be put into the request body, if there should be one.
|
||||
/// Returns `false` if there are no more bytes to be parsed in the buffer.
|
||||
///
|
||||
/// # Errors
|
||||
/// `ParseError` is returned when the body is larger than the specified content-length.
|
||||
fn parse_body(
|
||||
&mut self,
|
||||
line_start_index: &mut usize,
|
||||
@@ -296,7 +309,10 @@ impl<T: Read + Write> HttpConnection<T> {
|
||||
|
||||
/// Tries to write the first available response to the provided stream.
|
||||
/// Meant to be used only with non-blocking streams and an `EPOLL` structure.
|
||||
/// Should be called whenever an `EPOLLOUT` event is signaled.
|
||||
/// Should be called whenever an `EPOLLOUT` event is signaled. If no bytes
|
||||
/// were written to the stream or error occurred while trying to write to stream,
|
||||
/// we will discard all responses from response_queue because there is no way
|
||||
/// to deliver it to client.
|
||||
///
|
||||
/// # Errors
|
||||
/// `StreamError` is returned when an IO operation fails.
|
||||
|
||||
@@ -29,7 +29,7 @@ pub struct Uri {
|
||||
|
||||
impl Uri {
|
||||
fn new(slice: &str) -> Self {
|
||||
Uri {
|
||||
Self {
|
||||
string: String::from(slice),
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ impl Uri {
|
||||
}
|
||||
let utf8_slice =
|
||||
from_utf8(bytes).map_err(|_| RequestError::InvalidUri("Cannot parse URI as UTF-8."))?;
|
||||
Ok(Uri::new(utf8_slice))
|
||||
Ok(Self::new(utf8_slice))
|
||||
}
|
||||
|
||||
/// Returns the absolute path of the `Uri`.
|
||||
@@ -108,10 +108,15 @@ impl RequestLine {
|
||||
}
|
||||
|
||||
/// Tries to parse a byte stream in a request line. Fails if the request line is malformed.
|
||||
///
|
||||
/// # Errors
|
||||
/// `InvalidHttpMethod` is returned if the specified HTTP method is unsupported.
|
||||
/// `InvalidHttpVersion` is returned if the specified HTTP version is unsupported.
|
||||
/// `InvalidUri` is returned if the specified Uri is not valid.
|
||||
pub fn try_from(request_line: &[u8]) -> Result<Self, RequestError> {
|
||||
let (method, uri, version) = RequestLine::parse_request_line(request_line);
|
||||
let (method, uri, version) = Self::parse_request_line(request_line);
|
||||
|
||||
Ok(RequestLine {
|
||||
Ok(Self {
|
||||
method: Method::try_from(method)?,
|
||||
uri: Uri::try_from(uri)?,
|
||||
http_version: Version::try_from(version)?,
|
||||
@@ -127,7 +132,7 @@ impl RequestLine {
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn new(method: Method, uri: &str, http_version: Version) -> Self {
|
||||
RequestLine {
|
||||
Self {
|
||||
method,
|
||||
uri: Uri::new(uri),
|
||||
http_version,
|
||||
@@ -187,7 +192,7 @@ impl Request {
|
||||
match find(&byte_stream[request_line_end..], &[CR, LF, CR, LF]) {
|
||||
// If we have found a CR LF CR LF at the end of the Request Line, the request
|
||||
// is complete.
|
||||
Some(0) => Ok(Request {
|
||||
Some(0) => Ok(Self {
|
||||
request_line,
|
||||
headers: Headers::default(),
|
||||
body: None,
|
||||
@@ -225,7 +230,7 @@ impl Request {
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Request {
|
||||
Ok(Self {
|
||||
request_line,
|
||||
headers,
|
||||
body,
|
||||
@@ -260,7 +265,7 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
impl PartialEq for Request {
|
||||
fn eq(&self, other: &Request) -> bool {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
// Ignore the other fields of Request for now because they are not used.
|
||||
self.request_line == other.request_line
|
||||
&& self.headers.content_length() == other.headers.content_length()
|
||||
|
||||
@@ -35,14 +35,14 @@ impl StatusCode {
|
||||
/// Returns the status code as bytes.
|
||||
pub fn raw(self) -> &'static [u8; 3] {
|
||||
match self {
|
||||
StatusCode::Continue => b"100",
|
||||
StatusCode::OK => b"200",
|
||||
StatusCode::NoContent => b"204",
|
||||
StatusCode::BadRequest => b"400",
|
||||
StatusCode::NotFound => b"404",
|
||||
StatusCode::InternalServerError => b"500",
|
||||
StatusCode::NotImplemented => b"501",
|
||||
StatusCode::ServiceUnavailable => b"503",
|
||||
Self::Continue => b"100",
|
||||
Self::OK => b"200",
|
||||
Self::NoContent => b"204",
|
||||
Self::BadRequest => b"400",
|
||||
Self::NotFound => b"404",
|
||||
Self::InternalServerError => b"500",
|
||||
Self::NotImplemented => b"501",
|
||||
Self::ServiceUnavailable => b"503",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ struct StatusLine {
|
||||
|
||||
impl StatusLine {
|
||||
fn new(http_version: Version, status_code: StatusCode) -> Self {
|
||||
StatusLine {
|
||||
Self {
|
||||
http_version,
|
||||
status_code,
|
||||
}
|
||||
@@ -81,10 +81,10 @@ pub struct ResponseHeaders {
|
||||
|
||||
impl Default for ResponseHeaders {
|
||||
fn default() -> Self {
|
||||
ResponseHeaders {
|
||||
Self {
|
||||
content_length: Default::default(),
|
||||
content_type: Default::default(),
|
||||
server: "Firecracker API".to_string(),
|
||||
server: String::from("Firecracker API"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,7 +134,9 @@ impl ResponseHeaders {
|
||||
/// Wrapper over an HTTP Response.
|
||||
///
|
||||
/// The Response is created using a `Version` and a `StatusCode`. When creating a Response object,
|
||||
/// the body is initialized to `None`. The body can be updated with a call to `set_body`.
|
||||
/// the body is initialized to `None` and the header is initialized with the `default` value. The body
|
||||
/// can be updated with a call to `set_body`. The header can be updated with `set_content_type` and
|
||||
/// `set_server`.
|
||||
pub struct Response {
|
||||
status_line: StatusLine,
|
||||
headers: ResponseHeaders,
|
||||
@@ -143,11 +145,11 @@ pub struct Response {
|
||||
|
||||
impl Response {
|
||||
/// Creates a new HTTP `Response` with an empty body.
|
||||
pub fn new(http_version: Version, status_code: StatusCode) -> Response {
|
||||
Response {
|
||||
pub fn new(http_version: Version, status_code: StatusCode) -> Self {
|
||||
Self {
|
||||
status_line: StatusLine::new(http_version, status_code),
|
||||
headers: ResponseHeaders::default(),
|
||||
body: None,
|
||||
body: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ impl ServerRequest {
|
||||
/// Creates a new `ServerRequest` object from an existing `Request`,
|
||||
/// adding an identification token.
|
||||
pub fn new(request: Request, id: u64) -> Self {
|
||||
ServerRequest { request, id }
|
||||
Self { request, id }
|
||||
}
|
||||
|
||||
/// Returns a reference to the inner request.
|
||||
@@ -68,8 +68,8 @@ pub struct ServerResponse {
|
||||
}
|
||||
|
||||
impl ServerResponse {
|
||||
fn new(response: Response, id: u64) -> ServerResponse {
|
||||
ServerResponse { response, id }
|
||||
fn new(response: Response, id: u64) -> Self {
|
||||
Self { response, id }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ struct ClientConnection<T> {
|
||||
|
||||
impl<T: Read + Write> ClientConnection<T> {
|
||||
fn new(connection: HttpConnection<T>) -> Self {
|
||||
ClientConnection {
|
||||
Self {
|
||||
connection,
|
||||
state: ClientConnectionState::AwaitingIncoming,
|
||||
in_flight_response_count: 0,
|
||||
@@ -201,13 +201,14 @@ impl<T: Read + Write> ClientConnection<T> {
|
||||
/// HTTP Server implementation using Unix Domain Sockets and `EPOLL` to
|
||||
/// handle multiple connections on the same thread.
|
||||
///
|
||||
/// The function that does the data exchange is `handle_notifications`.
|
||||
/// The function that handles incoming connections, parses incoming
|
||||
/// 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, `handle_notifications` should be
|
||||
/// there is a notification on that fd, `requests` should be
|
||||
/// called once.
|
||||
///
|
||||
/// # Example
|
||||
@@ -263,7 +264,7 @@ impl HttpServer {
|
||||
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)?;
|
||||
Ok(HttpServer {
|
||||
Ok(Self {
|
||||
socket,
|
||||
epoll_fd,
|
||||
connections: HashMap::new(),
|
||||
@@ -477,7 +478,8 @@ impl HttpServer {
|
||||
/// Accepts a new incoming connection and adds it to the `epoll` notification structure.
|
||||
///
|
||||
/// # Errors
|
||||
/// `IOError` is returned when an `epoll::ctl` operation fails.
|
||||
/// `IOError` is returned when socket or epoll operations fail.
|
||||
/// `ServerFull` is returned if server full capacity has been reached.
|
||||
fn handle_new_connection(&mut self) -> Result<()> {
|
||||
if self.connections.len() == MAX_CONNECTIONS {
|
||||
// If we want a replacement policy for connections
|
||||
@@ -509,6 +511,9 @@ impl HttpServer {
|
||||
|
||||
/// Changes the event type for a connection to either listen for incoming bytes
|
||||
/// or for when the stream is ready for writing.
|
||||
///
|
||||
/// # 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(
|
||||
@@ -521,6 +526,9 @@ impl HttpServer {
|
||||
}
|
||||
|
||||
/// 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,
|
||||
|
||||
Reference in New Issue
Block a user