1use std::fmt::{Display, Formatter};
5use std::io::{Read, Write};
6use std::num::ParseIntError;
7use std::str::FromStr;
8
9use crate::error::Error;
10pub use client::*;
11pub use h2::*;
12pub use headers::*;
13use irox_enums::EnumIterItem;
14pub use request::*;
15pub use response::*;
16
17mod client;
18mod h2;
19mod headers;
20mod request;
21mod response;
22
23#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, EnumIterItem)]
26#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
27pub enum HttpProtocol {
28 HTTP,
29
30 #[default]
31 HTTPS,
32}
33
34impl HttpProtocol {
35 #[must_use]
36 pub const fn port(&self) -> u16 {
37 match self {
38 HttpProtocol::HTTP => 80,
39 HttpProtocol::HTTPS => 443,
40 }
41 }
42
43 #[must_use]
44 pub const fn name(&self) -> &'static str {
45 match self {
46 HttpProtocol::HTTP => "http",
47 HttpProtocol::HTTPS => "https",
48 }
49 }
50}
51
52impl FromStr for HttpProtocol {
53 type Err = Error;
54
55 fn from_str(s: &str) -> Result<Self, Self::Err> {
56 for item in HttpProtocol::iter_items() {
57 if item.name().to_lowercase() == s {
58 return Ok(item);
59 }
60 }
61 Error::unknown_scheme_err(format!("Unknown scheme: {s}"))
62 }
63}
64
65#[derive(Debug, Clone, Eq, PartialEq)]
67pub enum HttpMethod {
68 Connect,
70 Delete,
72 Get,
74 Head,
77 Options,
79 Patch,
81 Post,
84 Put,
87 Trace,
89
90 Other(String),
92}
93
94impl Display for HttpMethod {
95 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
96 write!(
97 f,
98 "{}",
99 match self {
100 HttpMethod::Connect => "CONNECT",
101 HttpMethod::Delete => "DELETE",
102 HttpMethod::Get => "GET",
103 HttpMethod::Head => "HEAD",
104 HttpMethod::Options => "OPTIONS",
105 HttpMethod::Patch => "PATCH",
106 HttpMethod::Post => "POST",
107 HttpMethod::Put => "PUT",
108 HttpMethod::Trace => "TRACE",
109 HttpMethod::Other(o) => o,
110 }
111 )
112 }
113}
114
115#[derive(Debug, Copy, Clone, Eq, PartialEq)]
117pub enum HttpCodeType {
118 Info,
120 Success,
122 Redirect,
124 ClientError,
126 ServerError,
128 UnknownOther,
130}
131
132#[allow(non_camel_case_types)]
133#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
134#[non_exhaustive]
135pub enum HttpCodes {
136 Info_100_Continue,
139 Info_101_SwitchingProtocols,
142 Info_102_Processing,
145 Info_103_EarlyHints,
149
150 Success_200_Ok,
152 Success_201_Created,
154 Success_202_Accepted,
156 Success_203_NotAuthoritative,
159 Success_204_NoContent,
161 Success_205_ResetContent,
163 Success_206_PartialContent,
166 Success_207_MultiStatus,
169 Success_208_AlreadyReported,
172 Success_226_IMUsed,
176
177 Redirect_300_MultipleChoices,
179 Redirect_301_MovedPermanently,
181 Redirect_302_Found,
183 Redirect_303_SeeOther,
186 Redirect_304_NotModified,
189 Redirect_305_UseProxy,
192 Redirect_306_Unused,
194 Redirect_307_Temporary,
197 Redirect_308_PermanentRedirect,
200
201 ClientError_400_BadRequest,
204 ClientError_401_Unauthorized,
207 ClientError_402_PaymentRequired,
211 ClientError_403_Forbidden,
213 ClientError_404_NotFound,
215 ClientError_405_MethodNotAllowed,
217 ClientError_406_NotAcceptable,
220 ClientError_407_ProxyAuthenticationRequired,
222 ClientError_408_RequestTimeout,
226 ClientError_409_Conflict,
228 ClientError_410_Gone,
231 ClientError_411_LengthRequired,
234 ClientError_412_PreconditionFailed,
236 ClientError_413_PayloadTooLarge,
238 ClientError_414_URITooLong,
240 ClientError_415_UnsupportedMediaType,
243 ClientError_416_RangeNotSatisfiable,
245 ClientError_417_ExpectationFailed,
248 ClientError_418_ImATeapot,
250 ClientError_421_MisdirectedRequest,
252 ClientError_422_UnprocessableContent,
254 ClientError_423_Locked,
256 ClientError_424_FailedDependency,
258 ClientError_425_TooEarly,
260 ClientError_426_UpgradeRequired,
263 ClientError_428_PreconditionRequired,
268 ClientError_429_TooManyRequests,
270 ClientError_431_RequestHeaderFieldsTooLarge,
272 ClientError_451_UnavailableForLegalReasons,
275
276 ServerError_500_InternalServerError,
278 ServerError_501_NotImplemented,
282 ServerError_502_BadGateway,
285 ServerError_503_ServiceUnavailable,
288 ServerError_504_GatewayTimeout,
291 ServerError_505_HTTPVersionNotSupported,
293 ServerError_506_VariantAlsoNegotiates,
297 ServerError_507_InsufficientStorage,
300 ServerError_508_LoopDetected,
302 ServerError_510_NotExtended,
304 ServerError_511_NetworkAuthenticationRequired,
306
307 UnknownOther(u16),
309}
310
311impl HttpCodes {
312 #[must_use]
314 pub fn code(&self) -> u16 {
315 match self {
316 HttpCodes::Info_100_Continue => 100,
317 HttpCodes::Info_101_SwitchingProtocols => 101,
318 HttpCodes::Info_102_Processing => 102,
319 HttpCodes::Info_103_EarlyHints => 103,
320
321 HttpCodes::Success_200_Ok => 200,
322 HttpCodes::Success_201_Created => 201,
323 HttpCodes::Success_202_Accepted => 202,
324 HttpCodes::Success_203_NotAuthoritative => 203,
325 HttpCodes::Success_204_NoContent => 204,
326 HttpCodes::Success_205_ResetContent => 205,
327 HttpCodes::Success_206_PartialContent => 206,
328 HttpCodes::Success_207_MultiStatus => 207,
329 HttpCodes::Success_208_AlreadyReported => 208,
330 HttpCodes::Success_226_IMUsed => 226,
331
332 HttpCodes::Redirect_300_MultipleChoices => 300,
333 HttpCodes::Redirect_301_MovedPermanently => 301,
334 HttpCodes::Redirect_302_Found => 302,
335 HttpCodes::Redirect_303_SeeOther => 303,
336 HttpCodes::Redirect_304_NotModified => 304,
337 HttpCodes::Redirect_305_UseProxy => 305,
338 HttpCodes::Redirect_306_Unused => 306,
339 HttpCodes::Redirect_307_Temporary => 307,
340 HttpCodes::Redirect_308_PermanentRedirect => 308,
341
342 HttpCodes::ClientError_400_BadRequest => 400,
343 HttpCodes::ClientError_401_Unauthorized => 401,
344 HttpCodes::ClientError_402_PaymentRequired => 402,
345 HttpCodes::ClientError_403_Forbidden => 403,
346 HttpCodes::ClientError_404_NotFound => 404,
347 HttpCodes::ClientError_405_MethodNotAllowed => 405,
348 HttpCodes::ClientError_406_NotAcceptable => 406,
349 HttpCodes::ClientError_407_ProxyAuthenticationRequired => 407,
350 HttpCodes::ClientError_408_RequestTimeout => 408,
351 HttpCodes::ClientError_409_Conflict => 409,
352 HttpCodes::ClientError_410_Gone => 410,
353 HttpCodes::ClientError_411_LengthRequired => 411,
354 HttpCodes::ClientError_412_PreconditionFailed => 412,
355 HttpCodes::ClientError_413_PayloadTooLarge => 413,
356 HttpCodes::ClientError_414_URITooLong => 414,
357 HttpCodes::ClientError_415_UnsupportedMediaType => 415,
358 HttpCodes::ClientError_416_RangeNotSatisfiable => 416,
359 HttpCodes::ClientError_417_ExpectationFailed => 417,
360 HttpCodes::ClientError_418_ImATeapot => 418,
361 HttpCodes::ClientError_421_MisdirectedRequest => 421,
362 HttpCodes::ClientError_422_UnprocessableContent => 422,
363 HttpCodes::ClientError_423_Locked => 423,
364 HttpCodes::ClientError_424_FailedDependency => 424,
365 HttpCodes::ClientError_425_TooEarly => 425,
366 HttpCodes::ClientError_426_UpgradeRequired => 426,
367 HttpCodes::ClientError_428_PreconditionRequired => 427,
368 HttpCodes::ClientError_429_TooManyRequests => 429,
369 HttpCodes::ClientError_431_RequestHeaderFieldsTooLarge => 431,
370 HttpCodes::ClientError_451_UnavailableForLegalReasons => 451,
371
372 HttpCodes::ServerError_500_InternalServerError => 500,
373 HttpCodes::ServerError_501_NotImplemented => 501,
374 HttpCodes::ServerError_502_BadGateway => 502,
375 HttpCodes::ServerError_503_ServiceUnavailable => 503,
376 HttpCodes::ServerError_504_GatewayTimeout => 504,
377 HttpCodes::ServerError_505_HTTPVersionNotSupported => 505,
378 HttpCodes::ServerError_506_VariantAlsoNegotiates => 506,
379 HttpCodes::ServerError_507_InsufficientStorage => 507,
380 HttpCodes::ServerError_508_LoopDetected => 508,
381 HttpCodes::ServerError_510_NotExtended => 510,
382 HttpCodes::ServerError_511_NetworkAuthenticationRequired => 511,
383
384 HttpCodes::UnknownOther(v) => *v,
385 }
386 }
387
388 pub fn code_type(&self) -> HttpCodeType {
390 match self.code() {
391 100..=199 => HttpCodeType::Info,
392 200..=299 => HttpCodeType::Success,
393 300..=399 => HttpCodeType::Redirect,
394 400..=499 => HttpCodeType::ClientError,
395 500..=599 => HttpCodeType::ServerError,
396 _v => HttpCodeType::UnknownOther,
397 }
398 }
399}
400
401impl FromStr for HttpCodes {
402 type Err = ParseIntError;
403
404 fn from_str(value: &str) -> Result<Self, Self::Err> {
405 let val = u16::from_str(value)?;
406 Ok(match val {
407 100 => HttpCodes::Info_100_Continue,
408 101 => HttpCodes::Info_101_SwitchingProtocols,
409 102 => HttpCodes::Info_102_Processing,
410 103 => HttpCodes::Info_103_EarlyHints,
411
412 200 => HttpCodes::Success_200_Ok,
413 201 => HttpCodes::Success_201_Created,
414 202 => HttpCodes::Success_202_Accepted,
415 203 => HttpCodes::Success_203_NotAuthoritative,
416 204 => HttpCodes::Success_204_NoContent,
417 205 => HttpCodes::Success_205_ResetContent,
418 206 => HttpCodes::Success_206_PartialContent,
419 207 => HttpCodes::Success_207_MultiStatus,
420 208 => HttpCodes::Success_208_AlreadyReported,
421 226 => HttpCodes::Success_226_IMUsed,
422
423 300 => HttpCodes::Redirect_300_MultipleChoices,
424 301 => HttpCodes::Redirect_301_MovedPermanently,
425 302 => HttpCodes::Redirect_302_Found,
426 303 => HttpCodes::Redirect_303_SeeOther,
427 304 => HttpCodes::Redirect_304_NotModified,
428 305 => HttpCodes::Redirect_305_UseProxy,
429 306 => HttpCodes::Redirect_306_Unused,
430 307 => HttpCodes::Redirect_307_Temporary,
431 308 => HttpCodes::Redirect_308_PermanentRedirect,
432
433 400 => HttpCodes::ClientError_400_BadRequest,
434 401 => HttpCodes::ClientError_401_Unauthorized,
435 402 => HttpCodes::ClientError_402_PaymentRequired,
436 403 => HttpCodes::ClientError_403_Forbidden,
437 404 => HttpCodes::ClientError_404_NotFound,
438 405 => HttpCodes::ClientError_405_MethodNotAllowed,
439 406 => HttpCodes::ClientError_406_NotAcceptable,
440 407 => HttpCodes::ClientError_407_ProxyAuthenticationRequired,
441 408 => HttpCodes::ClientError_408_RequestTimeout,
442 409 => HttpCodes::ClientError_409_Conflict,
443 410 => HttpCodes::ClientError_410_Gone,
444 411 => HttpCodes::ClientError_411_LengthRequired,
445 412 => HttpCodes::ClientError_412_PreconditionFailed,
446 413 => HttpCodes::ClientError_413_PayloadTooLarge,
447 414 => HttpCodes::ClientError_414_URITooLong,
448 415 => HttpCodes::ClientError_415_UnsupportedMediaType,
449 416 => HttpCodes::ClientError_416_RangeNotSatisfiable,
450 417 => HttpCodes::ClientError_417_ExpectationFailed,
451 418 => HttpCodes::ClientError_418_ImATeapot,
452 421 => HttpCodes::ClientError_421_MisdirectedRequest,
453 422 => HttpCodes::ClientError_422_UnprocessableContent,
454 423 => HttpCodes::ClientError_423_Locked,
455 424 => HttpCodes::ClientError_424_FailedDependency,
456 425 => HttpCodes::ClientError_425_TooEarly,
457 426 => HttpCodes::ClientError_426_UpgradeRequired,
458 427 => HttpCodes::ClientError_428_PreconditionRequired,
459 429 => HttpCodes::ClientError_429_TooManyRequests,
460 431 => HttpCodes::ClientError_431_RequestHeaderFieldsTooLarge,
461 451 => HttpCodes::ClientError_451_UnavailableForLegalReasons,
462
463 500 => HttpCodes::ServerError_500_InternalServerError,
464 501 => HttpCodes::ServerError_501_NotImplemented,
465 502 => HttpCodes::ServerError_502_BadGateway,
466 503 => HttpCodes::ServerError_503_ServiceUnavailable,
467 504 => HttpCodes::ServerError_504_GatewayTimeout,
468 505 => HttpCodes::ServerError_505_HTTPVersionNotSupported,
469 506 => HttpCodes::ServerError_506_VariantAlsoNegotiates,
470 507 => HttpCodes::ServerError_507_InsufficientStorage,
471 508 => HttpCodes::ServerError_508_LoopDetected,
472 510 => HttpCodes::ServerError_510_NotExtended,
473 511 => HttpCodes::ServerError_511_NetworkAuthenticationRequired,
474 e => HttpCodes::UnknownOther(e),
475 })
476 }
477}
478
479#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
480pub enum HttpVersion {
481 Http1_0,
482
483 #[default]
484 Http1_1,
485 Http2,
486 Http3,
487}
488
489impl Display for HttpVersion {
490 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
491 write!(
492 f,
493 "{}",
494 match self {
495 HttpVersion::Http1_0 => "HTTP/1.0",
496 HttpVersion::Http1_1 => "HTTP/1.1",
497 HttpVersion::Http2 => "h2",
498 HttpVersion::Http3 => "h3",
499 }
500 )
501 }
502}
503
504pub enum HttpBody {
505 Empty,
506 Read(Box<dyn Read>),
507 String(String),
508 Bytes(Vec<u8>),
509}
510
511impl HttpBody {
512 pub fn write_to<T: Write>(self, out: &mut T) -> Result<(), std::io::Error> {
513 match self {
514 HttpBody::Empty => {}
515 HttpBody::Read(mut read) => {
516 std::io::copy(read.as_mut(), out)?;
517 }
518 HttpBody::String(s) => {
519 out.write_all(s.as_bytes())?;
520 }
521 HttpBody::Bytes(b) => {
522 out.write_all(&b)?;
523 }
524 }
525 Ok(())
526 }
527}