wreq 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Cargo.toml +54 -0
- data/Gemfile +17 -0
- data/LICENSE +201 -0
- data/README.md +150 -0
- data/Rakefile +90 -0
- data/build.rs +9 -0
- data/examples/body.rb +42 -0
- data/examples/client.rb +33 -0
- data/examples/emulation_request.rb +37 -0
- data/examples/headers.rb +27 -0
- data/examples/proxy.rb +113 -0
- data/examples/send_stream.rb +85 -0
- data/examples/stream.rb +14 -0
- data/examples/thread_interrupt.rb +83 -0
- data/extconf.rb +7 -0
- data/lib/wreq.rb +313 -0
- data/lib/wreq_ruby/body.rb +36 -0
- data/lib/wreq_ruby/client.rb +516 -0
- data/lib/wreq_ruby/cookie.rb +144 -0
- data/lib/wreq_ruby/emulation.rb +186 -0
- data/lib/wreq_ruby/error.rb +159 -0
- data/lib/wreq_ruby/header.rb +197 -0
- data/lib/wreq_ruby/http.rb +132 -0
- data/lib/wreq_ruby/response.rb +208 -0
- data/script/build_platform_gem.rb +34 -0
- data/src/client/body/form.rs +2 -0
- data/src/client/body/json.rs +16 -0
- data/src/client/body/stream.rs +148 -0
- data/src/client/body.rs +57 -0
- data/src/client/param.rs +19 -0
- data/src/client/query.rs +2 -0
- data/src/client/req.rs +251 -0
- data/src/client/resp.rs +250 -0
- data/src/client.rs +392 -0
- data/src/cookie.rs +277 -0
- data/src/emulation.rs +317 -0
- data/src/error.rs +147 -0
- data/src/extractor.rs +199 -0
- data/src/gvl.rs +154 -0
- data/src/header.rs +177 -0
- data/src/http.rs +127 -0
- data/src/lib.rs +97 -0
- data/src/macros.rs +118 -0
- data/src/rt.rs +47 -0
- data/test/client_cookie_test.rb +46 -0
- data/test/client_test.rb +136 -0
- data/test/cookie_test.rb +166 -0
- data/test/emulation_test.rb +21 -0
- data/test/error_handling_test.rb +89 -0
- data/test/header_test.rb +290 -0
- data/test/module_methods_test.rb +75 -0
- data/test/request_parameters_test.rb +175 -0
- data/test/request_test.rb +234 -0
- data/test/response_test.rb +69 -0
- data/test/stream_test.rb +81 -0
- data/test/test_helper.rb +9 -0
- data/wreq.gemspec +68 -0
- metadata +112 -0
data/src/rt.rs
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
use std::sync::LazyLock;
|
|
2
|
+
|
|
3
|
+
use tokio::runtime::{Builder, Runtime};
|
|
4
|
+
|
|
5
|
+
use crate::{error::interrupt_error, gvl};
|
|
6
|
+
|
|
7
|
+
static RUNTIME: LazyLock<Runtime> = LazyLock::new(|| {
|
|
8
|
+
Builder::new_multi_thread()
|
|
9
|
+
.enable_all()
|
|
10
|
+
.build()
|
|
11
|
+
.expect("Failed to initialize Tokio runtime")
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
/// Block on a future to completion on the global Tokio runtime,
|
|
15
|
+
/// with support for cancellation via the provided `CancelFlag`.
|
|
16
|
+
pub fn try_block_on<F, T>(future: F) -> F::Output
|
|
17
|
+
where
|
|
18
|
+
F: Future<Output = Result<T, magnus::Error>>,
|
|
19
|
+
{
|
|
20
|
+
gvl::nogvl_cancellable(|flag| {
|
|
21
|
+
RUNTIME.block_on(async move {
|
|
22
|
+
tokio::select! {
|
|
23
|
+
biased;
|
|
24
|
+
_ = flag.cancelled() => Err(interrupt_error()),
|
|
25
|
+
result = future => result,
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/// Block on a future to completion on the global Tokio runtime,
|
|
32
|
+
/// returning `None` if cancelled via the provided `CancelFlag`.
|
|
33
|
+
#[inline]
|
|
34
|
+
pub fn maybe_block_on<F, T>(future: F) -> F::Output
|
|
35
|
+
where
|
|
36
|
+
F: Future<Output = Option<T>>,
|
|
37
|
+
{
|
|
38
|
+
gvl::nogvl_cancellable(|flag| {
|
|
39
|
+
RUNTIME.block_on(async move {
|
|
40
|
+
tokio::select! {
|
|
41
|
+
biased;
|
|
42
|
+
_ = flag.cancelled() => None,
|
|
43
|
+
result = future => result,
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class ClientCookieProviderTest < Minitest::Test
|
|
6
|
+
HOST = "http://localhost:8080"
|
|
7
|
+
|
|
8
|
+
def setup
|
|
9
|
+
@jar = Wreq::Jar.new
|
|
10
|
+
@client = Wreq::Client.new(
|
|
11
|
+
cookie_store: true,
|
|
12
|
+
cookie_provider: @jar,
|
|
13
|
+
allow_redirects: true
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_custom_jar_captures_set_cookie_and_sends_back
|
|
18
|
+
# starts empty
|
|
19
|
+
assert_kind_of Array, @jar.get_all
|
|
20
|
+
assert_equal 0, @jar.get_all.length
|
|
21
|
+
|
|
22
|
+
# server sets cookie; client follows redirect; jar should store it
|
|
23
|
+
res1 = @client.get("#{HOST}/cookies/set?foo=bar")
|
|
24
|
+
assert_equal 200, res1.code
|
|
25
|
+
|
|
26
|
+
names = @jar.get_all.map(&:name)
|
|
27
|
+
assert_includes names, "foo"
|
|
28
|
+
|
|
29
|
+
# subsequent request should send the stored cookie automatically
|
|
30
|
+
res2 = @client.get("#{HOST}/cookies")
|
|
31
|
+
assert_equal 200, res2.code
|
|
32
|
+
body = res2.json
|
|
33
|
+
assert_kind_of Hash, body
|
|
34
|
+
assert_equal "bar", body.dig("cookies", "foo")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_prepopulated_jar_is_used_by_client
|
|
38
|
+
# pre-populate jar
|
|
39
|
+
@jar.add_cookie_str("pref=1; Path=/", "#{HOST}/")
|
|
40
|
+
|
|
41
|
+
res = @client.get("#{HOST}/cookies")
|
|
42
|
+
assert_equal 200, res.code
|
|
43
|
+
cookies = res.json["cookies"]
|
|
44
|
+
assert_equal "1", cookies["pref"]
|
|
45
|
+
end
|
|
46
|
+
end
|
data/test/client_test.rb
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
class ClientTest < Minitest::Test
|
|
4
|
+
def setup
|
|
5
|
+
@client = Wreq::Client.new
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def test_client_creation
|
|
9
|
+
refute_nil @client
|
|
10
|
+
assert_instance_of Wreq::Client, @client
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_client_with_configuration
|
|
14
|
+
client = Wreq::Client.new(
|
|
15
|
+
timeout: 30,
|
|
16
|
+
headers: {"User-Agent" => "wreq-ruby/test"}
|
|
17
|
+
)
|
|
18
|
+
refute_nil client
|
|
19
|
+
assert_instance_of Wreq::Client, client
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_get_request
|
|
23
|
+
response = @client.get("http://localhost:8080/get")
|
|
24
|
+
refute_nil response
|
|
25
|
+
assert_equal 200, response.code
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_post_request
|
|
29
|
+
response = @client.post("http://localhost:8080/post",
|
|
30
|
+
json: {test: "wreq-ruby"})
|
|
31
|
+
refute_nil response
|
|
32
|
+
assert_equal 200, response.code
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_put_request
|
|
36
|
+
response = @client.put("http://localhost:8080/put",
|
|
37
|
+
json: {data: "test"})
|
|
38
|
+
refute_nil response
|
|
39
|
+
assert_equal 200, response.code
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def test_delete_request
|
|
43
|
+
response = @client.delete("http://localhost:8080/delete")
|
|
44
|
+
refute_nil response
|
|
45
|
+
assert_equal 200, response.code
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_patch_request
|
|
49
|
+
response = @client.patch("http://localhost:8080/patch",
|
|
50
|
+
json: {update: "field"})
|
|
51
|
+
refute_nil response
|
|
52
|
+
assert_equal 200, response.code
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def test_request_with_query_params
|
|
56
|
+
response = @client.get("http://localhost:8080/get",
|
|
57
|
+
query: {"param" => "value"})
|
|
58
|
+
refute_nil response
|
|
59
|
+
assert_equal 200, response.code
|
|
60
|
+
assert_includes response.text, "param"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def test_request_with_form_data
|
|
64
|
+
response = @client.post("http://localhost:8080/post",
|
|
65
|
+
form: {"field" => "value"})
|
|
66
|
+
refute_nil response
|
|
67
|
+
assert_equal 200, response.code
|
|
68
|
+
assert_includes response.text, "field"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def test_request_with_raw_body
|
|
72
|
+
response = @client.post("http://localhost:8080/post",
|
|
73
|
+
body: "raw content",
|
|
74
|
+
headers: {"Content-Type" => "text/plain"})
|
|
75
|
+
refute_nil response
|
|
76
|
+
assert_equal 200, response.code
|
|
77
|
+
assert_includes response.text, "raw content"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def test_basic_authentication
|
|
81
|
+
response = @client.get("http://localhost:8080/basic-auth/user/pass",
|
|
82
|
+
basic_auth: ["user", "pass"])
|
|
83
|
+
refute_nil response
|
|
84
|
+
assert_equal 200, response.code
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def test_bearer_authentication
|
|
88
|
+
response = @client.get("http://localhost:8080/bearer",
|
|
89
|
+
bearer_auth: "test-token")
|
|
90
|
+
refute_nil response
|
|
91
|
+
assert_equal 200, response.code
|
|
92
|
+
assert_includes response.text, "test-token"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def test_redirect_following
|
|
96
|
+
response = @client.get("http://localhost:8080/redirect/1",
|
|
97
|
+
allow_redirects: true)
|
|
98
|
+
refute_nil response
|
|
99
|
+
assert_equal 200, response.code
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def test_redirect_blocking
|
|
103
|
+
response = @client.get("http://localhost:8080/redirect/1",
|
|
104
|
+
allow_redirects: false)
|
|
105
|
+
refute_nil response
|
|
106
|
+
assert_equal 302, response.code
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def test_gzip_compression
|
|
110
|
+
response = @client.get("http://localhost:8080/gzip", gzip: true)
|
|
111
|
+
refute_nil response
|
|
112
|
+
assert_equal 200, response.code
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def test_timeout_handling
|
|
116
|
+
# Test that short timeouts properly raise exceptions
|
|
117
|
+
assert_raises(Wreq::TimeoutError) do
|
|
118
|
+
@client.get("http://localhost:8080/delay/10", timeout: 1)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Test that reasonable timeouts work normally
|
|
122
|
+
start_time = Time.now
|
|
123
|
+
response = @client.get("http://localhost:8080/delay/1", timeout: 5)
|
|
124
|
+
elapsed = Time.now - start_time
|
|
125
|
+
|
|
126
|
+
refute_nil response
|
|
127
|
+
assert_equal 200, response.code
|
|
128
|
+
assert elapsed < 5, "Request should complete within timeout"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def test_status_codes
|
|
132
|
+
response = @client.get("http://localhost:8080/status/404")
|
|
133
|
+
refute_nil response
|
|
134
|
+
assert_equal 404, response.code
|
|
135
|
+
end
|
|
136
|
+
end
|
data/test/cookie_test.rb
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class CookieTest < Minitest::Test
|
|
6
|
+
def setup
|
|
7
|
+
@jar = Wreq::Jar.new
|
|
8
|
+
@base_url = "https://example.com"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def test_jar_initially_empty
|
|
12
|
+
assert_instance_of Wreq::Jar, @jar
|
|
13
|
+
cookies = begin
|
|
14
|
+
Wreq::Jar.get_all(@jar)
|
|
15
|
+
rescue
|
|
16
|
+
@jar.get_all
|
|
17
|
+
end # support either binding style
|
|
18
|
+
assert_kind_of Array, cookies
|
|
19
|
+
assert_equal 0, cookies.length
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_add_cookie_str_and_get_all
|
|
23
|
+
set_cookie = "sid=abc123; Path=/; Domain=example.com; HttpOnly; Secure"
|
|
24
|
+
@jar.add_cookie_str(set_cookie, @base_url)
|
|
25
|
+
|
|
26
|
+
cookies = @jar.get_all
|
|
27
|
+
assert_kind_of Array, cookies
|
|
28
|
+
assert_equal 1, cookies.length
|
|
29
|
+
|
|
30
|
+
c = cookies.first
|
|
31
|
+
assert_instance_of Wreq::Cookie, c
|
|
32
|
+
assert_equal "sid", c.name
|
|
33
|
+
assert_equal "abc123", c.value
|
|
34
|
+
|
|
35
|
+
# attributes parsed from Set-Cookie
|
|
36
|
+
assert_equal "/", c.path
|
|
37
|
+
assert_equal "example.com", c.domain
|
|
38
|
+
|
|
39
|
+
# predicate-ish flags
|
|
40
|
+
assert_equal true, (c.http_only || c.http_only?)
|
|
41
|
+
assert_equal true, (c.secure || c.secure?)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def test_add_multiple_and_remove
|
|
45
|
+
@jar.add_cookie_str("a=1; Path=/", @base_url)
|
|
46
|
+
@jar.add_cookie_str("b=2; Path=/", @base_url)
|
|
47
|
+
@jar.add_cookie_str("c=3; Path=/", @base_url)
|
|
48
|
+
|
|
49
|
+
cookies = @jar.get_all
|
|
50
|
+
assert_equal 3, cookies.length
|
|
51
|
+
|
|
52
|
+
# remove one by name
|
|
53
|
+
@jar.remove("b", @base_url)
|
|
54
|
+
names = @jar.get_all.map(&:name)
|
|
55
|
+
refute_includes names, "b"
|
|
56
|
+
assert_includes names, "a"
|
|
57
|
+
assert_includes names, "c"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def test_clear
|
|
61
|
+
@jar.add_cookie_str("x=1; Path=/", @base_url)
|
|
62
|
+
@jar.add_cookie_str("y=2; Path=/", @base_url)
|
|
63
|
+
refute_empty @jar.get_all
|
|
64
|
+
|
|
65
|
+
@jar.clear
|
|
66
|
+
assert_empty @jar.get_all
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def test_max_age_and_expires_optional
|
|
70
|
+
# Max-Age only
|
|
71
|
+
@jar.clear
|
|
72
|
+
@jar.add_cookie_str("ma=1; Max-Age=3600; Path=/", @base_url)
|
|
73
|
+
c1 = @jar.get_all.find { |c| c.name == "ma" }
|
|
74
|
+
assert c1
|
|
75
|
+
# can be nil or Integer; just ensure responds and is truthy integer
|
|
76
|
+
if (v = c1.max_age)
|
|
77
|
+
assert_kind_of Integer, v
|
|
78
|
+
assert_operator v, :>=, 0
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Expires only
|
|
82
|
+
@jar.clear
|
|
83
|
+
t = Time.now + 3600
|
|
84
|
+
@jar.add_cookie_str("exp=1; Expires=#{t.gmtime.strftime("%a, %d %b %Y %H:%M:%S GMT")}; Path=/", @base_url)
|
|
85
|
+
c2 = @jar.get_all.find { |c| c.name == "exp" }
|
|
86
|
+
assert c2
|
|
87
|
+
# expires returns Float (unix seconds) or nil
|
|
88
|
+
if (e = c2.expires)
|
|
89
|
+
assert_kind_of Float, e
|
|
90
|
+
assert_operator e, :>, Time.now.to_f - 1_000_000 # sanity bound
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# -------- Wreq::Cookie unit tests --------
|
|
95
|
+
|
|
96
|
+
def test_cookie_new_minimal
|
|
97
|
+
c = Wreq::Cookie.new("sid", "abc")
|
|
98
|
+
|
|
99
|
+
assert_instance_of Wreq::Cookie, c
|
|
100
|
+
assert_equal "sid", c.name
|
|
101
|
+
assert_equal "abc", c.value
|
|
102
|
+
|
|
103
|
+
assert_nil c.path
|
|
104
|
+
assert_nil c.domain
|
|
105
|
+
assert_nil c.max_age
|
|
106
|
+
assert_nil c.expires
|
|
107
|
+
|
|
108
|
+
assert_equal false, (c.http_only || c.http_only?)
|
|
109
|
+
assert_equal false, (c.secure || c.secure?)
|
|
110
|
+
assert_equal false, c.same_site_lax?
|
|
111
|
+
assert_equal false, c.same_site_strict?
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def test_cookie_new_full_attributes
|
|
115
|
+
exp = Time.now.to_f + 7200.0
|
|
116
|
+
c = Wreq::Cookie.new("sess", "v",
|
|
117
|
+
domain: "example.com",
|
|
118
|
+
path: "/",
|
|
119
|
+
max_age: 3600,
|
|
120
|
+
expires: exp,
|
|
121
|
+
http_only: true,
|
|
122
|
+
secure: true,
|
|
123
|
+
same_site: Wreq::SameSite::Lax)
|
|
124
|
+
|
|
125
|
+
assert_equal "sess", c.name
|
|
126
|
+
assert_equal "v", c.value
|
|
127
|
+
assert_equal "example.com", c.domain
|
|
128
|
+
assert_equal "/", c.path
|
|
129
|
+
|
|
130
|
+
# Max-Age returns seconds as Integer
|
|
131
|
+
assert_equal 3600, c.max_age
|
|
132
|
+
|
|
133
|
+
# Expires returns Float seconds-since-epoch (with small tolerance)
|
|
134
|
+
assert c.expires
|
|
135
|
+
assert_kind_of Float, c.expires
|
|
136
|
+
assert_in_delta exp, c.expires, 2.0
|
|
137
|
+
|
|
138
|
+
assert_equal true, (c.http_only || c.http_only?)
|
|
139
|
+
assert_equal true, (c.secure || c.secure?)
|
|
140
|
+
# constructor currently sets SameSite to none
|
|
141
|
+
assert_equal true, c.same_site_lax?
|
|
142
|
+
assert_equal false, c.same_site_strict?
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def test_same_site_flags_from_parsed_header
|
|
146
|
+
@jar.clear
|
|
147
|
+
@jar.add_cookie_str("s1=1; Path=/; SameSite=Strict", @base_url)
|
|
148
|
+
@jar.add_cookie_str("s2=1; Path=/; SameSite=Lax", @base_url)
|
|
149
|
+
|
|
150
|
+
cookies = @jar.get_all
|
|
151
|
+
h = cookies.to_h { |ck| [ck.name, [ck.same_site_strict?, ck.same_site_lax?]] }
|
|
152
|
+
|
|
153
|
+
assert_equal [true, false], h["s1"]
|
|
154
|
+
assert_equal [false, true], h["s2"]
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def test_request_cookie_value_percent_encoding
|
|
158
|
+
raw_value = "hello world?"
|
|
159
|
+
client = Wreq::Client.new
|
|
160
|
+
resp = client.get(
|
|
161
|
+
"http://localhost:8080/cookies",
|
|
162
|
+
cookies: {"mykey" => raw_value}
|
|
163
|
+
)
|
|
164
|
+
assert_includes resp.text, "hello%20world%3F"
|
|
165
|
+
end
|
|
166
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class EmulationTest < Minitest::Test
|
|
6
|
+
def test_all_emulation_device_constants_are_non_nil
|
|
7
|
+
Wreq::EmulationDevice.constants.each do |name|
|
|
8
|
+
const = Wreq::EmulationDevice.const_get(name)
|
|
9
|
+
assert_instance_of Wreq::EmulationDevice, const,
|
|
10
|
+
"#{name} should be EmulationDevice, got #{const.inspect}"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_all_emulation_os_constants_are_non_nil
|
|
15
|
+
Wreq::EmulationOS.constants.each do |name|
|
|
16
|
+
const = Wreq::EmulationOS.const_get(name)
|
|
17
|
+
assert_instance_of Wreq::EmulationOS, const,
|
|
18
|
+
"#{name} should be EmulationOS, got #{const.inspect}"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
class ErrorHandlingTest < Minitest::Test
|
|
4
|
+
def test_network_error_handling
|
|
5
|
+
# Try to connect to a non-existent domain
|
|
6
|
+
response = Wreq.get("https://definitely-not-a-real-domain-12345.com")
|
|
7
|
+
flunk "Expected network error but got response: #{response.code}"
|
|
8
|
+
rescue => e
|
|
9
|
+
assert_instance_of Wreq::ConnectionError, e
|
|
10
|
+
# Network errors should be caught and wrapped appropriately
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_invalid_url_handling
|
|
14
|
+
# Invalid URL format
|
|
15
|
+
response = Wreq.get("not-a-valid-url")
|
|
16
|
+
flunk "Expected URL error but got response: #{response.code}"
|
|
17
|
+
rescue => e
|
|
18
|
+
assert_instance_of Wreq::BuilderError, e
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_http_error_status_codes
|
|
22
|
+
# These should not raise errors, just return responses with error codes
|
|
23
|
+
[400, 401, 403, 404, 500, 502, 503].each do |status_code|
|
|
24
|
+
response = Wreq.get("http://localhost:8080/status/#{status_code}")
|
|
25
|
+
assert_equal status_code, response.code
|
|
26
|
+
# Should not raise an exception for HTTP error codes
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_timeout_handling
|
|
31
|
+
# Test timeout with a delay that should definitely cause timeout
|
|
32
|
+
|
|
33
|
+
# Request with a very short timeout that should fail
|
|
34
|
+
response = Wreq.get("http://localhost:8080/delay/10", timeout: 1)
|
|
35
|
+
# If we get here, the request didn't timeout (unexpected)
|
|
36
|
+
flunk "Expected timeout error but got response: #{response.code}"
|
|
37
|
+
rescue => e
|
|
38
|
+
# Timeout error is expected
|
|
39
|
+
assert_instance_of Wreq::TimeoutError, e
|
|
40
|
+
# Could also check error message contains timeout-related keywords
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def test_invalid_json_response
|
|
44
|
+
# Get a non-JSON response and try to parse as JSON
|
|
45
|
+
response = Wreq.get("http://localhost:8080/html")
|
|
46
|
+
assert_equal 200, response.code
|
|
47
|
+
|
|
48
|
+
# Should raise an error when trying to parse HTML as JSON
|
|
49
|
+
begin
|
|
50
|
+
response.json
|
|
51
|
+
rescue => e
|
|
52
|
+
assert_instance_of Wreq::DecodingError, e
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def test_empty_response_json
|
|
57
|
+
response = Wreq.get("http://localhost:8080/status/204")
|
|
58
|
+
assert_equal 204, response.code
|
|
59
|
+
assert_equal "", response.text
|
|
60
|
+
|
|
61
|
+
# Empty body should raise error when parsing as JSON
|
|
62
|
+
begin
|
|
63
|
+
response.json
|
|
64
|
+
rescue => e
|
|
65
|
+
assert_instance_of Wreq::DecodingError, e
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def test_proxy_error_handling
|
|
70
|
+
invalid_proxies = [
|
|
71
|
+
"http://invalid.proxy:8080",
|
|
72
|
+
"https://invalid.proxy:8080",
|
|
73
|
+
"socks4://invalid.proxy:8080",
|
|
74
|
+
"socks4a://invalid.proxy:8080",
|
|
75
|
+
"socks5://invalid.proxy:8080",
|
|
76
|
+
"socks5h://invalid.proxy:8080"
|
|
77
|
+
]
|
|
78
|
+
target_urls = ["https://example.com", "http://example.com"]
|
|
79
|
+
|
|
80
|
+
invalid_proxies.each do |proxy|
|
|
81
|
+
target_urls.each do |url|
|
|
82
|
+
Wreq.get(url, proxy: proxy, timeout: 5)
|
|
83
|
+
flunk "Expected proxy connection error but got response"
|
|
84
|
+
rescue => e
|
|
85
|
+
assert_instance_of Wreq::ProxyConnectionError, e
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|