Impossible d'envoyer le message XMPP #25

Closed
opened 2026-09-11 07:52:25 +02:00 by AntoineVe · 2 comments
Owner

Description

Après la synchronisation d'agenda, le programme ne parvient pas à envoyer le message XMPP et reste dans cette état. J'ai du faire un Ctrl-C pour terminer le programme.

Log du programme

2026-09-10 21:20:38 | INFO     | pronote_sync.sync.executor | ajout de l'événement UID=29#DTxsgsayqhRTq55h873gQP2ocfzsCcp4OEV6oSvlcQ4
2026-09-10 21:20:38 | DEBUG    | asyncio | Using selector: EpollSelector
2026-09-10 21:21:08 | WARNING  | pronote_sync.channels.xmpp | Délai d'attente de session XMPP dépassé.
2026-09-10 21:21:08 | WARNING  | pronote_sync.pipeline.run | Étape send dégradée : Le canal XMPP a refusé l'envoi
2026-09-10 21:21:08 | ERROR    | pronote_sync.cli.main | Le canal XMPP a refusé l'envoi
^C

Log serveur XMPP

Le log serveur XMPP ne montre aucune connexion depuis l'identifiant paramétré dans le fichier .env.

# Description Après la synchronisation d'agenda, le programme ne parvient pas à envoyer le message XMPP et reste dans cette état. J'ai du faire un Ctrl-C pour terminer le programme. # Log du programme ``` 2026-09-10 21:20:38 | INFO | pronote_sync.sync.executor | ajout de l'événement UID=29#DTxsgsayqhRTq55h873gQP2ocfzsCcp4OEV6oSvlcQ4 2026-09-10 21:20:38 | DEBUG | asyncio | Using selector: EpollSelector 2026-09-10 21:21:08 | WARNING | pronote_sync.channels.xmpp | Délai d'attente de session XMPP dépassé. 2026-09-10 21:21:08 | WARNING | pronote_sync.pipeline.run | Étape send dégradée : Le canal XMPP a refusé l'envoi 2026-09-10 21:21:08 | ERROR | pronote_sync.cli.main | Le canal XMPP a refusé l'envoi ^C ``` # Log serveur XMPP Le log serveur XMPP ne montre aucune connexion depuis l'identifiant paramétré dans le fichier `.env`.
AntoineVe added the bug label 2026-09-11 07:52:25 +02:00
OpenCode was assigned by AntoineVe 2026-09-11 07:52:25 +02:00
Author
Owner

Diagnostic — Analyse de cause racine

Symptôme

Après la synchronisation d'agenda, l'envoi du message XMPP échoue silencieusement : aucun événement de session ne se déclenche pendant 30 secondes (le timeout configuré par défaut), puis le pipeline signale une dégradation. L'utilisateur doit interrompre manuellement le processus (Ctrl-C). Le serveur XMPP ne montre aucune connexion depuis l'identifiant configuré.

Cause racine primaire — Incompatibilité mode TLS / port par défaut

La configuration par défaut de XmppSettings produit une combinaison de protocole invalide pour un serveur XMPP standard :

  • port=5222 et use_tls=True (pronote_sync/config/settings.py:178,181)
  • use_tls=True est interprété comme du direct TLS (enable_direct_tls=True, enable_starttls=False) dans pronote_sync/channels/xmpp.py:253-261
  • Le port 5222 est conventionnellement le port XMPP en clair qui négocie via STARTTLS. Le direct TLS est conventionnellement sur le port 5223.
  • Le code commente d'ailleurs « TLS direct (port 5223 typically) » mais le défaut reste 5222.
  • Le validateur _validate_tls_policy() (settings.py:200-206) interdit use_tls=False (qui activerait STARTTLS) pour les hôtes non-loopback.

Conséquence : le client envoie un ClientHello TLS sur un port qui attend un stream XMPP en clair → le serveur ne reconnaît jamais l'identité configurée → aucun événement de session ne se déclenche → timeout 30 secondes.

