301 lines
8.3 KiB
Markdown
301 lines
8.3 KiB
Markdown
<!-- file: deltas/0.2.11/pre.003.md -->
|
|
<!-- version: 1 -->
|
|
|
|
# Delta `0.2.11-pre.003` — HTTP REST commun et rate limiting
|
|
|
|
## 1. Base requise
|
|
|
|
Cette tranche s'applique exclusivement après :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
0.2.11-pre.2.fix.3
|
|
```
|
|
|
|
La version Cargo de sortie est :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
Fixed
|
|
Dynamic
|
|
Unlimited
|
|
```
|
|
|
|
Une policy fixe utilise un token bucket lissé :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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 :
|
|
|
|
```text
|
|
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
|
|
|
|
```text
|
|
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
|
|
|
|
```text
|
|
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
|
|
|
|
```text
|
|
aucun
|
|
```
|
|
|
|
## 12. Fichiers volontairement inchangés
|
|
|
|
```text
|
|
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
|
|
|
|
```text
|
|
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 :
|
|
|
|
```bash
|
|
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 :
|
|
|
|
```text
|
|
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.
|