entity_tag response helper / conditional GET support
ResponseHelpers#entity_tag sets the ETag response header and (potentially) halts execution if an If-None-Match request header is present matches the value given. More information on HTTP's ETag header and conditional requests available in RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.19
This commit is contained in:
parent
302a03cc37
commit
ce4cf34e77
|
@ -459,6 +459,45 @@ module Sinatra
|
||||||
time
|
time
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Set the response entity tag (HTTP 'ETag' header) and halt if conditional
|
||||||
|
# GET matches. The +value+ argument is an identifier that uniquely
|
||||||
|
# identifies the current version of the resource. The +strength+ argument
|
||||||
|
# indicates whether the etag should be used as a :strong (default) or :weak
|
||||||
|
# cache validator.
|
||||||
|
#
|
||||||
|
# When the current request includes an 'If-None-Match' header with a
|
||||||
|
# matching etag, execution is immediately halted. If the request method is
|
||||||
|
# GET or HEAD, a '304 Not Modified' response is sent. For all other request
|
||||||
|
# methods, a '412 Precondition Failed' response is sent.
|
||||||
|
#
|
||||||
|
# Calling this method before perfoming heavy processing (e.g., lengthy
|
||||||
|
# database queries, template rendering, complex logic) can dramatically
|
||||||
|
# increase overall throughput with caching clients.
|
||||||
|
#
|
||||||
|
# === See Also
|
||||||
|
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.19[RFC2616: ETag],
|
||||||
|
# ResponseHelpers#last_modified
|
||||||
|
def entity_tag(value, strength=:strong)
|
||||||
|
value =
|
||||||
|
case strength
|
||||||
|
when :strong then '"%s"' % value
|
||||||
|
when :weak then 'W/"%s"' % value
|
||||||
|
else raise TypeError, "strength must be one of :strong or :weak"
|
||||||
|
end
|
||||||
|
response.header['ETag'] = value
|
||||||
|
|
||||||
|
# Check for If-None-Match request header and halt if match is found.
|
||||||
|
etags = (request.env['HTTP_IF_NONE_MATCH'] || '').split(/\s*,\s*/)
|
||||||
|
if etags.include?(value) || etags.include?('*')
|
||||||
|
# GET/HEAD requests: send Not Modified response
|
||||||
|
throw :halt, 304 if request.get? || request.head?
|
||||||
|
# Other requests: send Precondition Failed response
|
||||||
|
throw :halt, 412
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
alias :etag :entity_tag
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
module RenderingHelpers
|
module RenderingHelpers
|
||||||
|
|
|
@ -152,6 +152,32 @@ context "Sinatra" do
|
||||||
body.should.equal ''
|
body.should.equal ''
|
||||||
end
|
end
|
||||||
|
|
||||||
|
specify "supports conditional GETs with entity_tag" do
|
||||||
|
get '/strong' do
|
||||||
|
entity_tag 'FOO'
|
||||||
|
'foo response'
|
||||||
|
end
|
||||||
|
|
||||||
|
get_it '/strong'
|
||||||
|
should.be.ok
|
||||||
|
body.should.equal 'foo response'
|
||||||
|
|
||||||
|
get_it '/strong', {},
|
||||||
|
'HTTP_IF_NONE_MATCH' => '"BAR"'
|
||||||
|
should.be.ok
|
||||||
|
body.should.equal 'foo response'
|
||||||
|
|
||||||
|
get_it '/strong', {},
|
||||||
|
'HTTP_IF_NONE_MATCH' => '"FOO"'
|
||||||
|
status.should.equal 304
|
||||||
|
body.should.equal ''
|
||||||
|
|
||||||
|
get_it '/strong', {},
|
||||||
|
'HTTP_IF_NONE_MATCH' => '"BAR", *'
|
||||||
|
status.should.equal 304
|
||||||
|
body.should.equal ''
|
||||||
|
end
|
||||||
|
|
||||||
specify "delegates HEAD requests to GET handlers" do
|
specify "delegates HEAD requests to GET handlers" do
|
||||||
get '/invisible' do
|
get '/invisible' do
|
||||||
"I am invisible to the world"
|
"I am invisible to the world"
|
||||||
|
|
Loading…
Reference in New Issue