Je viens de passer un temps certain autour d’Azure Storage, et à la fin de mon appli tout a commencé à me lâcher. C’est courant, et les fanatiques du test unitaire vont se rouler par terre, mais bon…

Jusqu’à jeudi dernier je développais mon appli de vote comme un bourrin, et j’ai commencé jeudi à assembler les morceaux qui marchaient. En gros, j”ai écrit deux helpers : un qui me met en relation ave les APIs de Microsoft Tag, et un second qui me donne une couche pour Azure Blob Storage.

La dernière étape consistait à prendre les tags générés par Microsoft tag et à les stocker dans Azure Blob Storage. Rien de méchant puisque les deux helpers fonctionnaient.

Bref, je me retrouve à écrire quelque chose de ce style :

CloudVoteData.WSTagHandler ws = new CloudVoteData.WSTagHandler();
// 1 - Création du tag
ws.CreateTag(yesTag, webRoot + "/Y.aspx?ID=" + tagName);
// 2 - Sauvegarde du tag dans le cloud
var sh = new CloudVoteData.StorageTagHandler();
sh.CreateTag(ws.GetTagImageData(yesTag), yesTag);

(Au passage, une minute de pub : http://gallery.live.com/liveItemDetail.aspx?li=d8835a5e-28da-4242-82eb-e1a006b083b9&bt=9&pl=8
c’est le plug-in pour Live Writer qui chope ce que vous avez copié dans Visual Studio et qui le colle en respectant la mise en forme dudit Visual Studio).

Bref, rien que de très simple, je transfère une image de l’un dans l’autre. Seulement voilà, j’ai eu ce délicieux message d’erreur : image

Je cite, pour que d’autres malheureux puissent bénir Goo^H^H^HBing : “Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.”

Alors j’ai cherché. J’ai trimballé les infos de connexion à Azure dans le code source, dans un fichier de config, vérifié que j’avais pas d’écrasement de variables, tout codé en dur, simplifié mon code, changé StorageClient pour une version plus récente, tracé en pas à pas dans RestBlobStorage.cs, réinstallé ma machine (l’équivalent geek d’aller chez le coiffeur quand on est énervé, coucou chérie), jusqu’à ce que je me souvienne que les conteneurs dans Azure Blob Storage ne peuvent pas comporter de majuscule. Alors, me suis-je dit, comme il y a une phase de canonisation de la requête pour calculer sa signature, peut-être que mes en-têtes contiennent des majuscules quand elles sont envoyées au serveur mais que la signature est en minuscules ? Voilà le bout de code que je soupçonnais :

public  bool CreateTag(byte[] content, string tagId, string caption, string title)
{           
    StorageAccountInfo uc = new StorageAccountInfo(new Uri(endpoint), false, accountName, key);
   
    BlobStorage blobStorage = BlobStorage.Create(uc);

    BlobContainer container = blobStorage.GetBlobContainer("votes");

    container.CreateContainer(null, ContainerAccessControl.Public);

    BlobProperties properties = new BlobProperties(tagId);
    NameValueCollection metadata = new NameValueCollection();
    metadata["Id"] = tagId;
    metadata["caption"] = caption;
    metadata["title"] = title;
    metadata["Votes"] = "0";
    properties.Metadata = metadata;
    properties.ContentType = "image/png";
    BlobContents imageBlob = new BlobContents(content);
    return container.CreateBlob(properties, imageBlob, true);
    
}
public bool CreateTag(byte[] content, string tagId)
{
    return CreateTag(content, tagId, "", "");
}

J’ai donc consciencieusement (oui, c’est pénible à écrire autant qu’à faire) retravaillé les lignes qui parlent de métadonnées pour tout repasser en minuscules, pas de succès. Alors j’ai supprimé toutes ces belles lignes (un bon truc de désespéré : supprimer du code jusqu’à ce que ça compile), et là – miracle – tout s’est mis à remarcher. Ah-ah. J’ai repris mon code de test du tout début et regardé : je passais des métadonnées sans problème pendant la création du blob.

En fait, le vice est caché dans la dernière ligne de mon code : mon code de test appelle la version courte de CreateTag, qui passe la main à la version longue en mettant des valeurs par défaut. Sauf que, je passe des chaînes vides pour mes métadonnées, et ça gaufre gentiment la requête de création du blob. Je ne sais pas encore pourquoi, je soupçonne deux endroits : côté client les en-têtes sont ré-ordonnées avant d’être transmises au serveur, c’est un bon coin pour louper une chaîne vide. L’autre suspect c’est Azure  Blob Storage, mais pour le moment je vais déjà finir mon sample et je creuserai plus tard.

(for our english friends out there : don’t pass empty metadata when you create a blob).