apollo-http-client 0.2.0

HTTP client for Apollo platform
Documentation
use apollo_errors::Error;
use apollo_errors::miette;
use tower::BoxError;

/// Errors produced by the HTTP client.
#[non_exhaustive]
#[derive(Debug, Error, miette::Diagnostic)]
pub enum HttpClientError {
    /// The HTTP client could not be constructed.
    #[error("failed to build HTTP client")]
    #[diagnostic(code(http_client::build_failed))]
    Build {
        /// The underlying error.
        #[from]
        source: rustls::Error,
    },

    /// `tls.certificate_authorities` could not be parsed as PEM.
    #[error("failed to parse `tls.certificate_authorities` as PEM")]
    #[diagnostic(code(http_client::tls_certificate_authorities_invalid))]
    TlsCertificateAuthoritiesInvalid {
        /// The underlying error.
        #[source]
        source: std::io::Error,
    },

    /// `tls.certificate_authorities` contained no PEM certificate blocks.
    #[error("`tls.certificate_authorities` contained no certificates")]
    #[diagnostic(code(http_client::tls_certificate_authorities_empty))]
    TlsCertificateAuthoritiesEmpty,

    /// TLS verification is enabled but no trust anchors are configured.
    #[error(
        "TLS has no trust anchors: set `tls.certificate_authorities`, enable \
         `tls.use_native_certificate_store`, or set `tls.danger_accept_invalid_certs` \
         for development only"
    )]
    #[diagnostic(code(http_client::tls_no_trust_anchors))]
    TlsNoTrustAnchors,

    /// `tls.client_authentication.certificate_chain` could not be parsed as PEM.
    #[error("failed to parse `tls.client_authentication.certificate_chain` as PEM")]
    #[diagnostic(code(http_client::tls_client_certificate_chain_invalid))]
    TlsClientCertificateChainInvalid {
        /// The underlying error.
        #[source]
        source: std::io::Error,
    },

    /// `tls.client_authentication.certificate_chain` contained no certificates.
    #[error("`tls.client_authentication.certificate_chain` contained no certificates")]
    #[diagnostic(code(http_client::tls_client_certificate_chain_empty))]
    TlsClientCertificateChainEmpty,

    /// `tls.client_authentication.key` could not be parsed as PEM.
    #[error("failed to parse `tls.client_authentication.key` as PEM")]
    #[diagnostic(code(http_client::tls_client_key_invalid))]
    TlsClientKeyInvalid {
        /// The underlying error.
        #[source]
        source: std::io::Error,
    },

    /// `tls.client_authentication.key` contained no recognised private key block.
    #[error(
        "`tls.client_authentication.key` contained no PKCS#1, PKCS#8, or SEC1 private key block"
    )]
    #[diagnostic(code(http_client::tls_client_key_missing))]
    TlsClientKeyMissing,

    /// `tls.client_authentication.key` is encrypted. Encrypted keys are not supported.
    #[error(
        "`tls.client_authentication.key` is encrypted; only unencrypted private keys are supported"
    )]
    #[diagnostic(code(http_client::tls_client_key_encrypted))]
    TlsClientKeyEncrypted,

    /// `tls.client_authentication` was rejected by rustls — typically because
    /// the private key is not usable as a TLS signer or does not match the
    /// leaf certificate's public key.
    ///
    /// Cert/key pair mismatches that pass the build but fail the handshake
    /// surface as [`Transport`](Self::Transport).
    #[error(
        "failed to configure TLS client authentication from `tls.client_authentication` \
         — verify `certificate_chain` and `key` are from the same key pair"
    )]
    #[diagnostic(code(http_client::tls_client_auth))]
    TlsClientAuth {
        /// The underlying error.
        #[source]
        source: rustls::Error,
    },

    /// The request URI could not be constructed from its components.
    #[error("invalid request URI")]
    #[diagnostic(code(http_client::invalid_uri))]
    InvalidUri {
        /// The underlying error.
        #[source]
        source: http::Error,
    },

    /// The connection timed out before being established.
    #[error("connection timed out")]
    #[diagnostic(code(http_client::connection_timeout))]
    ConnectionTimeout,

    /// A TCP or TLS transport error.
    #[error("transport error")]
    #[diagnostic(code(http_client::transport_error))]
    Transport {
        /// The underlying error.
        #[source]
        source: BoxError,
    },

    /// The HTTP/1.1 or HTTP/2 handshake failed.
    #[error("HTTP handshake failed")]
    #[diagnostic(code(http_client::handshake_failed))]
    Handshake {
        /// The underlying error.
        #[source]
        source: hyper::Error,
    },

    /// Sending or receiving the HTTP request failed.
    #[error("HTTP request failed")]
    #[diagnostic(code(http_client::request_failed))]
    Request {
        /// The underlying error.
        #[source]
        source: hyper::Error,
    },

    /// The proxy returned a non-200 response to a CONNECT request.
    #[error("proxy CONNECT rejected with status {status}")]
    #[diagnostic(code(http_client::proxy_connect_rejected))]
    ProxyTunnel {
        /// The HTTP status code returned by the proxy.
        status: http::StatusCode,
    },

    /// The configuration failed validation.
    ///
    /// `apollo_configuration::parse_yaml` runs validation before returning, so
    /// this only fires when the config reached [`HttpClient::new`] through a
    /// path that bypassed it.
    ///
    /// [`HttpClient::new`]: crate::HttpClient::new
    #[error("invalid HTTP client configuration")]
    #[diagnostic(code(http_client::invalid_config))]
    InvalidConfig {
        /// The collected validation errors.
        #[source]
        source: apollo_configuration::ValidationErrors,
    },

    /// A `unix://` URI's authority could not be hex-decoded into a socket path.
    #[cfg(unix)]
    #[error("invalid Unix socket path in URI authority")]
    #[diagnostic(code(http_client::invalid_unix_socket_path))]
    InvalidUnixSocketPath {
        /// The underlying decoding error.
        #[source]
        source: hex::FromHexError,
    },

    /// A `unix://` URI's authority decoded to bytes that are not valid UTF-8.
    ///
    /// Non-UTF-8 socket paths are not supported.
    #[cfg(unix)]
    #[error("Unix socket path is not valid UTF-8")]
    #[diagnostic(code(http_client::non_utf8_unix_socket_path))]
    NonUtf8UnixSocketPath {
        /// The underlying UTF-8 decoding error.
        #[source]
        source: std::string::FromUtf8Error,
    },

    /// The request URI's scheme is not supported on this platform.
    #[error("unsupported URI scheme: {scheme:?}")]
    #[diagnostic(code(http_client::unsupported_scheme))]
    UnsupportedScheme {
        /// The scheme from the request URI.
        scheme: String,
    },
}