omaha_client/
http_request.rs

1// Copyright 2019 The Fuchsia Authors
2//
3// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
4// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
5// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
6// This file may not be copied, modified, or distributed except according to
7// those terms.
8
9use {
10    futures::future::BoxFuture,
11    futures::prelude::*,
12    hyper::{Body, Request, Response},
13};
14
15pub mod mock;
16
17/// A trait for providing HTTP capabilities to the StateMachine.
18///
19/// This trait is a wrapper around Hyper, to provide a simple request->response style of API for
20/// the state machine to use.
21///
22/// In particular, it's meant to be easy to mock for tests.
23pub trait HttpRequest {
24    /// Make a request, and return an Response, as the header Parts and collect the entire collected
25    /// Body as a Vec of bytes.
26    fn request(&mut self, req: Request<Body>) -> BoxFuture<'_, Result<Response<Vec<u8>>, Error>>;
27}
28
29#[derive(Debug, thiserror::Error)]
30// Parentheses are needed for .source, but will trigger unused_parens, so a tuple is used.
31#[error("Http request failed: {}", match (.source, ()).0 {
32    Some(source) => format!("{source}"),
33    None => format!("kind: {:?}", .kind),
34})]
35pub struct Error {
36    kind: ErrorKind,
37    #[source]
38    source: Option<hyper::Error>,
39}
40
41#[derive(Debug, Eq, PartialEq)]
42enum ErrorKind {
43    User,
44    Transport,
45    Timeout,
46}
47
48impl Error {
49    /// Create a timeout error
50    ///
51    /// This is valid for use in tests as well as production implementations of the trait, if
52    /// application-layer timeouts are being implemented.
53    pub fn new_timeout() -> Self {
54        Self {
55            kind: ErrorKind::Timeout,
56            source: None,
57        }
58    }
59
60    /// Returns true if this error the result of the Hyper API being incorrectly used (a "user"
61    /// error in Hyper)
62    pub fn is_user(&self) -> bool {
63        self.kind == ErrorKind::User
64    }
65
66    /// Returns true if this error is the result of a timeout when trying to full-fill the request
67    ///
68    /// Note: Connect timeouts may be returned as io errors,  not timeouts, depending on where in
69    /// the network / http client stack the timeout occurs in.
70    pub fn is_timeout(&self) -> bool {
71        self.kind == ErrorKind::Timeout
72    }
73}
74
75impl From<hyper::Error> for Error {
76    fn from(error: hyper::Error) -> Self {
77        let kind = if error.is_user() {
78            ErrorKind::User
79        } else {
80            ErrorKind::Transport
81        };
82        Error {
83            kind,
84            source: error.into(),
85        }
86    }
87}
88
89pub mod mock_errors {
90    use super::*;
91
92    pub fn make_user_error() -> Error {
93        Error {
94            kind: ErrorKind::User,
95            source: None,
96        }
97    }
98
99    pub fn make_transport_error() -> Error {
100        Error {
101            kind: ErrorKind::Transport,
102            source: None,
103        }
104    }
105}
106
107/// A stub HttpRequest that does nothing and returns an empty response immediately.
108pub struct StubHttpRequest;
109
110impl HttpRequest for StubHttpRequest {
111    fn request(&mut self, _req: Request<Body>) -> BoxFuture<'_, Result<Response<Vec<u8>>, Error>> {
112        future::ok(Response::default()).boxed()
113    }
114}