Causes secondaires

  • Événement connection_failed non géré : XmppChannel n'écoute que session_start, failed_auth, disconnected. Slixmpp émet connection_failed en cas d'échec réseau (DNS, TCP, firewall) — cet événement n'est pas capturé, donc session_future reste pending jusqu'au timeout de session.
  • Pas de timeout sur await connect_future (xmpp.py:284-285) : un SYN blackhole ou une résolution DNS bloquée peut pendre indéfiniment avant même que le timeout de session ne soit armé.
  • Pas de timeout sur await disconnect_future dans le finally (xmpp.py:308-315) : après le timeout de session, le cleanup peut bloquer indéfiniment — le processus ne se termine pas proprement, d'où le Ctrl-C nécessaire.
  • Tests ne couvrent pas les cas d'échec réels : FakeClientXMPP résout immédiatement connect() et disconnect(), ne simule pas connection_failed, retry, DNS failure, ou TLS mismatch.

Directions de correction

  1. Séparer le mode de transport TLS (direct TLS, STARTTLS, désactivé) d'un booléen simple ; rendre le défaut compatible avec le port 5222 (STARTTLS).
  2. Ajouter des deadlines distincts : connexion (DNS/TCP/TLS), session/auth, et cleanup/disconnect avec un chemin d'annulation forcée.
  3. Gérer l'événement connection_failed de Slixmpp comme un échec terminal de connexion plutôt que d'attendre le timeout de session.
  4. Ajouter des tests de cycle de vie avec des futures pending réalistes (connection_failed, retry, disconnect bloqué, TLS mismatch).
  5. Améliorer l'observabilité : classifier l'échec (DNS, TCP refusé, TLS, auth, timeout) dans les logs expurgés.

ℹ️ Méthodologie : Ce diagnostic a été réalisé par analyse statique du code source et reproduction en environnement de test isolé (endpoint local sans listener, mock de cleanup bloqué). Aucun accès au serveur XMPP de production n'a été nécessaire. Les 30 secondes exactes entre Using selector: EpollSelector (21:20:38) et le timeout (21:21:08) confirment que connect_future s'est résolu et que c'est bien session_future qui a expiré sans qu'aucun événement attendu ne se déclenche.

## Diagnostic — Analyse de cause racine ### Symptôme Après la synchronisation d'agenda, l'envoi du message XMPP échoue silencieusement : aucun événement de session ne se déclenche pendant 30 secondes (le timeout configuré par défaut), puis le pipeline signale une dégradation. L'utilisateur doit interrompre manuellement le processus (Ctrl-C). Le serveur XMPP ne montre aucune connexion depuis l'identifiant configuré. ### Cause racine primaire — Incompatibilité mode TLS / port par défaut La configuration par défaut de `XmppSettings` produit une combinaison de protocole invalide pour un serveur XMPP standard : - `port=5222` et `use_tls=True` (`pronote_sync/config/settings.py:178,181`) - `use_tls=True` est interprété comme du **direct TLS** (`enable_direct_tls=True`, `enable_starttls=False`) dans `pronote_sync/channels/xmpp.py:253-261` - Le port **5222** est conventionnellement le port XMPP en clair qui négocie via **STARTTLS**. Le direct TLS est conventionnellement sur le port **5223**. - Le code commente d'ailleurs « TLS direct (port 5223 typically) » mais le défaut reste 5222. - Le validateur `_validate_tls_policy()` (`settings.py:200-206`) **interdit** `use_tls=False` (qui activerait STARTTLS) pour les hôtes non-loopback. **Conséquence** : le client envoie un ClientHello TLS sur un port qui attend un stream XMPP en clair → le serveur ne reconnaît jamais l'identité configurée → aucun événement de session ne se déclenche → timeout 30 secondes. ### Causes secondaires - **Événement `connection_failed` non géré** : `XmppChannel` n'écoute que `session_start`, `failed_auth`, `disconnected`. Slixmpp émet `connection_failed` en cas d'échec réseau (DNS, TCP, firewall) — cet événement n'est pas capturé, donc `session_future` reste pending jusqu'au timeout de session. - **Pas de timeout sur `await connect_future`** (`xmpp.py:284-285`) : un SYN blackhole ou une résolution DNS bloquée peut pendre indéfiniment avant même que le timeout de session ne soit armé. - **Pas de timeout sur `await disconnect_future` dans le `finally`** (`xmpp.py:308-315`) : après le timeout de session, le cleanup peut bloquer indéfiniment — le processus ne se termine pas proprement, d'où le Ctrl-C nécessaire. - **Tests ne couvrent pas les cas d'échec réels** : `FakeClientXMPP` résout immédiatement `connect()` et `disconnect()`, ne simule pas `connection_failed`, retry, DNS failure, ou TLS mismatch. ### Directions de correction 1. **Séparer le mode de transport TLS** (direct TLS, STARTTLS, désactivé) d'un booléen simple ; rendre le défaut compatible avec le port 5222 (STARTTLS). 2. **Ajouter des deadlines distincts** : connexion (DNS/TCP/TLS), session/auth, et cleanup/disconnect avec un chemin d'annulation forcée. 3. **Gérer l'événement `connection_failed`** de Slixmpp comme un échec terminal de connexion plutôt que d'attendre le timeout de session. 4. **Ajouter des tests de cycle de vie** avec des futures pending réalistes (connection_failed, retry, disconnect bloqué, TLS mismatch). 5. **Améliorer l'observabilité** : classifier l'échec (DNS, TCP refusé, TLS, auth, timeout) dans les logs expurgés. > ℹ️ **Méthodologie** : Ce diagnostic a été réalisé par analyse statique du code source et reproduction en environnement de test isolé (endpoint local sans listener, mock de cleanup bloqué). Aucun accès au serveur XMPP de production n'a été nécessaire. Les 30 secondes exactes entre `Using selector: EpollSelector` (21:20:38) et le timeout (21:21:08) confirment que `connect_future` s'est résolu et que c'est bien `session_future` qui a expiré sans qu'aucun événement attendu ne se déclenche.
Author
Owner

