IIS 7 zwraca 304 zamiast 200

10

Mam dziwny problem z IIS 7.
Czasami wydaje się, że zwraca 304 zamiast 200.

Oto przykładowe żądanie przechwycone przez Fiddler:
(Zauważ, że żądany plik nie znajduje się jeszcze w pamięci podręcznej przeglądarki).

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: ...

Zauważ, że w żądaniu nie ma żadnych elementów If-Modified-Since lub If-None-Match.
Ale nadal odpowiedź brzmi:

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

Czy ktoś ma pojęcie, co może być tutaj nie tak?

Używam IIS 7 na Windows Web Server 2008 R2.

EDYTOWAĆ:

Znalazłem obejście, włączyłem buforowanie, a następnie wyłączyłem je na poziomie rozszerzenia.

<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
źródło
Mam DOKŁADNY ten sam problem, który powoduje wiele problemów na bardzo obciążonym serwerze internetowym. Usługi IIS zwracają wartość 304, mimo że klient nie ma jeszcze kopii zasobu.
Philippe Leybaert
@Filippe, znalazłeś już rozwiązanie? W przeciwnym razie zapoznaj się z moją edycją powyżej w celu obejścia tego problemu i sprawdź nową odpowiedź poniżej.
Ola Herrdahl
@OlaHerrdahl, twoje obejście jest idealne :) Też miałem dokładnie ten problem. Dzięki!
Ardee Aram

Odpowiedzi:

3

Według sekcji 14,9 z HTTP1.1 Spec The no-cachedyrektywa dla nagłówka Cache-Control jest imposable tylko przez serwer pochodzenia, co oznacza IIS ignoruje nagłówka w swoim wniosku.

Dyrektywy dotyczące kontroli pamięci podręcznej można podzielić na następujące ogólne kategorie:

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

przez serwer pochodzenia.

Sekcja 14.9.1 definiuje public, privatei no-cachejako dyrektyw ograniczających co jest buforowalny, które mogą być nakładane jedynie przez serwer.

Jeśli nie chcesz buforować pliku .js, musisz ustawić no-cachedyrektywę w aplikacji (tj. Kod ASP.NET) lub zmienić Cache-Controlnagłówek w żądaniu, aby użyć no-storedyrektywy zamiast no-cache.

EDYCJA:
Na podstawie twojego komentarza - tak, zakładam, że nie chcesz buforować pliku. Zatem 304 może pochodzić z powodu umieszczenia pliku w jednej z wewnętrznych pamięci podręcznych IIS. Spójrz na te:

