2012-01-12 4 views
5

Ho un personalizzato HttpHandler in cui è possibile attivare manualmente la compressione di uscita, in questo modo:"Content-Encoding" scompare dalla risposta HttpHandler se si verifica un'eccezione

context.Response.AppendHeader("Content-encoding", "gzip"); 
context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress); 

Questo funziona bene per la maggior parte delle richieste, ma quando un l'eccezione si verifica quando l'intestazione "Content-encoding" scompare dalla risposta, mentre il filtro di compressione rimane sul posto. Il risultato è che la pagina di errore è compressa gzip, ma il browser non riceve alcuna intestazione che lo indichi. Il browser tenta quindi di visualizzare i dati compressi come testo, ovvero gobbledygook.

Il codice completo del test case è mostrato di seguito. Prova alternativamente a disabilitare la compressione o a non generare l'eccezione.

Qualcuno può far luce sul motivo per cui l'intestazione "Content-encoding" scompare?

Suppongo che potrei semplicemente abilitare la compressione come l'ultima cosa che il gestore lo fa, in modo che se si verifica un'eccezione non raggiunge mai il punto in cui si aggiunge il filtro di compressione; ma il comportamento che sto vedendo mi colpisce come un insetto. Qualcuno può confermare?

public class TestHandler : IHttpHandler 
{ 
    public void ProcessRequest(HttpContext context) 
    { 
     CompressResponse(context); 
     context.Response.Write("Hello world"); 

     // Throw an exception for testing purposes 
     throw new Exception("Just testing..."); 
    } 

    private void CompressResponse(HttpContext context) 
    { 
     string acceptEncoding = context.Request.Headers["Accept-Encoding"]; 
     if (String.IsNullOrEmpty(acceptEncoding)) 
     { 
      return; 
     } 

     // gzip or wildcard 
     if (acceptEncoding.ToLower().Contains("gzip") || acceptEncoding.Contains("*")) 
     { 
      context.Response.AppendHeader("Content-encoding", "gzip"); 
      context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress); 
      return; 
     } 

     // Also handles deflate (not shown here) 
     // <snip> 
    } 

    public bool IsReusable 
    { 
     get { return true; } 
    } 
} 

EDIT: Schermata della risposta ancora codificato che sto vedendo con il mio banco di prova: http://i.imgur.com/49Vcl.png

risposta

-1

I test del codice e non riesco a trovare alcun problema. Sì, il gzip non è impostato, ma il filtro non è stato impostato e asp ottiene il controllo e invia un errore.

Forzare l'intestazione per irrigare fare un vero e proprio problema

CompressResponse(context); 
context.Response.Flush(); 

Se forzo l'intestazione gzip, allora la pagina non viene visualizzato correttamente.

Due pensano che questo sia il tuo problema. Non è stata impostata la pagina di codifica

context.Response.ContentEncoding = new UTF8Encoding(); 

e non avete impostato ContentType

context.Response.ContentType = "text/plain"; 

Forse alcune di queste è la ragione per cui non si ottiene corretti di rendering della pagina. Come mai nei miei test, anche con questo, il problema che descrivi non appare.

+0

Quindi, se si esegue il mio codice così com'è, si ottiene un vero e proprio schermo giallo della morte, piuttosto che un gobbledygook ancora compresso? –

+0

sì, così com'è, faccio copia/incolla e ottengo un vero schermo giallo di morte. Il problema si presenta quando scarico() !!!!!!!! scarichi da qualche parte prima dell'errore? Il gzip non appare, ma anche il filtro non è impostato. – Aristos

+0

Forse da qualche altra parte hai impostato un errore personalizzato? – Aristos

0

Se si dispone di un'eccezione, il server svuota le intestazioni e il contenuto correntemente impostati, perché sono errati, come dopo l'eccezione.

