All links of one day
in a single page.
<Previous day - Next day>

rss_feedDaily RSS Feed
floral_left The Daily Shaarli floral_right
——————————— November 15, 2020 - Sunday 15, November 2020 ———————————

Je me suis toujours demandé comment fonctionne les barres de progression d'un téléchargement / téléversement que l'on trouve sur les sites web : comment le code du site web peut-il être au courant de l'avancement d'une telle action puisqu'il est exécuté une fois l'upload terminé ?

Au temps du module PHP pour Apache httpd, je me disais que, vu qu'il s'agit d'un module, il est incrusté au cœur d'Apache httpd donc il doit certainement disposer d'infos supplémentaires. Mais quid des installations dans lesquelles le serveur web est uniquement un reverse proxy vers un serveur d'applications (FastCGI, PHP-FPM, Python uwsgi) ?

La première réponse qui vient à l'esprit est : on surveille la progression de l'upload entièrement côté client avec Ajax.

J'ignorais qu'il est également possible de surveiller l'upload côté serveur. Enfin, pas totalement, puisqu'il faudra toujours de l'Ajax pour actualiser la barre de progression. Mais le reste est fait côté serveur.


Fonctionnement d'une barre de progression côté serveur

A priori, tout le monde s'est mis d'accord sans pour autant produire une norme (en tout cas, le RFC 1867, souvent cité, normalise l'envoi de fichiers avec un formulaire web, mais pas la mesure de la progression d'un upload) :

  • Le script côté client génère un identifiant unique pour chaque upload ;

  • Le navigateur web lance l'upload ;

  • Le serveur fait les calculs de la progression de l'upload ;

  • En parallèle de l'upload, le client interroge le serveur grâce à Ajax (requêtes asynchrones) en utilisant une URL convenue à l'avance choisie par l'application et en rappelant l'identifiant unique de l'upload. Exemple : XHR GET https://www.monorganisation.example/progressbarupload/upload_progress?X-Progress-ID=42122089-d6dd-4570-9ded-09e75f8a50d2 ;

  • Le serveur répond du JSON. Exemple : « { "state" : "uploading", "received" : 49478497, "size" : 65730134 } ».

  • Le client actualise la barre de progression, puis refait une requête au serveur web afin de connaître l'avancement de l'upload, donc retour au 4e point ci-dessus ;



Quand j'écris « le serveur fait » ceci ou cela, de quel composant précis est-ce que je parle ?

  • Cela peut être PHP, nativement ou couplé au module APC. Dans ces deux cas, le suivi de la progression fonctionne uniquement avec le module PHP pour Apache httpd, pas en FastCGI ni avec PHP-FPM ;

  • Cela peut être le serveur web. Il existe un module pour nginx, un module pour Apache httpd, un module pour Lighttpd, etc. Dans Debian GNU/Linux, celui pour nginx est activé par défaut, celui pour Apache httpd est disponible dans le paquet libapache2-mod-upload-progress. Celui pour nginx est plutôt bien documenté (peut mieux faire sur les valeurs par défaut des paramètres, par exemple) alors que celui pour Apache httpd ne l'est quasiment pas (ça ne permet pas de comprendre que l'URL à laquelle récupérer les rapports de progression est configurable, par exemple).


Configurer nginx

  • Dans le bloc http du fichier ngnix.conf, il faut ajouter : upload_progress uploadp 1m;. On réserve un espace mémoire d'un mégaoctet nommé « uploadp » pour le suivi des uploads ;

  • Dans le serveur virtuel, dans le bloc location qui envoie les requêtes vers un serveur d'applications, il faut ajouter track_uploads uploadp 30s. On surveille tous les uploads en les enregistrant dans l'espace mémoire « uploadp » défini ci-dessus. On conserve la trace d'un upload jusqu'à 30 secondes après sa complétion ;

  • Dans le serveur virtuel, on ajoute un nouveau bloc location capturant l'URL à laquelle l'application est programmée pour venir chercher les informations concernant un upload à coup de XMLHTTPRequest. nginx répondra aux requêtes GET qu'il recevra à cette URL avec les informations concernant un upload dont l'identifiant est passé en argument si celui-ci a été surveillé dans l'espace mémoire « uploadp » :

    location ^~ /progressbarupload/upload_progress {
        upload_progress_json_output
        report_uploads uploadp;
    }


  • On recharge la configuration : sudo systemctl reload nginx.

Notons que le module nginx prend en charge JSONP, JSON with Padding, qui encapsule les données dans un appel de fonction afin de contourner les restrictions inter-domaines : la récupération d'un fichier depuis un autre domaine avec XMLHTTPRequest est interdite par défaut par les navigateurs web sauf si l'on positionne des en-têtes CORS sur le site web sur lequel les données doivent être récupérées, alors que la récupération d'un script JavaScript sans utiliser XMLHTTPRequest est autorisée par défaut. Pour activer un retour au format JSONP, il faut remplacer upload_progress_json_output par upload_progress_jsonp_output.


Configurer Apache httpd :

  • On installe le module (il sera activé et httpd sera redémarré) : sudo apt-get install libapache2-mod-upload-progress ;

  • Dans le serveur virtuel, dans le bloc location qui envoie les requêtes vers un serveur d'applications, il faut ajouter TrackUploads On ;

  • Dans le serveur virtuel, on ajoute un nouveau bloc location capturant à l'URL à laquelle l'application est programmée pour venir chercher les informations concernant un upload à coup de XMLHTTPRequest. nginx répondra aux requêtes GET qu'il recevra à cette URL avec les informations concernant un upload dont l'indentifiant est passé en argument :

    <Location ~ '^/progressbarupload/upload_progress'>
        ReportUploads On
    </Location>


  • On recharge la configuration : sudo systemctl restart apache2.

Le module Apache httpd prend aussi en charge JSONP. Pour l'activer, il suffit d'ajouter le paramètre callback=<nom_désiré_pour_la_callback> à la requête HTTP GET qui viendra consulter le rapport d'avancement de l'upload produit par le serveur, il y a rien à activer dans la configuration du serveur web.

Réponse : ajouter « ORDERING integerOrderingMatch » à la définition de l'attribut.

Sur les attributs numériques (integer) de notre annuaire LDAP (OpenLDAP), nous aimerions faire des recherches de la forme « valeur supérieure ou égale à ». Exemples : trouver les prénoms+nom des personnels dont employeeNumber est >= XXXXXXXX ; trouver tous les uid des personnels dont pwdLastChange est <= 20191231 (cela fonctionne car la date est au format ISO 8601 dont le Hollandais Volant présente les avantages) ; trouver tous les uid des personnels dont gidNumber >= XXXXXXXX.

Notons que la norme LDAP ne permet pas des recherches avec les opérateurs strictement inférieur (<) et strictement supérieur (>). Uniquement <= et >=.

Notre recherche sur le gidNumber fonctionne. Elle échoue sur les autres attributs : aucun résultat est retourné alors que des objets correspondent à notre recherche.

J'ai déjà présenté les règles de comparaison qui peuvent être affectées à un attribut LDAP afin de pouvoir comparer deux objets LDAP entre eux et le fait que, par défaut, aucune régle de comparaison est associée à un attribut. Je pressens qu'il en va de même pour une recherche ordonnée portant sur des attributs numériques.

Je parcours à nouveau le RFC 4517. Bingo, il existe bien des règles de comparaison ordonnée.

Ainsi, pour être en mesure d'effectuer une recherche <= ou >= sur un attribut LDAP de type entier, il faut ajouter « ORDERING integerOrderingMatch » à sa définition en mettant à jour le schéma qui la contient.

-