Faut que vous y allez rapidement. Optimisation des demandes de contenu de messagerie IMAP

Bonjour à tous! Dans un article précédent, j'ai expliqué comment synchroniser rapidement le contenu d'une boîte dans le cache local. Ici, je veux parler des caractéristiques de la demande de contenu de lettres et de la meilleure façon de demander du contenu, sans avoir peur de la forte consommation de trafic.

image

Souvenons-nous rapidement de ce que nous avons appris dans le dernier article:

  • IMAP est un protocole avec état
  • Pour voir le contenu de la boîte de réception, vous devez d'abord le sélectionner avec la commande SELECT
  • Pour synchroniser rapidement la boîte dans laquelle nous nous trouvons, vous pouvez utiliser la commande NOOP
  • Afin de ne pas trier les messages du stockage local pour mettre à jour la boîte aux lettres que nous avons déjà quittée, vous pouvez utiliser CONDSTORE et QRESYNC, à condition que votre serveur prenne en charge les données d'extension de protocole

Suffisant!


Permettez-moi de vous rappeler la commande de demander le corps de la lettre:

1 FETCH number (BODY[])

Cela va créer une demande pour obtenir le corps entier de la lettre et toutes les pièces jointes. Il suffit de voir combien de temps il faut pour obtenir un message en 42 paragraphe de Lorem Ipsum et avec une photo de 2 mégaoctets.

Tout d'abord, demandez la taille du message sur le serveur. Cela se fait par la commande:

1 FETCH 18871 (RFC822.SIZE)

RFC822.SIZE renvoie la taille du message en octets:

* 18871 FETCH (RFC822.SIZE 3937793)

Autrement dit, notre message occupe près de 4 mégaoctets.

Maintenant, néanmoins, nous allons utiliser la demande pour le corps complet de la lettre et jeter un œil à l'heure:

1 OK Fetch completed (0.007 + 3.265 secs).

3,3 secondes! Et ce n'est qu'un seul message avec la pièce jointe, et imaginez-les tel sera la boîte entière. Ensuite, il faudra plus d'une minute pour télécharger au moins les vingt et unièmes.

Vous devez admettre que l'activité d'un client qui, en 2020, ne peut pas synchroniser le courrier plus rapidement qu'en une minute est mauvaise. Mais que faire?

Donnez-moi une bouchée une fois


Si vous bruissez RFC3501 dans la clause 6.5.4 , qui décrit les paramètres possibles pour la commande FETCH, vous remarquerez une demande intéressante:

BODY[<section>]<<partial>>

  • section - quelle partie de la lettre obtenir
  • partiel - la taille de cette partie

Comment est construit partiellement? Et c'est très simple. Tout d'abord, l'octet à partir duquel vous devez commencer la lecture est écrit à travers le point, puis combien d'octets en général doivent être lus:

BODY[<section>]<<0.1024>>

Ici, nous demandons la partie de la lettre de zéro octet à 1024.

D'accord, quelle est la section? Tout d'abord, je vais parler d'un paramètre aussi utile dans une requête FETCH que BODYSTRUCTURE:

1 FETCH 18871 (BODYSTRUCTURE)

Ce paramètre, comme vous l'avez probablement compris à partir de la signature, renvoie la structure de la lettre sous la forme décrite dans MIME-IMB .

* 18871 FETCH (BODYSTRUCTURE ((("text" "plain" ("charset" "utf-8") NIL NIL "quoted-printable" 25604 337 NIL NIL NIL NIL)("text" "html" ("charset" "utf-8") NIL NIL "quoted-printable" 29593 390 NIL NIL NIL NIL) "alternative" ("boundary" "--=_Part_763_774309787.1586268692") NIL NIL NIL)("image" "jpeg" ("name" "IMG_20200217_000236.jpg") NIL NIL "base64" 3880726 NIL ("attachment" ("filename" "IMG_20200217_000236.jpg")) NIL NIL) "mixed" ("boundary" "--=_Part_210_297656922.1586268692") NIL NIL NIL))


Jetez un œil à cette structure et votre tête tourne? N'ayez pas peur, maintenant nous allons le découvrir. Comparez les supports d'ouverture et de fermeture.