squillman
źródło
Myślę, że źle zrozumiałeś mój problem. Plik nie znajduje się jeszcze w mojej pamięci podręcznej. Ale serwer odpowiada 304 ... Wydaje się, że dzieje się to losowo podczas przeglądania strony we wszystkich głównych przeglądarkach.
Ola Herrdahl
@Ola Herrdahl: Zobacz moją edycję.
squillman
Próbowałem również wyłączyć buforowanie w IIS, a to nie robi różnicy ... :(
Ola Herrdahl
1

Od jakiegoś czasu mam ten sam problem i wyłączam buforowanie ... Jednak w pewnym momencie zainstalowałem moduł kompresji dla IIS7, który domyślnie włączył kompresję plików statycznych na moich istniejących stronach. Wyłączyłem całą kompresję dla dotkniętych stron, a teraz wydaje się, że działają dobrze w dotyku drewna .


źródło
1

Ten błąd również występował, ale korzystaliśmy z biblioteki zarządzania zasobami (kasety). Po dogłębnym zbadaniu tego problemu stwierdziliśmy, że główną przyczyną tego problemu jest kombinacja ASP.NET, IIS i kasety. Nie jestem pewien, czy to jest twój problem (używanie Headersinterfejsu API zamiast Cacheinterfejsu API), ale wzorzec wydaje się być taki sam.

Błąd nr 1

Cassette ustawia Vary: Accept-Encodingnagłówek jako część swojej odpowiedzi na pakiet, ponieważ może kodować zawartość za pomocą gzip / deflate:

Jednak wyjściowa pamięć podręczna ASP.NET zawsze zwróci odpowiedź, która była najpierw buforowana. Na przykład, jeśli pierwsze żądanie ma, Accept-Encoding: gzipa kaseta zwraca zawartość spakowaną gzip, wyjściowa pamięć podręczna ASP.NET buforuje adres URL jako Content-Encoding: gzip. Następne żądanie do tego samego adresu URL, ale z innym dopuszczalnym kodowaniem (np. Accept-Encoding: deflate), Zwróci buforowaną odpowiedź za pomocą Content-Encoding: gzip.

Ten błąd jest spowodowany przez ustawienie kasety przy użyciu HttpResponseBase.Cacheinterfejsu API do ustawienia wyjściowej pamięci podręcznej (np. Cache-Control: public), Ale użycie HttpResponseBase.Headersinterfejsu API do ustawienia Vary: Accept-Encodingnagłówka. Problemem jest to, że ASP.NET OutputCacheModulejest nie świadomy nagłówków odpowiedzi; działa tylko poprzez CacheAPI. Oznacza to, że oczekuje od programisty użycia niewidocznie ściśle powiązanego interfejsu API, a nie tylko standardowego protokołu HTTP.

Błąd nr 2

Podczas korzystania z IIS 7.5 (Windows Server 2008 R2) błąd nr 1 może powodować osobny problem z jądrem IIS i pamięciami podręcznymi użytkowników. Na przykład po udanym buforowaniu pakietu Content-Encoding: gzipmożna go zobaczyć w pamięci podręcznej jądra IIS za pomocą netsh http show cachestate. Pokazuje odpowiedź z kodem stanu 200 i kodowaniem zawartości „gzip”. Jeśli kolejna prośba ma inną dopuszczalną kodowania (np Accept-Encoding: deflate) i jest If-None-Matchnagłówek, który pasuje do mieszania wiązki, tym prośba do jądra i trybu użytkownika skrytek IIS będą uważane za chybienie . W ten sposób powodując, że żądanie jest obsługiwane przez kasetę, która zwraca 304:

Jednak gdy jądro i tryby użytkownika IIS przetworzą odpowiedź, zobaczą, że odpowiedź na adres URL uległa zmianie i pamięć podręczna powinna zostać zaktualizowana. Jeśli pamięć podręczna jądra IIS zostanie netsh http show cachestateponownie zaznaczona , odpowiedź w pamięci podręcznej 200 zostanie zastąpiona odpowiedzią 304. Wszystkie kolejne żądania do pakietu, niezależnie od tego Accept-Encodingi If-None-Matchzwracają odpowiedź 304. Widzieliśmy druzgocące skutki tego błędu, w którym wszyscy użytkownicy otrzymali 304 dla naszego głównego skryptu z powodu losowego żądania, które miało nieoczekiwane Accept-Encodingi If-None-Match.

Problem polega na tym, że jądra usług IIS i pamięci podręczne trybu użytkownika nie mogą się różnić w zależności od Accept-Encodingnagłówka. Jako dowód na to, używając Cacheinterfejsu API z poniższym obejściem, wydaje się, że pamięci podręczne jądra IIS i trybu użytkownika są zawsze pomijane (używana jest tylko wyjściowa pamięć podręczna ASP.NET). Można to potwierdzić, sprawdzając, czy pole netsh http show cachestatejest puste, korzystając z poniższego obejścia. Program ASP.NET komunikuje się bezpośrednio z procesorem roboczym IIS w celu selektywnego włączania lub wyłączania pamięci podręcznej jądra IIS i trybu użytkownika na żądanie.

Nie byliśmy w stanie odtworzyć tego błędu w nowszych wersjach IIS (np. IIS Express 10). Jednak błąd nr 1 był nadal powtarzalny.

Naszym oryginalnym rozwiązaniem tego błędu było wyłączenie buforowania trybu jądra / trybu użytkownika IIS tylko dla żądań kasety, jak wspomniano inni. W ten sposób odkryliśmy błąd nr 1 podczas wdrażania dodatkowej warstwy buforowania przed naszymi serwerami WWW. Powodem, że siekać ciąg kwerendy pracował dlatego, że OutputCacheModulenagra miss cache jeśli CacheAPI nie został wykorzystany do zależą od QueryString a jeżeli wniosek maQueryString .

Obejście

W każdym razie planowaliśmy odejść od Cassette, więc zamiast utrzymywać własne rozwidlenie kasety (lub próbować scalić PR), zdecydowaliśmy się użyć modułu HTTP do obejścia tego problemu.

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()
    {

    }
}

Mam nadzieję, że to komuś pomoże 😄!

TheCloudlessSky
źródło