Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions lib/protocol/http/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,34 @@ def idempotent?
return false
end

# Prepare the request body to be sent again, if it is safe to retry.
# @returns [Boolean] Whether the request was prepared for retry.
def retry!
# Only idempotent request methods can be retried safely.
if @method == Methods::POST || @method == Methods::PATCH || @method == Methods::CONNECT
return false
end

# Requests without a body can be sent again immediately.
if body = @body
# Empty, non-rewindable bodies have nothing left to send.
if body.empty? && !body.rewindable?
return true
end

# Non-empty bodies must be rewindable so the same data can be sent again.
if !body.rewindable?
return false
end

if !body.rewind
return false
end
end

return true
end

# Convert the request to a hash, suitable for serialization.
#
# @returns [Hash] The request as a hash.
Expand Down
50 changes: 50 additions & 0 deletions test/protocol/http/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,56 @@
end
end

with "PATCH request without a body" do
let(:request) {subject["PATCH", "/resource"]}

it "should be idempotent" do
expect(request).to be(:idempotent?)
end
end

with "#retry!" do
it "allows idempotent requests without a body" do
expect(request.retry!).to be == true
end

with "idempotent request with a rewindable body" do
let(:request) {subject["PUT", "/resource", body: "content"]}

it "allows retry and rewinds the body" do
expect(request.body.read).to be == "content"

expect(request.retry!).to be == true
expect(request.body.read).to be == "content"
end
end

with "idempotent request with a non-rewindable body" do
let(:body) {Protocol::HTTP::Body::Readable.new}
let(:request) {subject.new(nil, nil, "PUT", "/resource", nil, headers, body)}

it "does not allow retry" do
expect(request.retry!).to be == false
end
end

with "non-idempotent request" do
let(:request) {subject["POST", "/submit"]}

it "does not allow retry" do
expect(request.retry!).to be == false
end
end

with "PATCH request" do
let(:request) {subject["PATCH", "/resource"]}

it "does not allow retry" do
expect(request.retry!).to be == false
end
end
end

it "should have a string representation" do
expect(request.to_s).to be == "http://localhost: GET /index.html HTTP/1.0"
end
Expand Down
Loading