Skip to content

Commit e0143db

Browse files
committed
Preserve idempotent compatibility
1 parent 077b8d5 commit e0143db

2 files changed

Lines changed: 32 additions & 13 deletions

File tree

‎lib/protocol/http/request.rb‎

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -132,21 +132,32 @@ def self.[](method, path = nil, _headers = nil, _body = nil, scheme: nil, author
132132
self.new(scheme, authority, method, path, nil, headers, body, protocol, interim_response)
133133
end
134134

135-
# Whether the request method is idempotent according to HTTP semantics.
135+
# Whether the request can be replayed without side-effects.
136136
def idempotent?
137-
case @method
138-
when Methods::POST, Methods::PATCH, Methods::CONNECT
139-
false
140-
else
141-
true
137+
# QUERY requests are idempotent, even if they have a body:
138+
if @method == Methods::QUERY
139+
return true
140+
end
141+
142+
# POST requests are not idempotent, even if they have no body:
143+
if @method == Methods::POST
144+
return false
142145
end
146+
147+
# All other requests are idempotent if they have no body:
148+
if @body.nil? || @body.empty?
149+
return true
150+
end
151+
152+
# Otherwise, we don't know if the request is idempotent or not, so we assume it is not:
153+
return false
143154
end
144155

145156
# Prepare the request body to be sent again, if it is safe to retry.
146157
# @returns [Boolean] Whether the request was prepared for retry.
147158
def retry!
148-
# Only idempotent requests can be retried safely.
149-
if !self.idempotent?
159+
# Only idempotent request methods can be retried safely.
160+
if @method == Methods::POST || @method == Methods::PATCH || @method == Methods::CONNECT
150161
return false
151162
end
152163

‎test/protocol/http/request.rb‎

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -147,16 +147,16 @@
147147
with "PUT request with a body" do
148148
let(:request) {subject["PUT", "/resource", body: "content"]}
149149

150-
it "should be idempotent" do
151-
expect(request).to be(:idempotent?)
150+
it "should not be idempotent" do
151+
expect(request).not.to be(:idempotent?)
152152
end
153153
end
154154

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

158-
it "should not be idempotent" do
159-
expect(request).not.to be(:idempotent?)
158+
it "should be idempotent" do
159+
expect(request).to be(:idempotent?)
160160
end
161161
end
162162

@@ -168,7 +168,7 @@
168168
with "idempotent request with a rewindable body" do
169169
let(:request) {subject["PUT", "/resource", body: "content"]}
170170

171-
it "rewinds the body" do
171+
it "allows retry and rewinds the body" do
172172
expect(request.body.read).to be == "content"
173173

174174
expect(request.retry!).to be == true
@@ -192,6 +192,14 @@
192192
expect(request.retry!).to be == false
193193
end
194194
end
195+
196+
with "PATCH request" do
197+
let(:request) {subject["PATCH", "/resource"]}
198+
199+
it "does not allow retry" do
200+
expect(request.retry!).to be == false
201+
end
202+
end
195203
end
196204

197205
it "should have a string representation" do

0 commit comments

Comments
 (0)