Skip to content

Commit 8a8d7bb

Browse files
AlexandruCihodaruacatangiu
authored andcommitted
Made InvalidHeader error more verbose.
Changed the way we handle TransferEncoding and added new test cases. Signed-off-by: cihodar <[email protected]> Signed-off-by: YUAN LYU <[email protected]>
1 parent eeae4ac commit 8a8d7bb

File tree

6 files changed

+217
-45
lines changed

6 files changed

+217
-45
lines changed

src/common/headers.rs

Lines changed: 80 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
use std::result::Result;
55

6+
use crate::HttpHeaderError;
67
use crate::RequestError;
78

89
/// Wrapper over an HTTP Header type.
@@ -51,7 +52,9 @@ impl Header {
5152
"transfer-encoding" => Ok(Self::TransferEncoding),
5253
"server" => Ok(Self::Server),
5354
"accept" => Ok(Self::Accept),
54-
_ => Err(RequestError::InvalidHeader),
55+
invalid_key => Err(RequestError::HeaderError(HttpHeaderError::UnsupportedName(
56+
invalid_key.to_string(),
57+
))),
5558
}
5659
} else {
5760
Err(RequestError::InvalidRequest)
@@ -129,7 +132,9 @@ impl Headers {
129132
Ok(headers_str) => {
130133
let entry = headers_str.splitn(2, ':').collect::<Vec<&str>>();
131134
if entry.len() != 2 {
132-
return Err(RequestError::InvalidHeader);
135+
return Err(RequestError::HeaderError(HttpHeaderError::InvalidFormat(
136+
entry[0].to_string(),
137+
)));
133138
}
134139
if let Ok(head) = Header::try_from(entry[0].as_bytes()) {
135140
match head {
@@ -138,43 +143,75 @@ impl Headers {
138143
self.content_length = content_length;
139144
Ok(())
140145
}
141-
Err(_) => Err(RequestError::InvalidHeader),
146+
Err(_) => {
147+
Err(RequestError::HeaderError(HttpHeaderError::InvalidValue(
148+
entry[0].to_string(),
149+
entry[1].to_string(),
150+
)))
151+
}
142152
},
143153
Header::ContentType => {
144154
match MediaType::try_from(entry[1].trim().as_bytes()) {
145155
Ok(_) => Ok(()),
146-
Err(_) => Err(RequestError::UnsupportedHeader),
156+
Err(_) => Err(RequestError::HeaderError(
157+
HttpHeaderError::UnsupportedValue(
158+
entry[0].to_string(),
159+
entry[1].to_string(),
160+
),
161+
)),
147162
}
148163
}
149164
Header::Accept => match MediaType::try_from(entry[1].trim().as_bytes()) {
150165
Ok(accept_type) => {
151166
self.accept = accept_type;
152167
Ok(())
153168
}
154-
Err(_) => Err(RequestError::UnsupportedHeader),
169+
Err(_) => Err(RequestError::HeaderError(
170+
HttpHeaderError::UnsupportedValue(
171+
entry[0].to_string(),
172+
entry[1].to_string(),
173+
),
174+
)),
155175
},
156176
Header::TransferEncoding => match entry[1].trim() {
157177
"chunked" => {
158178
self.chunked = true;
159179
Ok(())
160180
}
161-
"identity; q=0" => Err(RequestError::InvalidHeader),
162-
_ => Err(RequestError::UnsupportedHeader),
181+
"identity" => Ok(()),
182+
_ => Err(RequestError::HeaderError(
183+
HttpHeaderError::UnsupportedValue(
184+
entry[0].to_string(),
185+
entry[1].to_string(),
186+
),
187+
)),
163188
},
164189
Header::Expect => match entry[1].trim() {
165190
"100-continue" => {
166191
self.expect = true;
167192
Ok(())
168193
}
169-
_ => Err(RequestError::InvalidHeader),
194+
_ => Err(RequestError::HeaderError(
195+
HttpHeaderError::UnsupportedValue(
196+
entry[0].to_string(),
197+
entry[1].to_string(),
198+
),
199+
)),
170200
},
171201
Header::Server => Ok(()),
172202
}
173203
} else {
174-
Err(RequestError::UnsupportedHeader)
204+
Err(RequestError::HeaderError(
205+
HttpHeaderError::UnsupportedValue(
206+
entry[0].to_string(),
207+
entry[1].to_string(),
208+
),
209+
))
175210
}
176211
}
177-
_ => Err(RequestError::InvalidHeader),
212+
Err(utf8_err) => Err(RequestError::HeaderError(
213+
HttpHeaderError::InvalidUtf8String(utf8_err),
214+
)),
178215
}
179216
}
180217

