cache returns 304 as appropriate; more pages set etags
* tekuti/cache.scm (update-cache): Expand cache size back to 20. (cached-response-and-body, make-entry): Entries are now procedures that return the cached pair. They also have some more smarts regarding etags, last-modified, and conditional requests. * tekuti/page-helpers.scm (respond): Add etag arg. Set a date. * tekuti/page.scm: Set etags on a number of pages.
This commit is contained in:
parent
15f024a12d
commit
839d2e27eb
3 changed files with 81 additions and 22 deletions
|
@ -30,13 +30,19 @@
|
||||||
#:use-module (tekuti util)
|
#:use-module (tekuti util)
|
||||||
#:use-module (web request)
|
#:use-module (web request)
|
||||||
#:use-module (web response)
|
#:use-module (web response)
|
||||||
|
#:use-module (srfi srfi-19)
|
||||||
#:export (make-empty-cache
|
#:export (make-empty-cache
|
||||||
cached-response-and-body
|
cached-response-and-body
|
||||||
update-cache))
|
update-cache))
|
||||||
|
|
||||||
(define (cacheable-request? request)
|
(define (cacheable-request? request)
|
||||||
(and (memq (request-method request) '(GET HEAD))
|
(and (memq (request-method request) '(GET HEAD))
|
||||||
(not (request-authorization request))))
|
(not (request-authorization request))
|
||||||
|
;; We don't cache these conditional requests; just
|
||||||
|
;; if-modified-since and if-none-match.
|
||||||
|
(not (request-if-match request))
|
||||||
|
(not (request-if-range request))
|
||||||
|
(not (request-if-unmodified-since request))))
|
||||||
|
|
||||||
(define (cacheable-response? response)
|
(define (cacheable-response? response)
|
||||||
(and (not (memq 'no-cache (response-pragma response)))
|
(and (not (memq 'no-cache (response-pragma response)))
|
||||||
|
@ -47,31 +53,75 @@
|
||||||
(define (make-empty-cache)
|
(define (make-empty-cache)
|
||||||
'())
|
'())
|
||||||
|
|
||||||
(define (make-entry matcher response body)
|
(define-syntax build-headers
|
||||||
(cons matcher (cons response body)))
|
(syntax-rules ()
|
||||||
(define (entry-matcher entry)
|
((_ k v-exp rest ...)
|
||||||
(car entry))
|
(let ((v v-exp))
|
||||||
(define (entry-response-body-pair entry)
|
(let ((tail (build-headers rest ...)))
|
||||||
(cdr entry))
|
(if v
|
||||||
|
(acons 'k v tail)
|
||||||
|
tail))))
|
||||||
|
((_ tail)
|
||||||
|
tail)))
|
||||||
|
|
||||||
|
(define (make-entry request response body)
|
||||||
|
(let ((uri (request-uri request))
|
||||||
|
(method (request-method request)))
|
||||||
|
(case (response-code response)
|
||||||
|
((304)
|
||||||
|
(lambda (request)
|
||||||
|
(and (equal? (request-uri request) uri)
|
||||||
|
(eq? (request-method request) method)
|
||||||
|
(let ((last-modified (response-last-modified response))
|
||||||
|
(since (request-if-modified-since request)))
|
||||||
|
(if (and last-modified since)
|
||||||
|
(<= (date->time-utc last-modified) (date->time-utc since))
|
||||||
|
#t))
|
||||||
|
(let ((etag (response-etag response))
|
||||||
|
(match (request-if-none-match request)))
|
||||||
|
(if (and etag match)
|
||||||
|
(and (list? match) (member etag match))
|
||||||
|
#t))
|
||||||
|
(cons response body))))
|
||||||
|
((200)
|
||||||
|
(lambda (request)
|
||||||
|
(and (equal? (request-uri request) uri)
|
||||||
|
(eq? (request-method request) method)
|
||||||
|
(or (let ((last-modified (response-last-modified response))
|
||||||
|
(since (request-if-modified-since request))
|
||||||
|
(etag (response-etag response))
|
||||||
|
(match (request-if-none-match request)))
|
||||||
|
(and (or since match)
|
||||||
|
(or (not since)
|
||||||
|
(and last-modified
|
||||||
|
(<= (date->time-utc last-modified)
|
||||||
|
(date->time-utc since))))
|
||||||
|
(or (not match)
|
||||||
|
(and etag (list? match) (member etag match)))
|
||||||
|
(cons (build-response
|
||||||
|
#:code 304
|
||||||
|
#:headers (build-headers
|
||||||
|
etag etag
|
||||||
|
last-modified last-modified
|
||||||
|
date (current-date)
|
||||||
|
'()))
|
||||||
|
#f)))
|
||||||
|
(cons response body)))))
|
||||||
|
(else
|
||||||
|
(lambda (request)
|
||||||
|
(and (equal? (request-uri request) uri)
|
||||||
|
(eq? (request-method request) method)
|
||||||
|
(cons response body)))))))
|
||||||
|
|
||||||
(define (cached-response-and-body cache request)
|
(define (cached-response-and-body cache request)
|
||||||
(and cache
|
(and cache
|
||||||
(cacheable-request? request)
|
(cacheable-request? request)
|
||||||
(or-map (lambda (entry)
|
(or-map (lambda (entry) (entry request))
|
||||||
(and ((entry-matcher entry) request)
|
|
||||||
(entry-response-body-pair entry)))
|
|
||||||
cache)))
|
cache)))
|
||||||
|
|
||||||
(define (make-matcher request response)
|
|
||||||
(let ((uri (request-uri request))
|
|
||||||
(method (request-method request)))
|
|
||||||
(lambda (request)
|
|
||||||
(and (equal? (request-uri request) uri)
|
|
||||||
(eq? (request-method request) method)))))
|
|
||||||
|
|
||||||
(define (update-cache cache request response body)
|
(define (update-cache cache request response body)
|
||||||
(if (and (cacheable-request? request)
|
(if (and (cacheable-request? request)
|
||||||
(cacheable-response? response))
|
(cacheable-response? response))
|
||||||
(cons (make-entry (make-matcher request response) response body)
|
(cons (make-entry request response body)
|
||||||
(take-max (or cache '()) 9))
|
(take-max (or cache '()) 19))
|
||||||
(or cache '())))
|
(or cache '())))
|
||||||
|
|
|
@ -80,6 +80,7 @@
|
||||||
(status (if redirect 302 200))
|
(status (if redirect 302 200))
|
||||||
(title *title*)
|
(title *title*)
|
||||||
last-modified
|
last-modified
|
||||||
|
etag
|
||||||
(doctype xhtml-doctype)
|
(doctype xhtml-doctype)
|
||||||
(content-type-params '(("charset" . "utf-8")))
|
(content-type-params '(("charset" . "utf-8")))
|
||||||
(content-type "text/html")
|
(content-type "text/html")
|
||||||
|
@ -91,6 +92,8 @@
|
||||||
location (and=> redirect ensure-public-uri)
|
location (and=> redirect ensure-public-uri)
|
||||||
last-modified last-modified
|
last-modified last-modified
|
||||||
content-type (cons content-type content-type-params)
|
content-type (cons content-type content-type-params)
|
||||||
|
date (current-date)
|
||||||
|
etag (if (string? etag) (cons etag #t) etag)
|
||||||
extra-headers))
|
extra-headers))
|
||||||
(lambda (port)
|
(lambda (port)
|
||||||
(if sxml
|
(if sxml
|
||||||
|
|
|
@ -171,7 +171,8 @@
|
||||||
(respond `(,(main-sidebar request index)
|
(respond `(,(main-sidebar request index)
|
||||||
,@(map (lambda (post)
|
,@(map (lambda (post)
|
||||||
(show-post post #f))
|
(show-post post #f))
|
||||||
(published-posts index 10)))))
|
(published-posts index 10)))
|
||||||
|
#:etag (assq-ref index 'master)))
|
||||||
|
|
||||||
(define (page-show-post request body index year month day post)
|
(define (page-show-post request body index year month day post)
|
||||||
(cond
|
(cond
|
||||||
|
@ -180,7 +181,8 @@
|
||||||
=> (lambda (post)
|
=> (lambda (post)
|
||||||
(respond `(,(post-sidebar post index)
|
(respond `(,(post-sidebar post index)
|
||||||
,(show-post post #t))
|
,(show-post post #t))
|
||||||
#:title (string-append (post-title post) " -- " *title*))))
|
#:title (string-append (post-title post) " -- " *title*)
|
||||||
|
#:etag (assq-ref index 'master))))
|
||||||
(else
|
(else
|
||||||
(page-not-found request body index))))
|
(page-not-found request body index))))
|
||||||
|
|
||||||
|
@ -233,7 +235,8 @@
|
||||||
(cond
|
(cond
|
||||||
((or (null? posts) (too-early? (car posts)))
|
((or (null? posts) (too-early? (car posts)))
|
||||||
(respond (reverse out)
|
(respond (reverse out)
|
||||||
#:title (string-append "archives -- " *title*)))
|
#:title (string-append "archives -- " *title*)
|
||||||
|
#:etag (assq-ref index 'master)))
|
||||||
((new-header (car posts))
|
((new-header (car posts))
|
||||||
=> (lambda (sxml)
|
=> (lambda (sxml)
|
||||||
(lp (cdr posts) (make-date-header (car posts))
|
(lp (cdr posts) (make-date-header (car posts))
|
||||||
|
@ -256,6 +259,7 @@
|
||||||
(respond `((div (@ (id "tag-cloud"))
|
(respond `((div (@ (id "tag-cloud"))
|
||||||
(h2 "all tags")
|
(h2 "all tags")
|
||||||
,@(tag-cloud (top-tags index 200))))
|
,@(tag-cloud (top-tags index 200))))
|
||||||
|
#:etag (assq-ref index 'master)
|
||||||
#:title (string-append "all tags -- " *title*)))
|
#:title (string-append "all tags -- " *title*)))
|
||||||
|
|
||||||
(define (page-show-tag request body index tag)
|
(define (page-show-tag request body index tag)
|
||||||
|
@ -268,6 +272,7 @@
|
||||||
,@(map (lambda (post) `(p ,(post-link post)))
|
,@(map (lambda (post) `(p ,(post-link post)))
|
||||||
posts)
|
posts)
|
||||||
,(related-tag-cloud tag index))
|
,(related-tag-cloud tag index))
|
||||||
|
#:etag (assq-ref index 'master)
|
||||||
#:title (string-append "posts tagged \"" tag "\""))
|
#:title (string-append "posts tagged \"" tag "\""))
|
||||||
(respond `((h2 "Unknown tag " ,tag)
|
(respond `((h2 "Unknown tag " ,tag)
|
||||||
(p "No posts were found tagged as \"" ,tag "\"."))
|
(p "No posts were found tagged as \"" ,tag "\"."))
|
||||||
|
@ -305,6 +310,7 @@
|
||||||
#:last-modified (and=> last-modified timestamp->date)
|
#:last-modified (and=> last-modified timestamp->date)
|
||||||
#:doctype #f
|
#:doctype #f
|
||||||
#:content-type "application/atom+xml"
|
#:content-type "application/atom+xml"
|
||||||
|
#:etag (assq-ref index 'master)
|
||||||
#:sxml (append (atom-header last-modified)
|
#:sxml (append (atom-header last-modified)
|
||||||
(map
|
(map
|
||||||
(lambda (post)
|
(lambda (post)
|
||||||
|
|
Loading…
Reference in a new issue