Skip to content

Commit 7826d00

Browse files
(PUP-11853) Wait for request completion before closing
Before this commit, puppet would attempt to `finish` a connection in the block passed to an `http.request` function. This will not work because `finish` sets the `@socket` to nil, and the net/http request assumes that the `@socket` is set for the duration of that function. With this change, we mark some state during the request function that indicates the connection should be closed after the completion of the `http.request`.
1 parent c9f17b1 commit 7826d00

File tree

2 files changed

+28
-5
lines changed

2 files changed

+28
-5
lines changed

lib/puppet/http/client.rb

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ def execute_streaming(request, options: {}, &block)
367367
apply_auth(request, basic_auth) if redirects.zero?
368368

369369
# don't call return within the `request` block
370+
close_and_sleep = nil
370371
http.request(request) do |nethttp|
371372
response = Puppet::HTTP::ResponseNetHTTP.new(request.uri, nethttp)
372373
begin
@@ -380,12 +381,14 @@ def execute_streaming(request, options: {}, &block)
380381
interval = @retry_after_handler.retry_after_interval(request, response, retries)
381382
retries += 1
382383
if interval
383-
if http.started?
384-
Puppet.debug("Closing connection for #{Puppet::HTTP::Site.from_uri(request.uri)}")
385-
http.finish
384+
close_and_sleep = proc do
385+
if http.started?
386+
Puppet.debug("Closing connection for #{Puppet::HTTP::Site.from_uri(request.uri)}")
387+
http.finish
388+
end
389+
Puppet.warning(_("Sleeping for %{interval} seconds before retrying the request") % { interval: interval })
390+
::Kernel.sleep(interval)
386391
end
387-
Puppet.warning(_("Sleeping for %{interval} seconds before retrying the request") % { interval: interval })
388-
::Kernel.sleep(interval)
389392
next
390393
end
391394
end
@@ -404,6 +407,10 @@ def execute_streaming(request, options: {}, &block)
404407

405408
done = true
406409
end
410+
ensure
411+
# If a server responded with a retry, make sure the connection is closed and then
412+
# sleep the specified time.
413+
close_and_sleep.call if close_and_sleep
407414
end
408415
end
409416

spec/integration/http/client_spec.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,22 @@
175175
end
176176
end
177177

178+
context 'ensure that retrying does not attempt to read the body after closing the connection' do
179+
let(:client) { Puppet::HTTP::Client.new(retry_limit: 1) }
180+
it 'raises a retry error instead' do
181+
response_proc = -> (req, res) {
182+
res['Retry-After'] = 1
183+
res.status = 503
184+
}
185+
186+
https_server.start_server(response_proc: response_proc) do |port|
187+
uri = URI("https://127.0.0.1:#{port}")
188+
kwargs = {headers: {'Content-Type' => 'text/plain'}, options: {ssl_context: root_context}}
189+
expect{client.post(uri, '', **kwargs)}.to raise_error(Puppet::HTTP::TooManyRetryAfters)
190+
end
191+
end
192+
end
193+
178194
context 'persistent connections' do
179195
it "detects when the server has closed the connection and reconnects" do
180196
Puppet[:http_debug] = true

0 commit comments

Comments
 (0)