@@ -231,7 +268,10 @@ impl Headers {
231268
break;
232269
}
233270
match headers.parse_header_line(header_line.as_bytes()) {
234-
Ok(_) | Err(RequestError::UnsupportedHeader) => continue,
271+
Ok(_)
272+
| Err(RequestError::HeaderError(HttpHeaderError::UnsupportedValue(_, _))) => {
273+
continue
274+
}
235275
Err(e) => return Err(e),
236276
};
237277
}
@@ -411,42 +451,60 @@ mod tests {
411451
// Invalid header syntax.
412452
assert_eq!(
413453
header.parse_header_line(b"Expect"),
414-
Err(RequestError::InvalidHeader)
454+
Err(RequestError::HeaderError(HttpHeaderError::InvalidFormat(
455+
"Expect".to_string()
456+
)))
415457
);
416458

417459
// Invalid content length.
418460
assert_eq!(
419461
header.parse_header_line(b"Content-Length: five"),
420-
Err(RequestError::InvalidHeader)
462+
Err(RequestError::HeaderError(HttpHeaderError::InvalidValue(
463+
"Content-Length".to_string(),
464+
" five".to_string()
465+
)))
421466
);
422467

423468
// Invalid transfer encoding.
424469
assert_eq!(
425470
header.parse_header_line(b"Transfer-Encoding: gzip"),
426-
Err(RequestError::UnsupportedHeader)
471+
Err(RequestError::HeaderError(
472+
HttpHeaderError::UnsupportedValue(
473+
"Transfer-Encoding".to_string(),
474+
" gzip".to_string()
475+
)
476+
))
427477
);
428478

429479
// Invalid expect.
430480
assert_eq!(
431481
header
432482
.parse_header_line(b"Expect: 102-processing")
433483
.unwrap_err(),
434-
RequestError::InvalidHeader
484+
RequestError::HeaderError(HttpHeaderError::UnsupportedValue(
485+
"Expect".to_string(),
486+
" 102-processing".to_string()
487+
))
435488
);
436489

437490
// Unsupported media type.
438491
assert_eq!(
439492
header
440493
.parse_header_line(b"Content-Type: application/json-patch")
441494
.unwrap_err(),
442-
RequestError::UnsupportedHeader
495+
RequestError::HeaderError(HttpHeaderError::UnsupportedValue(
496+
"Content-Type".to_string(),
497+
" application/json-patch".to_string()
498+
))
443499
);
444500

445501
// Invalid input format.
446502
let input: [u8; 10] = [130, 140, 150, 130, 140, 150, 130, 140, 150, 160];
447503
assert_eq!(
448504
header.parse_header_line(&input[..]).unwrap_err(),
449-
RequestError::InvalidHeader
505+
RequestError::HeaderError(HttpHeaderError::InvalidUtf8String(
506+
String::from_utf8(input.to_vec()).unwrap_err().utf8_error()
507+
))
450508
);
451509

452510
// Test valid transfer encoding.
@@ -480,7 +538,10 @@ mod tests {
480538
// Invalid content length.
481539
assert_eq!(
482540
header.parse_header_line(b"Content-Length: -1"),
483-
Err(RequestError::InvalidHeader)
541+
Err(RequestError::HeaderError(HttpHeaderError::InvalidValue(
542+
"Content-Length".to_string(),
543+
" -1".to_string()
544+
)))
484545
);
485546
}
486547

@@ -530,7 +591,7 @@ mod tests {
530591
// Bad header.
531592
assert_eq!(
532593
Header::try_from(b"Encoding").unwrap_err(),
533-
RequestError::InvalidHeader
594+
RequestError::HeaderError(HttpHeaderError::UnsupportedName("encoding".to_string()))
534595
);
535596

536597
// Invalid encoding.

src/common/mod.rs

Lines changed: 106 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
use std::fmt::{Display, Error, Formatter};
5+
use std::str::Utf8Error;
56

67
pub mod headers;
78

@@ -13,6 +14,55 @@ pub mod ascii {
1314
pub const CRLF_LEN: usize = 2;
1415
}
1516

17+
///Errors associated with a header that is invalid.
18+
#[derive(Debug, PartialEq)]
19+
pub enum HttpHeaderError {
20+
/// The content length specified is longer than the limit imposed by Micro Http.
21+
SizeLimitExceeded(String),
22+
/// The header specified is not supported.
23+
UnsupportedName(String),
24+
/// The value for the specified header is not supported.
25+
UnsupportedValue(String, String),
26+
/// The specified header contains illegal characters.
27+
InvalidUtf8String(Utf8Error),
28+
/// The requested feature is not currently supported.
29+
UnsupportedFeature(String, String),
30+
/// The header is misformatted.
31+
InvalidFormat(String),
32+
///The value specified is not valid.
33+
InvalidValue(String, String),
34+
}
35+
36+
impl Display for HttpHeaderError {
37+
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
38+
match self {
39+
Self::SizeLimitExceeded(inner) => {
40+
write!(f, "Invalid content length. Header: {}", inner)
41+
}
42+
Self::UnsupportedName(inner) => write!(f, "Unsupported header name. Key: {}", inner),
43+
Self::UnsupportedValue(header_key, header_value) => write!(
44+
f,
45+
"Unsupported value. Key:{}; Value:{}",
46+
header_key, header_value
47+
),
48+
Self::InvalidUtf8String(header_key) => {
49+
write!(f, "Header contains invalid characters. Key: {}", header_key)
50+
}
51+
Self::UnsupportedFeature(header_key, header_value) => write!(
52+
f,
53+
"Unsupported feature. Key: {}; Value: {}",
54+
header_key, header_value
55+
),
56+
Self::InvalidFormat(header_key) => {
57+
write!(f, "Header is incorrectly formatted. Key: {}", header_key)
58+
}
59+
Self::InvalidValue(header_name, value) => {
60+
write!(f, "Invalid value. Key:{}; Value:{}", header_name, value)
61+
}
62+
}
63+
}
64+
}
65+
1666
/// Errors associated with parsing the HTTP Request from a u8 slice.
1767
#[derive(Debug, PartialEq)]
1868
pub enum RequestError {
@@ -26,10 +76,8 @@ pub enum RequestError {
2676
InvalidUri(&'static str),
2777
/// The HTTP Version in the Request is not supported or it is invalid.
2878
InvalidHttpVersion(&'static str),
29-
/// The header specified may be valid, but is not supported by this HTTP implementation.
30-
UnsupportedHeader,
31-
/// Header specified is invalid.
32-
InvalidHeader,
79+
/// Header specified is either invalid or not supported by this HTTP implementation.
80+
HeaderError(HttpHeaderError),
3381
/// The Request is invalid and cannot be served.
3482
InvalidRequest,
3583
/// Overflow occurred when parsing a request.
@@ -52,8 +100,7 @@ impl Display for RequestError {
52100
Self::InvalidHttpMethod(inner) => write!(f, "Invalid HTTP Method: {}", inner),
53101
Self::InvalidUri(inner) => write!(f, "Invalid URI: {}", inner),
54102
Self::InvalidHttpVersion(inner) => write!(f, "Invalid HTTP Version: {}", inner),
55-
Self::UnsupportedHeader => write!(f, "Unsupported header."),
56-
Self::InvalidHeader => write!(f, "Invalid header."),
103+
Self::HeaderError(inner) => write!(f, "Invalid header. Reason: {}", inner),
57104
Self::InvalidRequest => write!(f, "Invalid request."),
58105
Self::Overflow => write!(f, "Overflow occurred when parsing a request."),
59106
Self::Underflow => write!(f, "Underflow occurred when parsing a request."),
@@ -340,10 +387,6 @@ mod tests {
340387
format!("{}", RequestError::HeadersWithoutPendingRequest),
341388
"No request was pending while the request headers were being parsed."
342389
);
343-
assert_eq!(
344-
format!("{}", RequestError::InvalidHeader),
345-
"Invalid header."
346-
);
347390
assert_eq!(
348391
format!("{}", RequestError::InvalidHttpMethod("test")),
349392
"Invalid HTTP Method: test"
@@ -368,9 +411,60 @@ mod tests {
368411
format!("{}", RequestError::Underflow),
369412
"Underflow occurred when parsing a request."
370413
);
414+
}
415+
416+
#[test]
417+
fn test_display_header_error() {
418+
assert_eq!(
419+
format!(
420+
"{}",
421+
RequestError::HeaderError(HttpHeaderError::SizeLimitExceeded("test".to_string()))
422+
),
423+
"Invalid header. Reason: Invalid content length. Header: test"
424+
);
425+
assert_eq!(
426+
format!(
427+
"{}",
428+
RequestError::HeaderError(HttpHeaderError::UnsupportedName("test".to_string()))
429+
),
430+
"Invalid header. Reason: Unsupported header name. Key: test"
431+
);
371432
assert_eq!(
372-
format!("{}", RequestError::UnsupportedHeader),
373-
"Unsupported header."
433+
format!(
434+
"{}",
435+
RequestError::HeaderError(HttpHeaderError::UnsupportedValue(
436+
"test".to_string(),
437+
"test".to_string()
438+
))
439+
),
440+
"Invalid header. Reason: Unsupported value. Key:test; Value:test"
441+
);
442+
let value = String::from_utf8(vec![0, 159]);
443+
assert_eq!(
444+
format!(
445+
"{}",
446+
RequestError::HeaderError(HttpHeaderError::InvalidUtf8String(
447+
value.unwrap_err().utf8_error()
448+
))
449+
),
450+
"Invalid header. Reason: Header contains invalid characters. Key: invalid utf-8 sequence of 1 bytes from index 1"
451+
);
452+
assert_eq!(
453+
format!(
454+
"{}",
455+
RequestError::HeaderError(HttpHeaderError::UnsupportedFeature(
456+
"test".to_string(),
457+
"test".to_string()
458+
))
459+
),
460+
"Invalid header. Reason: Unsupported feature. Key: test; Value: test"
461+
);
462+
assert_eq!(
463+
format!(
464+
"{}",
465+
RequestError::HeaderError(HttpHeaderError::InvalidFormat("test".to_string()))
466+
),
467+
"Invalid header. Reason: Header is incorrectly formatted. Key: test"
374468
);
375469
}
376470

0 commit comments

Comments
 (0)