8.3 KiB
Delta 0.2.11-pre.003 — HTTP REST commun et rate limiting
1. Base requise
Cette tranche s'applique exclusivement après :
v0.2.10
+ 0.2.11-pre.001
+ 0.2.11-pre.001-fix.001
+ 0.2.11-pre.002
+ 0.2.11-pre.002-fix.001
+ 0.2.11-pre.002-fix.002
+ 0.2.11-pre.002-fix.003
La version Cargo attendue à l'entrée est :
0.2.11-pre.2.fix.3
La version Cargo de sortie est :
0.2.11-pre.3
2. Gate d'entrée acquis
L'opérateur a validé 0.2.11-pre.002-fix.003 le 2026-08-25 avec :
cargo fmt --all exécuté
python3 scripts/audit_rust_workspace_rules.py clean
python3 scripts/audit_markdown_tables.py clean, 116 tables / 105 files
cargo check --workspace PASS
cargo clippy --workspace --all-targets PASS
cargo test -p ksp-offchain-transport-lib PASS, 10 unit + 1 boundary + 2 public API
cargo test --workspace n'a pas été fourni pour cet état exact et n'est pas déclaré PASS ici.
3. Objectif
Matérialiser les primitives HTTP REST communes de ksp-offchain-transport-lib et le rate limiting local sans avancer aucun provider.
La tranche doit préparer directement pre.004+ tout en conservant les frontières :
http_* = mécanique transport crate-wide et crate-private
market_price_* = première capacité métier, SOL/USD uniquement
aucun client HTTP générique exporté aux consumers
aucun SDK provider
aucun Config -> Off-chain ajouté avant pre.009
aucun refresh registry/service avant pre.007/pre.008
4. HTTP REST commun
http_client.rs matérialise un reqwest::Client réutilisable par les adapters internes.
La construction impose explicitement :
rustls
redirects désactivés
Referer automatique désactivé
proxy système désactivé
retries implicites reqwest désactivés
User-Agent KSP explicite
connect timeout borné
request timeout borné
reqwest reste une dépendance directe de la crate et aucun SDK provider n'est ajouté.
La requête GET interne :
accepte uniquement HTTPS pour les adapters runtime
refuse credentials dans l'URL
encode les query pairs via Url
permet des headers sensibles marqués sensitive
redacte URL et headers dans Debug
Un constructeur HTTP non HTTPS existe uniquement sous cfg(test) pour les serveurs loopback déterministes.
5. Bornes de réponse et JSON
Les valeurs initiales sont :
connect timeout défaut = 5 s
connect timeout hard max = 30 s
request timeout défaut = 10 s
request timeout hard max = 120 s
body défaut = 1 MiB
body hard max = 4 MiB
Le body est borné pendant sa lecture par chunks. La présence ou l'absence de Content-Length ne permet donc pas de contourner la limite.
Après lecture, la syntaxe JSON est validée avec serde::de::IgnoredAny. Le document brut borné reste disponible pour le futur adapter typed, ce qui évite de faire passer un nombre provider par une représentation générique f64 avant le parsing MarketPriceDecimal.
6. Classification HTTP
Les codes crate-wide ajoutés sous le domaine offchain_transport distinguent :
http_settings_invalid
http_client_build_failed
http_request_invalid
http_connection_failed
http_timeout
http_request_failed
http_access_denied
http_rate_limited
http_temporary_failure
http_response_too_large
http_invalid_json
http_rate_limit_invalid
La classification de statut est :
401/403 -> access denied
429 -> rate limited
408 et 5xx -> temporary failure
autre non-2xx -> request failed
2xx -> body borné puis JSON syntaxiquement validé
Les erreurs reqwest sont converties après without_url(). Aucun body remote n'est recopié dans KspError.
Retry-After est actuellement exploité sous sa forme delta-seconds, bornée défensivement à une heure. Une forme HTTP-date non parseable reste ignorée plutôt que devinée.
7. Admission et cooldown
http_admission.rs fournit un limiter non bloquant.
Les policies internes sont :
Fixed
Dynamic
Unlimited
Une policy fixe utilise un token bucket lissé :
requests/window
burst explicite uniquement lorsqu'il est documenté
burst absent -> capacité locale conservatrice de 1
Une policy dynamique n'invente aucune cadence locale. Elle applique seulement les cooldowns appris du provider.
try_admit() ne dort jamais : il retourne immédiatement Ready ou Deferred(duration). Cette propriété prépare le refresh multiple/all de pre.008, qui devra continuer avec les autres providers au lieu d'attendre un provider en cooldown.
Un 429 peut étendre le cooldown avec Retry-After; la durée provider est bornée à une heure et comparée au fallback local.
8. Tests ajoutés
Les nouveaux tests unitaires couvrent :
settings HTTP par défaut et bornes pathologiques
policy fixe invalide
burst absent lissé conservativement
burst documenté consommé atomiquement
refill déterministe du token bucket
cooldown provider et borne Retry-After
Debug request sans URL/credential
GET JSON valide
redirect refusé
body chunked dépassant la limite
429 + Retry-After sans propagation du body canari
JSON invalide après statut 2xx
Les canaries d'intégration vérifient aussi :
reqwest rustls présent
Config toujours absent
SDK providers toujours absents
tracing direct toujours absent
modules http_* présents
HttpRestClient non exporté publiquement
redirect/no-proxy/no-retry/without_url explicites dans le client
codes d'erreur HTTP dans le domaine offchain_transport
9. Fichiers ajoutés
crates/ksp-offchain-transport-lib/src/http_admission.rs
crates/ksp-offchain-transport-lib/src/http_client.rs
crates/ksp-offchain-transport-lib/src/http_settings.rs
crates/ksp-offchain-transport-lib/unit_tests/http_admission.rs
crates/ksp-offchain-transport-lib/unit_tests/http_client.rs
crates/ksp-offchain-transport-lib/unit_tests/http_settings.rs
deltas/0.2.11/pre.003.md
10. Fichiers modifiés
Cargo.toml
crates/ksp-offchain-transport-lib/Cargo.toml
crates/ksp-offchain-transport-lib/src/error.rs
crates/ksp-offchain-transport-lib/src/lib.rs
crates/ksp-offchain-transport-lib/tests/dependency_boundary.rs
crates/ksp-offchain-transport-lib/tests/public_api.rs
docs/plans/018-V0_2_11_OFFCHAIN_PRICE_TRANSPORT_PLAN.md
docs/validation/014-V0_2_11_OFFCHAIN_PRICE_TRANSPORT.md
11. Fichiers supprimés
aucun
12. Fichiers volontairement inchangés
CHANGELOG.md
ROADMAP.md
README.md
.env.example
config/**
crates/ksp-config-lib/**
crates/ksp-onchain-transport-lib/**
prompts/**
ROADMAP.md et CHANGELOG.md restent hors de la tranche conformément à leur ownership de release.
13. Validations exécutées dans le sandbox
python3 scripts/audit_rust_workspace_rules.py
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.11
clean
contrôle lignes Rust > 160 sur ksp-offchain-transport-lib
PASS
inspection diff contre 0.2.11-pre.002-fix.003
aucun provider adapter ajouté
aucune dépendance ksp-config-lib ajoutée
aucune dépendance tracing directe ajoutée
14. Validations non exécutées dans le sandbox
Le sandbox de génération ne fournit pas cargo, rustc ou rustfmt.
Après application, exécuter :
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.11
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-offchain-transport-lib
cargo test --workspace
15. Décisions et points différés
Aucune question ne bloque pre.004.
Restent volontairement différés :
mapping MarketPriceProviderRateLimit -> HttpAdmissionPolicy par provider
CoinGecko/CoinMarketCap/CoinPaprika wire DTOs et endpoints
credentials provider effectifs
registry et availability runtime
refresh single/multiple
Config std.offchain_transport
Retry-After HTTP-date si un provider retenu l'exige réellement
POST/écritures HTTP pour futures familles off-chain
pre.004 reste propriétaire du premier lot d'adapters : CoinGecko, CoinMarketCap et CoinPaprika.