bf37a1852e61 — Mahlon E. Smith 4 years ago
Add Last-Modified/If-None-Match support.

This allows caching to still take place even if not using the checksum plugin.
2 files changed, 53 insertions(+), 16 deletions(-)

M lib/thingfish/handler.rb
M spec/thingfish/handler_spec.rb
M lib/thingfish/handler.rb +24 -13
@@ 333,7 333,7 @@ class Thingfish::Handler < Strelka::App
 		metadata = self.metastore.fetch( uuid )
 
 		res = req.response
-		self.add_etag_headers( req, metadata )
+		self.add_cache_headers( req, metadata )
 		self.add_content_disposition( res, metadata )
 
 		res.body = object

          
@@ 356,7 356,7 @@ class Thingfish::Handler < Strelka::App
 		res = req.response
 		res.content_type = metadata['format']
 
-		self.add_etag_headers( req, metadata )
+		self.add_cache_headers( req, metadata )
 		self.add_content_disposition( res, metadata )
 
 		if object.respond_to?( :path )

          
@@ 716,9 716,9 @@ class Thingfish::Handler < Strelka::App
 	### Return a Hash of default metadata extracted from the given +request+.
 	def extract_default_metadata( request )
 		return self.extract_connection_metadata( request ).merge(
-			'extent'        => request.headers.content_length,
-			'format'        => request.content_type,
-			'created'       => Time.now.getgm
+			'extent'  => request.headers.content_length,
+			'format'  => request.content_type,
+			'created' => Time.now.getgm
 		)
 	end
 

          
@@ 787,19 787,30 @@ class Thingfish::Handler < Strelka::App
 	end
 
 
-	### Add browser cache headers for resources.  This requires the sha256
+	### Add browser cache headers for resources.
+	### Last-Modified is always added.  ETag support requires the sha256
 	### processor plugin to be enabled for stored resources.
-	def add_etag_headers( request, metadata )
+	###
+	def add_cache_headers( request, metadata )
 		response = request.response
-		checksum = metadata[ 'checksum' ]
-		return unless checksum
 
-		if (( match = request.headers[ :if_none_match ] ))
-			match = match.gsub( '"', '' ).split( /,\s*/ )
-			finish_with( HTTP::NOT_MODIFIED ) if match.include?( checksum )
+		# ETag takes precedence if available.
+		#
+		if (( checksum = metadata['checksum'] ))
+			if (( match = request.headers[ :if_none_match ] ))
+				match = match.gsub( '"', '' ).split( /,\s*/ )
+				finish_with( HTTP::NOT_MODIFIED ) if match.include?( checksum )
+			end
+			response.headers[ :etag ] = checksum
 		end
 
-		response.headers[ :etag ] = checksum
+		return unless metadata[ 'created' ]
+
+		if (( modified = request.headers[ :if_modified_since ] ))
+			finish_with( HTTP::NOT_MODIFIED ) if Time.parse( modified ) <= metadata['created'].round
+		end
+		response.headers[ :last_modified ] = metadata[ 'created' ].httpdate
+
 		return
 	end
 

          
M spec/thingfish/handler_spec.rb +29 -3
@@ 359,7 359,20 @@ describe Thingfish::Handler do
 		end
 
 
-		it "adds browser cache headers to resources with a checksum attribute" do
+		it "adds date cache headers to resources" do
+			created = Time.now
+			uuid = @handler.datastore.save( @png_io )
+			@handler.metastore.save( uuid, 'format' => 'image/png', 'created' => created )
+
+			req = factory.get( "/#{uuid}" )
+			result = @handler.handle( req )
+
+			expect( result.status_line ).to match( /200 ok/i )
+			expect( result.headers.last_modified ).to eq( created.httpdate )
+		end
+
+
+		it "adds content cache headers to resources with a checksum attribute" do
 			uuid = @handler.datastore.save( @png_io )
 			@handler.metastore.save( uuid, 'format' => 'image/png', 'checksum' => '123456' )
 

          
@@ 385,7 398,21 @@ describe Thingfish::Handler do
 		end
 
 
-		it "returns a 304 not modified for unchanged client cache requests" do
+		it "returns a 304 not modified for unchanged date cache requests" do
+			created = Time.now
+			uuid = @handler.datastore.save( @png_io )
+			@handler.metastore.save( uuid, 'format' => 'image/png', 'created' => created )
+
+			req = factory.get( "/#{uuid}" )
+			req.headers[ :if_modified_since ] = ( Time.now - 300 ).httpdate
+			result = @handler.handle( req )
+
+			expect( result.status_line ).to match( /304 not modified/i )
+			expect( result.body.read ).to be_empty
+		end
+
+
+		it "returns a 304 not modified for unchanged content cache requests" do
 			uuid = @handler.datastore.save( @png_io )
 			@handler.metastore.save( uuid, 'format' => 'image/png', 'checksum' => '123456' )
 

          
@@ 398,7 425,6 @@ describe Thingfish::Handler do
 		end
 
 
-
 		it "can remove everything associated with an object id" do
 			uuid = @handler.datastore.save( @png_io )
 			@handler.metastore.save( uuid, {