1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3#[cfg(feature = "server")]
6#[macro_use]
7extern crate log;
8
9#[cfg(feature = "openssl")]
10mod tls;
11
12pub mod request;
13pub mod response;
14
15#[cfg(feature = "client")]
16#[cfg_attr(docsrs, doc(cfg(feature = "client")))]
17pub mod client;
18
19#[cfg(feature = "server")]
20#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
21pub mod server;
22
23#[cfg(feature = "ws")]
24#[cfg_attr(docsrs, doc(cfg(feature = "ws")))]
25pub mod ws;
26
27#[cfg(any(feature = "client", feature = "server", feature = "ws"))]
28#[cfg_attr(
29 docsrs,
30 doc(cfg(any(feature = "client", feature = "server", feature = "ws")))
31)]
32pub mod connection {
34 pub use ttpkit_utils::io::{
35 Connection, ConnectionBuilder, ConnectionReader, ConnectionWriter, UpgradeFuture,
36 UpgradeRequest, Upgraded,
37 };
38}
39
40use std::{
41 fmt::{self, Display, Formatter},
42 io,
43 str::FromStr,
44};
45
46use bytes::Bytes;
47use ttpkit::Error as BaseError;
48
49#[cfg(feature = "server")]
50use self::server::OutgoingResponse;
51
52pub use ttpkit::{
53 self,
54 body::{self, Body},
55 error::CodecError,
56 header,
57};
58
59#[cfg(any(feature = "client", feature = "server"))]
60#[cfg_attr(docsrs, doc(cfg(any(feature = "client", feature = "server"))))]
61pub use ttpkit_url as url;
62
63pub use self::{
64 request::{Request, RequestHeader},
65 response::{Response, ResponseHeader, Status},
66};
67
68#[derive(Debug)]
70enum InnerError {
71 Error(BaseError),
72
73 #[cfg(feature = "server")]
74 ErrorWithResponse(Box<dyn ErrorToResponse + Send + Sync>),
75}
76
77#[derive(Debug)]
79pub struct Error {
80 inner: InnerError,
81}
82
83impl Error {
84 pub fn from_msg<T>(msg: T) -> Self
86 where
87 T: Into<String>,
88 {
89 Self {
90 inner: InnerError::Error(BaseError::from_msg(msg)),
91 }
92 }
93
94 #[inline]
96 pub const fn from_static_msg(msg: &'static str) -> Self {
97 Self {
98 inner: InnerError::Error(BaseError::from_static_msg(msg)),
99 }
100 }
101
102 pub fn from_other<T>(err: T) -> Self
104 where
105 T: Into<Box<dyn std::error::Error + Send + Sync>>,
106 {
107 Self {
108 inner: InnerError::Error(BaseError::from_cause(err)),
109 }
110 }
111
112 #[cfg(feature = "server")]
114 #[cfg_attr(docsrs, doc(cfg(feature = "server")))]
115 pub fn from_other_with_response<T>(err: T) -> Self
116 where
117 T: ErrorToResponse + Send + Sync + 'static,
118 {
119 Self {
120 inner: InnerError::ErrorWithResponse(Box::new(err)),
121 }
122 }
123
124 #[cfg(feature = "server")]
126 #[cfg_attr(docsrs, doc(cfg(feature = "server")))]
127 pub fn to_response(&self) -> Option<OutgoingResponse> {
128 if let InnerError::ErrorWithResponse(err) = &self.inner {
129 Some(err.to_response())
130 } else {
131 None
132 }
133 }
134}
135
136impl Display for Error {
137 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
138 match &self.inner {
139 InnerError::Error(err) => Display::fmt(err, f),
140
141 #[cfg(feature = "server")]
142 InnerError::ErrorWithResponse(err) => Display::fmt(err, f),
143 }
144 }
145}
146
147impl std::error::Error for Error {
148 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
149 match &self.inner {
150 InnerError::Error(err) => err.source(),
151
152 #[cfg(feature = "server")]
153 InnerError::ErrorWithResponse(err) => err.source(),
154 }
155 }
156}
157
158impl From<io::Error> for Error {
159 #[inline]
160 fn from(err: io::Error) -> Self {
161 Self {
162 inner: InnerError::Error(BaseError::from_static_msg_and_cause("IO", err)),
163 }
164 }
165}
166
167impl From<ttpkit::Error> for Error {
168 #[inline]
169 fn from(err: ttpkit::Error) -> Self {
170 Self {
171 inner: InnerError::Error(err),
172 }
173 }
174}
175
176impl From<Error> for ttpkit::Error {
177 fn from(err: Error) -> Self {
178 match err.inner {
179 InnerError::Error(err) => err,
180
181 #[cfg(feature = "server")]
182 InnerError::ErrorWithResponse(_) => ttpkit::Error::from_cause(err),
183 }
184 }
185}
186
187#[cfg(feature = "server")]
189#[cfg_attr(docsrs, doc(cfg(feature = "server")))]
190pub trait ErrorToResponse: std::error::Error {
191 fn to_response(&self) -> OutgoingResponse;
193}
194
195#[derive(Debug, Copy, Clone, Eq, PartialEq)]
197struct Protocol;
198
199impl AsRef<[u8]> for Protocol {
200 #[inline]
201 fn as_ref(&self) -> &[u8] {
202 b"HTTP"
203 }
204}
205
206impl TryFrom<Bytes> for Protocol {
207 type Error = Error;
208
209 fn try_from(value: Bytes) -> Result<Self, Self::Error> {
210 match value.as_ref() {
211 b"HTTP" => Ok(Self),
212 _ => Err(Error::from_static_msg("invalid protocol string")),
213 }
214 }
215}
216
217#[derive(Debug, Copy, Clone, Eq, PartialEq)]
219pub enum Version {
220 Version10,
221 Version11,
222}
223
224impl AsRef<[u8]> for Version {
225 #[inline]
226 fn as_ref(&self) -> &[u8] {
227 match *self {
228 Self::Version10 => b"1.0",
229 Self::Version11 => b"1.1",
230 }
231 }
232}
233
234impl AsRef<str> for Version {
235 #[inline]
236 fn as_ref(&self) -> &str {
237 match *self {
238 Self::Version10 => "1.0",
239 Self::Version11 => "1.1",
240 }
241 }
242}
243
244impl Display for Version {
245 #[inline]
246 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
247 f.write_str(self.as_ref())
248 }
249}
250
251impl TryFrom<Bytes> for Version {
252 type Error = Error;
253
254 fn try_from(value: Bytes) -> Result<Self, Self::Error> {
255 let v = match value.as_ref() {
256 b"1.0" => Self::Version10,
257 b"1.1" => Self::Version11,
258 _ => return Err(Error::from_static_msg("unsupported HTTP protocol version")),
259 };
260
261 Ok(v)
262 }
263}
264
265#[derive(Debug, Copy, Clone, Eq, PartialEq)]
267pub enum Method {
268 Options,
269 Head,
270 Get,
271 Post,
272 Put,
273 Delete,
274 Trace,
275 Connect,
276}
277
278impl AsRef<[u8]> for Method {
279 fn as_ref(&self) -> &[u8] {
280 match *self {
281 Self::Options => b"OPTIONS",
282 Self::Head => b"HEAD",
283 Self::Get => b"GET",
284 Self::Post => b"POST",
285 Self::Put => b"PUT",
286 Self::Delete => b"DELETE",
287 Self::Trace => b"TRACE",
288 Self::Connect => b"CONNECT",
289 }
290 }
291}
292
293impl AsRef<str> for Method {
294 #[inline]
295 fn as_ref(&self) -> &str {
296 match *self {
297 Self::Options => "OPTIONS",
298 Self::Head => "HEAD",
299 Self::Get => "GET",
300 Self::Post => "POST",
301 Self::Put => "PUT",
302 Self::Delete => "DELETE",
303 Self::Trace => "TRACE",
304 Self::Connect => "CONNECT",
305 }
306 }
307}
308
309impl Display for Method {
310 #[inline]
311 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
312 f.write_str(self.as_ref())
313 }
314}
315
316impl TryFrom<Bytes> for Method {
317 type Error = Error;
318
319 fn try_from(value: Bytes) -> Result<Self, Self::Error> {
320 let m = match value.as_ref() {
321 b"OPTIONS" => Self::Options,
322 b"HEAD" => Self::Head,
323 b"GET" => Self::Get,
324 b"POST" => Self::Post,
325 b"PUT" => Self::Put,
326 b"DELETE" => Self::Delete,
327 b"TRACE" => Self::Trace,
328 b"CONNECT" => Self::Connect,
329 _ => return Err(Error::from_static_msg("unsupported HTTP method")),
330 };
331
332 Ok(m)
333 }
334}
335
336#[allow(clippy::upper_case_acronyms)]
338#[derive(Debug, Copy, Clone, Eq, PartialEq)]
339pub enum Scheme {
340 HTTP,
341 HTTPS,
342}
343
344impl Scheme {
345 #[inline]
347 pub const fn default_port(self) -> u16 {
348 match self {
349 Scheme::HTTP => 80,
350 Scheme::HTTPS => 443,
351 }
352 }
353}
354
355impl AsRef<str> for Scheme {
356 #[inline]
357 fn as_ref(&self) -> &str {
358 match *self {
359 Self::HTTP => "http",
360 Self::HTTPS => "https",
361 }
362 }
363}
364
365impl Display for Scheme {
366 #[inline]
367 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
368 f.write_str(self.as_ref())
369 }
370}
371
372impl FromStr for Scheme {
373 type Err = Error;
374
375 fn from_str(method: &str) -> Result<Self, Self::Err> {
376 let scheme = match &method.to_lowercase() as &str {
377 "http" => Scheme::HTTP,
378 "https" => Scheme::HTTPS,
379 _ => return Err(Error::from_static_msg("invalid HTTP URL scheme")),
380 };
381
382 Ok(scheme)
383 }
384}