Skip to content

Commit 4d258e9

Browse files
feat: handle thread interrupts in the core HTTP client
1 parent 766ded9 commit 4d258e9

File tree

2 files changed

+53
-24
lines changed

2 files changed

+53
-24
lines changed

lib/imagekit/internal/transport/pooled_net_requester.rb

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -141,40 +141,48 @@ def execute(request)
141141
url, deadline = request.fetch_values(:url, :deadline)
142142

143143
req = nil
144-
eof = false
145144
finished = false
146-
closing = nil
147145

148146
# rubocop:disable Metrics/BlockLength
149147
enum = Enumerator.new do |y|
150148
next if finished
151149

152150
with_pool(url, deadline: deadline) do |conn|
153-
req, closing = self.class.build_request(request) do
154-
self.class.calibrate_socket_timeout(conn, deadline)
155-
end
156-
157-
self.class.calibrate_socket_timeout(conn, deadline)
158-
unless conn.started?
159-
conn.keep_alive_timeout = self.class::KEEP_ALIVE_TIMEOUT
160-
conn.start
161-
end
151+
eof = false
152+
closing = nil
153+
::Thread.handle_interrupt(Object => :never) do
154+
::Thread.handle_interrupt(Object => :immediate) do
155+
req, closing = self.class.build_request(request) do
156+
self.class.calibrate_socket_timeout(conn, deadline)
157+
end
162158

163-
self.class.calibrate_socket_timeout(conn, deadline)
164-
conn.request(req) do |rsp|
165-
y << [req, rsp]
166-
break if finished
167-
168-
rsp.read_body do |bytes|
169-
y << bytes.force_encoding(Encoding::BINARY)
170-
break if finished
159+
self.class.calibrate_socket_timeout(conn, deadline)
160+
unless conn.started?
161+
conn.keep_alive_timeout = self.class::KEEP_ALIVE_TIMEOUT
162+
conn.start
163+
end
171164

172165
self.class.calibrate_socket_timeout(conn, deadline)
166+
conn.request(req) do |rsp|
167+
y << [req, rsp]
168+
break if finished
169+
170+
rsp.read_body do |bytes|
171+
y << bytes.force_encoding(Encoding::BINARY)
172+
break if finished
173+
174+
self.class.calibrate_socket_timeout(conn, deadline)
175+
end
176+
eof = true
177+
end
178+
end
179+
ensure
180+
begin
181+
conn.finish if !eof && conn&.started?
182+
ensure
183+
closing&.call
173184
end
174-
eof = true
175185
end
176-
ensure
177-
conn.finish if !eof && conn&.started?
178186
end
179187
rescue Timeout::Error
180188
raise Imagekit::Errors::APITimeoutError.new(url: url, request: req)
@@ -187,8 +195,6 @@ def execute(request)
187195
body = Imagekit::Internal::Util.fused_enum(enum, external: true) do
188196
finished = true
189197
loop { enum.next }
190-
ensure
191-
closing&.call
192198
end
193199
[Integer(response.code), response, body]
194200
end

test/imagekit/internal/util_test.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,29 @@ def test_rewind_closing
343343
assert_equal(0, steps)
344344
end
345345

346+
def test_thread_interrupts
347+
once = 0
348+
que = Queue.new
349+
enum = Enumerator.new do |y|
350+
10.times { y << _1 }
351+
ensure
352+
once = once.succ
353+
end
354+
355+
fused_1 = Imagekit::Internal::Util.fused_enum(enum, external: true) { loop { enum.next } }
356+
fused_2 = Imagekit::Internal::Util.chain_fused(fused_1) { fused_1.each(&_1) }
357+
fused_3 = Imagekit::Internal::Util.chain_fused(fused_2) { fused_2.each(&_1) }
358+
359+
th = ::Thread.new do
360+
que << "🐶"
361+
fused_3.each { sleep(10) }
362+
end
363+
364+
assert_equal("🐶", que.pop)
365+
th.kill.join
366+
assert_equal(1, once)
367+
end
368+
346369
def test_closing
347370
arr = [1, 2, 3]
348371
once = 0

0 commit comments

Comments
 (0)