O IIS 7 retorna 304 em vez de 200

10

Eu tenho um problema estranho no IIS 7.
Às vezes, parece retornar um 304 em vez de um 200.

Aqui está um exemplo de solicitação capturada com o Fiddler:
(Observe que o arquivo solicitado ainda não está localizado no cache de meus navegadores.)

GET https://[mysite]/Content/js/jquery.form.js HTTP/1.1
Accept: */*
Referer: https://[mysite]/Welcome/News
Accept-Language: sv-SE
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; OfficeLiveConnector.1.4; OfficeLivePatch.1.3; .NET4.0C; .NET4.0E)
Accept-Encoding: gzip, deflate
Host: [mysite]
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: ...

Observe que não há If-Modified-Since ou If-None-Match na solicitação.
Mas ainda a resposta é:

HTTP/1.1 304 Not Modified
Cache-Control: public
Expires: Tue, 02 Mar 2010 06:26:08 GMT
Last-Modified: Mon, 22 Feb 2010 21:58:44 GMT
ETag: "1CAB40A337D4200"
Server: Microsoft-IIS/7.5
X-Powered-By: ASP.NET
Date: Mon, 01 Mar 2010 17:06:34 GMT

Alguém tem uma idéia do que poderia estar errado aqui?

Estou executando o IIS 7 no Windows Web Server 2008 R2.

EDITAR:

Encontrei uma solução alternativa, ative o cache e desative-o em um nível de extensão.

<configuration>
  <system.webServer>
    <caching enabled="true" enableKernelCache="true">
      <profiles>
        <add extension=".png" policy="DisableCache" kernelCachePolicy="DisableCache" />
        <add extension=".gif" policy="DisableCache" kernelCachePolicy="DisableCache" />
        <add extension=".js" policy="DisableCache" kernelCachePolicy="DisableCache" />
        <add extension=".css" policy="DisableCache" kernelCachePolicy="DisableCache" />
      </profiles>
    </caching>
    <staticContent>
      <clientCache cacheControlMode="NoControl" />
    </staticContent>
  </system.webServer>
</configuration>
Ola Herrdahl
fonte
Estou com o mesmo problema EXATO e está causando muitos problemas em um servidor Web muito ocupado. O IIS retorna 304, mesmo que o cliente ainda não tenha uma cópia do recurso.
Philippe Leybaert
@ Philippe, você já encontrou uma solução? Caso contrário, consulte minha edição acima para obter uma solução alternativa e confira a nova resposta abaixo.
Ola Herrdahl
@OlaHerrdahl, sua solução alternativa é perfeita :) Eu também tive esse problema. Obrigado!
Ardee Aram

Respostas:

3

De acordo com a seção 14.9 da especificação HTTP1.1 , a no-cachediretiva para o cabeçalho Cache-Control só pode ser imposta pelo servidor de origem, o que significa que o IIS está ignorando o cabeçalho em sua solicitação.

As diretivas de controle de cache podem ser divididas nessas categorias gerais:

  - Restrictions on what are cacheable; these may only be imposed

pelo servidor de origem.

A Seção 14.9.1 define public, privatee no-cachecomo as diretivas que restringem o que pode ser armazenado em cache, que somente pode ser imposto pelo servidor.

Se você não deseja que seu arquivo .js seja armazenado em cache, será necessário definir a no-cachediretiva no aplicativo (ou seja, o código ASP.NET) ou alterar o Cache-Controlcabeçalho na solicitação para usar a no-storediretiva em vez de no-cache.

EDIT:
Com base no seu comentário - sim, assumi que você não queria que o arquivo fosse armazenado em cache. O 304, portanto, pode estar chegando como resultado do arquivo estar em um dos caches internos do IIS. De uma olhada nesses:

squillman
fonte
Acho que você não entendeu meu problema aqui. O arquivo ainda não está localizado no meu cache. Mas o servidor ainda responde com 304 ... Isso parece acontecer aleatoriamente ao navegar no site nos principais navegadores.
Ola Herrdahl
@Ola Herrdahl: Dê uma olhada na minha edição.
squillman
Eu tentei desabilitar o cache em IIS bem e não faz diferença ... :(
Ola Herrdahl
1

Estou com o mesmo problema há algum tempo e todo o cache foi desativado ... No entanto, instalei o módulo de compactação para o IIS7 em algum momento que, por padrão, permitia a compactação de arquivos estáticos nos sites existentes. Desliguei toda a compactação dos sites afetados e agora eles parecem estar funcionando com madeira de toque fino .


fonte
1

Também estávamos com esse bug, mas estávamos usando uma biblioteca de gerenciamento de ativos (Cassette). Após uma extensa investigação sobre esse problema, descobrimos que a causa raiz desse problema está na combinação de ASP.NET, IIS e Cassette. Não tenho certeza se esse é o seu problema (usando a HeadersAPI e não a CacheAPI), mas o padrão parece ser o mesmo.

Bug # 1

Cassette define o Vary: Accept-Encodingcabeçalho como parte de sua resposta a um pacote, pois pode codificar o conteúdo com gzip / deflate:

No entanto, o cache de saída do ASP.NET sempre retornará a resposta que foi armazenada em cache primeiro. Por exemplo, se a primeira solicitação tiver Accept-Encoding: gzipe Cassette retornar conteúdo compactado com gzip, o cache de saída do ASP.NET armazenará o URL em cache como Content-Encoding: gzip. A próxima solicitação para o mesmo URL, mas com uma codificação aceitável diferente (por exemplo Accept-Encoding: deflate), retornará a resposta em cache com Content-Encoding: gzip.

Esse bug é causado pelo Cassette usando a HttpResponseBase.CacheAPI para definir as configurações do cache de saída (por exemplo Cache-Control: public), mas usando a HttpResponseBase.HeadersAPI para definir o Vary: Accept-Encodingcabeçalho. O problema é que o ASP.NET nãoOutputCacheModule está ciente dos cabeçalhos de resposta; funciona apenas através da API. Ou seja, espera que o desenvolvedor use uma API invisivelmente acoplada em vez de apenas HTTP padrão.Cache

Bug # 2

Ao usar o IIS 7.5 (Windows Server 2008 R2), o bug nº 1 pode causar um problema separado nos caches do kernel e do usuário do IIS. Por exemplo, depois que um pacote é armazenado em cache com êxito Content-Encoding: gzip, é possível vê-lo no cache do kernel do IIS com netsh http show cachestate. Ele mostra uma resposta com 200 códigos de status e codificação de conteúdo de "gzip". Se a próxima solicitação tiver uma codificação aceitável diferente (por exemplo Accept-Encoding: deflate) e um If-None-Matchcabeçalho que corresponda ao hash do pacote configurável, a solicitação nos caches do kernel e do modo de usuário do IIS será considerada um erro . Assim, fazendo com que o pedido seja tratado pelo Cassette, que retorna um 304:

No entanto, assim que o kernel e os modos de usuário do IIS processarem a resposta, eles verão que a resposta para a URL foi alterada e o cache deve ser atualizado. Se o cache do kernel do IIS for verificado netsh http show cachestatenovamente, a resposta em cache 200 será substituída por uma resposta 304. Todas as solicitações subsequentes ao pacote configurável, independentemente Accept-Encodinge If-None-Matchretornarão uma resposta 304. Vimos os efeitos devastadores desse bug em que todos os usuários receberam um 304 para nosso script principal por causa de uma solicitação aleatória que teve um inesperado Accept-Encodinge If-None-Match.

O problema parece ser que os caches do kernel do IIS e do modo de usuário não podem variar com base no Accept-Encodingcabeçalho. Como prova disso, usando a CacheAPI com a solução alternativa abaixo, os caches do kernel e do modo de usuário do IIS parecem sempre ser ignorados (apenas o cache de saída do ASP.NET é usado). Isso pode ser confirmado verificando se netsh http show cachestateestá vazio com a solução alternativa abaixo. O ASP.NET se comunica diretamente com o trabalhador do IIS para habilitar ou desabilitar seletivamente os kernel do IIS e os caches do modo de usuário por solicitação.

Não foi possível reproduzir esse bug nas versões mais recentes do IIS (por exemplo, IIS Express 10). No entanto, o bug nº 1 ainda era reproduzível.

Nossa correção original para esse bug foi desativar o cache do modo kernel / usuário do IIS apenas para solicitações de cassete, como as mencionadas anteriormente. Ao fazer isso, descobrimos o bug nº 1 ao implantar uma camada extra de cache na frente de nossos servidores da web. A razão que o hack string de consulta trabalhou é porque o OutputCacheModuleirá gravar um cache miss se a CacheAPI não foi usada para variar baseado no QueryString e se o pedido tem umQueryString .

Gambiarra

Planejamos nos afastar do Cassette de qualquer maneira. Portanto, em vez de manter nosso próprio fork do Cassette (ou tentar obter uma fusão de relações públicas), optamos por usar um módulo HTTP para solucionar esse problema.

public class FixCassetteContentEncodingOutputCacheBugModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        context.PostRequestHandlerExecute += Context_PostRequestHandlerExecute;
    }

    private void Context_PostRequestHandlerExecute(object sender, EventArgs e)
    {
        var httpContext = HttpContext.Current;

        if (httpContext == null)
        {
            return;
        }

        var request = httpContext.Request;
        var response = httpContext.Response;

        if (request.HttpMethod != "GET")
        {
            return;
        }

        var path = request.Path;

        if (!path.StartsWith("/cassette.axd", StringComparison.InvariantCultureIgnoreCase))
        {
            return;
        }

        if (response.Headers["Vary"] == "Accept-Encoding")
        {
            httpContext.Response.Cache.VaryByHeaders.SetHeaders(new[] { "Accept-Encoding" });
        }
    }

    public void Dispose()
    {

    }
}

Espero que isso ajude alguém 😄!

TheCloudlessSky
fonte