(
BODYSTRUCTURE 
(
[1] (
[1.1] ("text" "plain" ("charset" "utf-8") NIL NIL "quoted-printable" 25604 337 NIL NIL NIL NIL)
[1.2] ("text" "html" ("charset" "utf-8") NIL NIL "quoted-printable" 29593 390 NIL NIL NIL NIL) "alternative" ("boundary" "--=_Part_763_774309787.1586268692") NIL NIL NIL
)
[2] ("image" "jpeg" ("name" "IMG_20200217_000236.jpg") NIL NIL "base64" 3880726 NIL ("attachment" ("filename" "IMG_20200217_000236.jpg")) NIL NIL) "mixed" ("boundary" "--=_Part_210_297656922.1586268692") NIL NIL NIL
)
)


Vous remarquerez peut-être que j'ai mis des chiffres près de quelques crochets. Ceci est une section.
Comment les calculer? La première parenthèse doit être sautée, car elle contient simplement la réponse à la demande, puis chaque parenthèse ouvrante doit être numérotée selon la règle car les en-têtes des documents sont numérotés:

  • Nous numérotons chaque support d'ouverture en tenant compte de la section précédente
  • Si la section est imbriquée, la section actuelle passant par le point est ajoutée au numéro précédent
  • Si la section n'est pas imbriquée, augmentez son nombre d'une unité.


