Dot.Blog

C#, XAML, Xamarin, UWP/Android/iOS

Quand browsers et proxies jouent à cache-cache avec vos Xap...

[new:30/06/2011]Lorsqu’on travaille avec Silverlight on peut facilement devenir fou à cause d’un proxy ou d’un browser dont le cache est mal géré. En vous resservant un vieux Xap sans vous le dire, vous avez l’impression que votre session de debug ne sert à rien ou bien vos utilisateurs vous décrivent des bugs pourtant réglés depuis un moment... Dans tous les cas un proxy (ou un browser) est en train de jouer à cache-cache avec vos Xap et vos nerfs !

Vider moi ce cache !

Le bon réflexe est de vider le cache du browser. Si le problème ne vient pas d’un proxy sur la route entre le serveur et le client cela va arranger les choses. Mais d’une part c’est fastidieux et, d’autre part, c’est encore pire s’il faut l’expliquer à un utilisateur.

De plus cela doit être recommencé à chaque changement dans le Xap, autant dire qu’en développement cela peut s’avérer très lourd ! Heureusement le problème ne se pose pas avec IE dans ses versions récentes. Mais il arrive qu’on debug aussi avec d’autres browsers.

Bref, vider le cache du butineur, cela peut être utile, mais c’est à la fois lourd et pas toujours efficace.

Tromper les caches

Voilà une alternative plus intéressante. C’est un peu comme passer à fond devant un radar mais avec de fausses plaques. Le radar est toujours là, mais il ne sait pas que c’est “vous”. Bien entendu ce jeu là est interdit. Tromper les caches des browsers et des proxies est une opération largement moins risquée ! Le principe est le même, passer à fond devant le contrôleur sans qu’il vous reconnaisse. Mais sans les ennuis avec la police.

Tromper, c’est mal, mais ça peut aussi être utile, nécessaire voire même héroïque.

Nécessité faisant loi, nous allons donc tromper les caches !

Mais comment tromper un cache ?

Tromper un cache c’est facile direz-vous, un cache c’est bête.

Oui, mais par nature un système qui contrôle, radar, cache ou autre, plus c’est bête, plus c’est dur à tromper justement ! Car ça marche de façon automatique, sans se poser de question.

On peut embrouiller l’esprit de celui qui par intelligence pratique le doute métaphysique, mais avec un cache, vous pouvez toujours tenter une question du type “l’existentialisme est-il un humanisme ?” ... vous attendrez longtemps la réponse ! Il va rester concentrer sur son job le bougre (essayez avec un radar aussi, vous verrez bien).

De fait, les caches, de browsers ou de proxies, sont des butors obstinés. On les fabrique pour ça d’ailleurs. Mais certains ne sont pas exempts de défauts.  Et votre mission, si vous l’acceptez, sera de faire passer un vieux Xap pour un Xap tout neuf ne devant pas être caché.

En réalité non : vous voulez que les Xap neufs ne passent pas pour de vieux Xap justement, tout l’inverse ! A moins que cela ne revienne au même ici ? ... A méditer Sourire!

Quelques balises de plus

Le plugin Silverlight s’instancie généralement dans une balise <object> même sous Asp.Net (bien qu’il existe un contrôle plus direct). De même, le plugin peut-être placé dans une page Html classique mais il est souvent préférable de le placer dans une page ASPX pour la souplesse que cela autorise.

Justement ici nous supposerons que le plugin est instancié par une balise <object> dans une page ASPX ! C’est inouï les coïncidences parfois (comme les phrases avec deux ï) !

Une balise classique

Voici à quoi ressemble une form Asp.Net avec balise <object> classique pour instancier une application Silverlight via son plugin :

<form id="form1" runat="server" style="height:100%">
   <div id="silverlightControlHost">    
     <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" 
                width="100%" height="100%">
           <param name="source" value="ClientBin/SilverlightApplication2.xap"/>
           <param name="onError" value="onSilverlightError" />
           <param name="background" value="white" />
           <param name="minRuntimeVersion" value="4.0.50826.0" />
           <param name="autoUpgrade" value="true" />
           <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=4.0.50826.0" 
                 style="text-decoration:none">
                           <img src="http://go.microsoft.com/fwlink/?LinkId=161376" 
                           alt="Get Microsoft Silverlight" style="border-style:none"/>
           </a>
      </object>
      <iframe id="_sl_historyFrame" style="visibility:hidden;height:0px;width:0px;border:0px">
      </iframe>
  </div>
</form> 

 

Deux situations

Il y a deux situations particulières à gérer : le debug et l’exploitation. On peut vouloir mettre en œuvre l’astuce dans l’un ou l’autre des cas ou dans les deux.

A vous de décider et surtout de savoir si cela est justifié dans votre cas précis.

Une seule approche

Elle consiste à passer un paramètre “bidon” dans le nom du Xap, paramètre ayant à chaque fois une valeur différente.

Pour ce faire nous allons utiliser la possibilité d’intégrer du C# directement dans le code HTML d’une page Asp.Net, code C# qui modifiera l’écriture du code HTML, agissant ainsi comme un préprocesseur très sophistiqué. En réalité c’est le mode de programmation Asp.Net des “débuts”, avant le “code behind”. A la mode PHP donc. J’ai horreur de ça, mais il faut avouer que là, précisément, c’est utile.

