Implements WebModule that allows other modules to make HTTP requests through IIO pub/sub messaging system. Features: - HTTP GET/POST support via existing HttpClient - Request/response via IIO topics (web:request/web:response) - Security: blocks localhost and private IPs - Statistics tracking (total, success, failed) - Hot-reload state preservation - Custom headers and timeout configuration Module architecture: - WebModule.h/cpp: 296 lines total (within 300 line limit) - config/web.json: Configuration file - 10 integration tests (TI_WEB_001 to TI_WEB_010) Tests: 120/120 passing (110 existing + 10 new) Protocol: - Subscribe: web:request - Publish: web:response - Request fields: requestId, url, method, headers, body, timeoutMs - Response fields: requestId, success, statusCode, body, error, durationMs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
461 lines
12 KiB
Markdown
461 lines
12 KiB
Markdown
# Plan d'Implémentation: WebModule
|
|
|
|
**Objectif**: Ajouter un module hot-reload permettant aux autres modules de faire des requêtes HTTP de manière dynamique via IIO.
|
|
|
|
**Status**: 📋 Planifié
|
|
**Priorité**: Moyenne
|
|
**Durée estimée**: 2-3h
|
|
|
|
---
|
|
|
|
## 1. Vue d'Ensemble
|
|
|
|
### Architecture
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────┐
|
|
│ Modules Clients │
|
|
│ (AI, Monitoring, Scheduler, etc.) │
|
|
│ │
|
|
│ → Publient: "web:request" │
|
|
│ ← Reçoivent: "web:response" │
|
|
└──────────────┬──────────────────────────────────┘
|
|
│ IIO pub/sub
|
|
▼
|
|
┌─────────────────────────────────────────────────┐
|
|
│ WebModule (Hot-reload) │
|
|
│ │
|
|
│ • Subscribe: "web:request" │
|
|
│ • Execute HTTP via HttpClient.hpp │
|
|
│ • Publish: "web:response" │
|
|
│ • Track stats (total, success, failed) │
|
|
│ • State preservation (hot-reload) │
|
|
└──────────────┬──────────────────────────────────┘
|
|
│ Uses existing HttpClient.hpp
|
|
▼
|
|
Internet
|
|
```
|
|
|
|
### Avantages
|
|
|
|
✅ **Hot-reload**: Rechargement sans arrêter le système
|
|
✅ **Découplage**: Modules clients ignorent les détails HTTP
|
|
✅ **Centralisé**: Un seul point pour toutes les requêtes HTTP
|
|
✅ **Statistiques**: Tracking automatique (total, succès, échecs)
|
|
✅ **État persistant**: Stats survivent au hot-reload
|
|
✅ **Réutilisation**: Utilise HttpClient.hpp existant
|
|
|
|
---
|
|
|
|
## 2. Fichiers à Créer
|
|
|
|
### 2.1 Module Core
|
|
|
|
#### `src/modules/WebModule.h` (70 lignes)
|
|
```cpp
|
|
#pragma once
|
|
#include <grove/IModule.h>
|
|
#include <memory>
|
|
#include <spdlog/spdlog.h>
|
|
|
|
namespace aissia {
|
|
|
|
class WebModule : public grove::IModule {
|
|
public:
|
|
WebModule();
|
|
~WebModule() override = default;
|
|
|
|
// IModule interface
|
|
void setConfiguration(const grove::IDataNode& config,
|
|
grove::IIO* io,
|
|
grove::ITaskScheduler* scheduler) override;
|
|
const grove::IDataNode& getConfiguration() override;
|
|
void process(const grove::IDataNode& input) override;
|
|
std::unique_ptr<grove::IDataNode> getHealthStatus() override;
|
|
void shutdown() override;
|
|
|
|
// State management (hot-reload)
|
|
std::unique_ptr<grove::IDataNode> getState() override;
|
|
void setState(const grove::IDataNode& state) override;
|
|
|
|
private:
|
|
void processMessages();
|
|
void handleWebRequest(const grove::IDataNode& request);
|
|
|
|
grove::IIO* m_io = nullptr;
|
|
std::shared_ptr<spdlog::logger> m_logger;
|
|
std::unique_ptr<grove::IDataNode> m_config;
|
|
|
|
// Configuration
|
|
int m_requestTimeoutMs = 30000;
|
|
int m_maxConcurrentRequests = 10;
|
|
|
|
// Statistics
|
|
int m_totalRequests = 0;
|
|
int m_successfulRequests = 0;
|
|
int m_failedRequests = 0;
|
|
};
|
|
|
|
} // namespace aissia
|
|
```
|
|
|
|
#### `src/modules/WebModule.cpp` (180 lignes)
|
|
- Implémentation complète
|
|
- Gestion GET/POST
|
|
- Gestion des erreurs
|
|
- Publication des réponses
|
|
- État pour hot-reload
|
|
|
|
### 2.2 Configuration
|
|
|
|
#### `config/web.json` (nouveau)
|
|
```json
|
|
{
|
|
"enabled": true,
|
|
"requestTimeoutMs": 30000,
|
|
"maxConcurrentRequests": 10,
|
|
"allowedDomains": [
|
|
"api.example.com",
|
|
"*.openai.com"
|
|
],
|
|
"blockedDomains": []
|
|
}
|
|
```
|
|
|
|
### 2.3 Tests
|
|
|
|
#### `tests/modules/WebModuleTests.cpp` (10 tests)
|
|
```
|
|
TI_WEB_001: Simple GET Request
|
|
TI_WEB_002: POST Request with Body
|
|
TI_WEB_003: Invalid URL Handling
|
|
TI_WEB_004: Timeout Handling
|
|
TI_WEB_005: Multiple Concurrent Requests
|
|
TI_WEB_006: Request ID Tracking
|
|
TI_WEB_007: Statistics Tracking
|
|
TI_WEB_008: State Serialization
|
|
TI_WEB_009: Configuration Loading
|
|
TI_WEB_010: Error Response Format
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Protocole IIO
|
|
|
|
### 3.1 Requête (`web:request`)
|
|
|
|
**Topic**: `web:request`
|
|
|
|
**Payload**:
|
|
```json
|
|
{
|
|
"requestId": "unique-id-123",
|
|
"url": "https://api.example.com/data",
|
|
"method": "GET",
|
|
"headers": {
|
|
"Authorization": "Bearer xxx",
|
|
"Content-Type": "application/json"
|
|
},
|
|
"body": "{\"key\": \"value\"}",
|
|
"timeoutMs": 5000
|
|
}
|
|
```
|
|
|
|
**Champs**:
|
|
- `requestId` (string, required): ID unique pour matcher la réponse
|
|
- `url` (string, required): URL complète avec schéma
|
|
- `method` (string, optional): "GET", "POST", "PUT", "DELETE" (default: "GET")
|
|
- `headers` (object, optional): Headers HTTP custom
|
|
- `body` (string, optional): Body pour POST/PUT
|
|
- `timeoutMs` (int, optional): Timeout custom (default: 30000)
|
|
|
|
### 3.2 Réponse (`web:response`)
|
|
|
|
**Topic**: `web:response`
|
|
|
|
**Payload (Success)**:
|
|
```json
|
|
{
|
|
"requestId": "unique-id-123",
|
|
"success": true,
|
|
"statusCode": 200,
|
|
"body": "{\"result\": \"data\"}",
|
|
"headers": {
|
|
"Content-Type": "application/json"
|
|
},
|
|
"durationMs": 234
|
|
}
|
|
```
|
|
|
|
**Payload (Error)**:
|
|
```json
|
|
{
|
|
"requestId": "unique-id-123",
|
|
"success": false,
|
|
"error": "Connection timeout",
|
|
"errorCode": "TIMEOUT",
|
|
"durationMs": 30001
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Étapes d'Implémentation
|
|
|
|
### Phase 1: Core Module (1h)
|
|
|
|
1. **Créer les fichiers de base**
|
|
- [ ] `src/modules/WebModule.h`
|
|
- [ ] `src/modules/WebModule.cpp`
|
|
- [ ] `config/web.json`
|
|
|
|
2. **Implémenter les méthodes IModule**
|
|
- [ ] `setConfiguration()` - Subscribe "web:request"
|
|
- [ ] `process()` - Appeler processMessages()
|
|
- [ ] `getHealthStatus()` - Retourner stats
|
|
- [ ] `shutdown()` - Log final
|
|
- [ ] `getState()` / `setState()` - Sérialisation stats
|
|
|
|
3. **Implémenter handleWebRequest()**
|
|
- [ ] Extraction des paramètres
|
|
- [ ] Validation URL
|
|
- [ ] Appel HttpClient (GET/POST)
|
|
- [ ] Gestion erreurs (try/catch)
|
|
- [ ] Publication "web:response"
|
|
|
|
4. **Ajouter au CMakeLists.txt**
|
|
```cmake
|
|
add_library(WebModule SHARED
|
|
src/modules/WebModule.cpp
|
|
)
|
|
target_link_libraries(WebModule PRIVATE grove_impl)
|
|
```
|
|
|
|
5. **Ajouter au main.cpp**
|
|
```cpp
|
|
// Load WebModule
|
|
moduleConfigs.push_back({
|
|
"WebModule",
|
|
"./build/modules/libWebModule.so",
|
|
"./config/web.json"
|
|
});
|
|
```
|
|
|
|
### Phase 2: Tests (1h)
|
|
|
|
6. **Créer les tests**
|
|
- [ ] `tests/modules/WebModuleTests.cpp`
|
|
- [ ] Mock HTTP server (simple echo server)
|
|
- [ ] 10 tests unitaires
|
|
- [ ] Intégration avec MockIO
|
|
|
|
7. **Exécuter les tests**
|
|
```bash
|
|
cmake --build build --target aissia_tests
|
|
./build/tests/aissia_tests "[web]"
|
|
```
|
|
|
|
### Phase 3: Documentation & Exemples (30min)
|
|
|
|
8. **Documenter l'utilisation**
|
|
- [ ] Ajouter exemple dans `docs/modules/WebModule.md`
|
|
- [ ] Mettre à jour `CLAUDE.md`
|
|
- [ ] Mettre à jour `README.md`
|
|
|
|
9. **Créer un exemple d'utilisation**
|
|
- [ ] Exemple dans AIModule ou nouveau module
|
|
|
|
---
|
|
|
|
## 5. Exemple d'Utilisation
|
|
|
|
### Dans un module client (ex: AIModule)
|
|
|
|
```cpp
|
|
// 1. Dans setConfiguration() - S'abonner aux réponses
|
|
if (m_io) {
|
|
grove::SubscriptionConfig subConfig;
|
|
m_io->subscribe("web:response", subConfig);
|
|
}
|
|
|
|
// 2. Faire une requête HTTP
|
|
void AIModule::fetchExternalData() {
|
|
auto request = std::make_unique<grove::JsonDataNode>("request");
|
|
request->setString("requestId", "weather-" + std::to_string(m_requestCounter++));
|
|
request->setString("url", "https://api.weather.com/current");
|
|
request->setString("method", "GET");
|
|
|
|
// Optional headers
|
|
auto headers = std::make_unique<grove::JsonDataNode>("headers");
|
|
headers->setString("Authorization", "Bearer " + m_apiKey);
|
|
request->setChild("headers", std::move(headers));
|
|
|
|
m_io->publish("web:request", std::move(request));
|
|
}
|
|
|
|
// 3. Dans processMessages() - Recevoir la réponse
|
|
if (msg.topic == "web:response" && msg.data) {
|
|
std::string requestId = msg.data->getString("requestId", "");
|
|
|
|
if (requestId.find("weather-") == 0) {
|
|
bool success = msg.data->getBool("success", false);
|
|
|
|
if (success) {
|
|
std::string body = msg.data->getString("body", "");
|
|
int statusCode = msg.data->getInt("statusCode", 0);
|
|
|
|
m_logger->info("Weather data received: {} bytes", body.size());
|
|
// Parse JSON and use data
|
|
} else {
|
|
std::string error = msg.data->getString("error", "Unknown");
|
|
m_logger->error("Weather request failed: {}", error);
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Sécurité & Limitations
|
|
|
|
### Sécurité
|
|
|
|
1. **Validation URL**
|
|
- Vérifier schéma (https:// recommandé)
|
|
- Whitelist/Blacklist de domaines (config)
|
|
- Pas d'exécution de code arbitraire
|
|
|
|
2. **Rate Limiting**
|
|
- Max concurrent requests (config)
|
|
- Timeout par défaut (30s)
|
|
- Pas de retry automatique (responsabilité du client)
|
|
|
|
3. **Sanitization**
|
|
- Headers validés
|
|
- Pas d'injection possible
|
|
|
|
### Limitations
|
|
|
|
- **Pas de streaming**: Requêtes one-shot uniquement
|
|
- **Pas de websockets**: HTTP classique seulement
|
|
- **Pas de retry**: Le client doit gérer
|
|
- **Pas de cache**: Chaque requête est exécutée
|
|
- **Thread-safe**: Mais pas de parallélisation interne
|
|
|
|
---
|
|
|
|
## 7. Extensions Futures (Optionnel)
|
|
|
|
### Phase 4: Features Avancées
|
|
|
|
- [ ] **Cache HTTP**: LRU cache pour réponses GET
|
|
- [ ] **Retry automatique**: Avec backoff exponentiel
|
|
- [ ] **Circuit breaker**: Protection contre services down
|
|
- [ ] **Métriques avancées**: Latence P50/P95/P99
|
|
- [ ] **Support HTTPS custom**: Certificats custom
|
|
- [ ] **Compression**: gzip/deflate support
|
|
- [ ] **Streaming**: Support chunked transfer
|
|
|
|
### Phase 5: Tools pour l'Agent LLM
|
|
|
|
- [ ] Ajouter `fetch_url` tool dans InternalTools
|
|
- [ ] Agent peut faire "Fetch https://example.com"
|
|
- [ ] Parsing HTML automatique (option)
|
|
|
|
---
|
|
|
|
## 8. Critères de Succès
|
|
|
|
### Fonctionnel
|
|
- [ ] Module compile et charge sans erreur
|
|
- [ ] GET requests fonctionnent
|
|
- [ ] POST requests fonctionnent
|
|
- [ ] Erreurs sont gérées correctement
|
|
- [ ] Stats sont trackées
|
|
- [ ] Hot-reload préserve l'état
|
|
|
|
### Tests
|
|
- [ ] 10/10 tests passent
|
|
- [ ] Coverage > 80%
|
|
- [ ] Pas de memory leaks (valgrind)
|
|
|
|
### Documentation
|
|
- [ ] README.md updated
|
|
- [ ] Exemples d'utilisation fournis
|
|
- [ ] Protocol IIO documenté
|
|
|
|
### Performance
|
|
- [ ] Latency < 100ms overhead (vs direct HTTP)
|
|
- [ ] Pas de blocking de la main loop
|
|
- [ ] Memory footprint < 1MB
|
|
|
|
---
|
|
|
|
## 9. Checklist de Validation
|
|
|
|
Avant de merger:
|
|
|
|
- [ ] Code review (self-review)
|
|
- [ ] Tests unitaires passent
|
|
- [ ] Tests d'intégration passent
|
|
- [ ] Documentation à jour
|
|
- [ ] Pas de warnings de compilation
|
|
- [ ] Module hot-reload testé manuellement
|
|
- [ ] Config JSON validé
|
|
- [ ] Exemple fonctionnel créé
|
|
- [ ] Commit message descriptif
|
|
- [ ] Branch mergée vers master
|
|
|
|
---
|
|
|
|
## 10. Fichiers Affectés
|
|
|
|
### Nouveaux fichiers
|
|
```
|
|
src/modules/WebModule.h
|
|
src/modules/WebModule.cpp
|
|
config/web.json
|
|
tests/modules/WebModuleTests.cpp
|
|
plans/webmodule-implementation.md (ce fichier)
|
|
```
|
|
|
|
### Fichiers modifiés
|
|
```
|
|
CMakeLists.txt # Ajouter WebModule target
|
|
src/main.cpp # Charger WebModule
|
|
README.md # Documentation
|
|
CLAUDE.md # Règles de développement
|
|
```
|
|
|
|
### Fichiers utilisés (existants)
|
|
```
|
|
src/shared/http/HttpClient.hpp # Réutilisé pour HTTP
|
|
external/GroveEngine/ # IModule, IIO, etc.
|
|
```
|
|
|
|
---
|
|
|
|
## Notes Techniques
|
|
|
|
### Dépendances
|
|
- ✅ HttpClient.hpp (déjà implémenté)
|
|
- ✅ GroveEngine (IModule, IIO)
|
|
- ✅ nlohmann/json (parsing)
|
|
- ✅ spdlog (logging)
|
|
|
|
### Compatibilité
|
|
- ✅ Windows (HTTPS via WinHTTP)
|
|
- ✅ Linux/WSL (HTTPS via libcurl si disponible)
|
|
- ✅ Hot-reload ready
|
|
|
|
### Performance
|
|
- Requêtes asynchrones dans le sens où elles ne bloquent pas la main loop
|
|
- Mais chaque requête est synchrone (HttpClient bloque jusqu'à réponse)
|
|
- Pour async vrai, il faudrait un thread pool (Phase 4+)
|
|
|
|
---
|
|
|
|
**Auteur**: Claude Code
|
|
**Date**: 2025-11-28
|
|
**Version**: 1.0
|