rcs_backend 0.2.1

otafablab rust code server backend
Documentation
use crate::config::Config;
use crate::errors::DecodeTokenError;
use backoff::ExponentialBackoffBuilder;
use cookie::Cookie;
use http::{header::COOKIE, uri::Authority, HeaderMap, HeaderValue, Uri};
use hyper::Client;
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
use serde::{Deserialize, Serialize};

pub(crate) type HttpClient = Client<hyper::client::HttpConnector>;

#[derive(Debug, Serialize, Deserialize)]
/// The [auth
/// cookie](https://authp.github.io/docs/authenticate/auth-cookie) set
/// by caddy-security
pub(crate) struct AuthCookie {
    pub exp: usize,
    pub iat: usize,
    pub iss: String,
    pub nbf: usize,
    pub sub: String,
    pub name: String,
    pub email: String,
    pub origin: String,
    pub addr: String,
    pub picture: String,
}

/// Validates and decodes the auth cookie
pub(crate) fn decode_auth_cookie(
    headers: &HeaderMap<HeaderValue>,
    config: &Config,
) -> Result<AuthCookie, DecodeTokenError> {
    let cookie_header = headers
        .get(COOKIE)
        .ok_or(DecodeTokenError::NoCookieHeader)?;

    let cookie = cookie_header
        .to_str()
        .unwrap()
        .split(';')
        .map(str::trim)
        .filter(|s| !s.is_empty())
        .flat_map(Cookie::parse)
        .find(|cookie| cookie.name() == config.auth_cookie_name)
        .ok_or(DecodeTokenError::ParseError)?;

    let token = decode::<AuthCookie>(
        cookie.value(),
        &DecodingKey::from_secret(config.secret.as_ref().as_bytes()),
        &Validation::new(Algorithm::HS512),
    )
    .map_err(DecodeTokenError::JWTError)?;

    Ok(token.claims)
}

/// Checks if the HTTP server at `authority` is alive
pub(crate) async fn http_healthcheck(
    client: HttpClient,
    authority: Authority,
) -> Result<(), backoff::Error<hyper::Error>> {
    println!("waiting for server {} to start up", &authority);
    let uri = Uri::builder()
        .scheme("http")
        .authority(authority)
        .path_and_query("/")
        .build()
        .unwrap();
    client.get(uri).await?;
    Ok(())
}

/// Waits for the HTTP server at `authority`
pub(crate) async fn wait_for_http_service(authority: Authority) {
    let client = Client::new();

    backoff::future::retry(
        ExponentialBackoffBuilder::new()
            .with_multiplier(1.5)
            .build(),
        || http_healthcheck(client.clone(), authority.clone()),
    )
    .await
    .unwrap();
}