1use std::fmt;
2
3use http::{Method, StatusCode, Version};
4
5#[derive(Debug, PartialEq, Eq)]
7#[allow(missing_docs)]
8#[non_exhaustive]
9pub enum Error {
10 BadHeader(String),
11 UnsupportedVersion,
12 MethodVersionMismatch(Method, Version),
13 TooManyHostHeaders,
14 TooManyContentLengthHeaders,
15 BadHostHeader,
16 BadAuthorizationHeader,
17 BadContentLengthHeader,
18 OutputOverflow,
19 ChunkLenNotAscii,
20 ChunkLenNotANumber,
21 ChunkExpectedCrLf,
22 BodyContentAfterFinish,
23 BodyLargerThanContentLength,
24 BodyNotAllowed,
25 HttpParseFail(String),
26 HttpParseTooManyHeaders,
27 NoLocationHeader,
28 BadLocationHeader(String),
29 HeadersWith100,
30 BodyIsChunked,
31 BadReject100Status(StatusCode),
32}
33
34impl From<httparse::Error> for Error {
35 fn from(value: httparse::Error) -> Self {
36 Error::HttpParseFail(value.to_string())
37 }
38}
39
40impl std::error::Error for Error {}
41
42impl fmt::Display for Error {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 match self {
45 Error::BadHeader(v) => write!(f, "bad header: {}", v),
46 Error::UnsupportedVersion => write!(f, "unsupported http version"),
47 Error::MethodVersionMismatch(m, v) => {
48 write!(f, "{} not valid for HTTP version {:?}", m, v)
49 }
50 Error::TooManyHostHeaders => write!(f, "more than one host header"),
51 Error::TooManyContentLengthHeaders => write!(f, "more than one content-length header"),
52 Error::BadHostHeader => write!(f, "host header is not a string"),
53 Error::BadAuthorizationHeader => write!(f, "authorization header is not a string"),
54 Error::BadContentLengthHeader => write!(f, "content-length header not a number"),
55 Error::OutputOverflow => write!(f, "output too small to write output"),
56 Error::ChunkLenNotAscii => write!(f, "chunk length is not ascii"),
57 Error::ChunkLenNotANumber => write!(f, "chunk length cannot be read as a number"),
58 Error::ChunkExpectedCrLf => write!(f, "chunk expected crlf as next character"),
59 Error::BodyContentAfterFinish => {
60 write!(f, "attempt to stream body after sending finish (&[])")
61 }
62 Error::BodyLargerThanContentLength => {
63 write!(f, "attempt to write larger body than content-length")
64 }
65 Error::BodyNotAllowed => {
66 write!(f, "body not allowed by method")
67 }
68 Error::HttpParseFail(v) => write!(f, "http parse fail: {}", v),
69 Error::HttpParseTooManyHeaders => write!(f, "http parse resulted in too many headers"),
70 Error::NoLocationHeader => write!(f, "missing a location header"),
71 Error::BadLocationHeader(v) => write!(f, "location header is malformed: {}", v),
72 Error::HeadersWith100 => write!(f, "received headers with 100-continue response"),
73 Error::BodyIsChunked => write!(f, "body is chunked"),
74 Error::BadReject100Status(v) => {
75 write!(f, "expect-100 must be rejected with 4xx or 5xx: {}", v)
76 }
77 }
78 }
79}
80
81#[cfg(test)]
82#[cfg(feature = "client")]
83mod tests_client {
84 use super::*;
85 use crate::client::{
86 state::{RecvResponse, Redirect, SendBody, SendRequest},
87 Call, RecvResponseResult, RedirectAuthHeaders, SendRequestResult,
88 };
89 use http::{HeaderValue, Method, Request, Version};
90
91 fn setup_call(req: Request<()>) -> (Call<SendRequest>, Vec<u8>) {
95 let call = Call::new(req).unwrap();
96 let call = call.proceed();
97 let output = vec![0; 1024];
98 (call, output)
99 }
100
101 fn setup_recv_response_call() -> (Call<RecvResponse>, Vec<u8>) {
103 let req = Request::get("http://example.com").body(()).unwrap();
104 let (mut call, mut output) = setup_call(req);
105
106 call.write(&mut output).unwrap();
108
109 let call = call.proceed().unwrap().unwrap();
111 let SendRequestResult::RecvResponse(call) = call else {
112 panic!("Expected SendRequestResult::RecvResponse");
113 };
114
115 (call, output)
116 }
117
118 fn setup_send_body_call(req: Request<()>) -> (Call<SendBody>, Vec<u8>, usize) {
120 let (mut call, mut output) = setup_call(req);
121
122 let n = call.write(&mut output).unwrap();
124
125 let call = call.proceed().unwrap().unwrap();
127 let SendRequestResult::SendBody(call) = call else {
128 panic!("Expected SendBody");
129 };
130
131 (call, output, n)
132 }
133
134 fn setup_redirect_call(response: &[u8]) -> Result<(Call<Redirect>, Vec<u8>), Error> {
136 let (mut call, output) = setup_recv_response_call();
137
138 let (_, _) = call.try_response(response, false)?;
140
141 let RecvResponseResult::Redirect(call) = call.proceed().unwrap() else {
143 panic!("Expected RecvResponseResult::Redirect");
144 };
145
146 Ok((call, output))
147 }
148
149 #[test]
151 fn test_bad_header() {
152 let req = Request::get("http://example.com").body(()).unwrap();
154 let mut call = Call::new(req).unwrap();
155
156 let result = call.header("Invalid\0Header", "value");
158
159 assert!(result.is_err());
161 let err = result.unwrap_err();
162 assert!(matches!(err, Error::BadHeader(_)));
163 }
164
165 #[test]
167 fn test_unsupported_version() {
168 let req = Request::builder()
170 .uri("http://example.com")
171 .version(Version::HTTP_2)
172 .body(())
173 .unwrap();
174
175 let (mut call, mut output) = setup_call(req);
176
177 let err = call.write(&mut output).unwrap_err();
179
180 assert!(matches!(err, Error::UnsupportedVersion));
181 }
182
183 #[test]
185 fn test_method_version_mismatch() {
186 let m = Method::from_bytes(b"TRACE").unwrap();
188 let req = Request::builder()
189 .method(m.clone())
190 .uri("http://example.com")
191 .version(Version::HTTP_10)
192 .body(())
193 .unwrap();
194
195 let (mut call, mut output) = setup_call(req);
196
197 let err = call.write(&mut output).unwrap_err();
199
200 assert!(matches!(err, Error::MethodVersionMismatch(_, _)));
201 if let Error::MethodVersionMismatch(method, version) = err {
202 assert_eq!(method, m);
203 assert_eq!(version, Version::HTTP_10);
204 }
205 }
206
207 #[test]
209 fn test_too_many_host_headers() {
210 let req = Request::builder()
212 .uri("http://example.com")
213 .header("Host", "example.com")
214 .header("Host", "another-example.com")
215 .body(())
216 .unwrap();
217
218 let (mut call, mut output) = setup_call(req);
219
220 let err = call.write(&mut output).unwrap_err();
222
223 assert!(matches!(err, Error::TooManyHostHeaders));
225 }
226
227 #[test]
229 fn test_too_many_content_length_headers() {
230 let req = Request::builder()
232 .uri("http://example.com")
233 .header("Content-Length", "10")
234 .header("Content-Length", "20")
235 .body(())
236 .unwrap();
237
238 let (mut call, mut output) = setup_call(req);
239
240 let err = call.write(&mut output).unwrap_err();
242
243 assert!(matches!(err, Error::TooManyContentLengthHeaders));
245 }
246
247 #[test]
249 fn test_bad_host_header() {
250 let req = Request::builder()
252 .uri("http://example.com")
253 .header("Host", HeaderValue::from_bytes(&[0xFF, 0xFE]).unwrap())
254 .body(())
255 .unwrap();
256
257 let (mut call, mut output) = setup_call(req);
258
259 let err = call.write(&mut output).unwrap_err();
261
262 assert!(matches!(err, Error::BadHostHeader));
264 }
265
266 #[test]
268 fn test_bad_authorization_header() {
269 let req = Request::builder()
271 .uri("http://example.com")
272 .header(
273 "Authorization",
274 HeaderValue::from_bytes(&[0xFF, 0xFE]).unwrap(),
275 )
276 .body(())
277 .unwrap();
278
279 let (mut call, mut output) = setup_call(req);
280
281 let err = call.write(&mut output).unwrap_err();
283
284 assert!(matches!(err, Error::BadAuthorizationHeader));
286 }
287
288 #[test]
290 fn test_bad_content_length_header() {
291 let req = Request::builder()
293 .uri("http://example.com")
294 .header("Content-Length", "not-a-number")
295 .body(())
296 .unwrap();
297
298 let (mut call, mut output) = setup_call(req);
299
300 let err = call.write(&mut output).unwrap_err();
302
303 assert!(matches!(err, Error::BadContentLengthHeader));
305 }
306
307 #[test]
309 fn test_output_overflow() {
310 let req = Request::builder()
312 .uri("http://example.com")
313 .header("x-long-header", "a".repeat(100))
314 .body(())
315 .unwrap();
316
317 let (mut call, _) = setup_call(req);
318
319 let mut tiny_output = vec![0; 10];
321 let err = call.write(&mut tiny_output).unwrap_err();
322
323 assert!(matches!(err, Error::OutputOverflow));
324 }
325
326 fn test_chunk_error(chunk_data: &[u8]) -> Error {
328 let (mut call, mut output) = setup_recv_response_call();
329
330 const RES_PREFIX: &[u8] = b"HTTP/1.1 200 OK\r\n\
331 Transfer-Encoding: chunked\r\n\
332 \r\n";
333
334 call.try_response(RES_PREFIX, false).unwrap();
335
336 let RecvResponseResult::RecvBody(mut call) = call.proceed().unwrap() else {
337 panic!("Expected RecvResponseResult::RecvBody");
338 };
339
340 call.read(chunk_data, &mut output).unwrap_err()
341 }
342
343 #[test]
345 fn test_chunk_len_not_ascii() {
346 let err = test_chunk_error(b"\xFF\r\ndata\r\n");
347 assert!(matches!(err, Error::ChunkLenNotAscii));
348 }
349
350 #[test]
352 fn test_chunk_len_not_a_number() {
353 let err = test_chunk_error(b"xyz\r\ndata\r\n");
354 assert!(matches!(err, Error::ChunkLenNotANumber));
355 }
356
357 #[test]
359 fn test_chunk_expected_crlf() {
360 let err = test_chunk_error(b"5abcdefghijabcdefghijabcdefghij\r\n");
361 assert!(matches!(err, Error::ChunkExpectedCrLf));
362 }
363
364 #[test]
366 fn test_body_content_after_finish() {
367 let req = Request::post("http://example.com").body(()).unwrap();
369
370 let (mut call, mut output, n) = setup_send_body_call(req);
371
372 let (_, n2) = call.write(b"data", &mut output[n..]).unwrap();
374
375 let (_, n3) = call.write(&[], &mut output[n + n2..]).unwrap();
377
378 let err = call
380 .write(b"more data", &mut output[n + n2 + n3..])
381 .unwrap_err();
382 assert!(matches!(err, Error::BodyContentAfterFinish));
383 }
384
385 #[test]
387 fn test_body_larger_than_content_length() {
388 let req = Request::post("http://example.com")
390 .header("content-length", "5")
391 .body(())
392 .unwrap();
393
394 let (mut call, mut output, n) = setup_send_body_call(req);
395
396 let err = call.write(b"too much data", &mut output[n..]).unwrap_err();
398 assert!(matches!(err, Error::BodyLargerThanContentLength));
399 }
400
401 #[test]
403 fn test_http_parse_fail() {
404 let (mut call, _) = setup_recv_response_call();
405
406 const RES: &[u8] = b"HTTP/1.1200 OK\r\n\r\n";
408
409 let err = call.try_response(RES, false).unwrap_err();
410
411 assert!(matches!(err, Error::HttpParseFail(_)));
412 }
413
414 #[test]
416 fn test_http_parse_too_many_headers() {
417 let (mut call, _) = setup_recv_response_call();
418
419 let mut res = String::from("HTTP/1.1 200 OK\r\n");
421 for i in 0..1000 {
422 res.push_str(&format!("X-Header-{}: value\r\n", i));
423 }
424 res.push_str("\r\n");
425
426 let err = call.try_response(res.as_bytes(), false).unwrap_err();
427
428 assert!(matches!(err, Error::HttpParseTooManyHeaders));
429 }
430
431 #[test]
433 fn test_no_location_header() {
434 const RES: &[u8] = b"HTTP/1.1 302 Found\r\n\
436 \r\n";
437
438 let (mut call, _) = setup_redirect_call(RES).unwrap();
439
440 let err = call.as_new_call(RedirectAuthHeaders::Never).unwrap_err();
442
443 assert!(matches!(err, Error::NoLocationHeader));
444 }
445
446 #[test]
448 fn test_bad_location_header() {
449 const RES: &[u8] = b"HTTP/1.1 302 Found\r\n\
451 Location: \xFF\r\n\
452 \r\n";
453
454 let (mut call, _) = setup_redirect_call(RES).unwrap();
455
456 let err = call.as_new_call(RedirectAuthHeaders::Never).unwrap_err();
458
459 assert!(matches!(err, Error::BadLocationHeader(_)));
460 }
461
462 #[test]
464 fn test_headers_with_100() {
465 let (mut call, _) = setup_recv_response_call();
466
467 const RES: &[u8] = b"HTTP/1.1 100 Continue\r\n\
469 Content-Type: text/plain\r\n\
470 \r\n";
471
472 let err = call.try_response(RES, false).unwrap_err();
473
474 assert!(matches!(err, Error::HeadersWith100));
475 }
476
477 #[test]
479 fn test_body_is_chunked() {
480 let req = Request::post("http://example.com")
482 .header("transfer-encoding", "chunked")
483 .body(())
484 .unwrap();
485
486 let (mut call, _, _) = setup_send_body_call(req);
487
488 let err = call.consume_direct_write(5).unwrap_err();
490 assert!(matches!(err, Error::BodyIsChunked));
491 }
492
493 #[test]
495 fn test_from_httparse_error() {
496 let httparse_error = httparse::Error::HeaderName;
497 let error: Error = httparse_error.into();
498 assert!(matches!(error, Error::HttpParseFail(_)));
499 let Error::HttpParseFail(_) = error else {
500 panic!("Not Error::HttpParseFail");
501 };
502 }
503}