Compte de test sur serveur Prosody — décision

Une proposition de création d'un compte XMPP temporaire sur le serveur Prosody de production (avec logs en debug) a été étudiée pour valider le diagnostic.

Décision : écartée. La reproduction locale est suffisante pour les raisons suivantes :

  • La cause racine est un fait protocolaire côté client : le mode TLS (direct TLS vs STARTTLS) est déterminé par les paramètres passés à ClientXMPP.connect(), pas par le serveur. Un vrai serveur ne ferait que confirmer le symptôme (timeout), sans infirmer le mécanisme.
  • Règle projet : les tests et diagnostics doivent s'exécuter sans réseau, avec des mocks uniquement (AGENTS.md §4 & §5). Cette règle vaut aussi pour les phases de validation.
  • Risque d'exposition : credentials temporaires dans le shell/historique, oubli de suppression, logs Prosody debug pouvant capturer des données d'autres utilisateurs du service.

Alternatives retenues si un signal serveur réel est jugé utile :

  1. Enrichir FakeClientXMPP pour simuler les cas d'échec réels (connection_failed, connect_future pending, disconnect_future bloqué, TLS mismatch).
  2. Prosody éphémère en conteneur local isolé (Docker jetable) pour une vérification manuelle ponctuelle hors CI, jamais intégré à la suite tests/.

La correction est lancée sur cette base.

### Compte de test sur serveur Prosody — décision Une proposition de création d'un compte XMPP temporaire sur le serveur Prosody de production (avec logs en debug) a été étudiée pour valider le diagnostic. **Décision : écartée.** La reproduction locale est suffisante pour les raisons suivantes : - **La cause racine est un fait protocolaire côté client** : le mode TLS (direct TLS vs STARTTLS) est déterminé par les paramètres passés à `ClientXMPP.connect()`, pas par le serveur. Un vrai serveur ne ferait que confirmer le symptôme (timeout), sans infirmer le mécanisme. - **Règle projet** : les tests et diagnostics doivent s'exécuter sans réseau, avec des mocks uniquement (AGENTS.md §4 & §5). Cette règle vaut aussi pour les phases de validation. - **Risque d'exposition** : credentials temporaires dans le shell/historique, oubli de suppression, logs Prosody debug pouvant capturer des données d'autres utilisateurs du service. **Alternatives retenues si un signal serveur réel est jugé utile** : 1. Enrichir `FakeClientXMPP` pour simuler les cas d'échec réels (`connection_failed`, `connect_future` pending, `disconnect_future` bloqué, TLS mismatch). 2. Prosody éphémère en conteneur local isolé (Docker jetable) pour une vérification manuelle ponctuelle hors CI, jamais intégré à la suite `tests/`. La correction est lancée sur cette base.
Codex added the area:operationsarea:xmpp labels 2026-09-12 13:14:37 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: AntoineVe/college-infos#25