Andrea "Kiraya"Magini

IT Professional Master

Single post

Comprimere il contenuto di una risposta web, e analisi dell'Accept-Encoding

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.
[cce lang=”csharp”]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;
}
}
[/cce]
Realizzato il nostro CompressFilterAttribute, nel nostro controller basta annotare le action che devono restituire contenuto compresso:
 
[cce lang=”csharp”]
[HttpPost()]
[CompressionFilter]
public ActionResult ListaElementi(string key){
//codice dell’action
}
[/cce]
In automatico prima della response, verrà applicato il content encoding corretto .
Ciao.