1#![allow(non_snake_case)]
2#![allow(non_camel_case_types)]
3
4use std::{collections::HashMap, hash::Hash};
5
6#[derive(Debug, Clone)]
7pub enum HttpVersion {
8 Http09,
9 Http10,
10 Http11,
11 Http20,
12 Http30,
13 Unknown,
14}
15
16impl HttpVersion {
17 pub fn to_string(&self) -> String {
18 match self {
19 HttpVersion::Http09 => "HTTP/0.9".to_string(),
20 HttpVersion::Http10 => "HTTP/1.0".to_string(),
21 HttpVersion::Http11 => "HTTP/1.1".to_string(),
22 HttpVersion::Http20 => "HTTP/2.0".to_string(),
23 HttpVersion::Http30 => "HTTP/3.0".to_string(),
24 _ => "UNKNOWN".to_string(),
25 }
26 }
27
28 pub fn from_string(version: &str) -> Self {
29 match version {
30 "HTTP/0.9" => HttpVersion::Http09,
31 "HTTP/1.0" => HttpVersion::Http10,
32 "HTTP/1.1" => HttpVersion::Http11,
33 "HTTP/2.0" => HttpVersion::Http20,
34 "HTTP/3.0" => HttpVersion::Http30,
35 _ => HttpVersion::Unknown,
36 }
37 }
38}
39
40impl std::fmt::Display for HttpVersion {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 write!(f, "{}", self.to_string())
43 }
44}
45
46#[derive(Debug, Clone, PartialEq)]
47pub enum HttpMethod {
48 GET,
49 POST,
50 PUT,
51 DELETE,
52 HEAD,
53 OPTIONS,
54 PATCH,
55 TRACE,
56 CONNECT,
57 UNKNOWN,
58}
59
60impl HttpMethod {
61 pub fn to_string(&self) -> String {
62 match self {
63 HttpMethod::GET => "GET".to_string(),
64 HttpMethod::POST => "POST".to_string(),
65 HttpMethod::PUT => "PUT".to_string(),
66 HttpMethod::DELETE => "DELETE".to_string(),
67 HttpMethod::HEAD => "HEAD".to_string(),
68 HttpMethod::OPTIONS => "OPTIONS".to_string(),
69 HttpMethod::PATCH => "PATCH".to_string(),
70 HttpMethod::TRACE => "TRACE".to_string(),
71 HttpMethod::CONNECT => "CONNECT".to_string(),
72 _ => "UNKNOWN".to_string(),
73 }
74 }
75
76 pub fn from_string(method: &str) -> Self {
77 match method {
78 "GET" => HttpMethod::GET,
79 "POST" => HttpMethod::POST,
80 "PUT" => HttpMethod::PUT,
81 "DELETE" => HttpMethod::DELETE,
82 "HEAD" => HttpMethod::HEAD,
83 "OPTIONS" => HttpMethod::OPTIONS,
84 "PATCH" => HttpMethod::PATCH,
85 "TRACE" => HttpMethod::TRACE,
86 "CONNECT" => HttpMethod::CONNECT,
87 _ => HttpMethod::UNKNOWN,
88 }
89 }
90}
91
92impl std::fmt::Display for HttpMethod {
93 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94 write!(f, "{}", self.to_string())
95 }
96}
97
98impl Default for HttpMethod {
99 fn default() -> Self {
100 HttpMethod::GET
101 }
102}
103
104impl PartialEq<&HttpMethod> for HttpMethod {
105 fn eq(&self, other: &&HttpMethod) -> bool {
106 self == *other
107 }
108}
109
110impl PartialEq<HttpMethod> for &HttpMethod {
111 fn eq(&self, other: &HttpMethod) -> bool {
112 **self == *other
113 }
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, Hash)]
122pub enum StatusCode {
123 CONTINUE = 100,
125 SWITCHING_PROTOCOLS = 101,
126 PROCESSING = 102,
127 EARLY_HINTS = 103,
128
129 OK = 200,
131 CREATED = 201,
132 ACCEPTED = 202,
133 NON_AUTHORITATIVE_INFORMATION = 203,
134 NO_CONTENT = 204,
135 RESET_CONTENT = 205,
136 PARTIAL_CONTENT = 206,
137 MULTI_STATUS = 207,
138 ALREADY_REPORTED = 208,
139 IM_USED = 226,
140
141 MULTIPLE_CHOICES = 300,
143 MOVED_PERMANENTLY = 301,
144 FOUND = 302,
145 SEE_OTHER = 303,
146 NOT_MODIFIED = 304,
147 USE_PROXY = 305,
148 TEMPORARY_REDIRECT = 307,
149 PERMANENT_REDIRECT = 308,
150
151 BAD_REQUEST = 400,
153 UNAUTHORIZED = 401,
154 PAYMENT_REQUIRED = 402,
155 FORBIDDEN = 403,
156 NOT_FOUND = 404,
157 METHOD_NOT_ALLOWED = 405,
158 NOT_ACCEPTABLE = 406,
159 PROXY_AUTHENTICATION_REQUIRED = 407,
160 REQUEST_TIMEOUT = 408,
161 CONFLICT = 409,
162 GONE = 410,
163 LENGTH_REQUIRED = 411,
164 PRECONDITION_FAILED = 412,
165 PAYLOAD_TOO_LARGE = 413,
166 URI_TOO_LONG = 414,
167 UNSUPPORTED_MEDIA_TYPE = 415,
168 RANGE_NOT_SATISFIABLE = 416,
169 EXPECTATION_FAILED = 417,
170 IM_A_TEAPOT = 418,
171 MISDIRECTED_REQUEST = 421,
172 UNPROCESSABLE_ENTITY = 422,
173 LOCKED = 423,
174 FAILED_DEPENDENCY = 424,
175 TOO_EARLY = 425,
176 UPGRADE_REQUIRED = 426,
177 PRECONDITION_REQUIRED = 428,
178 TOO_MANY_REQUESTS = 429,
179 REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
180 UNAVAILABLE_FOR_LEGAL_REASONS = 451,
181
182 INTERNAL_SERVER_ERROR = 500,
184 NOT_IMPLEMENTED = 501,
185 BAD_GATEWAY = 502,
186 SERVICE_UNAVAILABLE = 503,
187 GATEWAY_TIMEOUT = 504,
188 HTTP_VERSION_NOT_SUPPORTED = 505,
189 VARIANT_ALSO_NEGOTIATES = 506,
190 INSUFFICIENT_STORAGE = 507,
191 LOOP_DETECTED = 508,
192 NOT_EXTENDED = 510,
193 NETWORK_AUTHENTICATION_REQUIRED = 511,
194
195 UNKNOWN = 0,
197}
198
199impl StatusCode {
200 pub fn as_u16(&self) -> u16 {
213 self.clone() as u16
214 }
215
216 pub fn to_string(&self) -> String {
229 match self {
230 StatusCode::CONTINUE => "100 Continue",
231 StatusCode::SWITCHING_PROTOCOLS => "101 Switching Protocols",
232 StatusCode::PROCESSING => "102 Processing",
233 StatusCode::EARLY_HINTS => "103 Early Hints",
234
235 StatusCode::OK => "200 OK",
236 StatusCode::CREATED => "201 Created",
237 StatusCode::ACCEPTED => "202 Accepted",
238 StatusCode::NON_AUTHORITATIVE_INFORMATION => "203 Non-Authoritative Information",
239 StatusCode::NO_CONTENT => "204 No Content",
240 StatusCode::RESET_CONTENT => "205 Reset Content",
241 StatusCode::PARTIAL_CONTENT => "206 Partial Content",
242 StatusCode::MULTI_STATUS => "207 Multi-Status",
243 StatusCode::ALREADY_REPORTED => "208 Already Reported",
244 StatusCode::IM_USED => "226 IM Used",
245
246 StatusCode::MULTIPLE_CHOICES => "300 Multiple Choices",
247 StatusCode::MOVED_PERMANENTLY => "301 Moved Permanently",
248 StatusCode::FOUND => "302 Found",
249 StatusCode::SEE_OTHER => "303 See Other",
250 StatusCode::NOT_MODIFIED => "304 Not Modified",
251 StatusCode::USE_PROXY => "305 Use Proxy",
252 StatusCode::TEMPORARY_REDIRECT => "307 Temporary Redirect",
253 StatusCode::PERMANENT_REDIRECT => "308 Permanent Redirect",
254
255 StatusCode::BAD_REQUEST => "400 Bad Request",
256 StatusCode::UNAUTHORIZED => "401 Unauthorized",
257 StatusCode::PAYMENT_REQUIRED => "402 Payment Required",
258 StatusCode::FORBIDDEN => "403 Forbidden",
259 StatusCode::NOT_FOUND => "404 Not Found",
260 StatusCode::METHOD_NOT_ALLOWED => "405 Method Not Allowed",
261 StatusCode::NOT_ACCEPTABLE => "406 Not Acceptable",
262 StatusCode::PROXY_AUTHENTICATION_REQUIRED => "407 Proxy Authentication Required",
263 StatusCode::REQUEST_TIMEOUT => "408 Request Timeout",
264 StatusCode::CONFLICT => "409 Conflict",
265 StatusCode::GONE => "410 Gone",
266 StatusCode::LENGTH_REQUIRED => "411 Length Required",
267 StatusCode::PRECONDITION_FAILED => "412 Precondition Failed",
268 StatusCode::PAYLOAD_TOO_LARGE => "413 Payload Too Large",
269 StatusCode::URI_TOO_LONG => "414 URI Too Long",
270 StatusCode::UNSUPPORTED_MEDIA_TYPE => "415 Unsupported Media Type",
271 StatusCode::RANGE_NOT_SATISFIABLE => "416 Range Not Satisfiable",
272 StatusCode::EXPECTATION_FAILED => "417 Expectation Failed",
273 StatusCode::IM_A_TEAPOT => "418 I'm a teapot",
274 StatusCode::MISDIRECTED_REQUEST => "421 Misdirected Request",
275 StatusCode::UNPROCESSABLE_ENTITY => "422 Unprocessable Entity",
276 StatusCode::LOCKED => "423 Locked",
277 StatusCode::FAILED_DEPENDENCY => "424 Failed Dependency",
278 StatusCode::TOO_EARLY => "425 Too Early",
279 StatusCode::UPGRADE_REQUIRED => "426 Upgrade Required",
280 StatusCode::PRECONDITION_REQUIRED => "428 Precondition Required",
281 StatusCode::TOO_MANY_REQUESTS => "429 Too Many Requests",
282 StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE => "431 Request Header Fields Too Large",
283 StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS => "451 Unavailable For Legal Reasons",
284
285 StatusCode::INTERNAL_SERVER_ERROR => "500 Internal Server Error",
286 StatusCode::NOT_IMPLEMENTED => "501 Not Implemented",
287 StatusCode::BAD_GATEWAY => "502 Bad Gateway",
288 StatusCode::SERVICE_UNAVAILABLE => "503 Service Unavailable",
289 StatusCode::GATEWAY_TIMEOUT => "504 Gateway Timeout",
290 StatusCode::HTTP_VERSION_NOT_SUPPORTED => "505 HTTP Version Not Supported",
291 StatusCode::VARIANT_ALSO_NEGOTIATES => "506 Variant Also Negotiates",
292 StatusCode::INSUFFICIENT_STORAGE => "507 Insufficient Storage",
293 StatusCode::LOOP_DETECTED => "508 Loop Detected",
294 StatusCode::NOT_EXTENDED => "510 Not Extended",
295 StatusCode::NETWORK_AUTHENTICATION_REQUIRED => "511 Network Authentication Required",
296
297 StatusCode::UNKNOWN => "0 Unknown",
298 }.to_string()
299 }
300
301 pub fn reason_phrase(&self) -> &'static str {
307 match self {
308 StatusCode::CONTINUE => "Continue",
309 StatusCode::SWITCHING_PROTOCOLS => "Switching Protocols",
310 StatusCode::PROCESSING => "Processing",
311 StatusCode::EARLY_HINTS => "Early Hints",
312
313 StatusCode::OK => "OK",
314 StatusCode::CREATED => "Created",
315 StatusCode::ACCEPTED => "Accepted",
316 StatusCode::NON_AUTHORITATIVE_INFORMATION => "Non-Authoritative Information",
317 StatusCode::NO_CONTENT => "No Content",
318 StatusCode::RESET_CONTENT => "Reset Content",
319 StatusCode::PARTIAL_CONTENT => "Partial Content",
320 StatusCode::MULTI_STATUS => "Multi-Status",
321 StatusCode::ALREADY_REPORTED => "Already Reported",
322 StatusCode::IM_USED => "IM Used",
323
324 StatusCode::MULTIPLE_CHOICES => "Multiple Choices",
325 StatusCode::MOVED_PERMANENTLY => "Moved Permanently",
326 StatusCode::FOUND => "Found",
327 StatusCode::SEE_OTHER => "See Other",
328 StatusCode::NOT_MODIFIED => "Not Modified",
329 StatusCode::USE_PROXY => "Use Proxy",
330 StatusCode::TEMPORARY_REDIRECT => "Temporary Redirect",
331 StatusCode::PERMANENT_REDIRECT => "Permanent Redirect",
332
333 StatusCode::BAD_REQUEST => "Bad Request",
334 StatusCode::UNAUTHORIZED => "Unauthorized",
335 StatusCode::PAYMENT_REQUIRED => "Payment Required",
336 StatusCode::FORBIDDEN => "Forbidden",
337 StatusCode::NOT_FOUND => "Not Found",
338 StatusCode::METHOD_NOT_ALLOWED => "Method Not Allowed",
339 StatusCode::NOT_ACCEPTABLE => "Not Acceptable",
340 StatusCode::PROXY_AUTHENTICATION_REQUIRED => "Proxy Authentication Required",
341 StatusCode::REQUEST_TIMEOUT => "Request Timeout",
342 StatusCode::CONFLICT => "Conflict",
343 StatusCode::GONE => "Gone",
344 StatusCode::LENGTH_REQUIRED => "Length Required",
345 StatusCode::PRECONDITION_FAILED => "Precondition Failed",
346 StatusCode::PAYLOAD_TOO_LARGE => "Payload Too Large",
347 StatusCode::URI_TOO_LONG => "URI Too Long",
348 StatusCode::UNSUPPORTED_MEDIA_TYPE => "Unsupported Media Type",
349 StatusCode::RANGE_NOT_SATISFIABLE => "Range Not Satisfiable",
350 StatusCode::EXPECTATION_FAILED => "Expectation Failed",
351 StatusCode::IM_A_TEAPOT => "I'm a teapot",
352 StatusCode::MISDIRECTED_REQUEST => "Misdirected Request",
353 StatusCode::UNPROCESSABLE_ENTITY => "Unprocessable Entity",
354 StatusCode::LOCKED => "Locked",
355 StatusCode::FAILED_DEPENDENCY => "Failed Dependency",
356 StatusCode::TOO_EARLY => "Too Early",
357 StatusCode::UPGRADE_REQUIRED => "Upgrade Required",
358 StatusCode::PRECONDITION_REQUIRED => "Precondition Required",
359 StatusCode::TOO_MANY_REQUESTS => "Too Many Requests",
360 StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE => "Request Header Fields Too Large",
361 StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS => "Unavailable For Legal Reasons",
362
363 StatusCode::INTERNAL_SERVER_ERROR => "Internal Server Error",
364 StatusCode::NOT_IMPLEMENTED => "Not Implemented",
365 StatusCode::BAD_GATEWAY => "Bad Gateway",
366 StatusCode::SERVICE_UNAVAILABLE => "Service Unavailable",
367 StatusCode::GATEWAY_TIMEOUT => "Gateway Timeout",
368 StatusCode::HTTP_VERSION_NOT_SUPPORTED => "HTTP Version Not Supported",
369 StatusCode::VARIANT_ALSO_NEGOTIATES => "Variant Also Negotiates",
370 StatusCode::INSUFFICIENT_STORAGE => "Insufficient Storage",
371 StatusCode::LOOP_DETECTED => "Loop Detected",
372 StatusCode::NOT_EXTENDED => "Not Extended",
373 StatusCode::NETWORK_AUTHENTICATION_REQUIRED => "Network Authentication Required",
374
375 StatusCode::UNKNOWN => "Unknown",
376 }
377 }
378
379 pub fn from_u16(code: u16) -> Self {
389 match code {
390 100 => StatusCode::CONTINUE,
391 101 => StatusCode::SWITCHING_PROTOCOLS,
392 102 => StatusCode::PROCESSING,
393 103 => StatusCode::EARLY_HINTS,
394
395 200 => StatusCode::OK,
396 201 => StatusCode::CREATED,
397 202 => StatusCode::ACCEPTED,
398 203 => StatusCode::NON_AUTHORITATIVE_INFORMATION,
399 204 => StatusCode::NO_CONTENT,
400 205 => StatusCode::RESET_CONTENT,
401 206 => StatusCode::PARTIAL_CONTENT,
402 207 => StatusCode::MULTI_STATUS,
403 208 => StatusCode::ALREADY_REPORTED,
404 226 => StatusCode::IM_USED,
405
406 300 => StatusCode::MULTIPLE_CHOICES,
407 301 => StatusCode::MOVED_PERMANENTLY,
408 302 => StatusCode::FOUND,
409 303 => StatusCode::SEE_OTHER,
410 304 => StatusCode::NOT_MODIFIED,
411 305 => StatusCode::USE_PROXY,
412 307 => StatusCode::TEMPORARY_REDIRECT,
413 308 => StatusCode::PERMANENT_REDIRECT,
414
415 400 => StatusCode::BAD_REQUEST,
416 401 => StatusCode::UNAUTHORIZED,
417 402 => StatusCode::PAYMENT_REQUIRED,
418 403 => StatusCode::FORBIDDEN,
419 404 => StatusCode::NOT_FOUND,
420 405 => StatusCode::METHOD_NOT_ALLOWED,
421 406 => StatusCode::NOT_ACCEPTABLE,
422 407 => StatusCode::PROXY_AUTHENTICATION_REQUIRED,
423 408 => StatusCode::REQUEST_TIMEOUT,
424 409 => StatusCode::CONFLICT,
425 410 => StatusCode::GONE,
426 411 => StatusCode::LENGTH_REQUIRED,
427 412 => StatusCode::PRECONDITION_FAILED,
428 413 => StatusCode::PAYLOAD_TOO_LARGE,
429 414 => StatusCode::URI_TOO_LONG,
430 415 => StatusCode::UNSUPPORTED_MEDIA_TYPE,
431 416 => StatusCode::RANGE_NOT_SATISFIABLE,
432 417 => StatusCode::EXPECTATION_FAILED,
433 418 => StatusCode::IM_A_TEAPOT,
434 421 => StatusCode::MISDIRECTED_REQUEST,
435 422 => StatusCode::UNPROCESSABLE_ENTITY,
436 423 => StatusCode::LOCKED,
437 424 => StatusCode::FAILED_DEPENDENCY,
438 425 => StatusCode::TOO_EARLY,
439 426 => StatusCode::UPGRADE_REQUIRED,
440 428 => StatusCode::PRECONDITION_REQUIRED,
441 429 => StatusCode::TOO_MANY_REQUESTS,
442 431 => StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE,
443 451 => StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS,
444
445 500 => StatusCode::INTERNAL_SERVER_ERROR,
446 501 => StatusCode::NOT_IMPLEMENTED,
447 502 => StatusCode::BAD_GATEWAY,
448 503 => StatusCode::SERVICE_UNAVAILABLE,
449 504 => StatusCode::GATEWAY_TIMEOUT,
450 505 => StatusCode::HTTP_VERSION_NOT_SUPPORTED,
451 506 => StatusCode::VARIANT_ALSO_NEGOTIATES,
452 507 => StatusCode::INSUFFICIENT_STORAGE,
453 508 => StatusCode::LOOP_DETECTED,
454 510 => StatusCode::NOT_EXTENDED,
455 511 => StatusCode::NETWORK_AUTHENTICATION_REQUIRED,
456
457 _ => StatusCode::UNKNOWN,
458 }
459 }
460
461 pub fn from_string(code: &str) -> Self {
471 if let Ok(num) = code.split_whitespace().next().unwrap_or("").parse::<u16>() {
473 return StatusCode::from_u16(num);
474 }
475
476 match code {
478 "100 Continue" => StatusCode::CONTINUE,
479 "101 Switching Protocols" => StatusCode::SWITCHING_PROTOCOLS,
480 "102 Processing" => StatusCode::PROCESSING,
481 "103 Early Hints" => StatusCode::EARLY_HINTS,
482
483 "200 OK" => StatusCode::OK,
484 "201 Created" => StatusCode::CREATED,
485 "202 Accepted" => StatusCode::ACCEPTED,
486 "203 Non-Authoritative Information" => StatusCode::NON_AUTHORITATIVE_INFORMATION,
487 "204 No Content" => StatusCode::NO_CONTENT,
488 "205 Reset Content" => StatusCode::RESET_CONTENT,
489 "206 Partial Content" => StatusCode::PARTIAL_CONTENT,
490 "207 Multi-Status" => StatusCode::MULTI_STATUS,
491 "208 Already Reported" => StatusCode::ALREADY_REPORTED,
492 "226 IM Used" => StatusCode::IM_USED,
493
494 "300 Multiple Choices" => StatusCode::MULTIPLE_CHOICES,
495 "301 Moved Permanently" => StatusCode::MOVED_PERMANENTLY,
496 "302 Found" => StatusCode::FOUND,
497 "303 See Other" => StatusCode::SEE_OTHER,
498 "304 Not Modified" => StatusCode::NOT_MODIFIED,
499 "305 Use Proxy" => StatusCode::USE_PROXY,
500 "307 Temporary Redirect" => StatusCode::TEMPORARY_REDIRECT,
501 "308 Permanent Redirect" => StatusCode::PERMANENT_REDIRECT,
502
503 "400 Bad Request" => StatusCode::BAD_REQUEST,
504 "401 Unauthorized" => StatusCode::UNAUTHORIZED,
505 "402 Payment Required" => StatusCode::PAYMENT_REQUIRED,
506 "403 Forbidden" => StatusCode::FORBIDDEN,
507 "404 Not Found" => StatusCode::NOT_FOUND,
508 "405 Method Not Allowed" => StatusCode::METHOD_NOT_ALLOWED,
509 "406 Not Acceptable" => StatusCode::NOT_ACCEPTABLE,
510 "407 Proxy Authentication Required" => StatusCode::PROXY_AUTHENTICATION_REQUIRED,
511 "408 Request Timeout" => StatusCode::REQUEST_TIMEOUT,
512 "409 Conflict" => StatusCode::CONFLICT,
513 "410 Gone" => StatusCode::GONE,
514 "411 Length Required" => StatusCode::LENGTH_REQUIRED,
515 "412 Precondition Failed" => StatusCode::PRECONDITION_FAILED,
516 "413 Payload Too Large" => StatusCode::PAYLOAD_TOO_LARGE,
517 "414 URI Too Long" => StatusCode::URI_TOO_LONG,
518 "415 Unsupported Media Type" => StatusCode::UNSUPPORTED_MEDIA_TYPE,
519 "416 Range Not Satisfiable" => StatusCode::RANGE_NOT_SATISFIABLE,
520 "417 Expectation Failed" => StatusCode::EXPECTATION_FAILED,
521 "418 I'm a teapot" => StatusCode::IM_A_TEAPOT,
522 "421 Misdirected Request" => StatusCode::MISDIRECTED_REQUEST,
523 "422 Unprocessable Entity" => StatusCode::UNPROCESSABLE_ENTITY,
524 "423 Locked" => StatusCode::LOCKED,
525 "424 Failed Dependency" => StatusCode::FAILED_DEPENDENCY,
526 "425 Too Early" => StatusCode::TOO_EARLY,
527 "426 Upgrade Required" => StatusCode::UPGRADE_REQUIRED,
528 "428 Precondition Required" => StatusCode::PRECONDITION_REQUIRED,
529 "429 Too Many Requests" => StatusCode::TOO_MANY_REQUESTS,
530 "431 Request Header Fields Too Large" => StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE,
531 "451 Unavailable For Legal Reasons" => StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS,
532
533 "500 Internal Server Error" => StatusCode::INTERNAL_SERVER_ERROR,
534 "501 Not Implemented" => StatusCode::NOT_IMPLEMENTED,
535 "502 Bad Gateway" => StatusCode::BAD_GATEWAY,
536 "503 Service Unavailable" => StatusCode::SERVICE_UNAVAILABLE,
537 "504 Gateway Timeout" => StatusCode::GATEWAY_TIMEOUT,
538 "505 HTTP Version Not Supported" => StatusCode::HTTP_VERSION_NOT_SUPPORTED,
539 "506 Variant Also Negotiates" => StatusCode::VARIANT_ALSO_NEGOTIATES,
540 "507 Insufficient Storage" => StatusCode::INSUFFICIENT_STORAGE,
541 "508 Loop Detected" => StatusCode::LOOP_DETECTED,
542 "510 Not Extended" => StatusCode::NOT_EXTENDED,
543 "511 Network Authentication Required" => StatusCode::NETWORK_AUTHENTICATION_REQUIRED,
544
545 _ => StatusCode::UNKNOWN,
546 }
547 }
548
549 pub fn is_informational(&self) -> bool {
555 let code = self.as_u16();
556 (100..=199).contains(&code)
557 }
558
559 pub fn is_success(&self) -> bool {
565 let code = self.as_u16();
566 (200..=299).contains(&code)
567 }
568
569 pub fn is_redirection(&self) -> bool {
575 let code = self.as_u16();
576 (300..=399).contains(&code)
577 }
578
579 pub fn is_client_error(&self) -> bool {
585 let code = self.as_u16();
586 (400..=499).contains(&code)
587 }
588
589 pub fn is_server_error(&self) -> bool {
595 let code = self.as_u16();
596 (500..=599).contains(&code)
597 }
598
599 pub fn is_error(&self) -> bool {
605 self.is_client_error() || self.is_server_error()
606 }
607
608 pub fn is_not_found(&self) -> bool {
614 *self == StatusCode::NOT_FOUND
615 }
616
617 pub fn is_ok(&self) -> bool {
623 *self == StatusCode::OK
624 }
625
626 pub fn is_no_content(&self) -> bool {
632 *self == StatusCode::NO_CONTENT
633 }
634
635 pub fn is_created(&self) -> bool {
641 *self == StatusCode::CREATED
642 }
643
644 pub fn is_unauthorized(&self) -> bool {
650 *self == StatusCode::UNAUTHORIZED
651 }
652}
653
654impl std::fmt::Display for StatusCode {
655 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
656 write!(f, "{}", self.to_string())
657 }
658}
659
660impl From<u16> for StatusCode {
661 fn from(code: u16) -> Self {
662 StatusCode::from_u16(code)
663 }
664}
665
666impl From<&str> for StatusCode {
667 fn from(code: &str) -> Self {
668 StatusCode::from_string(code)
669 }
670}
671
672impl From<String> for StatusCode {
673 fn from(code: String) -> Self {
674 StatusCode::from_string(&code)
675 }
676}
677
678impl From<StatusCode> for u16 {
679 fn from(code: StatusCode) -> Self {
680 code.as_u16()
681 }
682}
683
684impl From<StatusCode> for String {
685 fn from(code: StatusCode) -> Self {
686 code.to_string()
687 }
688}
689
690#[derive(Debug, Clone, PartialEq, Eq)]
695pub enum HttpContentType {
696 Text { subtype: String, charset: Option<String> },
698 Application { subtype: String, parameters: Option<Vec<(String, String)>> },
699 Image { subtype: String },
700 Audio { subtype: String },
701 Video { subtype: String },
702 Multipart { subtype: String, boundary: Option<String> },
703 Other { type_name: String, subtype: String, parameters: Option<Vec<(String, String)>> },
704}
705
706impl HttpContentType {
707 pub fn from_str(content_type: &str) -> Self {
719 let parts: Vec<&str> = content_type.split(';').collect();
720 let main_part = parts[0].trim();
721 let mut parameters = Vec::new();
722
723 for part in &parts[1..] {
724 let param_parts: Vec<&str> = part.split('=').collect();
725 if param_parts.len() == 2 {
726 parameters.push((param_parts[0].trim().to_string(), param_parts[1].trim().to_string()));
727 }
728 }
729
730 let (type_name, subtype) = if let Some(pos) = main_part.find('/') {
731 (&main_part[..pos], &main_part[pos + 1..])
732 } else {
733 ("unknown", "unknown")
734 };
735
736 match type_name {
737 "text" => Self::Text {
738 subtype: subtype.to_string(),
739 charset: Self::find_value_from_vec(¶meters, "charset"),
740 },
741 "application" => Self::Application {
742 subtype: subtype.to_string(),
743 parameters: Some(parameters)
744 },
745 "image" => Self::Image {
746 subtype: subtype.to_string()
747 },
748 "audio" => Self::Audio {
749 subtype: subtype.to_string()
750 },
751 "video" => Self::Video {
752 subtype: subtype.to_string()
753 },
754 "multipart" => Self::Multipart {
755 subtype: subtype.to_string(),
756 boundary: Self::find_value_from_vec(¶meters, "boundary"),
757 },
758 _ => Self::Other {
759 type_name: type_name.to_string(),
760 subtype: subtype.to_string(),
761 parameters: Some(parameters)
762 },
763 }
764 }
765
766 pub fn find_value_from_vec(vec: &Vec<(String, String)>, key: &str) -> Option<String> {
775 for (k, v) in vec {
776 if k == key {
777 return Some(v.clone());
778 }
779 }
780 None
781 }
782
783 pub fn to_string(&self) -> String {
785 match self {
786 HttpContentType::Text { subtype, .. } => format!("text/{}", subtype),
787 HttpContentType::Application { subtype, .. } => format!("application/{}", subtype),
788 HttpContentType::Image { subtype } => format!("image/{}", subtype),
789 HttpContentType::Audio { subtype } => format!("audio/{}", subtype),
790 HttpContentType::Video { subtype } => format!("video/{}", subtype),
791 HttpContentType::Multipart { subtype, .. } => format!("multipart/{}", subtype),
792 HttpContentType::Other { type_name, subtype, .. } => format!("{}/{}", type_name, subtype),
793 }
794 }
795
796 pub fn TextHtml() -> Self {
797 Self::Text { subtype: "html".to_string(), charset: Some("UTF-8".to_string()) }
798 }
799
800 pub fn TextPlain() -> Self {
801 Self::Text { subtype: "plain".to_string(), charset: Some("UTF-8".to_string()) }
802 }
803
804 pub fn TextCss() -> Self {
805 Self::Text {
806 subtype: "css".to_string(),
807 charset: Some("UTF-8".to_string())
808 }
809 }
810
811 pub fn ApplicationJavascript() -> Self {
812 Self::Application {
813 subtype: "javascript".to_string(),
814 parameters: Some(vec![("charset".to_string(), "UTF-8".to_string())])
815 }
816 }
817
818 pub fn ApplicationJson() -> Self {
819 Self::Application { subtype: "json".to_string(), parameters: Some(vec![("charset".to_string(), "UTF-8".to_string())]) }
820 }
821
822 pub fn ApplicationUrlEncodedForm() -> Self {
823 Self::Application { subtype: "x-www-form-urlencoded".to_string(), parameters: Some(vec![("charset".to_string(), "UTF-8".to_string())]) }
824 }
825
826 pub fn ApplicationXml() -> Self {
827 Self::Application { subtype: "xml".to_string(), parameters: Some(vec![("charset".to_string(), "UTF-8".to_string())]) }
828 }
829
830 pub fn ApplicationOctetStream() -> Self {
831 Self::Application { subtype: "octet-stream".to_string(), parameters: Some(vec![("charset".to_string(), "UTF-8".to_string())]) }
832 }
833
834 pub fn ImagePng() -> Self {
835 Self::Image { subtype: "png".to_string() }
836 }
837
838 pub fn ImageJpeg() -> Self {
839 Self::Image { subtype: "jpeg".to_string() }
840 }
841
842 pub fn ImageGif() -> Self {
843 Self::Image { subtype: "gif".to_string() }
844 }
845}
846
847impl std::fmt::Display for HttpContentType {
848 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
849 write!(f, "{}", self.to_string())
850 }
851}
852
853impl Default for HttpContentType {
854 fn default() -> Self {
855 Self::Other {
856 type_name: "Unknown".to_string(),
857 subtype: "Unknown".to_string(),
858 parameters: None,
859 }
860 }
861}
862
863pub struct HeaderConstructor{
864 pub headers: Vec<HeaderAttribute>
865}
866
867impl HeaderConstructor{
868 pub fn build<T: Into<String>>(string: T) -> Self {
869 let mut headers = Vec::new();
870 let string = string.into();
871 let parts: Vec<&str> = string.split(';').collect();
872 for part in parts {
873 let part = part.trim();
874 if !part.is_empty() {
875 headers.push(HeaderAttribute::build(part));
876 }
877 }
878 Self { headers }
879 }
880}
881
882pub struct HeaderAttribute{
883 pub main_value: String,
884 pub attributes: HashMap<String, String>,
885}
886
887impl HeaderAttribute{
888 pub fn build<T: Into<String>>(part: T) -> Self{
889 let part = part.into();
890 let mut attributes = HashMap::new();
891 let main_value = part.split(':').next().unwrap_or("").trim().to_string();
892 for attr in part.split(';').skip(1) {
893 let attr_parts: Vec<&str> = attr.split('=').collect();
894 if attr_parts.len() == 2 {
895 attributes.insert(attr_parts[0].trim().to_string(), attr_parts[1].trim().to_string());
896 }
897 }
898 Self { main_value, attributes }
899 }
900}
901
902#[derive(Debug, Clone)]
903pub struct RequestPath{
904 path: Vec<String>
905}
906
907impl RequestPath{
908 pub fn new(path: Vec<String>) -> Self{
909 Self { path }
910 }
911
912 pub fn to_string(&self) -> String{
913 let mut result = String::new();
914 for part in &self.path {
915 result.push('/');
916 result.push_str(part);
917 }
918 result
919 }
920
921 pub fn from_string(url: &str) -> Self{
922 let mut path = Vec::new();
923 let parts: Vec<&str> = url.split('/').collect();
924 for part in parts {
925 if !part.is_empty() {
926 path.push(part.to_string());
927 }
928 }
929 Self { path }
930 }
931
932 pub fn url_part(&self, part: usize) -> String{
933 if part >= self.path.len() {
940 return "".to_string();
941 }
942 self.path[part].clone()
943 }
944}
945
946impl Default for RequestPath {
947 fn default() -> Self {
948 Self::new(Vec::new())
949 }
950}
951
952#[derive(Debug, Clone, PartialEq)]
962pub struct AcceptLang {
963 langs: Vec<(String, f32)>,
964}
965
966impl AcceptLang {
967 pub fn from_str<S: AsRef<str>>(s: S) -> Self {
974 let mut langs = Vec::new();
975
976 for lang_str in s.as_ref().split(',') {
977 let mut parts = lang_str.splitn(2, ';');
978 let lang = parts.next().unwrap().trim().to_string();
979
980 let mut weight = 1.0;
982
983 if let Some(q_part) = parts.next() {
985 if let Some(q_str) = q_part.trim().strip_prefix("q=") {
986 weight = q_str.trim().parse().unwrap_or(1.0);
987 }
988 }
989
990 langs.push((lang, weight));
991 }
992
993 AcceptLang { langs }
994 }
995
996 pub fn most_preferred(&self) -> String {
1007 self.langs.iter()
1008 .max_by(|(_, w1), (_, w2)| w1.total_cmp(w2))
1009 .map(|(lang, _)| lang.clone())
1010 .unwrap_or_else(|| "en".to_string())
1011 }
1012
1013 pub fn all_languages(&self) -> Vec<String> {
1015 self.langs.iter().map(|(lang, _)| lang.clone()).collect()
1016 }
1017
1018 pub fn get_weight(&self, lang: &str) -> f32 {
1022 self.langs.iter()
1023 .find(|(l, _)| l.eq_ignore_ascii_case(lang))
1024 .map(|(_, w)| *w)
1025 .unwrap_or(0.0)
1026 }
1027
1028 pub fn add_language(&mut self, lang: String, weight: f32) {
1030 self.langs.push((lang, weight));
1031 }
1032
1033 pub fn remove_language(&mut self, lang: &str) {
1035 self.langs.retain(|(l, _)| !l.eq_ignore_ascii_case(lang));
1036 }
1037
1038 pub fn to_header_string(&self) -> String {
1045 self.langs.iter()
1046 .map(|(lang, weight)| {
1047 if (weight - 1.0).abs() < f32::EPSILON {
1048 lang.clone()
1049 } else {
1050 let weight_str = format!("{:.3}", weight)
1051 .trim_end_matches('0')
1052 .trim_end_matches('.')
1053 .to_string();
1054 format!("{};q={}", lang, weight_str)
1055 }
1056 })
1057 .collect::<Vec<_>>()
1058 .join(", ")
1059 }
1060
1061 pub fn to_response_header(&self) -> String {
1062 self.most_preferred()
1063 }
1064}