Per lo meno, è chiaro che lo stato 200 che avevi intenzione di inviare (perché tutte le risposte riuscite che non cambiano lo stato inviano un 200, e su un'eccezione non gestita non era più riuscita) è sbagliato, ma tutto il resto riguardava qualcosa che avresti fatto ma non è riuscito a raggiungere, quindi è tutto sbagliato e tutto va bene.

Tuttavia, i filtri non vengono ripristinati.

Ripristinare le intestazioni nella pagina di errore, se appropriato, oppure non impostare il filtro a meno che non si sia certi che tutto sia pronto per lo svuotamento. Mi piacerebbe scegliere il primo, nessuna ragione per cui anche le pagine di errore non possono essere compresse.

Non è possibile inviare un'intestazione se si chiama Flush(), perché, perché si è svuotato. Dov'è l'intestazione?

+0

Quindi, quando si verifica un errore, il gestore rimuove eventuali intestazioni e contenuto che sono stati scritti (ma non ancora scaricati), ma trascura di rimuovere il filtro che ho inserito? –

+0

Sì. È irritante, ma penso che il filtro fosse pensato per sedersi concettualmente ad un livello superiore nella pipeline, quindi aveva senso per qualcuno ... –

+0

Mi sembra che se il gestore degli errori rimuove le intestazioni che ho scritto allora dovrebbe anche rimuovere i filtri che ho assegnato. Mi chiedo se questo sia un bug, o di progettazione ... Altrimenti, puoi chiarire cosa intendi con "lo stato 200 che avresti inviato è sbagliato"? Il gestore di eccezioni è ciò che imposta lo stato 500. Perché dovrei impostare manualmente uno stato diverso da 200? –

0

Ho riscontrato anche questo problema. È stato complicato rintracciare. Non sono sicuro delle specifiche esatte di questa intera situazione, ma quello che penso succederà è che c'è una perdita di memoria.

prima volta che si esegue questa operazione:

context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress); 

si assegna un risorsa non gestita a Filter. Normalmente questo sarebbe incapsulato in una dichiarazione using in modo che fosse correttamente smaltita nel caso qualcosa fosse andato storto.

Quindi, quando qualcosa va storto, c'è un problema. Lo Filter contiene uno stream che è ancora aperto anche se la risposta è stata scritta con lo schermo giallo della morte. Ciò che segue è follia (come mostrato nella schermata).

Non temere! C'è in realtà un modo semplice per superare questo problema. Smaltire il filtro. Fortunatamente, c'è già un posto dove applicare questo controllo globale per lo smaltimento dei filtri.

global.asax.cs

public static void RegisterGlobalFilters(GlobalFilterCollection filters) 
{ 
     filters.Add(new HandleErrorAttribute());//default handler 
     filters.Add(new HandleErrorEncodingAttribute());//extra check for filter disposal 
} 

errore gestore namespace

public class HandleErrorEncodingAttribute : FilterAttribute, IExceptionFilter 
{ 
    public virtual void OnException(ExceptionContext filterContext) 
    { 
     if (filterContext == null) 
     { 
      throw new ArgumentNullException("filterContext"); 
     } 
     if (filterContext.IsChildAction) 
     { 
      return; 
     } 
     // If custom errors are disabled, we need to let the normal ASP.NET exception handler 
     // execute so that the user can see useful debugging information. 
     if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled) 
     { 
      filterContext.HttpContext.Response.Filter.Dispose();//fixes response stream 
      return; 
     } 
    } 
} 
0

ho avuto la stessa cosa accade quando costringendo gzip su un WebForms applicazione. Al fine di risolvere il problema che ho dovuto cancellare il filtro nella metodo Application_Error in Global.asax.cs

protected void Application_Error(Object sender, EventArgs e) 
{ 
    Response.Filter = null; 
} 

La ragione per questo sta accadendo è b/c il filtro viene posto davanti l'applicazione ha un errore . E per qualche motivo il messaggio di errore schermo giallo cancella l'intestazione Codifica contenuti ma non fa nulla al filtro risposta.