Par exemple, dans ce cas, dans la première partie qui se termine par «alternative» (c'est-à-dire, c'est la partie de la lettre en plusieurs parties / alternative, où nous sommes libres de choisir laquelle des parties afficher pour l'utilisateur), il y a deux sections qui sont numérotées par un point. J'ai rencontré un serveur où il peut y avoir une imbrication à trois niveaux (c'est-à-dire [1.1.1], [1.1.2], etc.).
Analysons la partie [1.1] examinant la structure de tout cela dans le document MIME-IMB . À en juger, l'en-tête Content-Type est le premier. Il comprend:

  • Type MIME, ici c'est text / plain
  • Encodage (charset = utf8)


Voici deux paramètres écrits comme NIL. Franchement, je n'ai pas compris de quoi il s'agissait, mais jusqu'à présent je n'en ai pas eu besoin, donc je vais le manquer. Je m'excuse pour cette frivolité.
Ensuite est l'en-tête Content-Transfer-Encoding, il y est inclus, qui décrit le mécanisme de codage, ici il est cité-imprimable. 
Les deux nombres suivants décrivent la taille de la partie en octets et le nombre de lignes, si possible. Avec leur aide, nous pouvons calculer le nombre d'octets à prendre pour afficher un certain nombre de lignes.
Les lignes suivantes qui ne figurent pas dans cette partie:

  • Content-Id, qui est utilisé dans la ligne de la lettre
  • Content-Description, une ligne qui décrit ce qu'est cette partie


Pour les deux autres paramètres, je n'ai pas pu trouver de réponse définitive, mais l'un de ces paramètres peut contenir des parties MD5, ce qui peut parfois être utile.
Pour la partie [2], tout est pareil, sauf qu'il s'agit d'une image, d'une pièce jointe avec un nom et d'un encodage base64. Si ce n'est pas encore complètement clair ce qui se passe ici, alors sur ce site, il est parfaitement expliqué exactement comment calculer la section.

Qu'est-ce que ça donne? Et le fait qu'au stade de l'affichage de la lettre, nous ne pouvons déjà demander que la partie supérieure du contenu et ne pas charger les pièces jointes jusqu'à ce que l'utilisateur lui-même entre le message et clique sur le bouton "télécharger". Toutes les informations pour afficher les pièces jointes nous parviennent dans BODYSCTRUCTURE afin que le nom, le format et la taille puissent être affichés sans charger la pièce jointe elle-même. 

Passons à la pratique. Nous demanderons un kilo-octet de contenu de message sans pièce jointe, juste pour savoir ce qu'ils nous ont envoyé.

1 fetch 18871 (body[1.1]<0.1024>)
* 18871 FETCH (BODY[1.1]<0> {1024}
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus consecte=
tur enim in nisi venenatis, id varius tellus viverra. Praesent et enim te=
llus. Nunc vestibulum diam tortor, id posuere turpis tempor luctus. Vivam=
us molestie non nunc nec placerat. Cras finibus ut erat et tristique. Cur=
abitur vitae commodo risus. Etiam sed scelerisque erat. Quisque cursus bl=
andit finibus. Nullam ac lectus accumsan, molestie quam non, mollis urna.=
 Nulla at arcu in libero condimentum mollis ut non velit. Vestibulum sed =
risus et magna congue iaculis. Vestibulum nec interdum elit, ut commodo m=
auris. Nulla ipsum leo, vestibulum nec ligula non, elementum ullamcorper =
risus. Nunc et malesuada sem, id venenatis massa. Integer dolor ante, max=
imus in eleifend nec, ultricies ut risus. Mauris posuere eget tortor at p=
orttitor.=0AIn porta elementum ornare. Suspendisse aliquam, tortor sed al=
iquam bibendum, nulla ante rhoncus elit, placerat accumsan augue nibh non=
 est. Duis finibus vel tortor finibu)
1 OK Fetch completed (0.073 + 0.000 + 0.072 secs).


Environ 100 millisecondes et nous voyons déjà une partie du contenu de la lettre! C'est juste un excellent résultat, étant donné qu'auparavant, il nous fallait près de 4 secondes pour télécharger le contenu d'une lettre. Ensuite, vous pouvez simplement charger tout le contenu de la lettre dans le flux d'arrière-plan, de l'extérieur, il semblerait que les lettres soient chargées instantanément. Tout ce qui était nécessaire était de regarder la structure de la lettre et de télécharger uniquement ce qui était nécessaire pour un affichage rapide. 
Un seul instant. Cette demande fera apparaître le message sur le serveur comme lu. Mais vous pouvez résoudre ce problème en ajoutant uniquement PEEK à la demande de corps

1 fetch 18871 (BODY.PEEK[1.1]<0.1024>)
* 18871 FETCH (BODY[1.1]<0> {1024}
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus consecte=
tur enim in nisi venenatis, id varius tellus viverra. Praesent et enim te=
llus. Nunc vestibulum diam tortor, id posuere turpis tempor luctus. Vivam=
us molestie non nunc nec placerat. Cras finibus ut erat et tristique. Cur=
abitur vitae commodo risus. Etiam sed scelerisque erat. Quisque cursus bl=
andit finibus. Nullam ac lectus accumsan, molestie quam non, mollis urna.=
 Nulla at arcu in libero condimentum mollis ut non velit. Vestibulum sed =
risus et magna congue iaculis. Vestibulum nec interdum elit, ut commodo m=
auris. Nulla ipsum leo, vestibulum nec ligula non, elementum ullamcorper =
risus. Nunc et malesuada sem, id venenatis massa. Integer dolor ante, max=
imus in eleifend nec, ultricies ut risus. Mauris posuere eget tortor at p=
orttitor.=0AIn porta elementum ornare. Suspendisse aliquam, tortor sed al=
iquam bibendum, nulla ante rhoncus elit, placerat accumsan augue nibh non=
 est. Duis finibus vel tortor finibu)
1 OK Fetch completed (0.001 + 0.000 secs).


Et le tour est joué! La lettre reste non lue et une partie du contenu que nous avons reçu.
Tout devient encore plus facile si la fonction de requête PREVIEW est implémentée sur votre serveur. 

1 fetch 18871 (PREVIEW)
* 18871 FETCH (PREVIEW (FUZZY "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus consectetur enim in nisi venenatis, id varius tellus viverra. Praesent et enim tellus. Nunc vestibulum diam tortor, id posuere turpis t"))
1 OK Fetch completed (0.001 + 0.000 secs).


Ici, nous ne passons pas de temps à interroger la structure et nous obtenons l'aperçu du message instantanément. Mais n'oubliez pas que la structure de requête est utile pour définir des pièces jointes afin qu'elles ne soient pas chargées dans l'inactif.

Attendre


Presque tous les clients de messagerie implémentent le bouton "Actualiser" si l'utilisateur souhaite recevoir de nouvelles lettres maintenant. Mais en quelque sorte, ce n'est pas cool pour notre temps, où il y a des notifications à la fois dans les appareils et dans les navigateurs. Que dit IMAP à ce sujet? Et il dit IDLE . Cette opération conserve la connexion au dossier et vous informe des modifications apportées au dossier. Veuillez noter, pas la boîte aux lettres, mais les dossiers. Pour ce faire, vous avez besoin du serveur pour implémenter la fonction IDLE. 

Sélectionnez d'abord le dossier pour lequel le serveur enverra des alertes, puis activez IDLE

1 SELECT Inbox
* FLAGS (\Answered \Flagged \Deleted \Seen \Draft $Forwarded $MDNSent)
* OK [PERMANENTFLAGS (\Answered \Flagged \Deleted \Seen \Draft $Forwarded $MDNSent \*)] Flags permitted.
* 18872 EXISTS
* 0 RECENT
* OK [UNSEEN 18685] First unseen.
* OK [UIDVALIDITY 1532079879] UIDs valid
* OK [UIDNEXT 20155] Predicted next UID
* OK [HIGHESTMODSEQ 26338] Highest
1 OK [READ-WRITE] Select completed (0.002 + 0.000 + 0.001 secs).
1 IDLE
+ idling


La réponse "+ ralenti" signale l'inclusion du ralenti dans le dossier. Que se passe-t-il si une nouvelle lettre arrive?

* 18873 EXISTS
* 1 RECENT


Je me suis envoyé la même lettre et Idle m'a informé que je devrais demander la lettre 18873, qu'il y avait 18873 lettres dans le dossier et qu'une lettre venait d'arriver.
Ensuite, je demanderai cette lettre dans une autre connexion, nous sommes intéressés par la lettre avec la réponse EXISTS.

1 fetch 18873 (BODY.PEEK[1.1]<0.1024>)
* 18873 FETCH (BODY[1.1]<0> {1024}
---- Original Message ---- Tue, Apr 7, 2020, 17:11=0ASubject=
: Lorem Ipsum=0A  Lorem ipsum dolor sit amet, consectetur adipiscing elit=
. Vivamus consectetur enim in nisi venenatis, id varius tellus viverra. P=
raesent et enim tellus. Nunc vestibulum diam tortor, id posuere turpis te=
mpor luctus. Vivamus molestie non nunc nec placerat. Cras finibus ut erat=


Il est très important de comprendre. IDLE nécessite une connexion distincte, vous ne pouvez donc pas recevoir de modifications ni demander de messages dans la même session.
Que peut faire IDLE d'autre? Il est capable de notifier les lettres supprimées et les lettres dont les drapeaux ont changé. Examinons la lettre à titre d'exemple, mettant ainsi le drapeau "/ vu" dessus et supprimons la lettre.

* 18873 FETCH (FLAGS (\Seen \Recent))
* OK Still here
* OK Still here
* 18873 EXPUNGE
* 18871 EXPUNGE
* 0 RECENT


J'ai supprimé la conversation (18873, 18871) et j'ai regardé une autre lettre (réponse FETCH). Pourquoi cette lettre est-elle devenue 18871? Parce que IMAP recompose le numéro de lettre si quelque chose a changé. Depuis qu'il est devenu le top, son nombre a également changé. 
Avec IDLE, on peut synchroniser rapidement l'état de la box, mais il est désagréable qu'il nécessite une connexion séparée. Cela pourrait-il être mieux? Voilà pourquoi je suis ici.

Criez comment allez-vous


Et si je vous dis qu'il existe une fonctionnalité qui vous permet de recevoir des notifications du serveur dans la même connexion, et même spécialement configurées pour les boîtes aux lettres, et même pas une. Cela ressemble à un conte de fées, mais ne devenez pas fou, c'est une vraie capacité NOTIFIER . Il en sait beaucoup, par exemple:

  • Configurer des dossiers spécifiques à partir desquels nous attendons des notifications
  • Écoutez les changements d'état du dossier (lecture des lettres, nouvelles lettres)
  • Définissez le format de notification, c'est-à-dire ce que nous voulons voir lors du changement de dossier
  • Écoutez les changements de nom de dossier
  • Écouter les modifications des métadonnées du dossier


Regardons un exemple de la façon dont nous pouvons écouter les changements d'état des dossiers

1 notify set (inboxes (MessageNew FlagChange MessageExpunge))
1 OK NOTIFY completed (0.001 + 0.000 secs).


Maintenant, le serveur nous enverra des notifications avec les statuts des dossiers, par exemple, j'ajouterai quelques messages à différents dossiers

* STATUS INBOX/Ozon (MESSAGES 312 UIDNEXT 321 UNSEEN 48)
* STATUS "INBOX/Company News" (MESSAGES 178 UIDNEXT 179 UNSEEN 1)
* STATUS "INBOX/Company News" (MESSAGES 177 UIDNEXT 179 UNSEEN 0)


J'analyserai la commande:
D'abord vient la commande NOTIFY SET. Ensuite, entre parenthèses, nous sélectionnons les dossiers que nous écouterons:

  • Boîtes de réception - pour tous les dossiers que vous pouvez sélectionner
  • Personnel - dossiers qui se trouvent dans l'espace de noms de l'utilisateur
  • Abonné - dossiers auxquels l'utilisateur est abonné
  • Sous-arbre - sous-arbre du dossier à spécifier
  • Boîtes aux lettres - ici vous pouvez lister les dossiers à écouter.
  • Sélectionné - alerte uniquement pour les dossiers sélectionnés


Et les paramètres qui sont responsables du filtre d'alerte:

  • MessageNew - si un nouveau message arrive
  • FlagChange - si le drapeau a changé
  • MessageExpunge - si le message a été supprimé ou déplacé


Mais avec une telle commande, nous ne pouvons pas recevoir les paramètres d'un message nouveau, modifié ou supprimé. Pour ce faire, sélectionnez le paramètre Selected et spécifiez exactement ce qui doit être renvoyé. Nous pouvons ajouter une autre alerte sans supprimer la précédente.

1 notify set status (selected (MessageNew (uid preview) MessageExpunge))


Ici, dans MessageNew, nous spécifions les paramètres que la notification doit renvoyer. Je vais choisir Inbox et encore une fois je vais me jeter lorem ipsum.

* 18868 FETCH (UID 20157 PREVIEW (FUZZY "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus consectetur enim in nisi venenatis, id varius tellus viverra. Praesent et enim tellus. Nunc vestibulum diam tortor, id posuere turpis t"))


Ça vous plaît? Pour l'inactif, nous devons conserver deux connexions, dont l'une demande également des messages que l'inactif nous a renvoyés. Immédiatement, ils nous apportent tout sur un plateau d'argent. 
Et pour que nous puissions écouter les changements de nom de dossier

1 notify set (inboxes (MailboxName))


Renommez un dossier et voyez le résultat.

* LIST () "/" 1111 ("OLDNAME" (aaaa))


Et maintenant, nous savons qu'il y avait un dossier "aaaa", et est devenu "1111".
Maintenant, vous pouvez écouter le changement de drapeaux et la suppression des messages. Pour ce faire, utilisez le paramètre FlagChange

1 notify set (selected (MessageNew (uid) FlagChange MessageExpunge))


Et lorsque vous modifiez les indicateurs de message et supprimez, nous obtenons

* 18865 EXPUNGE
* 18864 FETCH (FLAGS ())
* 18864 FETCH (FLAGS (\Answered))


Et après


Toutes ces fonctionnalités aident le client de messagerie à travailler aussi rapidement et facilement que possible pour l'utilisateur. IDLE et NOTIFY informent l'utilisateur des changements dans les dossiers, demandant qu'une partie de la lettre accélère son chargement. 
Dans le dernier article, je voudrais parler du mécanisme de recherche dans IMAP et comment il peut être accéléré et réduire la charge sur le réseau. Merci d'avoir lu jusqu'au bout. 

All Articles