micro-http: added MethodNotAllow status code
When server returns `Method Not Allow` responses, it can attach to the response a HTTP `Allow` header. Added support for HTTP response `Allow` header as well. Signed-off-by: Iulian Barbu <iul@amazon.com> Signed-off-by: YUAN LYU <lyuyuan92@gmail.com>
This commit is contained in:
committed by
Adrian Catangiu
parent
530b36bfd9
commit
d37f9671af
+1
-1
@@ -142,7 +142,7 @@ impl Body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Supported HTTP Methods.
|
/// Supported HTTP Methods.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum Method {
|
pub enum Method {
|
||||||
/// GET Method.
|
/// GET Method.
|
||||||
Get,
|
Get,
|
||||||
|
|||||||
+68
-3
@@ -3,9 +3,10 @@
|
|||||||
|
|
||||||
use std::io::{Error as WriteError, Write};
|
use std::io::{Error as WriteError, Write};
|
||||||
|
|
||||||
use crate::common::ascii::{COLON, CR, LF, SP};
|
use ascii::{COLON, CR, LF, SP};
|
||||||
use crate::common::headers::{Header, MediaType};
|
use common::{Body, Version};
|
||||||
use crate::common::{Body, Version};
|
use headers::{Header, MediaType};
|
||||||
|
use Method;
|
||||||
|
|
||||||
/// Wrapper over a response status code.
|
/// Wrapper over a response status code.
|
||||||
///
|
///
|
||||||
@@ -23,6 +24,8 @@ pub enum StatusCode {
|
|||||||
BadRequest,
|
BadRequest,
|
||||||
/// 404, Not Found
|
/// 404, Not Found
|
||||||
NotFound,
|
NotFound,
|
||||||
|
/// 405, Method Not Allowed
|
||||||
|
MethodNotAllowed,
|
||||||
/// 500, Internal Server Error
|
/// 500, Internal Server Error
|
||||||
InternalServerError,
|
InternalServerError,
|
||||||
/// 501, Not Implemented
|
/// 501, Not Implemented
|
||||||
@@ -40,6 +43,7 @@ impl StatusCode {
|
|||||||
Self::NoContent => b"204",
|
Self::NoContent => b"204",
|
||||||
Self::BadRequest => b"400",
|
Self::BadRequest => b"400",
|
||||||
Self::NotFound => b"404",
|
Self::NotFound => b"404",
|
||||||
|
Self::MethodNotAllowed => b"405",
|
||||||
Self::InternalServerError => b"500",
|
Self::InternalServerError => b"500",
|
||||||
Self::NotImplemented => b"501",
|
Self::NotImplemented => b"501",
|
||||||
Self::ServiceUnavailable => b"503",
|
Self::ServiceUnavailable => b"503",
|
||||||
@@ -77,6 +81,7 @@ pub struct ResponseHeaders {
|
|||||||
content_length: i32,
|
content_length: i32,
|
||||||
content_type: MediaType,
|
content_type: MediaType,
|
||||||
server: String,
|
server: String,
|
||||||
|
allow: Vec<Method>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ResponseHeaders {
|
impl Default for ResponseHeaders {
|
||||||
@@ -85,11 +90,31 @@ impl Default for ResponseHeaders {
|
|||||||
content_length: Default::default(),
|
content_length: Default::default(),
|
||||||
content_type: Default::default(),
|
content_type: Default::default(),
|
||||||
server: String::from("Firecracker API"),
|
server: String::from("Firecracker API"),
|
||||||
|
allow: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseHeaders {
|
impl ResponseHeaders {
|
||||||
|
// The logic pertaining to `Allow` header writing.
|
||||||
|
fn write_allow_header<T: Write>(&self, buf: &mut T) -> Result<(), WriteError> {
|
||||||
|
if self.allow.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.write_all(b"Allow: ")?;
|
||||||
|
|
||||||
|
let delimitator = b", ";
|
||||||
|
for (idx, method) in self.allow.iter().enumerate() {
|
||||||
|
buf.write_all(method.raw())?;
|
||||||
|
if idx < self.allow.len() - 1 {
|
||||||
|
buf.write_all(delimitator)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.write_all(&[CR, LF])
|
||||||
|
}
|
||||||
|
|
||||||
/// Writes the headers to `buf` using the HTTP specification.
|
/// Writes the headers to `buf` using the HTTP specification.
|
||||||
pub fn write_all<T: Write>(&self, buf: &mut T) -> Result<(), WriteError> {
|
pub fn write_all<T: Write>(&self, buf: &mut T) -> Result<(), WriteError> {
|
||||||
buf.write_all(Header::Server.raw())?;
|
buf.write_all(Header::Server.raw())?;
|
||||||
@@ -100,6 +125,8 @@ impl ResponseHeaders {
|
|||||||
buf.write_all(b"Connection: keep-alive")?;
|
buf.write_all(b"Connection: keep-alive")?;
|
||||||
buf.write_all(&[CR, LF])?;
|
buf.write_all(&[CR, LF])?;
|
||||||
|
|
||||||
|
self.write_allow_header(buf)?;
|
||||||
|
|
||||||
if self.content_length != 0 {
|
if self.content_length != 0 {
|
||||||
buf.write_all(Header::ContentType.raw())?;
|
buf.write_all(Header::ContentType.raw())?;
|
||||||
buf.write_all(&[COLON, SP])?;
|
buf.write_all(&[COLON, SP])?;
|
||||||
@@ -172,6 +199,16 @@ impl Response {
|
|||||||
self.headers.set_server(server);
|
self.headers.set_server(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the HTTP allowed methods.
|
||||||
|
pub fn set_allow(&mut self, methods: Vec<Method>) {
|
||||||
|
self.headers.allow = methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allows a specific HTTP method.
|
||||||
|
pub fn allow_method(&mut self, method: Method) {
|
||||||
|
self.headers.allow.push(method);
|
||||||
|
}
|
||||||
|
|
||||||
fn write_body<T: Write>(&self, mut buf: T) -> Result<(), WriteError> {
|
fn write_body<T: Write>(&self, mut buf: T) -> Result<(), WriteError> {
|
||||||
if let Some(ref body) = self.body {
|
if let Some(ref body) = self.body {
|
||||||
buf.write_all(body.raw())?;
|
buf.write_all(body.raw())?;
|
||||||
@@ -216,6 +253,11 @@ impl Response {
|
|||||||
pub fn http_version(&self) -> Version {
|
pub fn http_version(&self) -> Version {
|
||||||
self.status_line.http_version
|
self.status_line.http_version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the allowed HTTP methods.
|
||||||
|
pub fn allow(&self) -> Vec<Method> {
|
||||||
|
self.headers.allow.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -246,6 +288,20 @@ mod tests {
|
|||||||
assert!(response.write_all(&mut response_buf.as_mut()).is_ok());
|
assert!(response.write_all(&mut response_buf.as_mut()).is_ok());
|
||||||
assert!(response_buf.as_ref() == expected_response);
|
assert!(response_buf.as_ref() == expected_response);
|
||||||
|
|
||||||
|
// Test response `Allow` header.
|
||||||
|
let mut response = Response::new(Version::Http10, StatusCode::OK);
|
||||||
|
let allowed_methods = vec![Method::Get, Method::Patch, Method::Put];
|
||||||
|
response.set_allow(allowed_methods.clone());
|
||||||
|
assert_eq!(response.allow(), allowed_methods);
|
||||||
|
|
||||||
|
let expected_response: &'static [u8] = b"HTTP/1.0 200 \r\n\
|
||||||
|
Server: Firecracker API\r\n\
|
||||||
|
Connection: keep-alive\r\n\
|
||||||
|
Allow: GET, PATCH, PUT\r\n\r\n";
|
||||||
|
let mut response_buf: [u8; 90] = [0; 90];
|
||||||
|
assert!(response.write_all(&mut response_buf.as_mut()).is_ok());
|
||||||
|
assert_eq!(response_buf.as_ref(), expected_response);
|
||||||
|
|
||||||
// Test write failed.
|
// Test write failed.
|
||||||
let mut response_buf: [u8; 1] = [0; 1];
|
let mut response_buf: [u8; 1] = [0; 1];
|
||||||
assert!(response.write_all(&mut response_buf.as_mut()).is_err());
|
assert!(response.write_all(&mut response_buf.as_mut()).is_err());
|
||||||
@@ -288,8 +344,17 @@ mod tests {
|
|||||||
assert_eq!(StatusCode::NoContent.raw(), b"204");
|
assert_eq!(StatusCode::NoContent.raw(), b"204");
|
||||||
assert_eq!(StatusCode::BadRequest.raw(), b"400");
|
assert_eq!(StatusCode::BadRequest.raw(), b"400");
|
||||||
assert_eq!(StatusCode::NotFound.raw(), b"404");
|
assert_eq!(StatusCode::NotFound.raw(), b"404");
|
||||||
|
assert_eq!(StatusCode::MethodNotAllowed.raw(), b"405");
|
||||||
assert_eq!(StatusCode::InternalServerError.raw(), b"500");
|
assert_eq!(StatusCode::InternalServerError.raw(), b"500");
|
||||||
assert_eq!(StatusCode::NotImplemented.raw(), b"501");
|
assert_eq!(StatusCode::NotImplemented.raw(), b"501");
|
||||||
assert_eq!(StatusCode::ServiceUnavailable.raw(), b"503");
|
assert_eq!(StatusCode::ServiceUnavailable.raw(), b"503");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_allow_method() {
|
||||||
|
let mut response = Response::new(Version::Http10, StatusCode::MethodNotAllowed);
|
||||||
|
response.allow_method(Method::Get);
|
||||||
|
response.allow_method(Method::Put);
|
||||||
|
assert_eq!(response.allow(), vec![Method::Get, Method::Put]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user