diff --git a/lib/remote_storage/swift.rb b/lib/remote_storage/swift.rb index 73e05f3..dc2d326 100644 --- a/lib/remote_storage/swift.rb +++ b/lib/remote_storage/swift.rb @@ -139,8 +139,18 @@ module RemoteStorage url = url_for_key(user, directory, key) if required_match = server.env["HTTP_IF_MATCH"] - unless required_match.gsub(/^"?W\//, "") == %Q("#{existing_metadata["e"]}") - server.halt 412, "Precondition Failed" + required_match = required_match.gsub(/^"?W\//, "") + unless required_match == %Q("#{existing_metadata["e"]}") + + # get actual metadata and compare in case redis metadata became out of sync + head_res = do_head_request(url) + + if required_match == %Q("#{head_res.headers[:etag]}") + # log previous size difference that was missed ealier because of redis failure + log_size_difference(user, existing_metadata["s"], head_res.headers[:content_length]) + else + server.halt 412, "Precondition Failed" + end end end if server.env["HTTP_IF_NONE_MATCH"] == "*" diff --git a/spec/swift/app_spec.rb b/spec/swift/app_spec.rb index 60d7fda..ae29074 100644 --- a/spec/swift/app_spec.rb +++ b/spec/swift/app_spec.rb @@ -284,11 +284,44 @@ describe "App" do it "fails the request if the header does not match the current ETag" do header "If-Match", "someotheretag" - put "/phil/food/aguacate", "aye" + head_stub = OpenStruct.new(headers: { + etag: "oldetag", + last_modified: "Fri, 04 Mar 2016 12:20:18 GMT", + content_type: "text/plain", + content_length: 23 + }) + + RestClient.stub :head, head_stub do + put "/phil/food/aguacate", "aye" + end last_response.status.must_equal 412 last_response.body.must_equal "Precondition Failed" end + + it "allows the request if redis metadata became out of sync" do + header "If-Match", "\"existingetag\"" + + head_stub = OpenStruct.new(headers: { + etag: "existingetag", + last_modified: "Fri, 04 Mar 2016 12:20:18 GMT", + content_type: "text/plain", + content_length: 23 + }) + + put_stub = OpenStruct.new(headers: { + etag: "newetag", + last_modified: "Fri, 04 Mar 2016 12:20:18 GMT" + }) + + RestClient.stub :head, head_stub do + RestClient.stub :put, put_stub do + put "/phil/food/aguacate", "aye" + end + end + + last_response.status.must_equal 200 + end end describe "If-None-Match header set to '*'" do @@ -307,7 +340,7 @@ describe "App" do last_response.status.must_equal 201 end - it "fails the request if the document already exsits" do + it "fails the request if the document already exists" do put_stub = OpenStruct.new(headers: { etag: "someetag", last_modified: "Fri, 04 Mar 2016 12:20:18 GMT"