From 87741d002aae3da4b54654319c3c6176dfe534af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Greg=20Kar=C3=A9kinian?= Date: Tue, 30 Jun 2015 14:06:40 +0200 Subject: [PATCH] Generate an ETag for a container's root --- lib/remote_storage/swift.rb | 62 +++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/lib/remote_storage/swift.rb b/lib/remote_storage/swift.rb index 8a9920f..68244f6 100644 --- a/lib/remote_storage/swift.rb +++ b/lib/remote_storage/swift.rb @@ -4,6 +4,7 @@ require "cgi" require "active_support/core_ext/time/conversions" require "active_support/core_ext/numeric/time" require "redis" +require 'digest/md5' module RemoteStorage class Swift @@ -57,36 +58,47 @@ module RemoteStorage end def get_head_directory_listing(user, directory) - res = do_head_request("#{url_for_directory(user, directory)}/") + is_root_listing = directory.empty? + if is_root_listing + # We need to calculate the etag ourselves + res = do_get_request("#{url_for_directory(user, directory)}/?format=json") + etag = etag_for(res.body) + else + res = do_head_request("#{url_for_directory(user, directory)}/") + etag = res.headers[:etag] + end server.headers["Content-Type"] = "application/json" - server.headers["ETag"] = %Q("#{res.headers[:etag]}") + server.headers["ETag"] = %Q("#{etag}") rescue RestClient::ResourceNotFound server.halt 404 end def get_directory_listing(user, directory) - server.headers["Content-Type"] = "application/json" - - do_head_request("#{url_for_directory(user, directory)}/") do |response| - if response.code == 404 - return directory_listing([]).to_json - else - server.headers["ETag"] = %Q("#{response.headers[:etag]}") - none_match = (server.env["HTTP_IF_NONE_MATCH"] || "").split(",").map(&:strip) - server.halt 304 if none_match.include? %Q("#{response.headers[:etag]}") - end - end - is_root_listing = directory.empty? - res = if is_root_listing - do_get_request("#{container_url_for(user)}/?format=json") - else - do_get_request("#{container_url_for(user)}/?format=json&path=#{escape(directory)}/") - end + server.headers["Content-Type"] = "application/json" - if body = JSON.parse(res.body) + etag, get_response = nil + + do_head_request("#{url_for_directory(user, directory)}/") do |response| + return directory_listing([]).to_json if response.code == 404 + + if is_root_listing + get_response = do_get_request("#{container_url_for(user)}/?format=json") + etag = etag_for(get_response.body) + else + get_response = do_get_request("#{container_url_for(user)}/?format=json&path=#{escape(directory)}/") + etag = response.headers[:etag] + end + + none_match = (server.env["HTTP_IF_NONE_MATCH"] || "").split(",").map(&:strip) + server.halt 304 if none_match.include? etag + end + + server.headers["ETag"] = %Q("#{etag}") + + if body = JSON.parse(get_response.body) listing = directory_listing(body, is_root_listing) else puts "listing not JSON" @@ -328,5 +340,15 @@ module RemoteStorage def redis @redis ||= Redis.new(host: settings.redis["host"], port: settings.redis["port"]) end + + def etag_for(body) + objects = JSON.parse(body) + + if objects.empty? + Digest::MD5.hexdigest '' + else + Digest::MD5.hexdigest objects.map { |o| o["hash"] }.join + end + end end end