In questi giorni mi sono occupato di ottimizzare le performance di alcuni siti che ho realizzato in ASP.NET MVC. Mi ero già preoccupato di minificare e combinare insieme tutti i fogli di stile e i javascript inclusi nelle pagine. A seguito, però, di una verifica con l’estensione PageSpeed della web developer di Chrome mi sono reso conto che IIS, anche se non per tutti i siti, non effettuava la compressione Gzip dei css e js come si può evincere dal questo snapshot: nel response header della richiesta http per un file css non compariva l’intestazione Content Enconding: gzip che speravo di trovare e che avrebbe evidenziato il comportamento voluto da parte di IIS
.
Ho iniziato quindi a ricercare una possibile soluzione e googlando un po’ ho trovato qualcuno che suggeriva di inserire questa sezione nel Web.config dell’applicazione
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<httpCompression directory="%SystemDrive%\inetpub\temp\IIS Temporary Compressed Files"> <scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll"/> <dynamicTypes> <add mimeType="text/*" enabled="true"/> <add mimeType="message/*" enabled="true"/> <add mimeType="application/javascript" enabled="true"/> <add mimeType="*/*" enabled="false"/> </dynamicTypes> <staticTypes> <add mimeType="text/*" enabled="true"/> <add mimeType="message/*" enabled="true"/> <add mimeType="application/javascript" enabled="true"/> <add mimeType="*/*" enabled="false"/> </staticTypes> </httpCompression> <urlCompression doStaticCompression="true" doDynamicCompression="true"/> |
Purtroppo però questa soluzione non va bene in medium trust, in quanto questa sezione del Web.config può essere inserita solo se si hanno i permessi e così non è in medium trust. Se si tenta di farlo viene generata una eccezione HTTP Error 500.19 – Internal Server Error quando IIS è in funzionamento .NET 4 e Integrated Pipiline. Ho quindi inizialmente contattato l’assistenza di Aruba aprendo un ticket e, dopo una dettagliata descrizione del problema, mi hanno così risposto:
“Gentile cliente,
le confermo che sui nostri server di hosting non è possibile utilizzare la compressione degli url. E’ necessario mantenere commentata tale riga di codice per il corretto funzionamento del sito.”
Ho cercato allora di aggirare l’ostacolo e trovare una soluzione alternativa pensando che fosse possibile effettuare la compressione a livello di applicazione creando un modulo HTTP Handler. Mi sono messo alla ricerca nel web e ho trovato diverse possibili soluzioni. Nessuna andava bene, se applicata alla lettera, ma dopo diversi tentativi, ne ho ricavata una mia arrivando al risultato sperato. Ho creato questo HTTPHandler:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
public class HttpCompressionModule : IHttpModule { private const string AcceptEncodingHeader = "Accept-Encoding"; private const string ContentEncodingHeader = "Content-Encoding"; private const string GZipContentEncoding = "gzip"; private const string DeflateContentEncoding = "deflate"; public HttpCompressionModule() { } public void Dispose() { } public String ModuleName { get { return "HttpCompressionModule"; } } // In the Init function, register for HttpApplication // events by adding your handlers. public void Init(HttpApplication application) { application.BeginRequest += (new EventHandler(this.Application_BeginRequest)); } private void Application_BeginRequest(Object source, EventArgs e) { // Create HttpApplication and HttpContext objects to access // request and response properties. HttpApplication application = (HttpApplication)source; HttpContext context = application.Context; if (HttpContext.Current.Handler is DefaultHttpHandler) { return; } String acceptEncoding = HttpContext.Current.Request.Headers[AcceptEncodingHeader]; if (string.IsNullOrEmpty(acceptEncoding)) { return; } HttpResponse response = HttpContext.Current.Response; acceptEncoding = acceptEncoding.ToLowerInvariant(); if (acceptEncoding.Contains(GZipContentEncoding)) { response.AddHeader(ContentEncodingHeader, GZipContentEncoding); response.Filter = new GZipStream(response.Filter, CompressionMode.Compress); } else if (acceptEncoding.Contains(DeflateContentEncoding)) { response.AddHeader(ContentEncodingHeader, DeflateContentEncoding); response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress); } } } |
Ad ogni richiesta HTTP che arriva all’applicazione controlla che nel Request Header ci sia nel “Accept-Encoding” la parola gzip e in quel
caso effettua la compressione dell’URL relativo. Fatto questo ho inserito le seguenti sezioni nel Web.config:
1 2 3 4 |
<modules runAllManagedModulesForAllRequests="true"> <remove name="HttpCompressionModule"/> <add name="HttpCompressionModule" type="Ingegnosi.Modules.HttpCompressionModule"/> </modules> |
e ho verificato con web developer di Chrome che tutto funzionasse: