v0.2.11-pre.003-fix.001
This commit is contained in:
@@ -1,30 +1,30 @@
|
||||
// file: crates/ksp-offchain-transport-lib/src/error.rs
|
||||
// version: 4
|
||||
// version: 5
|
||||
|
||||
/// Stable off-chain transport error for an invalid crate-wide HTTP runtime configuration.
|
||||
pub const ERROR_CODE_HTTP_SETTINGS_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_settings_invalid");
|
||||
/// Stable off-chain transport error when the hardened reqwest client cannot be initialized.
|
||||
pub const ERROR_CODE_HTTP_CLIENT_BUILD_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_client_build_failed");
|
||||
/// Stable off-chain transport error for an invalid crate-owned HTTP request definition.
|
||||
pub const ERROR_CODE_HTTP_REQUEST_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_request_invalid");
|
||||
/// Stable off-chain transport error for a connection failure without exposing the provider URL.
|
||||
pub const ERROR_CODE_HTTP_CONNECTION_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_connection_failed");
|
||||
/// Stable off-chain transport error for an end-to-end HTTP timeout.
|
||||
pub const ERROR_CODE_HTTP_TIMEOUT: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_timeout");
|
||||
/// Stable off-chain transport error for a generic unsuccessful or transport-level HTTP request.
|
||||
pub const ERROR_CODE_HTTP_REQUEST_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_request_failed");
|
||||
/// Stable off-chain transport error for HTTP 401/403 access denial.
|
||||
pub const ERROR_CODE_HTTP_ACCESS_DENIED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_access_denied");
|
||||
/// Stable off-chain transport error for HTTP 429 rate limiting.
|
||||
pub const ERROR_CODE_HTTP_RATE_LIMITED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_rate_limited");
|
||||
/// Stable off-chain transport error for transient HTTP status failures such as 408 or 5xx.
|
||||
pub const ERROR_CODE_HTTP_TEMPORARY_FAILURE: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_temporary_failure");
|
||||
/// Stable off-chain transport error when a response exceeds the defensive body limit.
|
||||
pub const ERROR_CODE_HTTP_RESPONSE_TOO_LARGE: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_response_too_large");
|
||||
/// Stable off-chain transport error when the hardened reqwest client cannot be initialized.
|
||||
pub const ERROR_CODE_HTTP_CLIENT_BUILD_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_client_build_failed");
|
||||
/// Stable off-chain transport error for a connection failure without exposing the provider URL.
|
||||
pub const ERROR_CODE_HTTP_CONNECTION_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_connection_failed");
|
||||
/// Stable off-chain transport error when a successful HTTP response is not syntactically valid JSON.
|
||||
pub const ERROR_CODE_HTTP_INVALID_JSON: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_invalid_json");
|
||||
/// Stable off-chain transport error for HTTP 429 rate limiting.
|
||||
pub const ERROR_CODE_HTTP_RATE_LIMITED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_rate_limited");
|
||||
/// Stable off-chain transport error for an invalid local request-admission/rate-limit policy.
|
||||
pub const ERROR_CODE_HTTP_RATE_LIMIT_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_rate_limit_invalid");
|
||||
/// Stable off-chain transport error for a generic unsuccessful or transport-level HTTP request.
|
||||
pub const ERROR_CODE_HTTP_REQUEST_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_request_failed");
|
||||
/// Stable off-chain transport error for an invalid crate-owned HTTP request definition.
|
||||
pub const ERROR_CODE_HTTP_REQUEST_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_request_invalid");
|
||||
/// Stable off-chain transport error when a response exceeds the defensive body limit.
|
||||
pub const ERROR_CODE_HTTP_RESPONSE_TOO_LARGE: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_response_too_large");
|
||||
/// Stable off-chain transport error for an invalid crate-wide HTTP runtime configuration.
|
||||
pub const ERROR_CODE_HTTP_SETTINGS_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_settings_invalid");
|
||||
/// Stable off-chain transport error for transient HTTP status failures such as 408 or 5xx.
|
||||
pub const ERROR_CODE_HTTP_TEMPORARY_FAILURE: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_temporary_failure");
|
||||
/// Stable off-chain transport error for an end-to-end HTTP timeout.
|
||||
pub const ERROR_CODE_HTTP_TIMEOUT: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "http_timeout");
|
||||
/// Stable off-chain transport error for an invalid exact market-price decimal.
|
||||
pub const ERROR_CODE_MARKET_PRICE_DECIMAL_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("offchain_transport", "market_price_decimal_invalid");
|
||||
/// Stable off-chain transport error for an invalid normalized market-price observation.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-offchain-transport-lib/src/http_admission.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
//! Provider-neutral local request admission and rate-limit cooldown primitives.
|
||||
|
||||
@@ -20,14 +20,14 @@ pub(crate) enum HttpAdmissionPolicy {
|
||||
Unlimited,
|
||||
}
|
||||
|
||||
impl HttpAdmissionPolicy {
|
||||
impl crate::HttpAdmissionPolicy {
|
||||
/// Creates a validated fixed-window admission policy.
|
||||
pub(crate) fn fixed(requests: u32, window: std::time::Duration, burst: std::option::Option<u32>) -> ksp_core_lib::Result<Self> {
|
||||
let burst = match burst {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => 1,
|
||||
};
|
||||
if requests == 0 || window.is_zero() || window > MAX_RATE_LIMIT_WINDOW || burst == 0 || burst > requests {
|
||||
if requests == 0 || window.is_zero() || window > MAX_RATE_LIMIT_WINDOW || burst == 0 {
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_HTTP_RATE_LIMIT_INVALID, "HTTP request-admission policy is invalid")
|
||||
.with_context("field", "rate_limit"),
|
||||
@@ -49,28 +49,28 @@ pub(crate) enum HttpAdmissionDecision {
|
||||
pub(crate) struct HttpAdmissionController {
|
||||
cooldown_until: std::sync::Mutex<std::option::Option<std::time::Instant>>,
|
||||
fallback_cooldown: std::time::Duration,
|
||||
policy: HttpAdmissionPolicy,
|
||||
policy: crate::HttpAdmissionPolicy,
|
||||
token_bucket: std::sync::Mutex<std::option::Option<HttpTokenBucketState>>,
|
||||
}
|
||||
|
||||
impl HttpAdmissionController {
|
||||
impl crate::HttpAdmissionController {
|
||||
/// Creates one limiter from a provider-owned local admission policy.
|
||||
pub(crate) fn new(policy: HttpAdmissionPolicy, fallback_cooldown: std::option::Option<std::time::Duration>) -> ksp_core_lib::Result<Self> {
|
||||
pub(crate) fn new(policy: crate::HttpAdmissionPolicy, fallback_cooldown: std::option::Option<std::time::Duration>) -> ksp_core_lib::Result<Self> {
|
||||
let fallback_cooldown = match fallback_cooldown {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => DEFAULT_RATE_LIMIT_COOLDOWN,
|
||||
};
|
||||
if fallback_cooldown.is_zero() || fallback_cooldown > HTTP_MAX_RETRY_AFTER {
|
||||
if fallback_cooldown.is_zero() || fallback_cooldown > crate::HTTP_MAX_RETRY_AFTER {
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_HTTP_RATE_LIMIT_INVALID, "HTTP fallback cooldown is outside the supported bounds")
|
||||
.with_context("field", "fallback_cooldown"),
|
||||
);
|
||||
}
|
||||
let token_bucket = match policy {
|
||||
HttpAdmissionPolicy::Fixed { requests, window, burst } => {
|
||||
crate::HttpAdmissionPolicy::Fixed { requests, window, burst } => {
|
||||
std::option::Option::Some(HttpTokenBucketState::new(requests, window, burst, std::time::Instant::now()))
|
||||
},
|
||||
HttpAdmissionPolicy::Dynamic | HttpAdmissionPolicy::Unlimited => std::option::Option::None,
|
||||
crate::HttpAdmissionPolicy::Dynamic | crate::HttpAdmissionPolicy::Unlimited => std::option::Option::None,
|
||||
};
|
||||
return std::result::Result::Ok(Self {
|
||||
cooldown_until: std::sync::Mutex::new(std::option::Option::None),
|
||||
@@ -82,19 +82,19 @@ impl HttpAdmissionController {
|
||||
|
||||
/// Returns the configured local policy.
|
||||
#[must_use]
|
||||
pub(crate) const fn policy(&self) -> HttpAdmissionPolicy {
|
||||
pub(crate) const fn policy(&self) -> crate::HttpAdmissionPolicy {
|
||||
return self.policy;
|
||||
}
|
||||
|
||||
/// Tries to admit one request immediately without sleeping.
|
||||
pub(crate) fn try_admit(&self) -> HttpAdmissionDecision {
|
||||
pub(crate) fn try_admit(&self) -> crate::HttpAdmissionDecision {
|
||||
return self.try_admit_at(std::time::Instant::now());
|
||||
}
|
||||
|
||||
/// Records a provider 429 and extends cooldown using a bounded `Retry-After` value when present.
|
||||
pub(crate) fn record_rate_limited(&self, provider_retry_after: std::option::Option<std::time::Duration>) -> std::time::Duration {
|
||||
let provider_delay = match provider_retry_after {
|
||||
std::option::Option::Some(value) => std::cmp::min(value, HTTP_MAX_RETRY_AFTER),
|
||||
std::option::Option::Some(value) => std::cmp::min(value, crate::HTTP_MAX_RETRY_AFTER),
|
||||
std::option::Option::None => std::time::Duration::ZERO,
|
||||
};
|
||||
let effective = std::cmp::max(self.fallback_cooldown, provider_delay);
|
||||
@@ -113,9 +113,9 @@ impl HttpAdmissionController {
|
||||
return self.cooldown_remaining_at(std::time::Instant::now());
|
||||
}
|
||||
|
||||
fn try_admit_at(&self, now: std::time::Instant) -> HttpAdmissionDecision {
|
||||
fn try_admit_at(&self, now: std::time::Instant) -> crate::HttpAdmissionDecision {
|
||||
if let std::option::Option::Some(remaining) = self.cooldown_remaining_at(now) {
|
||||
return HttpAdmissionDecision::Deferred(remaining);
|
||||
return crate::HttpAdmissionDecision::Deferred(remaining);
|
||||
}
|
||||
let lock_result = self.token_bucket.lock();
|
||||
let mut token_bucket = match lock_result {
|
||||
@@ -124,11 +124,11 @@ impl HttpAdmissionController {
|
||||
};
|
||||
let state = match token_bucket.as_mut() {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return HttpAdmissionDecision::Ready,
|
||||
std::option::Option::None => return crate::HttpAdmissionDecision::Ready,
|
||||
};
|
||||
return match state.try_consume_at(now) {
|
||||
std::option::Option::Some(delay) => HttpAdmissionDecision::Deferred(delay),
|
||||
std::option::Option::None => HttpAdmissionDecision::Ready,
|
||||
std::option::Option::Some(delay) => crate::HttpAdmissionDecision::Deferred(delay),
|
||||
std::option::Option::None => crate::HttpAdmissionDecision::Ready,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-offchain-transport-lib/src/http_client.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
//! Crate-wide hardened REST client used internally by off-chain capability adapters.
|
||||
|
||||
@@ -8,7 +8,7 @@ pub(crate) struct HttpJsonDocument {
|
||||
bytes: std::vec::Vec<u8>,
|
||||
}
|
||||
|
||||
impl HttpJsonDocument {
|
||||
impl crate::HttpJsonDocument {
|
||||
/// Returns the validated raw JSON bytes for provider-specific typed deserialization.
|
||||
#[must_use]
|
||||
pub(crate) fn as_bytes(&self) -> &[u8] {
|
||||
@@ -16,7 +16,7 @@ impl HttpJsonDocument {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for HttpJsonDocument {
|
||||
impl std::fmt::Debug for crate::HttpJsonDocument {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter.debug_struct("HttpJsonDocument").field("byte_len", &self.bytes.len()).finish();
|
||||
}
|
||||
@@ -30,7 +30,7 @@ pub(crate) struct HttpGetRequest {
|
||||
url: reqwest::Url,
|
||||
}
|
||||
|
||||
impl HttpGetRequest {
|
||||
impl crate::HttpGetRequest {
|
||||
/// Creates one HTTPS GET request from a crate-owned official provider URL.
|
||||
pub(crate) fn new_https(url: &'static str) -> ksp_core_lib::Result<Self> {
|
||||
return Self::parse(url, false);
|
||||
@@ -106,7 +106,7 @@ impl HttpGetRequest {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for HttpGetRequest {
|
||||
impl std::fmt::Debug for crate::HttpGetRequest {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter.write_str("HttpGetRequest(<redacted>)");
|
||||
}
|
||||
@@ -119,7 +119,7 @@ pub(crate) struct HttpRestClient {
|
||||
settings: crate::HttpClientSettings,
|
||||
}
|
||||
|
||||
impl HttpRestClient {
|
||||
impl crate::HttpRestClient {
|
||||
/// Builds one hardened client with redirects, system proxies and reqwest automatic retries disabled.
|
||||
pub(crate) fn new(settings: crate::HttpClientSettings) -> ksp_core_lib::Result<Self> {
|
||||
let client_result = reqwest::Client::builder()
|
||||
@@ -151,7 +151,12 @@ impl HttpRestClient {
|
||||
}
|
||||
|
||||
/// Executes one GET request and returns only a bounded syntactically valid JSON document.
|
||||
pub(crate) async fn get_json(&self, provider: &'static str, operation: &'static str, request: HttpGetRequest) -> ksp_core_lib::Result<HttpJsonDocument> {
|
||||
pub(crate) async fn get_json(
|
||||
&self,
|
||||
provider: &'static str,
|
||||
operation: &'static str,
|
||||
request: crate::HttpGetRequest,
|
||||
) -> ksp_core_lib::Result<crate::HttpJsonDocument> {
|
||||
let send_result = self.client.get(request.url).headers(request.headers).send().await;
|
||||
let mut response = match send_result {
|
||||
std::result::Result::Ok(value) => value,
|
||||
@@ -198,7 +203,7 @@ impl HttpRestClient {
|
||||
response_body_bytes = body.len(),
|
||||
"completed off-chain HTTP REST request"
|
||||
);
|
||||
return std::result::Result::Ok(HttpJsonDocument { bytes: body });
|
||||
return std::result::Result::Ok(crate::HttpJsonDocument { bytes: body });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +212,7 @@ fn classify_http_status(
|
||||
operation: &'static str,
|
||||
status: u16,
|
||||
retry_after: std::option::Option<std::time::Duration>,
|
||||
) -> ksp_core_lib::Result<HttpJsonDocument> {
|
||||
) -> ksp_core_lib::Result<crate::HttpJsonDocument> {
|
||||
if status == 401 || status == 403 {
|
||||
return std::result::Result::Err(http_status_error(
|
||||
crate::ERROR_CODE_HTTP_ACCESS_DENIED,
|
||||
@@ -303,7 +308,7 @@ fn parse_retry_after(headers: &reqwest::header::HeaderMap) -> std::option::Optio
|
||||
return std::option::Option::Some(std::cmp::min(std::time::Duration::from_secs(seconds), crate::HTTP_MAX_RETRY_AFTER));
|
||||
}
|
||||
|
||||
fn response_too_large(provider: &'static str, operation: &'static str, limit: usize) -> ksp_core_lib::Result<HttpJsonDocument> {
|
||||
fn response_too_large(provider: &'static str, operation: &'static str, limit: usize) -> ksp_core_lib::Result<crate::HttpJsonDocument> {
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_HTTP_RESPONSE_TOO_LARGE, "Off-chain provider response exceeded the configured body limit")
|
||||
.with_context("provider", provider)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-offchain-transport-lib/src/http_settings.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
//! Crate-wide bounded HTTP runtime settings shared by off-chain capability families.
|
||||
|
||||
@@ -18,7 +18,7 @@ pub(crate) struct HttpClientSettings {
|
||||
request_timeout: std::time::Duration,
|
||||
}
|
||||
|
||||
impl HttpClientSettings {
|
||||
impl crate::HttpClientSettings {
|
||||
/// Creates one validated HTTP settings value.
|
||||
pub(crate) fn new(
|
||||
connect_timeout: std::time::Duration,
|
||||
@@ -59,7 +59,7 @@ impl HttpClientSettings {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::default::Default for HttpClientSettings {
|
||||
impl std::default::Default for crate::HttpClientSettings {
|
||||
fn default() -> Self {
|
||||
return Self {
|
||||
connect_timeout: DEFAULT_CONNECT_TIMEOUT,
|
||||
@@ -69,7 +69,7 @@ impl std::default::Default for HttpClientSettings {
|
||||
}
|
||||
}
|
||||
|
||||
fn invalid_http_settings(message: &str, field: &'static str) -> ksp_core_lib::Result<HttpClientSettings> {
|
||||
fn invalid_http_settings(message: &str, field: &'static str) -> ksp_core_lib::Result<crate::HttpClientSettings> {
|
||||
ksp_logging_lib::warn!(target: crate::TRACING_TARGET, field = field, "rejected invalid off-chain HTTP client settings");
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(crate::ERROR_CODE_HTTP_SETTINGS_INVALID, message).with_context("field", field));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-offchain-transport-lib/src/lib.rs
|
||||
// version: 4
|
||||
// version: 5
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -7,18 +7,19 @@
|
||||
|
||||
//! KSP-owned off-chain transport foundation.
|
||||
//!
|
||||
//! `0.2.11-pre.003` keeps the first capability family deliberately narrow (`market_price`, SOL/USD only) while materializing crate-wide hardened HTTP REST
|
||||
//! primitives and non-blocking local admission/cooldown machinery. These `http_*` internals are shared transport mechanics for future off-chain capability
|
||||
//! families such as `swap_quote_*`; they are intentionally not re-exported as a generic consumer HTTP client. Provider wire DTOs, provider adapters, Config
|
||||
//! integration and refresh orchestration remain outside this tranche.
|
||||
//! `0.2.11-pre.003` keeps the first capability family deliberately narrow (`market_price`, SOL/USD only) while staging crate-wide hardened HTTP REST
|
||||
//! primitives and non-blocking local admission/cooldown machinery under `cfg(test)` until the first production adapter consumes them in `pre.004`.
|
||||
//! These `http_*` internals are shared transport mechanics for future off-chain capability families such as `swap_quote_*`; they are intentionally not
|
||||
//! re-exported as a generic consumer HTTP client. Provider wire DTOs, provider adapters, Config integration and refresh orchestration remain outside this
|
||||
//! tranche.
|
||||
|
||||
mod constants;
|
||||
mod error;
|
||||
#[allow(dead_code)] // Staged in pre.003 and consumed by provider adapters starting in pre.004.
|
||||
#[cfg(test)] // RUST-API-008: staged until the first production adapter consumes the HTTP admission path in pre.004.
|
||||
mod http_admission;
|
||||
#[allow(dead_code)] // Staged in pre.003 and consumed by provider adapters starting in pre.004.
|
||||
#[cfg(test)] // RUST-API-008: staged until the first production adapter consumes the HTTP client path in pre.004.
|
||||
mod http_client;
|
||||
#[allow(dead_code)] // Staged in pre.003 and consumed by provider adapters starting in pre.004.
|
||||
#[cfg(test)] // RUST-API-008: staged until the first production adapter consumes the HTTP settings path in pre.004.
|
||||
mod http_settings;
|
||||
mod market_price_decimal;
|
||||
mod market_price_observation;
|
||||
@@ -109,18 +110,26 @@ pub use self::market_price_settings::MarketPriceProviderCommonSettings;
|
||||
/// Owning tracing target for events emitted by Off-chain Transport.
|
||||
pub(crate) use self::constants::TRACING_TARGET;
|
||||
/// Maximum provider-directed cooldown accepted from a server `Retry-After` value.
|
||||
#[cfg(test)]
|
||||
pub(crate) use self::http_admission::HTTP_MAX_RETRY_AFTER;
|
||||
/// Crate-internal non-blocking request-admission controller.
|
||||
#[cfg(test)]
|
||||
pub(crate) use self::http_admission::HttpAdmissionController;
|
||||
/// Crate-internal result of one immediate request-admission attempt.
|
||||
#[cfg(test)]
|
||||
pub(crate) use self::http_admission::HttpAdmissionDecision;
|
||||
/// Crate-internal provider-neutral local request-admission policy.
|
||||
#[cfg(test)]
|
||||
pub(crate) use self::http_admission::HttpAdmissionPolicy;
|
||||
/// Crate-internal fixed-origin GET request with redacted diagnostics.
|
||||
#[cfg(test)]
|
||||
pub(crate) use self::http_client::HttpGetRequest;
|
||||
/// Crate-internal bounded syntactically valid JSON response document.
|
||||
#[cfg(test)]
|
||||
pub(crate) use self::http_client::HttpJsonDocument;
|
||||
/// Crate-internal hardened REST client shared by capability adapters.
|
||||
#[cfg(test)]
|
||||
pub(crate) use self::http_client::HttpRestClient;
|
||||
/// Crate-internal bounded HTTP runtime settings.
|
||||
#[cfg(test)]
|
||||
pub(crate) use self::http_settings::HttpClientSettings;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-offchain-transport-lib/unit_tests/http_admission.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
#[test]
|
||||
fn fixed_admission_smooths_undocumented_burst_and_refills_deterministically() -> ksp_core_lib::Result<()> {
|
||||
@@ -32,7 +32,7 @@ fn fixed_admission_smooths_undocumented_burst_and_refills_deterministically() ->
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn documented_burst_is_consumed_atomically_before_refill() -> ksp_core_lib::Result<()> {
|
||||
fn documented_burst_may_exceed_average_window_budget_and_is_consumed_atomically() -> ksp_core_lib::Result<()> {
|
||||
let policy = match crate::HttpAdmissionPolicy::fixed(1, std::time::Duration::from_secs(1), std::option::Option::Some(2)) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-offchain-transport-lib/unit_tests/http_client.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
#[tokio::test]
|
||||
async fn rest_client_accepts_bounded_json_and_never_exposes_request_debug() -> ksp_core_lib::Result<()> {
|
||||
@@ -36,6 +36,22 @@ async fn rest_client_accepts_bounded_json_and_never_exposes_request_debug() -> k
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn https_request_builder_accepts_official_style_url_without_exposing_it() -> ksp_core_lib::Result<()> {
|
||||
let mut request = match crate::HttpGetRequest::new_https("https://example.com/price") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
request.append_query_pair("symbol", "SOL/USD");
|
||||
if let std::result::Result::Err(error) = request.insert_sensitive_header("x-api-key", "redaction-canary") {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let debug = format!("{request:?}");
|
||||
assert!(!debug.contains("example.com"));
|
||||
assert!(!debug.contains("redaction-canary"));
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn rest_client_rejects_redirects_instead_of_following_them() -> ksp_core_lib::Result<()> {
|
||||
let server_result =
|
||||
|
||||
Reference in New Issue
Block a user