From 0e450c5e5c16559df19d868f07e64b1f733d0050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Greg=20Kar=C3=A9kinian?= Date: Mon, 29 Jun 2015 21:21:58 +0200 Subject: [PATCH 1/5] Fix root directory listing It was flattened by accident Refs #62 --- lib/remote_storage/swift.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/remote_storage/swift.rb b/lib/remote_storage/swift.rb index 2923c1f..8a9920f 100644 --- a/lib/remote_storage/swift.rb +++ b/lib/remote_storage/swift.rb @@ -78,14 +78,16 @@ module RemoteStorage end end - res = if directory.empty? + 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 if body = JSON.parse(res.body) - listing = directory_listing(body) + listing = directory_listing(body, is_root_listing) else puts "listing not JSON" end @@ -173,14 +175,15 @@ module RemoteStorage permission end - def directory_listing(res_body) + def directory_listing(res_body, is_root_listing = false) listing = { "@context" => "http://remotestorage.io/spec/folder-description", "items" => {} } res_body.each do |entry| - name = entry["name"].gsub("#{File.dirname(entry["name"])}/", '') + name = entry["name"] + name.sub!("#{File.dirname(entry["name"])}/", '') unless is_root_listing if name[-1] == "/" listing["items"].merge!({ name => { 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 2/5] 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 3/5] 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 4/5] 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}") From 898f59ba83ad01a854f61f22471049727e9ad5d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Greg=20Kar=C3=A9kinian?= Date: Wed, 1 Jul 2015 17:08:52 +0200 Subject: [PATCH 5/5] Handle invalid UTF8 in path ... by not using ActiveSupport's broken `String#blank?` anymore --- liquor-cabinet.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/liquor-cabinet.rb b/liquor-cabinet.rb index 21042d4..247648e 100644 --- a/liquor-cabinet.rb +++ b/liquor-cabinet.rb @@ -66,7 +66,8 @@ class LiquorCabinet < Sinatra::Base token = env["HTTP_AUTHORIZATION"] ? env["HTTP_AUTHORIZATION"].split(" ")[1] : "" - storage.authorize_request(@user, @directory, token, @key.blank?) unless request.options? + no_key = @key.nil? || @key.empty? + storage.authorize_request(@user, @directory, token, no_key) unless request.options? end options path do