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