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 1/3] 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 From 8c30bdcce573454c4f50390c5c1dc9dcccd4be2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Greg=20Kar=C3=A9kinian?= Date: Tue, 30 Jun 2015 14:59:07 +0200 Subject: [PATCH 2/3] Really fix the directory listing for a user's root --- lib/remote_storage/swift.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/remote_storage/swift.rb b/lib/remote_storage/swift.rb index 68244f6..c591b48 100644 --- a/lib/remote_storage/swift.rb +++ b/lib/remote_storage/swift.rb @@ -85,7 +85,7 @@ module RemoteStorage return directory_listing([]).to_json if response.code == 404 if is_root_listing - get_response = do_get_request("#{container_url_for(user)}/?format=json") + get_response = do_get_request("#{container_url_for(user)}/?format=json&path=") etag = etag_for(get_response.body) else get_response = do_get_request("#{container_url_for(user)}/?format=json&path=#{escape(directory)}/") @@ -99,7 +99,7 @@ module RemoteStorage server.headers["ETag"] = %Q("#{etag}") if body = JSON.parse(get_response.body) - listing = directory_listing(body, is_root_listing) + listing = directory_listing(body) else puts "listing not JSON" end @@ -187,7 +187,7 @@ module RemoteStorage permission end - def directory_listing(res_body, is_root_listing = false) + def directory_listing(res_body) listing = { "@context" => "http://remotestorage.io/spec/folder-description", "items" => {} @@ -195,14 +195,14 @@ module RemoteStorage res_body.each do |entry| name = entry["name"] - name.sub!("#{File.dirname(entry["name"])}/", '') unless is_root_listing - if name[-1] == "/" + name.sub!("#{File.dirname(entry["name"])}/", '') + if name[-1] == "/" # It's a directory listing["items"].merge!({ name => { "ETag" => entry["hash"], } }) - else + else # It's a file listing["items"].merge!({ name => { "ETag" => entry["hash"], From e07b763e2287c17bef42a9c3e6eebbbe3a99c292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Greg=20Kar=C3=A9kinian?= Date: Tue, 30 Jun 2015 15:05:11 +0200 Subject: [PATCH 3/3] Fix a regression with ETag and If-None-Match --- lib/remote_storage/swift.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/remote_storage/swift.rb b/lib/remote_storage/swift.rb index c591b48..6fad337 100644 --- a/lib/remote_storage/swift.rb +++ b/lib/remote_storage/swift.rb @@ -93,7 +93,7 @@ module RemoteStorage end none_match = (server.env["HTTP_IF_NONE_MATCH"] || "").split(",").map(&:strip) - server.halt 304 if none_match.include? etag + server.halt 304 if none_match.include? %Q("#{etag}") end server.headers["ETag"] = %Q("#{etag}")