Il seguente articolo, è inerente c# e in particolare all’integrazione di un CompressionFilter nel workflow di una chiamata web mediante il framework MVC2 di Microsoft, ma è applicabile in linea di massima a tutti i linguaggi. Si tratta fondamentalmente di applicare un algoritmo di compressione ai dati in output da una chiamata http, in modo da far viaggiare meno dati sulla rete, e accertarsi di quale metodi di compressione il client chiamante supporti.
Ci sono diversi tutorial sulla rete riguardo a questa problematica, ma è difficile trovare una risposta assoluta in quanto il vero problema non è la creazione di una funzione per la compressione (già implementate nei vari framework), ma nella decodifica corretta dell’Header Accept-Encoding che ci aiuta a dedurre che cosa supporti il chiamante, evitando così di applicare meccanismi di compressione non graditi o non supportati.
Banalmente basterebbe controllare se nell’header in questione sia presente la stringa gzip o deflate ,ad indicare che il client supporta i due algoritmi di compressione standard, ma in realtà le specifiche W3C sull’encoding, prevedono un funzionamento molto piu complesso.
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
Questo tipo di Headers si basano su un concetto di preferenza e pesi..
Alcuni esempi di valori possibili:
- il più banale “gzip” oppure solo “deflate” ad indicare l’algoritmo desiderato.
- “gzip,deflate” ad indicare la sequenza delle preferenze,
- “gzip;q=0,deflate” ad indicare che gzip non dovrebbe minimamente essere usato (ha un peso Q pari a zero)
- “gzip;q=0.4,deflate;q=0.4,identity;q=0.2” ad indicare la ripartizione tra le preferenze..
- identity sta a indicare che non va assolutamente usato un content-encoding.. se espresso senza q, il default sui parametri e’ q=1 e quindi avrebbe sempre la precedenda come in questo esempio: “gzip;q=0.5,deflate;q=0.5,identity”..
- “*” sta invece a indicare qualsiasi encoding
Da quello che si evince e’ impossibile andare a cercare semplicemente se nella stringa sia presente gzip o deflate.. perche si rischia di incappare in errori (come da esempio con lo “*” o con identity dove q=1)
Sulla rete si trovano gia delle List per valori pesati oppure realizziamo noi direttamente una semplice classe che tokenizza la stringa usando la virgola come separatore e poi tokenizza ogni token utlizzando il “;” . Ovviamente in caso di “*” la scelta di default possiamo attribuirla noi ad esempio gzip. Il sort dei token e’ eseguito sulla base del valore di q, andando a mettere q=1 a tutti gli elementi con q non specificato.
Poi passiamo alla parte MVC e realizziamo un ActionAttributeFilter che utilizzeremo per annotare le Action nel nostro Controller , che saranno sottoposte a compressione.
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 | using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Collections.Specialized; using System.IO.Compression; using System.IO; public class CompressionFilter : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { HttpRequestBase request = filterContext.HttpContext.Request; string acceptEncodingHeader = request.Headers["Accept-Encoding"]; if (acceptEncodingHeader == null) return; HttpResponseBase response = filterContext.HttpContext.Response; MyFantasticQList encodingPreference = new MyFantasticQList(acceptEncodingHeader); encodingPreference.setDefaultPreference("gzip"); string preferred = encodingPreference.findPreferred(); switch(preferred.Name){ case "gzip": Response.AppendHeader("Content-Encoding", "gzip"); Response.Filter = new GZipStream(Response.Filter, CompressionMode.Compress); break; case "deflate": Response.AppendHeader("Content-Encoding", "deflate"); Response.Filter = new DeflateStream(Response.Filter,CompressionMode.Compress); break; default: break; } } |
Realizzato il nostro CompressFilterAttribute, nel nostro controller basta annotare le action che devono restituire contenuto compresso:
1 2 3 4 5 6 7 8 9 | [HttpPost()] [CompressionFilter] public ActionResult ListaElementi(string key){ //codice dell'action } |
In automatico prima della response, verrà applicato il content encoding corretto .
Ciao.