Eviter les « Duplicate Headers » (error 349) lors de l’envoi d’un fichier dans la HTTP Response

Pour offrir la possibilité aux utilisateurs de télécharger un fichier généré depuis un navigateur, il faut en général envoyer le flux ou un chemin de fichier directement dans la HTTP Response, tout en enrichissant les Headers HTTP pour définir le nom du fichier et son type MIME.

Exemple en ASP.NET

// définit au navigateur que c'est un fichier à télécharger, et que son nom est "monfichier.pdf"
Response.AddHeader("Content-Disposition", "attachments; filename=monfichier.pdf");
// spécifie au navigateur que le fichier doit être traité comme un PDF
Response.ContentType = "application/pdf";
// envoi du flux ou d'un fichier physique
Response.OutputStream.Write(monstream); ou Response.WriteFile(chemin_vers_mon_fichier);

Si vous rencontrez une erreur « Duplicate Headers » (ou « En-têtes doubles envoyés par le serveur ») lors du téléchargement d’un fichier, c’est que vos entêtes HTTP ne respectent probablement pas les normes standard RFC 2231/5987.

Duplicate Headers Error 349
(net::ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION)

Pour plus d’informations, je vous invite à lire ces normes mais en ce qui nous concerne dans cet exemple, les headers HTTP, et plus particulièrement la valeur du paramètre « filename », ne doivent tout simplement pas contenir de caractère unicode (« é, %, ‘,’, ‘;’….).

A l’heure actuelle, seul le navigateur Chrome intercepte ces mauvais formatages d’header en erreur; les autres navigateurs quant à eux les ignorent (pour l’instant, je suppose). Par conséquent, vous ne devriez rencontrer cette erreur que sur Chrome.

Tentez alors de suivre ces 2 recommandations pour éviter les « duplicate headers »:

1. Encadrer le nom de votre fichier avec des  » (double-quotes)

Response.AddHeader("Content-Disposition", "attachments; filename="monfichier.pdf"");
au lieu de 
Response.AddHeader("Content-Disposition", "attachments; filename=monfichier.pdf");

2. Formater le nom de votre fichier en respectant les normes RFC 2231/5987
Votre fichier ne doit par exemple pas contenir de : ‘,’ , ‘.’, ‘;’…

attachments; filename=mon_fichier_test.pdf
au lieu de
attachments; filename=mon,fichier.test.pdf

A noter qu’en ASP.NET MVC, l’envoi de fichier vers la réponse est géré par FileResult et FileStreamResult (qui hérite du premier).
Si on décompile les sources de ces classes, on peut voir qu’il y a une méthode servant à formater directement le nom du fichier (
System.Web.Mvc.FileResult):

public static string GetHeaderValue(string fileName)
{
// If fileName contains any Unicode characters, encode according
// to RFC 2231 (with clarifications from RFC 5987)

foreach (char c in fileName)
{
if ((int)c > 127)
        {
             return CreateRfc2231HeaderValue(fileName);
        }
    }
// Knowing there are no Unicode characters in this fileName, rely on
    // ContentDisposition.ToString() to encode properly.
    // In .Net 4.0, ContentDisposition.ToString() throws FormatException if
    // the file name contains Unicode characters.
    // In .Net 4.5, ContentDisposition.ToString() no longer throws FormatException
    // if it contains Unicode, and it will not encode Unicode as we require here.
    // The Unicode test above is identical to the 4.0 FormatException test,
    // allowing this helper to give the same results in 4.0 and 4.5.        
 
    ContentDisposition disposition = new ContentDisposition() { FileName = fileName };
    return disposition.ToString();
}
private static string CreateRfc2231HeaderValue(string filename)
{
StringBuilder builder = new StringBuilder("attachment; filename*=UTF-8''");
    byte[] filenameBytes = Encoding.UTF8.GetBytes(filename);
    foreach (byte b in filenameBytes)
    {
        if (IsByteValidHeaderValueCharacter(b))
        {
        builder.Append((char)b);
        }
        else
        {
             AddByteToStringBuilder(b, builder);
        }
    }
    return builder.ToString(); }
// Application of RFC 2231 Encoding to Hypertext Transfer Protocol (HTTP) Header Fields, sec. 3.2
// http://greenbytes.de/tech/webdav/draft-reschke-rfc2231-in-http-latest.html

private static bool IsByteValidHeaderValueCharacter(byte b)
{
if ((byte)'0' <= b && b <= (byte)'9')
    {
    return true; // is digit
    }
    if ((byte)'a' <= b && b <= (byte)'z')
    {
    return true; // lowercase letter
    }
    if ((byte)'A' <= b && b <= (byte)'Z')
    {
    return true; // uppercase letter
    }
    switch (b)
    {
    case (byte)'-':
        case (byte)'.':
        case (byte)'_':
        case (byte)'~':
        case (byte)':':
        case (byte)'!':
        case (byte)'$':
        case (byte)'&':
        case (byte)'+':
        return true;
    }
    return false;
}

Avec tout ça, vous devriez donc pouvoir éviter les erreurs Duplicate Headers.

Source: http://www.gangarasa.com/lets-Do-GoodCode/tag/err_response_headers_multiple_content_disposition/

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s