25 de nov. de 2009

Erro com o Cache-Control no Internet Explorer

Estava buscando justificativas para um erro que ocorria com o sistema de meu cliente, e o erro era muito diferente, do que eu havia visto até hoje.

Contexto


Tínhamos dois ambientes e os dois ambientes, um na intranet e outro na internet, possuíam a mesma versão de código fonte e bibliotecas, tudo absolutamente normal, porém em determinada página que o usuário acessava para fazer download na internet não funcionava, estava dando erro, dizia que não encontrava a página. Mas existia uma outra variável, esta página retorna um relatório, ela gera um array de bytes e envia para o cliente como arquivo para download, em inúmeros formatos como excel, html, pdf, entre outros, mas mesmo assim marcava como página não encontrada, e então iniciei minha análise. Eis que muito tempo analisando o ambiente na qual o aplicativo se estabelecia e o código fonte que gerava estes arquivos me deparei com alguns headers de definição de Cache na página, mas mesmo assim não me atentei a este detalhe, foi então que comecei a isolar a situação em um projeto demonstrativo e então consegui encontrar o erro em si.
No projeto demonstrativo consegui simular o erro, porém tenho que tentar acessar a página que gera o array de bytes diretamente.

Descrição do problema

O código fonte adicionava os seguintes headers:
     this.Context.Response.ClearContent();
   this.Context.Response.ClearHeaders();
   this.Context.Response.AppendHeader("Cache-Control", "no-cache");
   this.Context.Response.AppendHeader("Cache-Control", "private");
   this.Context.Response.AppendHeader("Cache-Control", "no-store");
   this.Context.Response.AppendHeader("Cache-Control", "must-revalidate");
   this.Context.Response.AppendHeader("Cache-Control", "max-stale=0");
   this.Context.Response.AppendHeader("Cache-Control", "post-check=0");
   this.Context.Response.AppendHeader("Cache-Control", "pre-check=0");
   this.Context.Response.AppendHeader("Pragma", "no-cache");
   this.Context.Response.AppendHeader("Expires", "Mon, 26 Jul 1997 05:00:00 GMT");

Mas até ai qual o problema de se dizer que uma página não será gravada no cache?

Bom para o Internet Explorer, não gravar no cache é uma coisa tão séria, que ele decide não gravar a página/arquivo em lugar algum (nem em uma pasta temporária), apenas o mantém em memória e renderiza isso na tela. Mas ainda não chegamos ao ponto central do erro, quando o IE decide fazer isso não sabe se a resposta é uma página html ou um excel, etc, ou melhor não verifica se o resultado é uma página, ou outra coisa.


No caso de um arquivo xls (Excel, ou qualquer outro formato), o Internet Explorer faz uma chamada para o método IPersistFile::Load de um ActiveX, mas quando o método tenta carregar, o arquivo não é encontrado, pois o Internet Explorer não gravou nenhuma cópia temporária do arquivo, para que ele possa ser aberto, portanto acontece um erro de arquivo não encontrado.


Mas devo alertar que este erro acontece apenas com o Internet Explorer e realizei o teste apenas com as verões 6 e 7, também realizei o teste com o Google Chrome, Opera e Mozilla Firefox, todos conseguiram baixar o arquivo com sucesso.

Solução

Encontrei uma nota de suporte da microsoft informando que este erro ocorre apenas com protocolos seguros SSL, mas no meu caso não tínhamos isso e mesmo assim o problema ocorreu.
Bom vamos aos fatos, na  nota de suporte da microsoft, é expresso que como solução do problema remover o "no-cache" do "Control-Cache", mas disso nem tudo é verdade. Porque tanto o "no-cache" quanto o "no-story" do "Control-Cache" apresentaram este problema no teste realizado, na nota também é expresso que o "no-cache" do "Pragma" apresenta problemas, mas não foi possível simular este problema no nosso teste, nos deixando assim com a incerteza de que o problema realmente exista com o "Pragma", mas de qualquer maneira a melhor saída para esta situação é :
  1. Caso seja realmente necessário não manter o conteúdo no cache, adicione o "no-cache" apenas do "Pragma", que apresentou uma probabilidade menor de apresentar erros.
  2. Caso perceba que não irá fazer diferença entre adicionar ou não em cache, opte pelo padrão, pois dessa maneira você evita que problemas com o Internet Explorer ocorra, não adicione nada.
Simular o erro no Projeto de exemplo
  1. Faça download do arquivo zip
  2. Descompacte o arquivo em qualquer pasta que desejar.
  3. Abra o Visual Studio 2008 (qualquer versão).
  4. Acesse o item de menu File > Open > WebSite.
  5. Selecione a pasta que foi descompactada com o nome de "AspNet ControlCache Error".
  6. Pressione F5 ou "Run".
  7. Uma página irá abrir com um botão, clique no botão.
  8. Nesta primeira parte o download não ocorrerá pela sequência de eventos, mas substitua a url para a página na url " .../AspNet ControlCache Error/Download.aspx" , e ai sim o problema ocorrerá.
  9. Para solucionar o problema comente as linhas:   
    this.Context.Response.AppendHeader("Cache-Control", "no-cache");
    this.Context.Response.AppendHeader("Cache-Control", "no-store");
  10. Pressione novamente F5 ou "Run" e execute o teste novamente.

Links

Caso os links do post não funcionem aqui segue a lista de links:
Nota de suporte da Microsoft: http://support.microsoft.com/default.aspx?scid=kb;en-us;316431
Projeto de Exemplo: http://www.easy-share.com/1908531596/AspNet ControlCache Error.zip

Nenhum comentário: