2010-01-07 3 views
8

Après avoir beaucoup cherché, lire tous les tutoriels que j'ai trouvés et poser quelques questions ici, j'ai finalement réussi à répondre correctement (du moins je pense) aux requêtes HTTP-if-match et if-modified-since.Est-ce que mon implémentation des réponses HTTP Conditional Get en PHP est OK?

Pour faire un bref récapitulatif, voici ce que je fais sur toutes pages Cacheable:

session_cache_limiter('public'); //Cache on clients and proxies 
session_cache_expire(180); //3 hours 
header('Content-Type: ' . $documentMimeType . '; charset=' . $charset); 
header('ETag: "' . $eTag . '"'); //$eTag is a MD5 of $currentLanguage + $lastModified 
if ($isXML) 
    header('Vary: Accept'); //$documentMimeType can be either application/xhtml+xml or text/html for XHTML (based on $_SERVER['HTTP_ACCEPT']) 
header('Last-Modified: ' . $lastModified); 
header('Content-Language: ' . $currentLanguage); 

En outre, chaque page a son propre URL (pour toutes les langues). Par exemple, "index.php" sera servi sous l'URL "/ fr/home" en anglais et "/ fr/accueil" en français.

Mon gros problème était de répondre à un "304 non modifié" à if-none-match et if-modified-puisque les demandes HTTP seulement si nécessaire.

Le meilleur doc que j'ai trouvé est: http://rithiur.anthd.com/tutorials/conditionalget.php

Et c'est la mise en œuvre, je l'ai fait de celui-ci (ce morceau de code est appelé dès que possible sur les pages qui peuvent être mises en cache):

$ifNoneMatch = array_key_exists('HTTP_IF_NONE_MATCH', $_SERVER) ? $_SERVER['HTTP_IF_NONE_MATCH'] : false; 
$ifModifiedSince = array_key_exists('HTTP_IF_MODIFIED_SINCE', $_SERVER) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] : false; 

if ($ifNoneMatch !== false && $ifModifiedSince !== false) 
{ 
    //Both if-none-match and if-modified-since were received. 
    //They must match the document values in order to send a HTTP 304 answer. 
    if ($ifNoneMatch == $eTag && $ifModifiedSince == $lastModified) 
    { 
     header('Not Modified', true, 304); 
     exit(); 
    } 
} 
else 
{ 
    //Only one header received, it it match the document value, send a HTTP 304 answer. 
    if (($ifNoneMatch !== false && $ifNoneMatch == $eTag) || ($ifModifiedSince !== false && $ifModifiedSince == $lastModified)) 
    { 
     header('Not Modified', true, 304); 
     exit(); 
    } 
} 

Ma question est double:

  • Est-ce la bonne façon de le faire? Je veux dire quand if-none-match et if-modified-since sont envoyés, les deux doivent correspondre pour répondre à un 304, et si seulement l'un des deux est envoyé, seul celui correspondant à celui-ci est OK pour envoyer un 304?
  • Lorsqu'ils sont utilisés dans le contexte décrit ici, ces 2 extraits sont-ils compatibles avec le cache public (je veux dire le cache amical sur les proxies et)?

BTW, j'utilise PHP 5.1.0+ seulement (je ne supporte pas les versions plus bas que ça).

Modifier: Ajout de la prime ... J'attends une réponse de qualité. Ne répondez pas/votez si vous devinez quelque chose!

Répondre

22
  • Ce n'est pas tout à fait correct. S'il vous plaît jeter un oeil à l'algorithme: alt text http://img532.imageshack.us/img532/1017/cache.png
  • La solution est proxy-friendly, vous pouvez utiliser Cache-control: proxy-revalidate pour forcer les caches à obéir à toute information de fraîcheur que vous leur donnez sur une ressource (ne s'applique qu'à shared | proxy caches)

est la fonction ici qui pourrait aider:

function isModified($mtime, $etag) { 
    return !((
     isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) 
     && 
     strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $mtime 
    ) || (
     isset($_SERVER['HTTP_IF_NONE_MATCH']) 
     && 
     $_SERVER['HTTP_IF_NONE_MATCH'] == $etag 
    )) ; 
} 

Je vous suggère de jeter un oeil à l'article suivant: http://www.peej.co.uk/articles/http-caching.html

Mise à jour:

[AlexV] Est- est même possible de recevoir si aucune-match et si-Modified-Since en même temps?

Vous pouvez certainement avoir les deux ensemble.Cependant:

Si aucune des balises entité correspondance, le serveur peut effectuer la méthode demandée comme si le champ d'en-tête If-None-Match n'existait pas, mais elle doit aussi ignorer If-Modified-Since champ d'en-tête (s) dans la demande. Autrement dit, si aucune balise d'entité ne correspond, le serveur NE DOIT PAS renvoyer une réponse 304 (non modifiée).

RFC2616 #14.26

Exemples de valeurs (W signifie 'faible', lire la suite à RFC2616 #13.3.3):

If-None-Match: "xyzzy", "r2d2xxxx", "c3piozzzz" 
If-None-Match: W/"xyzzy", W/"r2d2xxxx", W/"c3piozzzz" 
If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT 
If-None-Match: * 

Comme cas particulier, la valeur "*" correspond à une entité actuelle du Ressource.

+3

Notez également ce que la section 14.26 spécifie RFC2616 (HTTP1.1) à propos de la syntaxe de If-None-Match. Ni cette question ni la question d'origine ne permettait d'avoir plusieurs valeurs etag ou "*". Cela étant dit, cette hypothèse est probablement exacte dans 99,9% des cas. – MZB

+1

@Mike Bell: Oui, je sais que ETag peut contenir beaucoup de valeurs ou * mais je ne connais pas de navigateur qui l'utilise pour l'instant ... Je me demande si un système utilise réellement cela. BTW comment analyser ETag quand plus de 1 valeur en elle? Que signifie * dans ETag? – AlexV

+0

@ St.Woland: J'ai lu l'article fourni et j'ai regardé votre graphique (bon travail) et il semble correspondre à ma partie "else" dans mon deuxième extrait (ai-je raison?). Ce que je trouve étrange avec ceci est quand BOTH si-none-match et if-modified-since sont reçus seulement 1 des 2 doit correspondre pour répondre avec un 304 ... Est même possible de recevoir if-none-match ET si -modifié-car en même temps? – AlexV