From eb8b8cb53dab5a74e75e780a0533201c9fa672a8 Mon Sep 17 00:00:00 2001 From: Luminita Voicu Date: Wed, 7 Jul 2021 13:32:33 +0300 Subject: [PATCH] Add optional limit on request size Signed-off-by: Luminita Voicu --- src/lib.rs | 3 +- src/request.rs | 82 ++++++++++++++++++++++++++++++-------------------- src/router.rs | 6 ++-- src/server.rs | 6 ++-- 4 files changed, 58 insertions(+), 39 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5920c87..d16a3bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,7 +47,8 @@ //! ``` //! use micro_http::{Request, Version}; //! -//! let http_request = Request::try_from(b"GET http://localhost/home HTTP/1.0\r\n\r\n").unwrap(); +//! let request_bytes = b"GET http://localhost/home HTTP/1.0\r\n\r\n"; +//! let http_request = Request::try_from(request_bytes, None).unwrap(); //! assert_eq!(http_request.http_version(), Version::Http10); //! assert_eq!(http_request.uri().get_abs_path(), "/home"); //! ``` diff --git a/src/request.rs b/src/request.rs index 6d26483..419331c 100644 --- a/src/request.rs +++ b/src/request.rs @@ -181,9 +181,18 @@ impl Request { /// ``` /// use micro_http::Request; /// - /// let http_request = Request::try_from(b"GET http://localhost/home HTTP/1.0\r\n\r\n").unwrap(); + /// let max_request_len = 2000; + /// let request_bytes = b"GET http://localhost/home HTTP/1.0\r\n\r\n"; + /// let http_request = Request::try_from(request_bytes, Some(max_request_len)).unwrap(); /// ``` - pub fn try_from(byte_stream: &[u8]) -> Result { + pub fn try_from(byte_stream: &[u8], max_len: Option) -> Result { + // If a size limit is provided, verify the request length does not exceed it. + if let Some(limit) = max_len { + if byte_stream.len() >= limit { + return Err(RequestError::InvalidRequest); + } + } + // The first line of the request is the Request Line. The line ending is CR LF. let request_line_end = match find(byte_stream, &[CR, LF]) { Some(len) => len, @@ -439,7 +448,7 @@ mod tests { }; let request_bytes = b"GET http://localhost/home HTTP/1.0\r\n\ Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT\r\n\r\n"; - let request = Request::try_from(request_bytes).unwrap(); + let request = Request::try_from(request_bytes, None).unwrap(); assert_eq!(request, expected_request); assert_eq!(request.uri(), &Uri::new("http://localhost/home")); assert_eq!(request.http_version(), Version::Http10); @@ -448,14 +457,14 @@ mod tests { // Test for invalid Request (missing CR LF). let request_bytes = b"GET / HTTP/1.1"; assert_eq!( - Request::try_from(request_bytes).unwrap_err(), + Request::try_from(request_bytes, None).unwrap_err(), RequestError::InvalidRequest ); // Test for invalid Request (length is less than minimum). let request_bytes = b"GET"; assert_eq!( - Request::try_from(request_bytes).unwrap_err(), + Request::try_from(request_bytes, None).unwrap_err(), RequestError::InvalidRequest ); @@ -464,18 +473,29 @@ mod tests { Content-Length: 13\r\n\ Content-Type: application/json\r\n\r\nwhatever body"; assert_eq!( - Request::try_from(request_bytes).unwrap_err(), + Request::try_from(request_bytes, None).unwrap_err(), RequestError::InvalidRequest ); + // Test for request larger than maximum len provided. + let request_bytes = b"GET http://localhost/home HTTP/1.0\r\n\ + Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT\r\n\r\n"; + assert_eq!( + Request::try_from(request_bytes, Some(20)).unwrap_err(), + RequestError::InvalidRequest + ); + + // Test request smaller than maximum len provided is ok. + let request_bytes = b"GET http://localhost/home HTTP/1.0\r\n\ + Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT\r\n\r\n"; + assert!(Request::try_from(request_bytes, Some(500)).is_ok()); + // Test for a request with the headers we are looking for. - let request = Request::try_from( - b"PATCH http://localhost/home HTTP/1.1\r\n\ - Expect: 100-continue\r\n\ - Transfer-Encoding: chunked\r\n\ - Content-Length: 26\r\n\r\nthis is not\n\r\na json \nbody", - ) - .unwrap(); + let request_bytes = b"PATCH http://localhost/home HTTP/1.1\r\n\ + Expect: 100-continue\r\n\ + Transfer-Encoding: chunked\r\n\ + Content-Length: 26\r\n\r\nthis is not\n\r\na json \nbody"; + let request = Request::try_from(request_bytes, None).unwrap(); assert_eq!(request.uri(), &Uri::new("http://localhost/home")); assert_eq!(request.http_version(), Version::Http11); assert_eq!(request.method(), Method::Patch); @@ -490,31 +510,26 @@ mod tests { ); // Test for an invalid request format. - Request::try_from(b"PATCH http://localhost/home HTTP/1.1\r\n").unwrap_err(); + Request::try_from(b"PATCH http://localhost/home HTTP/1.1\r\n", None).unwrap_err(); // Test for an invalid encoding. - assert!(Request::try_from( - b"PATCH http://localhost/home HTTP/1.1\r\n\ - Expect: 100-continue\r\n\ - Transfer-Encoding: identity; q=0\r\n\ - Content-Length: 26\r\n\r\nthis is not\n\r\na json \nbody", - ) - .is_ok()); + let request_bytes = b"PATCH http://localhost/home HTTP/1.1\r\n\ + Expect: 100-continue\r\n\ + Transfer-Encoding: identity; q=0\r\n\ + Content-Length: 26\r\n\r\nthis is not\n\r\na json \nbody"; + + assert!(Request::try_from(request_bytes, None).is_ok()); // Test for an invalid content length. - let request = Request::try_from( - b"PATCH http://localhost/home HTTP/1.1\r\n\ - Content-Length: 5000\r\n\r\nthis is a short body", - ) - .unwrap_err(); + let request_bytes = b"PATCH http://localhost/home HTTP/1.1\r\n\ + Content-Length: 5000\r\n\r\nthis is a short body"; + let request = Request::try_from(request_bytes, None).unwrap_err(); assert_eq!(request, RequestError::InvalidRequest); // Test for a request without a body and an optional header. - let request = Request::try_from( - b"GET http://localhost/ HTTP/1.0\r\n\ - Accept-Encoding: gzip\r\n\r\n", - ) - .unwrap(); + let request_bytes = b"GET http://localhost/ HTTP/1.0\r\n\ + Accept-Encoding: gzip\r\n\r\n"; + let request = Request::try_from(request_bytes, None).unwrap(); assert_eq!(request.uri(), &Uri::new("http://localhost/")); assert_eq!(request.http_version(), Version::Http10); assert_eq!(request.method(), Method::Get); @@ -523,8 +538,9 @@ mod tests { assert_eq!(request.headers.content_length(), 0); assert!(request.body.is_none()); - let request = Request::try_from(b"GET http://localhost/ HTTP/1.0\r\n\ - Accept-Encoding: identity;q=0\r\n\r\n"); + let request_bytes = b"GET http://localhost/ HTTP/1.0\r\n\ + Accept-Encoding: identity;q=0\r\n\r\n"; + let request = Request::try_from(request_bytes, None); assert_eq!( request.unwrap_err(), RequestError::HeaderError(HttpHeaderError::InvalidValue( diff --git a/src/router.rs b/src/router.rs index 05e03bb..f314e6f 100644 --- a/src/router.rs +++ b/src/router.rs @@ -79,8 +79,8 @@ impl HttpRoutes { /// let handler = MockHandler {}; /// router.add_route(Method::Get, "/func1".to_string(), Box::new(handler)).unwrap(); /// - /// let request = - /// Request::try_from(b"GET http://localhost/api/v1/func1 HTTP/1.1\r\n\r\n").unwrap(); + /// let request_bytes = b"GET http://localhost/api/v1/func1 HTTP/1.1\r\n\r\n"; + /// let request = Request::try_from(request_bytes, None).unwrap(); /// let arg = HandlerArg(true); /// let reply = router.handle_http_request(&request, &arg); /// assert_eq!(reply.status(), StatusCode::OK); @@ -148,7 +148,7 @@ mod tests { .unwrap(); let request = - Request::try_from(b"GET http://localhost/api/v1/func2 HTTP/1.1\r\n\r\n").unwrap(); + Request::try_from(b"GET http://localhost/api/v1/func2 HTTP/1.1\r\n\r\n", None).unwrap(); let arg = HandlerArg(true); let reply = router.handle_http_request(&request, &arg); assert_eq!(reply.status(), StatusCode::NotFound); diff --git a/src/server.rs b/src/server.rs index 1981c44..881d939 100644 --- a/src/server.rs +++ b/src/server.rs @@ -765,7 +765,8 @@ mod tests { second_server_request.request, Request::try_from( b"GET /machine-config HTTP/1.1\r\n\ - Content-Type: application/json\r\n\r\n" + Content-Type: application/json\r\n\r\n", + None ) .unwrap() ); @@ -989,7 +990,8 @@ mod tests { second_server_request.request, Request::try_from( b"GET /machine-config HTTP/1.1\r\n\ - Content-Type: application/json\r\n\r\n" + Content-Type: application/json\r\n\r\n", + None ) .unwrap() );