Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support HTTP ETags #86

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions classes/theme.php
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,97 @@ public function act_display( $paramarray = array( 'user_filters'=> array() ) )
}
}
}

// @todo probably need to make this private if the user is logged in so proxy's don't cache it?
// @todo this shouldn't be necessary at all after we sort out starting a session only for logged-in users
header('Pragma: public', true);
header('Cache-Control: public, max-age=' . HabariDateTime::DAY * 30, true);

// we need to manually build a representation of the current content displayed to be sure comments are included
$displayed_content = array();

// while we're at it, we'll also look for the most recently modified post, rather than iterating back over everything again later
$last_modified = null;

// is there actually anything?
if ( isset( $posts ) ) {

// if $posts is actually a single post (ie: we are displaying a single entry), wrap it up as an array isntead
if ( $posts instanceof Post ) {
$available_posts = array( $posts );
}
else {
$available_posts = $posts;
}

foreach ( $available_posts as $post ) {

// hash this post in possibly the most crude way possible
$displayed_content[] = sha1( serialize( $post ) );

// if the post was modified more recently than the last (or we don't have one, duh), use that date
if ( $last_modified == null || $post->modified > $last_modified ) {
$last_modified = $post->modified;
}

// if there are approved comments, we want to take those into account, too
foreach ( $post->comments->moderated->comments as $comment ) {

// hash the post comment too
$displayed_content[] = sha1( serialize( $comment ) );

// if it was modified more recently than anything else we've seen, use that
if ( $last_modified == null || $comment->date > $last_modified ) {
$last_modified = $comment->date;
}

}

}

}

// now generate an etag from the content we are displaying
$etag = sha1( serialize( $displayed_content ) );

// let the client know what content is on the page currently
header('ETag: "' . $etag . '"', true);

// and check to see if they gave us an etag they have cached
if ( isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) ) {
if ( $etag == trim( $_SERVER->raw('HTTP_IF_NONE_MATCH'), '"' ) ) {
header( 'HTTP/1.1 304 Not Modified', true, 304);
header( 'X-Habari-Cache-Match: ETag');
// don't send any further content
die();
}
}

// if we didn't match the etag, but we have a last modified date to check, do that as a fallback
if ( $last_modified != null ) {

// go ahead and send the header to let them know when it last changed, even if we match
header( 'Last-Modified: ' . $last_modified->set_timezone( 'UTC' )->format( 'D, d M Y H:i:s e'), true );

// does their browser already have something cached?
if ( isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) {
$if_modified_since = HabariDateTime::date_create( $_SERVER['HTTP_IF_MODIFIED_SINCE'] );

// if we don't have content modified more recently, tell them just to use what they have
if ( $last_modified <= $if_modified_since ) {
header( 'HTTP/1.1 304 Not Modified', true, 304 );
header( 'X-Habari-Cache-Match: Modified' );
// do not send any further content
die();
}
}

// and tell them how long they should expect this to be valid
$expires = HabariDateTime::date_create( '30 days' )->set_timezone( 'UTC' )->format( 'D, d M Y H:i:s e' );
header( 'Expires: ' . $expires, true );

}

return $this->display_fallback( $fallback );
}

Expand Down