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
use std::borrow::Cow;
use std::fmt::Debug;

use http::response::Parts;
use hyper::body::Bytes;

#[derive(Debug, thiserror::Error, Clone)]
/// The provided request is invalid and should not be counted.
pub enum ValidationError {
    #[error("The returned status code is not valid: {0}")]
    /// The returned status code is not valid
    InvalidStatus(u16),
    #[error("The body of the request does not match the expected structure: {0}")]
    /// The body of the request does not match the expected structure
    InvalidBody(Cow<'static, str>),
    #[error("The request is missing a required header: {0}")]
    /// The request is missing a required header
    MissingHeader(Cow<'static, str>),
    #[error("The request contained a header, but it was invalid: {0}")]
    /// The request contained a header, but it was invalid
    InvalidHeader(Cow<'static, str>),
    #[error("The connection was aborted by the remote serve.")]
    /// The connection was aborted by the remote server
    ConnectionAborted,
    #[error("The connection took to long to respond")]
    /// The connection took to long to respond
    Timeout,
    #[error("A validation error rejected the request: {0}")]
    /// A validation error rejected the request
    Other(Cow<'static, str>),
}

/// A validating utility for checking responses returned by the webserver are correct.
///
/// It's important that these operations are light weight as they are called on the same
/// runtime as the request runtime which may block operations.
///
/// # Example
///
/// This example is just the [DefaultValidator] implementation, it can do as much or
/// as little as you'd like but it's important that it does not block heavily as it
/// will reduce the overall throughput of the worker thread.
///
/// ```
/// use http::response::Parts;
/// use hyper::body::Bytes;
/// use rewrk_core::{ResponseValidator, ValidationError};
///
/// #[derive(Debug)]
/// pub struct DefaultValidator;
///
/// impl ResponseValidator for DefaultValidator {
///     fn validate(&self, head: Parts, _body: Bytes) -> Result<(), ValidationError> {
///         if head.status.is_success() {
///             Ok(())
///         } else {
///             Err(ValidationError::InvalidStatus(head.status.as_u16()))
///         }
///     }
/// }
/// ```
pub trait ResponseValidator: Send + Sync + 'static {
    fn validate(&self, head: Parts, body: Bytes) -> Result<(), ValidationError>;
}

#[derive(Debug)]
/// The default validator handler.
pub struct DefaultValidator;

impl ResponseValidator for DefaultValidator {
    fn validate(&self, head: Parts, _body: Bytes) -> Result<(), ValidationError> {
        if head.status.is_success() {
            Ok(())
        } else {
            Err(ValidationError::InvalidStatus(head.status.as_u16()))
        }
    }
}