cloud-sdk-reqwest 0.30.0

Optional provider-neutral reqwest transport boundary for cloud-sdk.
Documentation
use std::{string::String, vec::Vec};

use cloud_sdk::authentication::{
    AuthenticatedRequest, AuthenticationScopePolicy, BlockingAuthenticatedTransport,
    ScopeRequirement,
};
use cloud_sdk::rate_limit::RateLimit;
use cloud_sdk::transport::{
    BoundTransport, EndpointIdentity, HeaderName, MediaType, RawResponsePolicy, ResponseBuffer,
    ResponseMediaPolicy, StatusCode, TransportRequest, TransportResponse,
};

use super::super::{
    BearerCredential, BearerCredentialScope, BearerToken, BlockingClient, HttpsEndpoint,
    TransportError,
};

pub(super) fn test_credential(token: BearerToken, endpoint: &HttpsEndpoint) -> BearerCredential {
    BearerCredential::new(
        token,
        BearerCredentialScope::new(
            cloud_sdk::provider_id!("example"),
            cloud_sdk::service_id!("compute"),
            endpoint.clone(),
        ),
    )
}

pub(super) fn authenticated<'request, 'endpoint>(
    client: &'endpoint BlockingClient,
    request: TransportRequest<'request>,
) -> AuthenticatedRequest<'request, 'endpoint> {
    let endpoint = client
        .endpoint_identity()
        .unwrap_or_else(|_| unreachable!());
    AuthenticatedRequest::new(
        request,
        test_authentication_policy(endpoint),
        test_raw_response_policy(),
    )
}

pub(super) fn test_raw_response_policy() -> RawResponsePolicy<'static> {
    let names = [
        "content-type",
        "ratelimit-limit",
        "ratelimit-remaining",
        "ratelimit-reset",
    ];
    let headers = names.map(|name| HeaderName::new(name).unwrap_or_else(|_| std::process::abort()));
    RawResponsePolicy::new(
        8192,
        8192,
        ResponseMediaPolicy::Optional(&[MediaType::JSON]),
        ResponseMediaPolicy::Optional(&[MediaType::JSON]),
        &headers,
        8,
    )
    .unwrap_or_else(|_| std::process::abort())
}

const fn test_authentication_policy(
    endpoint: EndpointIdentity<'_>,
) -> AuthenticationScopePolicy<'_> {
    AuthenticationScopePolicy::new(
        ScopeRequirement::Required(cloud_sdk::provider_id!("example")),
        ScopeRequirement::Required(cloud_sdk::service_id!("compute")),
        ScopeRequirement::Required(endpoint),
        ScopeRequirement::Forbidden,
        ScopeRequirement::Forbidden,
        ScopeRequirement::Forbidden,
    )
}

pub(super) struct CapturedResponse {
    status: StatusCode,
    body: Vec<u8>,
    content_type: Option<String>,
    rate_limit: Option<RateLimit>,
    rate_limit_remaining: Option<Vec<u8>>,
    content_type_header: Option<Vec<u8>>,
}

impl CapturedResponse {
    fn capture(response: TransportResponse<'_, '_>) -> Self {
        Self {
            status: response.status(),
            body: response.body().to_vec(),
            content_type: response
                .content_type()
                .ok()
                .flatten()
                .map(|content_type| String::from(content_type.as_str())),
            rate_limit: response.rate_limit(),
            rate_limit_remaining: response
                .headers()
                .get("ratelimit-remaining")
                .map(|header| header.value().to_vec()),
            content_type_header: response
                .headers()
                .get("content-type")
                .map(|header| header.value().to_vec()),
        }
    }

    pub(super) const fn status(&self) -> StatusCode {
        self.status
    }

    pub(super) fn body(&self) -> &[u8] {
        &self.body
    }

    pub(super) fn content_type(&self) -> Option<&str> {
        self.content_type.as_deref()
    }

    pub(super) const fn rate_limit(&self) -> Option<RateLimit> {
        self.rate_limit
    }

    pub(super) fn rate_limit_remaining_header(&self) -> Option<&[u8]> {
        self.rate_limit_remaining.as_deref()
    }

    pub(super) fn content_type_header(&self) -> Option<&[u8]> {
        self.content_type_header.as_deref()
    }
}

pub(super) fn send_test(
    client: &BlockingClient,
    request: TransportRequest<'_>,
    output: &mut [u8],
) -> Result<CapturedResponse, TransportError> {
    let capacity = output.len();
    let mut headers = [0_u8; 8192];
    let mut response = ResponseBuffer::new(output, capacity, &mut headers);
    BlockingAuthenticatedTransport::send_authenticated(
        client,
        authenticated(client, request),
        response.writer(),
    )
    .map_err(cloud_sdk::transport::TransportFailure::into_error)?;
    response
        .with_response(CapturedResponse::capture)
        .map_err(|_| TransportError::ResponseCommitFailed)
}