From 974c5c25ce92e66e6303f777289c3402539e1f38 Mon Sep 17 00:00:00 2001 From: gngpp Date: Fri, 10 Jul 2026 06:36:37 +0800 Subject: [PATCH] fix(lib): correct Ruby API docs and interrupt handling --- lib/wreq.rb | 38 ++++++++++++++++++++------------------ lib/wreq_ruby/body.rb | 3 +++ lib/wreq_ruby/client.rb | 36 ++++++++++++++++++------------------ lib/wreq_ruby/cookie.rb | 4 ++-- lib/wreq_ruby/emulate.rb | 3 ++- lib/wreq_ruby/header.rb | 1 + lib/wreq_ruby/response.rb | 6 +++--- src/error.rs | 8 ++------ test/stream_test.rb | 36 ++++++++++++++++++++++++++++++++++++ 9 files changed, 87 insertions(+), 48 deletions(-) diff --git a/lib/wreq.rb b/lib/wreq.rb index b204f0b..fcefb11 100644 --- a/lib/wreq.rb +++ b/lib/wreq.rb @@ -18,6 +18,8 @@ unless defined?(Wreq) module Wreq + # Current wreq gem version. + # @return [String] VERSION = nil # Send an HTTP request. @@ -26,7 +28,7 @@ module Wreq # @param url [String] Target URL # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request # @param orig_headers [Array, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling - # @param default_headers [Hash{String=>String}, nil] Default headers to merge + # @param default_headers [Boolean, nil] Whether to apply default emulation headers # @param query [Hash, nil] URL query parameters # @param auth [String, nil] Authorization header value # @param bearer_auth [String, nil] Bearer token for Authorization header @@ -47,7 +49,7 @@ module Wreq # @param version [Wreq::Version, nil] HTTP version to use # @param form [Hash{String=>String}, nil] Form data (application/x-www-form-urlencoded) # @param json [Object, nil] JSON body (will be serialized) - # @param body [String, IO, nil] Raw request body (string or stream) + # @param body [String, Wreq::BodySender, nil] Request body bytes or streaming body sender # @return [Wreq::Response] HTTP response def self.request(method, url, **options) end @@ -57,7 +59,7 @@ def self.request(method, url, **options) # @param url [String] Target URL # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request # @param orig_headers [Array, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling - # @param default_headers [Hash{String=>String}, nil] Default headers to merge + # @param default_headers [Boolean, nil] Whether to apply default emulation headers # @param query [Hash, nil] URL query parameters # @param auth [String, nil] Authorization header value # @param bearer_auth [String, nil] Bearer token for Authorization header @@ -78,7 +80,7 @@ def self.request(method, url, **options) # @param version [Wreq::Version, nil] HTTP version to use # @param form [Hash{String=>String}, nil] Form data (application/x-www-form-urlencoded) # @param json [Object, nil] JSON body (will be serialized) - # @param body [String, IO, nil] Raw request body (string or stream) + # @param body [String, Wreq::BodySender, nil] Request body bytes or streaming body sender # @return [Wreq::Response] HTTP response def self.get(url, **options) end @@ -88,7 +90,7 @@ def self.get(url, **options) # @param url [String] Target URL # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request # @param orig_headers [Array, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling - # @param default_headers [Hash{String=>String}, nil] Default headers to merge + # @param default_headers [Boolean, nil] Whether to apply default emulation headers # @param query [Hash, nil] URL query parameters # @param auth [String, nil] Authorization header value # @param bearer_auth [String, nil] Bearer token for Authorization header @@ -109,7 +111,7 @@ def self.get(url, **options) # @param version [Wreq::Version, nil] HTTP version to use # @param form [Hash{String=>String}, nil] Form data (application/x-www-form-urlencoded) # @param json [Object, nil] JSON body (will be serialized) - # @param body [String, IO, nil] Raw request body (string or stream) + # @param body [String, Wreq::BodySender, nil] Request body bytes or streaming body sender # @return [Wreq::Response] HTTP response def self.head(url, **options) end @@ -119,7 +121,7 @@ def self.head(url, **options) # @param url [String] Target URL # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request # @param orig_headers [Array, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling - # @param default_headers [Hash{String=>String}, nil] Default headers to merge + # @param default_headers [Boolean, nil] Whether to apply default emulation headers # @param query [Hash, nil] URL query parameters # @param auth [String, nil] Authorization header value # @param bearer_auth [String, nil] Bearer token for Authorization header @@ -140,7 +142,7 @@ def self.head(url, **options) # @param version [Wreq::Version, nil] HTTP version to use # @param form [Hash{String=>String}, nil] Form data (application/x-www-form-urlencoded) # @param json [Object, nil] JSON body (will be serialized) - # @param body [String, IO, nil] Raw request body (string or stream) + # @param body [String, Wreq::BodySender, nil] Request body bytes or streaming body sender # @return [Wreq::Response] HTTP response def self.post(url, **options) end @@ -150,7 +152,7 @@ def self.post(url, **options) # @param url [String] Target URL # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request # @param orig_headers [Array, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling - # @param default_headers [Hash{String=>String}, nil] Default headers to merge + # @param default_headers [Boolean, nil] Whether to apply default emulation headers # @param query [Hash, nil] URL query parameters # @param auth [String, nil] Authorization header value # @param bearer_auth [String, nil] Bearer token for Authorization header @@ -171,7 +173,7 @@ def self.post(url, **options) # @param version [Wreq::Version, nil] HTTP version to use # @param form [Hash{String=>String}, nil] Form data (application/x-www-form-urlencoded) # @param json [Object, nil] JSON body (will be serialized) - # @param body [String, IO, nil] Raw request body (string or stream) + # @param body [String, Wreq::BodySender, nil] Request body bytes or streaming body sender # @return [Wreq::Response] HTTP response def self.put(url, **options) end @@ -181,7 +183,7 @@ def self.put(url, **options) # @param url [String] Target URL # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request # @param orig_headers [Array, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling - # @param default_headers [Hash{String=>String}, nil] Default headers to merge + # @param default_headers [Boolean, nil] Whether to apply default emulation headers # @param query [Hash, nil] URL query parameters # @param auth [String, nil] Authorization header value # @param bearer_auth [String, nil] Bearer token for Authorization header @@ -202,7 +204,7 @@ def self.put(url, **options) # @param version [Wreq::Version, nil] HTTP version to use # @param form [Hash{String=>String}, nil] Form data (application/x-www-form-urlencoded) # @param json [Object, nil] JSON body (will be serialized) - # @param body [String, IO, nil] Raw request body (string or stream) + # @param body [String, Wreq::BodySender, nil] Request body bytes or streaming body sender # @return [Wreq::Response] HTTP response def self.delete(url, **options) end @@ -212,7 +214,7 @@ def self.delete(url, **options) # @param url [String] Target URL # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request # @param orig_headers [Array, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling - # @param default_headers [Hash{String=>String}, nil] Default headers to merge + # @param default_headers [Boolean, nil] Whether to apply default emulation headers # @param query [Hash, nil] URL query parameters # @param auth [String, nil] Authorization header value # @param bearer_auth [String, nil] Bearer token for Authorization header @@ -233,7 +235,7 @@ def self.delete(url, **options) # @param version [Wreq::Version, nil] HTTP version to use # @param form [Hash{String=>String}, nil] Form data (application/x-www-form-urlencoded) # @param json [Object, nil] JSON body (will be serialized) - # @param body [String, IO, nil] Raw request body (string or stream) + # @param body [String, Wreq::BodySender, nil] Request body bytes or streaming body sender # @return [Wreq::Response] HTTP response def self.options(url, **options) end @@ -243,7 +245,7 @@ def self.options(url, **options) # @param url [String] Target URL # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request # @param orig_headers [Array, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling - # @param default_headers [Hash{String=>String}, nil] Default headers to merge + # @param default_headers [Boolean, nil] Whether to apply default emulation headers # @param query [Hash, nil] URL query parameters # @param auth [String, nil] Authorization header value # @param bearer_auth [String, nil] Bearer token for Authorization header @@ -264,7 +266,7 @@ def self.options(url, **options) # @param version [Wreq::Version, nil] HTTP version to use # @param form [Hash{String=>String}, nil] Form data (application/x-www-form-urlencoded) # @param json [Object, nil] JSON body (will be serialized) - # @param body [String, IO, nil] Raw request body (string or stream) + # @param body [String, Wreq::BodySender, nil] Request body bytes or streaming body sender # @return [Wreq::Response] HTTP response def self.trace(url, **options) end @@ -274,7 +276,7 @@ def self.trace(url, **options) # @param url [String] Target URL # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request # @param orig_headers [Array, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling - # @param default_headers [Hash{String=>String}, nil] Default headers to merge + # @param default_headers [Boolean, nil] Whether to apply default emulation headers # @param query [Hash, nil] URL query parameters # @param auth [String, nil] Authorization header value # @param bearer_auth [String, nil] Bearer token for Authorization header @@ -295,7 +297,7 @@ def self.trace(url, **options) # @param version [Wreq::Version, nil] HTTP version to use # @param form [Hash{String=>String}, nil] Form data (application/x-www-form-urlencoded) # @param json [Object, nil] JSON body (will be serialized) - # @param body [String, IO, nil] Raw request body (string or stream) + # @param body [String, Wreq::BodySender, nil] Request body bytes or streaming body sender # @return [Wreq::Response] HTTP response def self.patch(url, **options) end diff --git a/lib/wreq_ruby/body.rb b/lib/wreq_ruby/body.rb index c3489bf..15b1d4f 100644 --- a/lib/wreq_ruby/body.rb +++ b/lib/wreq_ruby/body.rb @@ -21,14 +21,17 @@ module Wreq # after being consumed by a request, further push or reuse is not allowed. class BodySender # @param capacity [Integer] channel buffer size, default 8 + # @return [Wreq::BodySender] A streaming request body sender def self.new(capacity = 8) end # @param data [String] binary chunk + # @return [void] def push(data) end # Close the sender, signaling end of data. + # @return [void] def close end end diff --git a/lib/wreq_ruby/client.rb b/lib/wreq_ruby/client.rb index 7718c01..4008ad0 100644 --- a/lib/wreq_ruby/client.rb +++ b/lib/wreq_ruby/client.rb @@ -240,7 +240,7 @@ def self.new(**options) # @param url [String] Target URL # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request # @param orig_headers [Array, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling - # @param default_headers [Hash{String=>String}, nil] Default headers to merge + # @param default_headers [Boolean, nil] Whether to apply default emulation headers # @param query [Hash, nil] URL query parameters # @param auth [String, nil] Authorization header value # @param bearer_auth [String, nil] Bearer token for Authorization header @@ -261,7 +261,7 @@ def self.new(**options) # @param version [Wreq::Version, nil] HTTP version to use # @param form [Hash{String=>String}, nil] Form data (application/x-www-form-urlencoded) # @param json [Object, nil] JSON body (will be serialized) - # @param body [String, IO, nil] Raw request body (string or stream) + # @param body [String, Wreq::BodySender, nil] Request body bytes or streaming body sender # @return [Wreq::Response] HTTP response def request(method, url, **options) end @@ -271,7 +271,7 @@ def request(method, url, **options) # @param url [String] Target URL # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request # @param orig_headers [Array, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling - # @param default_headers [Hash{String=>String}, nil] Default headers to merge + # @param default_headers [Boolean, nil] Whether to apply default emulation headers # @param query [Hash, nil] URL query parameters # @param auth [String, nil] Authorization header value # @param bearer_auth [String, nil] Bearer token for Authorization header @@ -292,7 +292,7 @@ def request(method, url, **options) # @param version [Wreq::Version, nil] HTTP version to use # @param form [Hash{String=>String}, nil] Form data (application/x-www-form-urlencoded) # @param json [Object, nil] JSON body (will be serialized) - # @param body [String, IO, nil] Raw request body (string or stream) + # @param body [String, Wreq::BodySender, nil] Request body bytes or streaming body sender # @return [Wreq::Response] HTTP response def get(url, **options) end @@ -302,7 +302,7 @@ def get(url, **options) # @param url [String] Target URL # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request # @param orig_headers [Array, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling - # @param default_headers [Hash{String=>String}, nil] Default headers to merge + # @param default_headers [Boolean, nil] Whether to apply default emulation headers # @param query [Hash, nil] URL query parameters # @param auth [String, nil] Authorization header value # @param bearer_auth [String, nil] Bearer token for Authorization header @@ -323,7 +323,7 @@ def get(url, **options) # @param version [Wreq::Version, nil] HTTP version to use # @param form [Hash{String=>String}, nil] Form data (application/x-www-form-urlencoded) # @param json [Object, nil] JSON body (will be serialized) - # @param body [String, IO, nil] Raw request body (string or stream) + # @param body [String, Wreq::BodySender, nil] Request body bytes or streaming body sender # @return [Wreq::Response] HTTP response def head(url, **options) end @@ -333,7 +333,7 @@ def head(url, **options) # @param url [String] Target URL # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request # @param orig_headers [Array, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling - # @param default_headers [Hash{String=>String}, nil] Default headers to merge + # @param default_headers [Boolean, nil] Whether to apply default emulation headers # @param query [Hash, nil] URL query parameters # @param auth [String, nil] Authorization header value # @param bearer_auth [String, nil] Bearer token for Authorization header @@ -354,7 +354,7 @@ def head(url, **options) # @param version [Wreq::Version, nil] HTTP version to use # @param form [Hash{String=>String}, nil] Form data (application/x-www-form-urlencoded) # @param json [Object, nil] JSON body (will be serialized) - # @param body [String, IO, nil] Raw request body (string or stream) + # @param body [String, Wreq::BodySender, nil] Request body bytes or streaming body sender # @return [Wreq::Response] HTTP response def post(url, **options) end @@ -364,7 +364,7 @@ def post(url, **options) # @param url [String] Target URL # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request # @param orig_headers [Array, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling - # @param default_headers [Hash{String=>String}, nil] Default headers to merge + # @param default_headers [Boolean, nil] Whether to apply default emulation headers # @param query [Hash, nil] URL query parameters # @param auth [String, nil] Authorization header value # @param bearer_auth [String, nil] Bearer token for Authorization header @@ -385,7 +385,7 @@ def post(url, **options) # @param version [Wreq::Version, nil] HTTP version to use # @param form [Hash{String=>String}, nil] Form data (application/x-www-form-urlencoded) # @param json [Object, nil] JSON body (will be serialized) - # @param body [String, IO, nil] Raw request body (string or stream) + # @param body [String, Wreq::BodySender, nil] Request body bytes or streaming body sender # @return [Wreq::Response] HTTP response def put(url, **options) end @@ -395,7 +395,7 @@ def put(url, **options) # @param url [String] Target URL # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request # @param orig_headers [Array, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling - # @param default_headers [Hash{String=>String}, nil] Default headers to merge + # @param default_headers [Boolean, nil] Whether to apply default emulation headers # @param query [Hash, nil] URL query parameters # @param auth [String, nil] Authorization header value # @param bearer_auth [String, nil] Bearer token for Authorization header @@ -416,7 +416,7 @@ def put(url, **options) # @param version [Wreq::Version, nil] HTTP version to use # @param form [Hash{String=>String}, nil] Form data (application/x-www-form-urlencoded) # @param json [Object, nil] JSON body (will be serialized) - # @param body [String, IO, nil] Raw request body (string or stream) + # @param body [String, Wreq::BodySender, nil] Request body bytes or streaming body sender # @return [Wreq::Response] HTTP response def delete(url, **options) end @@ -426,7 +426,7 @@ def delete(url, **options) # @param url [String] Target URL # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request # @param orig_headers [Array, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling - # @param default_headers [Hash{String=>String}, nil] Default headers to merge + # @param default_headers [Boolean, nil] Whether to apply default emulation headers # @param query [Hash, nil] URL query parameters # @param auth [String, nil] Authorization header value # @param bearer_auth [String, nil] Bearer token for Authorization header @@ -447,7 +447,7 @@ def delete(url, **options) # @param version [Wreq::Version, nil] HTTP version to use # @param form [Hash{String=>String}, nil] Form data (application/x-www-form-urlencoded) # @param json [Object, nil] JSON body (will be serialized) - # @param body [String, IO, nil] Raw request body (string or stream) + # @param body [String, Wreq::BodySender, nil] Request body bytes or streaming body sender # @return [Wreq::Response] HTTP response def options(url, **options) end @@ -457,7 +457,7 @@ def options(url, **options) # @param url [String] Target URL # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request # @param orig_headers [Array, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling - # @param default_headers [Hash{String=>String}, nil] Default headers to merge + # @param default_headers [Boolean, nil] Whether to apply default emulation headers # @param query [Hash, nil] URL query parameters # @param auth [String, nil] Authorization header value # @param bearer_auth [String, nil] Bearer token for Authorization header @@ -478,7 +478,7 @@ def options(url, **options) # @param version [Wreq::Version, nil] HTTP version to use # @param form [Hash{String=>String}, nil] Form data (application/x-www-form-urlencoded) # @param json [Object, nil] JSON body (will be serialized) - # @param body [String, IO, nil] Raw request body (string or stream) + # @param body [String, Wreq::BodySender, nil] Request body bytes or streaming body sender # @return [Wreq::Response] HTTP response def trace(url, **options) end @@ -488,7 +488,7 @@ def trace(url, **options) # @param url [String] Target URL # @param headers [Wreq::Headers, Hash{String=>String}, nil] Custom headers for this request # @param orig_headers [Array, nil] Original header names used to preserve raw header order and HTTP/1 case-sensitive header handling - # @param default_headers [Hash{String=>String}, nil] Default headers to merge + # @param default_headers [Boolean, nil] Whether to apply default emulation headers # @param query [Hash, nil] URL query parameters # @param auth [String, nil] Authorization header value # @param bearer_auth [String, nil] Bearer token for Authorization header @@ -509,7 +509,7 @@ def trace(url, **options) # @param version [Wreq::Version, nil] HTTP version to use # @param form [Hash{String=>String}, nil] Form data (application/x-www-form-urlencoded) # @param json [Object, nil] JSON body (will be serialized) - # @param body [String, IO, nil] Raw request body (string or stream) + # @param body [String, Wreq::BodySender, nil] Request body bytes or streaming body sender # @return [Wreq::Response] HTTP response def patch(url, **options) end diff --git a/lib/wreq_ruby/cookie.rb b/lib/wreq_ruby/cookie.rb index d7a9c5a..96fdbb9 100644 --- a/lib/wreq_ruby/cookie.rb +++ b/lib/wreq_ruby/cookie.rb @@ -4,9 +4,9 @@ module Wreq # # Values follow the Rust enum exposed by the native extension. class SameSite - # Lax same-site policy. - Strict = nil # Strict same-site policy. + Strict = nil + # Lax same-site policy. Lax = nil # None same-site policy. None = nil diff --git a/lib/wreq_ruby/emulate.rb b/lib/wreq_ruby/emulate.rb index 0d53baa..66fb744 100644 --- a/lib/wreq_ruby/emulate.rb +++ b/lib/wreq_ruby/emulate.rb @@ -154,7 +154,7 @@ class Profile Opera128 = nil Opera129 = nil Opera130 = nil - Opera131 = nils + Opera131 = nil end unless method_defined?(:to_s) @@ -225,6 +225,7 @@ class Emulation # @param platform [Wreq::Platform, nil] Operating system platform to emulate # @param http2 [Boolean] Whether HTTP/2 support is enabled # @param headers [Boolean] Whether default emulation headers are enabled + # @return [Wreq::Emulation] Configured emulation settings def self.new(profile: nil, platform: nil, http2: true, headers: true) end end diff --git a/lib/wreq_ruby/header.rb b/lib/wreq_ruby/header.rb index b6ce5c8..d74ad54 100644 --- a/lib/wreq_ruby/header.rb +++ b/lib/wreq_ruby/header.rb @@ -190,6 +190,7 @@ def each end # Convert headers to a string representation. + # @return [String] String representation of the headers def to_s end end diff --git a/lib/wreq_ruby/response.rb b/lib/wreq_ruby/response.rb index d1194a1..29609f3 100644 --- a/lib/wreq_ruby/response.rb +++ b/lib/wreq_ruby/response.rb @@ -91,13 +91,13 @@ def bytes # Get the response body as text with a specific charset. # This method allows you to specify a default encoding # to use when decoding the response body. - # # @param default_encoding [String] Default encoding to use (e.g., "UTF-8") - # # @return [String] Response body decoded as text using the specified encoding + # @param default_encoding [String] Default encoding to use (e.g., "UTF-8") + # @return [String] Response body decoded as text using the specified encoding # @example # html = response.text("ISO-8859-1") # puts html # @raise [Wreq::DecodingError] if body cannot be decoded with the specified encoding - def text(default_encoding: "UTF-8") + def text(default_encoding = "UTF-8") end # Parse the response body as JSON. diff --git a/src/error.rs b/src/error.rs index 80802ea..3e6896e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -72,17 +72,14 @@ define_exception!(DECODING_ERROR, "DecodingError", exception_runtime_error); // Configuration and builder errors define_exception!(BUILDER_ERROR, "BuilderError", exception_runtime_error); -// Thread interruption error -define_exception!(INTERRUPT_ERROR, "InterruptError", exception_interrupt); - /// Memory error constant pub fn memory_error() -> MagnusError { MagnusError::new(ruby!().get_inner(&MEMORY), RACE_CONDITION_ERROR_MSG) } -/// Thread interruption error (raised when Thread.kill cancels a request) +/// Create a Ruby thread interruption error. pub fn interrupt_error() -> MagnusError { - MagnusError::new(ruby!().get_inner(&INTERRUPT_ERROR), "request interrupted") + MagnusError::new(ruby!().exception_interrupt(), "request interrupted") } /// LocalJumpError for methods that require a Ruby block. @@ -159,5 +156,4 @@ pub fn include(ruby: &Ruby) { Lazy::force(&BODY_ERROR, ruby); Lazy::force(&DECODING_ERROR, ruby); Lazy::force(&BUILDER_ERROR, ruby); - Lazy::force(&INTERRUPT_ERROR, ruby); } diff --git a/test/stream_test.rb b/test/stream_test.rb index 5bf88bb..c444756 100644 --- a/test/stream_test.rb +++ b/test/stream_test.rb @@ -1,5 +1,6 @@ require "test_helper" require "socket" +require "timeout" class StreamTest < Minitest::Test def test_simple_push_stream @@ -174,6 +175,41 @@ def test_thread_interrupt_connect_with_timeout assert killed, "Connect+timeout phase should be interruptible" end + def test_thread_raise_propagates_ruby_interrupt + server = TCPServer.new("127.0.0.1", 0) + accepted = Queue.new + errors = Queue.new + + server_thread = Thread.new do + socket = server.accept + accepted << true + sleep + ensure + socket&.close + end + + request_thread = Thread.new do + Wreq.get("http://127.0.0.1:#{server.addr[1]}/", timeout: 60) + rescue Interrupt, StandardError => error + errors << error + end + + Timeout.timeout(5) { accepted.pop } + request_thread.raise(Interrupt, "test interrupt") + + assert request_thread.join(5), "Interrupted request thread should stop" + + error = Timeout.timeout(1) { errors.pop } + assert_instance_of Interrupt, error + assert_equal "test interrupt", error.message + ensure + request_thread&.kill + request_thread&.join(1) + server_thread&.kill + server_thread&.join(1) + server&.close + end + def test_thread_interrupt_body_reading url = "#{HTTPBIN_URL}/drip?duration=5&numbytes=5" thread = Thread.new do