1#![warn(rust_2018_idioms)]
46
47use std::error::Error as StdError;
48use std::fmt;
49use std::io;
50use std::time::Duration;
51
52use tao_log::warn;
53
54use body_image::{
55 BodyImage, BodyError, Encoding,
56 Prolog, RequestRecorded,
57};
58
59#[cfg(feature = "hyper-http")]
60use body_image::{Epilog, Dialog, BodySink};
61
62pub type Flaw = Box<dyn StdError + Send + Sync + 'static>;
67
68mod blocking;
69pub use blocking::{Blocking, BlockingArbiter, LenientArbiter, StatefulArbiter};
70
71mod decode;
72pub use decode::{decode_res_body, find_encodings, find_chunked};
73
74#[cfg(feature = "hyper-http")] mod fetch;
75#[cfg(feature = "hyper-http")] pub use self::fetch::{fetch, request_dialog};
76
77mod tune;
78pub use tune::{BlockingPolicy, FutioTunables, FutioTuner};
79
80mod sink;
81pub use sink::{InputBuf, AsyncBodySink, DispatchBodySink, PermitBodySink};
82
83mod stream;
84pub use stream::{
85 AsyncBodyImage, OutputBuf,
86 DispatchBodyImage, PermitBodyImage,
87 SplitBodyImage, YieldBodyImage,
88};
89
90#[cfg(feature = "mmap")] mod mem_map_buf;
91#[cfg(feature = "mmap")] use mem_map_buf::MemMapBuf;
92
93mod uni_body_buf;
94pub use uni_body_buf::UniBodyBuf;
95
96mod wrappers;
97pub use wrappers::{SinkWrapper, StreamWrapper, RequestRecorder};
98
99pub static VERSION: &str = env!("CARGO_PKG_VERSION");
101
102#[cfg(feature = "brotli")]
105pub static ACCEPT_ENCODINGS: &str = "br, gzip, deflate";
106
107#[cfg(not(feature = "brotli"))]
110pub static ACCEPT_ENCODINGS: &str = "gzip, deflate";
111
112pub static BROWSE_ACCEPT: &str =
115 "text/html, application/xhtml+xml, \
116 application/xml;q=0.9, \
117 */*;q=0.8";
118
119#[derive(Debug)]
123pub enum FutioError {
124 Body(BodyError),
126
127 ResponseTimeout(Duration),
130
131 BodyTimeout(Duration),
134
135 ContentLengthTooLong(u64),
137
138 Http(http::Error),
140
141 #[cfg(feature = "hyper-http")]
143 Hyper(hyper::Error),
144
145 UnsupportedEncoding(Encoding),
148
149 OpCanceled(blocking_permit::Canceled),
152
153 Other(Flaw),
155
156 _FutureProof
159}
160
161impl fmt::Display for FutioError {
162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163 match self {
164 FutioError::Body(ref be) =>
165 write!(f, "With body: {}", be),
166 FutioError::ResponseTimeout(d) =>
167 write!(f, "Timeout before initial response ({:?})", d),
168 FutioError::BodyTimeout(d) =>
169 write!(f, "Timeout before streaming body complete ({:?})", d),
170 FutioError::ContentLengthTooLong(l) =>
171 write!(f, "Response Content-Length too long: {}", l),
172 FutioError::Http(ref e) =>
173 write!(f, "Http error: {}", e),
174 #[cfg(feature = "hyper-http")]
175 FutioError::Hyper(ref e) =>
176 write!(f, "Hyper error: {}", e),
177 FutioError::UnsupportedEncoding(e) =>
178 write!(f, "Unsupported encoding: {}", e),
179 FutioError::OpCanceled(e) =>
180 write!(f, "Error: {}", e),
181 FutioError::Other(ref flaw) =>
182 write!(f, "Other error: {}", flaw),
183 FutioError::_FutureProof =>
184 unreachable!("Don't abuse the _FutureProof!")
185 }
186 }
187}
188
189impl StdError for FutioError {
190 fn source(&self) -> Option<&(dyn StdError + 'static)> {
191 match *self {
192 FutioError::Body(ref be) => Some(be),
193 FutioError::Http(ref ht) => Some(ht),
194 #[cfg(feature = "hyper-http")]
195 FutioError::Hyper(ref he) => Some(he),
196 FutioError::OpCanceled(ref ce) => Some(ce),
197 FutioError::Other(ref flaw) => Some(flaw.as_ref()),
198 _ => None
199 }
200 }
201}
202
203impl From<BodyError> for FutioError {
204 fn from(err: BodyError) -> FutioError {
205 FutioError::Body(err)
206 }
207}
208
209impl From<http::Error> for FutioError {
210 fn from(err: http::Error) -> FutioError {
211 FutioError::Http(err)
212 }
213}
214
215#[cfg(feature = "hyper-http")]
216impl From<hyper::Error> for FutioError {
217 fn from(err: hyper::Error) -> FutioError {
218 FutioError::Hyper(err)
219 }
220}
221
222impl From<io::Error> for FutioError {
223 fn from(err: io::Error) -> FutioError {
224 FutioError::Body(BodyError::Io(err))
225 }
226}
227
228pub fn user_agent() -> String {
234 format!("Mozilla/5.0 (compatible; body-image {}; \
235 +https://crates.io/crates/body-image)",
236 VERSION)
237}
238
239#[derive(Debug)]
248pub struct RequestRecord<B> {
249 request: http::Request<B>,
250 prolog: Prolog,
251}
252
253impl<B> RequestRecord<B> {
254 pub fn method(&self) -> &http::Method { &self.prolog.method }
256
257 pub fn url(&self) -> &http::Uri { &self.prolog.url }
259
260 pub fn request(&self) -> &http::Request<B> { &self.request }
262}
263
264impl<B> RequestRecorded for RequestRecord<B> {
265 fn req_headers(&self) -> &http::HeaderMap { &self.prolog.req_headers }
266 fn req_body(&self) -> &BodyImage { &self.prolog.req_body }
267}
268
269#[cfg(feature = "hyper-http")]
272#[derive(Debug)]
273struct Monolog {
274 prolog: Prolog,
275 response: http::Response<hyper::Body>,
276}
277
278#[derive(Debug)]
280#[cfg(feature = "hyper-http")]
281struct InDialog {
282 prolog: Prolog,
283 version: http::Version,
284 status: http::StatusCode,
285 res_headers: http::HeaderMap,
286 res_body: BodySink,
287}
288
289#[cfg(feature = "hyper-http")]
290impl InDialog {
291 fn prepare(self) -> Result<Dialog, FutioError> {
295 let res_decoded = if find_chunked(&self.res_headers) {
296 vec![Encoding::Chunked]
297 } else {
298 Vec::with_capacity(0)
299 };
300
301 Ok(Dialog::new(
302 self.prolog,
303 Epilog {
304 version: self.version,
305 status: self.status,
306 res_headers: self.res_headers,
307 res_body: self.res_body.prepare()?,
308 res_decoded,
309 }
310 ))
311 }
312}
313
314#[cfg(test)]
315mod tests {
316 mod forward;
317
318 #[cfg(feature = "hyper-http")]
319 mod server;
320
321 #[cfg(all(feature = "hyper-http", feature = "may-fail"))]
323 mod live;
324
325 use tao_log::{debug, debugv};
326 use super::{FutioError, Flaw};
327 use piccolog::test_logger;
328 use std::mem::size_of;
329
330 fn is_flaw(f: Flaw) -> bool {
331 debug!("Flaw Debug: {:?}, Display: \"{}\"", f, f);
332 true
333 }
334
335 #[test]
336 fn test_error_as_flaw() {
337 assert!(test_logger());
338 assert!(is_flaw(FutioError::ContentLengthTooLong(42).into()));
339 assert!(is_flaw(FutioError::Other("one off".into()).into()));
340 }
341
342 #[test]
343 fn test_error_size() {
344 assert!(test_logger());
345 assert!(debugv!(size_of::<FutioError>()) <= 32);
346 }
347
348 #[test]
349 #[should_panic]
350 fn test_error_future_proof() {
351 assert!(!FutioError::_FutureProof.to_string().is_empty(),
352 "should have panic'd before, unreachable")
353 }
354}