Skip to content

Commit 35f61b6

Browse files
wtnclaude
andcommitted
Add idempotent parameter to Request for overriding method-based heuristic.
Co-authored-by: Claude <[email protected]>
1 parent a5ca5d2 commit 35f61b6

File tree

2 files changed

+53
-3
lines changed

2 files changed

+53
-3
lines changed

lib/protocol/http/request.rb

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ class Request
3636
# @parameter body [Body::Readable] The request body.
3737
# @parameter protocol [String | Array(String) | Nil] The request protocol, usually empty, but occasionally `"websocket"` or `"webtransport"`.
3838
# @parameter interim_response [Proc] A callback which is called when an interim response is received.
39-
def initialize(scheme = nil, authority = nil, method = nil, path = nil, version = nil, headers = Headers.new, body = nil, protocol = nil, interim_response = nil)
39+
# @parameter idempotent [Boolean | Nil] An override for {#idempotent?}, or `nil` to use the default method-based heuristic.
40+
def initialize(scheme = nil, authority = nil, method = nil, path = nil, version = nil, headers = Headers.new, body = nil, protocol = nil, interim_response = nil, idempotent: nil)
4041
@scheme = scheme
4142
@authority = authority
4243
@method = method
@@ -46,6 +47,7 @@ def initialize(scheme = nil, authority = nil, method = nil, path = nil, version
4647
@body = body
4748
@protocol = protocol
4849
@interim_response = interim_response
50+
@idempotent = idempotent
4951
end
5052

5153
# @attribute [String] the request scheme, usually `"http"` or `"https"`.
@@ -75,6 +77,9 @@ def initialize(scheme = nil, authority = nil, method = nil, path = nil, version
7577
# @attribute [Proc] a callback which is called when an interim response is received.
7678
attr_accessor :interim_response
7779

80+
# @attribute [Boolean | Nil] an override for {#idempotent?}, or `nil` to use the default method-based heuristic.
81+
attr_accessor :idempotent
82+
7883
# A request that is generated by a server, may choose to include the peer (address) associated with the request. It should be implemented by a sub-class.
7984
#
8085
# @returns [Peer | Nil] The peer (address) associated with the request.
@@ -124,16 +129,21 @@ def connect?
124129
# @parameter path [String] The path, e.g. `"/index.html"`, `"/search?q=hello"`, etc.
125130
# @parameter headers [Hash] The headers, e.g. `{"accept" => "text/html"}`, etc.
126131
# @parameter body [String | Array(String) | Body::Readable] The body, e.g. `"Hello, World!"`, etc. See {Body::Buffered.wrap} for more information about .
127-
def self.[](method, path = nil, _headers = nil, _body = nil, scheme: nil, authority: nil, headers: _headers, body: _body, protocol: nil, interim_response: nil)
132+
# @parameter idempotent [Boolean | Nil] Override the default idempotency heuristic.
133+
def self.[](method, path = nil, _headers = nil, _body = nil, scheme: nil, authority: nil, headers: _headers, body: _body, protocol: nil, interim_response: nil, idempotent: nil)
128134
path = path&.to_s
129135
body = Body::Buffered.wrap(body)
130136
headers = Headers[headers]
131137

132-
self.new(scheme, authority, method, path, nil, headers, body, protocol, interim_response)
138+
self.new(scheme, authority, method, path, nil, headers, body, protocol, interim_response, idempotent: idempotent)
133139
end
134140

135141
# Whether the request can be replayed without side-effects.
136142
def idempotent?
143+
unless @idempotent.nil?
144+
return @idempotent
145+
end
146+
137147
@method != Methods::POST && (@body.nil? || @body.empty?)
138148
end
139149

test/protocol/http/request.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@
128128
expect(request).to be(:idempotent?)
129129
end
130130

131+
it "defaults idempotent to nil" do
132+
expect(request.idempotent).to be_nil
133+
end
134+
131135
it "should have a string representation" do
132136
expect(request.to_s).to be == "http://localhost: GET /index.html HTTP/1.0"
133137
end
@@ -141,6 +145,42 @@
141145
end
142146
end
143147

148+
with "simple POST request" do
149+
let(:request) {subject.new("http", "localhost", "POST", "/submit", "HTTP/1.1", headers, nil)}
150+
151+
it "should not be idempotent" do
152+
expect(request).not.to be(:idempotent?)
153+
end
154+
155+
with "idempotent: true" do
156+
let(:request) {subject.new("http", "localhost", "POST", "/submit", "HTTP/1.1", headers, nil, nil, nil, idempotent: true)}
157+
158+
it "should be idempotent" do
159+
expect(request).to be(:idempotent?)
160+
end
161+
end
162+
end
163+
164+
with "idempotent: false" do
165+
let(:request) {subject.new("http", "localhost", "GET", "/index.html", "HTTP/1.0", headers, body, nil, nil, idempotent: false)}
166+
167+
it "should not be idempotent" do
168+
expect(request).not.to be(:idempotent?)
169+
end
170+
end
171+
172+
with ".[] with idempotent: true" do
173+
let(:request) {subject["POST", "/submit", idempotent: true]}
174+
175+
it "should be idempotent" do
176+
expect(request).to be(:idempotent?)
177+
end
178+
179+
it "should expose the idempotent attribute" do
180+
expect(request.idempotent).to be == true
181+
end
182+
end
183+
144184
with "interim response" do
145185
let(:request) {subject.new("http", "localhost", "GET", "/index.html", "HTTP/1.0", headers, body)}
146186

0 commit comments

Comments
 (0)