Dans l’exemple de code plus haut, on voit que la balise déclarant l’application Silverlight utilise une série de sous balises <param> permettant de fixer les paramètres qui vont être transmis au plugin. Parmi ceux-ci se trouve le paramètre ayant le nom de “source” et fixant le nom du fichier Xap à charger.

C’est cette balise là que nous allons substituer par un bout de code C# comme celui-ci :

<%    
    const string strSourceFile = @"ClientBin/AppliSL.xap";
    string param;
    if (System.Diagnostics.Debugger.IsAttached)
    param = "<param name=\"source\" value=\"" + strSourceFile + "?" 
                                        + DateTime.Now.Ticks + "\" />";
    else
    {
       var xappath = HttpContext.Current.Server.MapPath(@"") + @"\" + strSourceFile;
       param = "<param name=\"source\" value=\"" + strSourceFile + "?ignore="
                + System.IO.File.GetLastWriteTime(xappath) + "\" />";
    }
    Response.Write(param);
%>

 

... Mais deux façons de faire

En effet, vous remarquerez que suivant que le mode Debug est reconnu ou pas la même astuce est utilisée mais avec deux techniques légèrement différentes.

D’abord il est intéressant de faire la séparation entre debug et mode d’exploitation normal. On peut vouloir utiliser l’astuce “anti cache” dans un cas et pas dans l’autre. Avec le code tel qu’il est écrit il est très facile de le faire.

Vous noterez au passage que le code ci-dessus remplace totalement la balise <param> “source” puisque son rôle est justement de la réécrire totalement (le Response.Write() à la fin).

Ensuite, on voit qu’en cas de debug on ajoute “?” plus les ticks de l’horloge au nom du Xap alors que dans mode normal on utilise une technique plus complexe.

Pourquoi cette différence ?

Dans le cas du debug ce qui compte c’est de berner le cache en lui faisant croire que le Xap est différent à chaque fois (ce qui est généralement vrai puisque justement on est en train de le tripoter). Il s’agit ici de régler un problème de cache local dans le browser. Ce problème n’est pas systématique et arrive avec certains browsers. Vous n’êtes pas obligés d’implémenter l’astuce, il suffit de ne pas ajouter les ticks derrière le nom du Xap...

Dans le second cas nous nous plaçons dans l’optique d’une utilisation en exploitation. Il y a donc un serveur avec votre Xap quelque part, et ailleurs un utilisateur avec son browser. Entre les deux il y a la terre... parcourue de câbles, de fibres optiques, survolées par des satellites le tout saupoudré de machines diverses et variées et pourquoi pas de proxies.

Les caches sont utiles : ils diminuent les temps de chargement en rapprochant les ressources du point où elles sont consommées. Tromper les caches aveuglément pénalisera vos applications en obligeant un chargement complet depuis vos serveurs.

L’astuce simplette des ticks de l’horloge parfaitement adaptée pour le debug serait contre productive en exploitation. Il faut laisser les caches cacher.

Sauf qu’on veut s’assurer que si le Xap a été modifié c’est bien la dernière version qui arrive dans le browser de l’utilisateur et non une version plus ancienne...

Pour régler le problème le code C# qui réécrit la balise <param> utilise une feinte plus intelligente que les ticks de l’horloge : la date de dernière écriture du Xap est récupérée et utilisée pour former le fameux paramètre “bidon” placé derrière le nom du fichier Xap.

Ainsi, le nom du Xap + paramètre “bidon” sera bien différent à chaque fois que le Xap aura été mis à jour, mais le reste du temps le fichier pourra continuer à être caché sans dégradation de performances.

Pour tester l’astuce il suffit de regarder le source de la page Web contenant l’application Silverlight en fonctionnement. Vous pourrez alors constater que le paramètre fixant le nom du fichier Xap comporte un “?xxxxxx” en fin de nom de fichier, les “xxxxx” étant soit un très grand nombre (les ticks en mode debug), soit “ignore=<date>” dans le mode “production”.

On notera pour finir qu’en debug on passe juste un nombre alors qu’en mode d’exploitation on fait vraiment “semblant” jusqu’au bout en créant un paramètre nommé (“ignore”) afin que les caches l’analysent correctement pensant à un véritable paramètre d’application comme cela se pratique communément dans les URL.

Conclusion

Il y a parfois des problèmes qui semblent insolubles car on n’a pas la “main” sur l’élément ou les éléments en cause. Ici le cache d'u browser ou la présence d’un proxy ... approximatif dans la chaine entre serveur et client. Toutefois, comme dans l’éternel jeu du gendarme et du voleur, il y a toujours de la place pour l’inventivité. Aucun système de contrôle ne peut tout avoir prévu. C’est une lueur d’espoir dans un monde de plus en plus régenté par des machines, qu’il s’agisse de radars, de caméra de vidéosurveillance ou de simples proxies...

Stay Tuned! ?ignore=456467897217897456 (on ne sait jamais... Sourire)

blog comments powered by Disqus