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.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/Cargo.toml +54 -0
  3. data/Gemfile +17 -0
  4. data/LICENSE +201 -0
  5. data/README.md +150 -0
  6. data/Rakefile +90 -0
  7. data/build.rs +9 -0
  8. data/examples/body.rb +42 -0
  9. data/examples/client.rb +33 -0
  10. data/examples/emulation_request.rb +37 -0
  11. data/examples/headers.rb +27 -0
  12. data/examples/proxy.rb +113 -0
  13. data/examples/send_stream.rb +85 -0
  14. data/examples/stream.rb +14 -0
  15. data/examples/thread_interrupt.rb +83 -0
  16. data/extconf.rb +7 -0
  17. data/lib/wreq.rb +313 -0
  18. data/lib/wreq_ruby/body.rb +36 -0
  19. data/lib/wreq_ruby/client.rb +516 -0
  20. data/lib/wreq_ruby/cookie.rb +144 -0
  21. data/lib/wreq_ruby/emulation.rb +186 -0
  22. data/lib/wreq_ruby/error.rb +159 -0
  23. data/lib/wreq_ruby/header.rb +197 -0
  24. data/lib/wreq_ruby/http.rb +132 -0
  25. data/lib/wreq_ruby/response.rb +208 -0
  26. data/script/build_platform_gem.rb +34 -0
  27. data/src/client/body/form.rs +2 -0
  28. data/src/client/body/json.rs +16 -0
  29. data/src/client/body/stream.rs +148 -0
  30. data/src/client/body.rs +57 -0
  31. data/src/client/param.rs +19 -0
  32. data/src/client/query.rs +2 -0
  33. data/src/client/req.rs +251 -0
  34. data/src/client/resp.rs +250 -0
  35. data/src/client.rs +392 -0
  36. data/src/cookie.rs +277 -0
  37. data/src/emulation.rs +317 -0
  38. data/src/error.rs +147 -0
  39. data/src/extractor.rs +199 -0
  40. data/src/gvl.rs +154 -0
  41. data/src/header.rs +177 -0
  42. data/src/http.rs +127 -0
  43. data/src/lib.rs +97 -0
  44. data/src/macros.rs +118 -0
  45. data/src/rt.rs +47 -0
  46. data/test/client_cookie_test.rb +46 -0
  47. data/test/client_test.rb +136 -0
  48. data/test/cookie_test.rb +166 -0
  49. data/test/emulation_test.rb +21 -0
  50. data/test/error_handling_test.rb +89 -0
  51. data/test/header_test.rb +290 -0
  52. data/test/module_methods_test.rb +75 -0
  53. data/test/request_parameters_test.rb +175 -0
  54. data/test/request_test.rb +234 -0
  55. data/test/response_test.rb +69 -0
  56. data/test/stream_test.rb +81 -0
  57. data/test/test_helper.rb +9 -0
  58. data/wreq.gemspec +68 -0
  59. metadata +112 -0
@@ -0,0 +1,234 @@
1
+ require "test_helper"
2
+
3
+ class WreqHttpbinTest < Minitest::Test
4
+ def setup
5
+ # Ensure we have a working client
6
+ @client = Wreq::Client.new(timeout: 30)
7
+ end
8
+
9
+ def test_module_get_method
10
+ response = Wreq.get("http://localhost:8080/get")
11
+ assert_equal 200, response.code
12
+ assert_respond_to response, :text
13
+ end
14
+
15
+ def test_module_post_with_json
16
+ data = {name: "wreq-ruby", version: "0.1.0"}
17
+ response = Wreq.post("http://localhost:8080/post", json: data)
18
+ assert_equal 200, response.code
19
+ end
20
+
21
+ def test_client_instance_basic
22
+ response = @client.get("http://localhost:8080/get")
23
+ assert_equal 200, response.code
24
+ end
25
+
26
+ def test_client_with_custom_headers
27
+ headers = {"User-Agent" => "wreq-ruby/test", "Accept" => "application/json"}
28
+ response = @client.get("http://localhost:8080/headers", headers: headers)
29
+ assert_equal 200, response.code
30
+ end
31
+
32
+ def test_all_http_methods_client
33
+ methods = [
34
+ Wreq::Method::GET,
35
+ Wreq::Method::POST,
36
+ Wreq::Method::PUT,
37
+ Wreq::Method::DELETE,
38
+ Wreq::Method::PATCH,
39
+ Wreq::Method::HEAD,
40
+ Wreq::Method::OPTIONS
41
+ ]
42
+
43
+ methods.each do |method|
44
+ response = @client.request(method, "http://localhost:8080/#{method}")
45
+ assert [200, 405].include?(response.code), "client.#{method} failed with status #{response.code}"
46
+ end
47
+ end
48
+
49
+ def test_request_with_query_params
50
+ params = {"param1" => "value1", "param2" => "value2", "param3" => 123, "param4" => true}
51
+ response = Wreq.get("http://localhost:8080/get", query: params)
52
+ assert_equal 200, response.code
53
+ assert_includes response.text, "param1=value1"
54
+ assert_includes response.text, "param2=value2"
55
+ assert_includes response.text, "param3=123"
56
+ assert_includes response.text, "param4=true"
57
+ end
58
+
59
+ def test_post_with_form_data
60
+ data = {"field1" => "value1", "field2" => "value2", "field3" => 456, "field4" => false}
61
+ response = Wreq.post("http://localhost:8080/post", form: data)
62
+ assert_equal 200, response.code
63
+ assert_includes response.text, "field1"
64
+ assert_includes response.text, "value1"
65
+ assert_includes response.text, "field2"
66
+ assert_includes response.text, "value2"
67
+ assert_includes response.text, "field3"
68
+ assert_includes response.text, "456"
69
+ assert_includes response.text, "field4"
70
+ assert_includes response.text, "false"
71
+ end
72
+
73
+ def test_post_with_raw_body
74
+ body = "This is raw body content"
75
+ headers = {"Content-Type" => "text/plain"}
76
+ response = Wreq.post("http://localhost:8080/post", body: body, headers: headers)
77
+ assert_equal 200, response.code
78
+ assert_includes response.text, body
79
+ end
80
+
81
+ def test_basic_authentication
82
+ response = Wreq.get("http://localhost:8080/basic-auth/user/pass", basic_auth: ["user", "pass"])
83
+ assert_equal 200, response.code
84
+ end
85
+
86
+ def test_bearer_authentication
87
+ response = Wreq.get("http://localhost:8080/bearer", bearer_auth: "test-token")
88
+ assert_equal 200, response.code
89
+ end
90
+
91
+ def test_redirect_following
92
+ response = Wreq.get("http://localhost:8080/redirect/2", allow_redirects: true)
93
+ assert_equal 200, response.code
94
+ end
95
+
96
+ def test_redirect_blocking
97
+ response = Wreq.get("http://localhost:8080/redirect/1", allow_redirects: false)
98
+ assert_equal 302, response.code
99
+ end
100
+
101
+ def test_response_status_methods
102
+ response = Wreq.get("http://localhost:8080/status/200")
103
+ assert_equal 200, response.code
104
+
105
+ if response.status.respond_to?(:success?)
106
+ assert response.status.success?
107
+ end
108
+ end
109
+
110
+ def test_404_response
111
+ response = Wreq.get("http://localhost:8080/status/404")
112
+ assert_equal 404, response.code
113
+
114
+ if response.status.respond_to?(:client_error?)
115
+ assert response.status.client_error?
116
+ end
117
+ end
118
+
119
+ def test_json_response_parsing
120
+ response = Wreq.get("http://localhost:8080/json")
121
+ assert_equal 200, response.code
122
+
123
+ if response.respond_to?(:json)
124
+ json_data = response.json
125
+ assert json_data.is_a?(Hash)
126
+ end
127
+ end
128
+
129
+ def test_gzip_compression
130
+ response = Wreq.get("http://localhost:8080/gzip", gzip: true)
131
+ assert_equal 200, response.code
132
+ end
133
+
134
+ def test_headers_iteration
135
+ response = Wreq.get("http://localhost:8080/get")
136
+
137
+ if response.respond_to?(:each_header)
138
+ count = 0
139
+ response.each_header do |name, value|
140
+ assert name.is_a?(String)
141
+ assert value.is_a?(String)
142
+ count += 1
143
+ break if count >= 5 # Just test first few
144
+ end
145
+ assert count > 0, "Should iterate over at least one header"
146
+ end
147
+ end
148
+
149
+ def test_response_properties
150
+ response = Wreq.get("http://localhost:8080/get")
151
+
152
+ assert_respond_to response, :code
153
+ assert_respond_to response, :status
154
+ assert_respond_to response, :version
155
+ assert_respond_to response, :url
156
+
157
+ assert response.code.is_a?(Integer)
158
+ assert response.url.is_a?(String)
159
+ end
160
+
161
+ def test_timeout_functionality
162
+ # Test that short timeouts properly raise exceptions
163
+ assert_raises(Wreq::TimeoutError) do
164
+ Wreq.get("http://localhost:8080/delay/10", timeout: 1)
165
+ end
166
+
167
+ # Test that reasonable timeouts work normally
168
+ start_time = Time.now
169
+ response = Wreq.get("http://localhost:8080/delay/1", timeout: 5)
170
+ elapsed = Time.now - start_time
171
+
172
+ assert_equal 200, response.code
173
+ assert elapsed < 5, "Request should complete within timeout"
174
+ end
175
+
176
+ def test_multiple_requests_performance
177
+ start_time = Time.now
178
+
179
+ 3.times do |i|
180
+ response = Wreq.get("http://localhost:8080/get?request=#{i}")
181
+ assert_equal 200, response.code
182
+ end
183
+
184
+ elapsed = Time.now - start_time
185
+ assert elapsed < 30, "3 requests should complete within 30 seconds"
186
+ end
187
+
188
+ def test_error_handling
189
+ assert_raises do
190
+ Wreq.get("https://this-domain-does-not-exist-12345.com")
191
+ end
192
+ end
193
+
194
+ def test_client_configuration
195
+ client = Wreq::Client.new(
196
+ timeout: 10,
197
+ headers: {"User-Agent" => "wreq-ruby-test"},
198
+ allow_redirects: false
199
+ )
200
+
201
+ # Test that client was created successfully
202
+ assert_instance_of Wreq::Client, client
203
+
204
+ # Test that it can make requests
205
+ response = client.get("http://localhost:8080/get")
206
+ assert_equal 200, response.code
207
+ end
208
+
209
+ def test_enum_constants
210
+ # Test Method enum
211
+ assert_const_defined "Wreq::Method"
212
+ if defined?(Wreq::Method)
213
+ %w[GET POST PUT DELETE HEAD PATCH OPTIONS].each do |method|
214
+ assert Wreq::Method.const_defined?(method), "Method::#{method} should be defined"
215
+ end
216
+ end
217
+
218
+ # Test Version enum
219
+ assert_const_defined "Wreq::Version"
220
+ if defined?(Wreq::Version)
221
+ %w[HTTP_11 HTTP_2].each do |version|
222
+ assert Wreq::Version.const_defined?(version), "Version::#{version} should be defined"
223
+ end
224
+ end
225
+ end
226
+
227
+ private
228
+
229
+ def assert_const_defined(const_name)
230
+ const_name.split("::").inject(Object) { |o, c| o.const_get(c) }
231
+ rescue NameError
232
+ flunk "#{const_name} should be defined"
233
+ end
234
+ end
@@ -0,0 +1,69 @@
1
+ require "test_helper"
2
+
3
+ class ResponseTest < Minitest::Test
4
+ def setup
5
+ @response = Wreq.get("http://localhost:8080/json")
6
+ end
7
+
8
+ def test_response_code
9
+ assert_respond_to @response, :code
10
+ assert_instance_of Integer, @response.code
11
+ assert_equal 200, @response.code
12
+ end
13
+
14
+ def test_response_text
15
+ assert_respond_to @response, :text
16
+ assert_instance_of String, @response.text
17
+ assert @response.text.length > 0
18
+ end
19
+
20
+ def test_response_json
21
+ assert_respond_to @response, :json
22
+ json_data = @response.json
23
+ assert_instance_of Hash, json_data
24
+
25
+ # httpbin.io/json returns a specific JSON structure
26
+ assert json_data.key?("slideshow")
27
+ end
28
+
29
+ def test_response_each_header
30
+ assert_respond_to @response, :headers
31
+
32
+ header_count = 0
33
+ @response.headers.each do |name, value|
34
+ assert_instance_of String, name
35
+ assert_instance_of String, value
36
+ header_count += 1
37
+ end
38
+ assert header_count > 0
39
+ end
40
+
41
+ def test_response_with_non_json_content
42
+ response = Wreq.get("http://localhost:8080/html")
43
+ assert_equal 200, response.code
44
+ assert_instance_of String, response.text
45
+ assert response.text.include?("<html>")
46
+
47
+ # JSON parsing should fail for HTML content
48
+ assert_raises(StandardError) { response.json }
49
+ end
50
+
51
+ def test_response_status_codes
52
+ # Test different status codes
53
+ [200, 404, 500].each do |status|
54
+ response = Wreq.get("http://localhost:8080/status/#{status}")
55
+ assert_equal status, response.code
56
+ end
57
+ end
58
+
59
+ def test_response_with_query_parameters
60
+ response = Wreq.get("http://localhost:8080/get",
61
+ query: {"param1" => "value1", "param2" => "value2"})
62
+ assert_equal 200, response.code
63
+
64
+ json_data = response.json
65
+ args = json_data["args"]
66
+ assert_equal "value1", args["param1"]
67
+ assert_equal "value2", args["param2"]
68
+ end
69
+ end
@@ -0,0 +1,81 @@
1
+ require "test_helper"
2
+
3
+ class StreamTest < Minitest::Test
4
+ def test_simple_push_stream
5
+ client = Wreq::Client.new
6
+ sender = Wreq::BodySender.new(4)
7
+ producer = Thread.new do
8
+ 3.times { |i| sender.push("chunk-#{i}\n") }
9
+ sender.close
10
+ end
11
+ resp = client.post("http://localhost:8080/post", body: sender, headers: {"Content-Type" => "text/plain"})
12
+ assert_equal 200, resp.code
13
+ echoed = resp.json["data"]
14
+ assert_includes echoed, "chunk-0"
15
+ assert_includes echoed, "chunk-1"
16
+ assert_includes echoed, "chunk-2"
17
+ producer.join
18
+ end
19
+
20
+ def test_response_body_chunks_stream
21
+ client = Wreq::Client.new
22
+ resp = client.get("http://localhost:8080/stream/5")
23
+ chunks = []
24
+ resp.chunks do |chunk|
25
+ chunks << chunk
26
+ assert_kind_of String, chunk
27
+ assert_match(/\{.*\}/, chunk)
28
+ end
29
+ assert_equal 5, chunks.size
30
+ end
31
+
32
+ def test_thread_interrupt_connect
33
+ url = "http://10.255.255.1:12345/"
34
+ thread = Thread.new do
35
+ Wreq.get(url)
36
+ rescue => _
37
+ end
38
+ sleep 2
39
+ thread.kill
40
+ killed = thread.join(5)
41
+ assert killed, "Connect phase should be interruptible"
42
+ end
43
+
44
+ def test_thread_interrupt_connect_with_timeout
45
+ url = "http://10.255.255.1:12345/"
46
+ thread = Thread.new do
47
+ Wreq.get(url, timeout: 60)
48
+ rescue => _
49
+ end
50
+ sleep 2
51
+ thread.kill
52
+ killed = thread.join(5)
53
+ assert killed, "Connect+timeout phase should be interruptible"
54
+ end
55
+
56
+ def test_thread_interrupt_body_reading
57
+ url = "http://localhost:8080/drip?duration=5&numbytes=5"
58
+ thread = Thread.new do
59
+ resp = Wreq.get(url)
60
+ resp.text
61
+ rescue => _
62
+ end
63
+ sleep 2
64
+ thread.kill
65
+ killed = thread.join(5)
66
+ assert killed, "Body reading should be interruptible"
67
+ end
68
+
69
+ def test_thread_interrupt_body_streaming
70
+ url = "http://localhost:8080/drip?duration=5&numbytes=5"
71
+ thread = Thread.new do
72
+ resp = Wreq.get(url)
73
+ resp.chunks { |chunk| chunk }
74
+ rescue => _
75
+ end
76
+ sleep 2
77
+ thread.kill
78
+ killed = thread.join(5)
79
+ assert killed, "Body streaming should be interruptible"
80
+ end
81
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Run httpbin server: `docker run -d -p 8080:80 --name httpbin kennethreitz/httpbin`
5
+
6
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
7
+ require "wreq"
8
+
9
+ require "minitest/autorun"
data/wreq.gemspec ADDED
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "wreq"
7
+ # RubyGems integrates and expects `cargo`.
8
+ # Read more about
9
+ # [Gem::Ext::CargoBuilder](https://github.com/rubygems/rubygems/blob/v3.5.23/lib/rubygems/ext/cargo_builder.rb)
10
+ #
11
+ # wreq relies on "version" in `Cargo.toml` for the release process. You can read this gem spec with:
12
+ # `bundle exec ruby -e 'puts Gem::Specification.load("wreq.gemspec")'`
13
+ #
14
+ # keep in sync the key "wreq-ruby" with `Rakefile`.
15
+ #
16
+ # uses `cargo` to extract the version.
17
+ cargo_output = `cargo metadata --format-version 1`
18
+ cargo_output.force_encoding("UTF-8")
19
+ spec.version = JSON.parse(cargo_output.strip)
20
+ .fetch("packages")
21
+ .find { |p| p["name"] == "wreq-ruby" }
22
+ .fetch("version")
23
+ spec.authors = ["SearchApi"]
24
+ spec.email = ["support@searchapi.io"]
25
+ spec.summary = "Ruby bindings for wreq, an HTTP client with TLS/HTTP2 browser fingerprinting"
26
+ spec.description = "An easy and powerful Ruby HTTP client with advanced browser fingerprinting " \
27
+ "that accurately emulates Chrome, Edge, Firefox, Safari, Opera, and OkHttp " \
28
+ "with precise TLS/HTTP2 signatures. Powered by wreq (Rust) and BoringSSL."
29
+ spec.homepage = "https://github.com/SearchApi/wreq-ruby"
30
+ spec.license = "Apache-2.0"
31
+ spec.metadata = {
32
+ "bug_tracker_uri" => "https://github.com/SearchApi/wreq-ruby/issues",
33
+ "changelog_uri" => "https://github.com/SearchApi/wreq-ruby/releases",
34
+ "documentation_uri" => "https://github.com/SearchApi/wreq-ruby#readme",
35
+ "homepage_uri" => spec.homepage,
36
+ "source_code_uri" => "https://github.com/SearchApi/wreq-ruby",
37
+ "rubygems_mfa_required" => "true"
38
+ }
39
+
40
+ # Specify which files should be added to a source release gem when we release wreq Ruby gem.
41
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
42
+ spec.files = Dir.chdir(__dir__) do
43
+ git_output = `git ls-files -z`
44
+ git_output.force_encoding("UTF-8")
45
+ git_output.split("\x0").reject do |f|
46
+ f.start_with?(*%w[gems/ pkg/ target/ tmp/ .git]) ||
47
+ f.match?(/\.gem$/) || # Exclude gem files
48
+ f.match?(/^wreq-.*\.gem$/) # Exclude any wreq gem files
49
+ end
50
+ end
51
+
52
+ spec.require_paths = ["lib"]
53
+
54
+ spec.extensions = ["./extconf.rb"]
55
+
56
+ # Exclude non-Ruby files from RDoc to prevent parsing errors
57
+ spec.rdoc_options = ["--exclude", "Cargo\\..*", "--exclude", "\\.rs$"]
58
+
59
+ spec.requirements = ["Rust >= 1.85"]
60
+ # use a Ruby version which:
61
+ # - supports Rubygems with the ability of compilation of Rust gem
62
+ # - not end of life
63
+ #
64
+ # keep in sync with `Rakefile`.
65
+ spec.required_ruby_version = ">= 3.3"
66
+
67
+ # intentionally skipping rb_sys gem because newer Rubygems will be present
68
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wreq
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - SearchApi
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: An easy and powerful Ruby HTTP client with advanced browser fingerprinting
13
+ that accurately emulates Chrome, Edge, Firefox, Safari, Opera, and OkHttp with precise
14
+ TLS/HTTP2 signatures. Powered by wreq (Rust) and BoringSSL.
15
+ email:
16
+ - support@searchapi.io
17
+ executables: []
18
+ extensions:
19
+ - "./extconf.rb"
20
+ extra_rdoc_files: []
21
+ files:
22
+ - "./extconf.rb"
23
+ - Cargo.toml
24
+ - Gemfile
25
+ - LICENSE
26
+ - README.md
27
+ - Rakefile
28
+ - build.rs
29
+ - examples/body.rb
30
+ - examples/client.rb
31
+ - examples/emulation_request.rb
32
+ - examples/headers.rb
33
+ - examples/proxy.rb
34
+ - examples/send_stream.rb
35
+ - examples/stream.rb
36
+ - examples/thread_interrupt.rb
37
+ - extconf.rb
38
+ - lib/wreq.rb
39
+ - lib/wreq_ruby/body.rb
40
+ - lib/wreq_ruby/client.rb
41
+ - lib/wreq_ruby/cookie.rb
42
+ - lib/wreq_ruby/emulation.rb
43
+ - lib/wreq_ruby/error.rb
44
+ - lib/wreq_ruby/header.rb
45
+ - lib/wreq_ruby/http.rb
46
+ - lib/wreq_ruby/response.rb
47
+ - script/build_platform_gem.rb
48
+ - src/client.rs
49
+ - src/client/body.rs
50
+ - src/client/body/form.rs
51
+ - src/client/body/json.rs
52
+ - src/client/body/stream.rs
53
+ - src/client/param.rs
54
+ - src/client/query.rs
55
+ - src/client/req.rs
56
+ - src/client/resp.rs
57
+ - src/cookie.rs
58
+ - src/emulation.rs
59
+ - src/error.rs
60
+ - src/extractor.rs
61
+ - src/gvl.rs
62
+ - src/header.rs
63
+ - src/http.rs
64
+ - src/lib.rs
65
+ - src/macros.rs
66
+ - src/rt.rs
67
+ - test/client_cookie_test.rb
68
+ - test/client_test.rb
69
+ - test/cookie_test.rb
70
+ - test/emulation_test.rb
71
+ - test/error_handling_test.rb
72
+ - test/header_test.rb
73
+ - test/module_methods_test.rb
74
+ - test/request_parameters_test.rb
75
+ - test/request_test.rb
76
+ - test/response_test.rb
77
+ - test/stream_test.rb
78
+ - test/test_helper.rb
79
+ - wreq.gemspec
80
+ homepage: https://github.com/SearchApi/wreq-ruby
81
+ licenses:
82
+ - Apache-2.0
83
+ metadata:
84
+ bug_tracker_uri: https://github.com/SearchApi/wreq-ruby/issues
85
+ changelog_uri: https://github.com/SearchApi/wreq-ruby/releases
86
+ documentation_uri: https://github.com/SearchApi/wreq-ruby#readme
87
+ homepage_uri: https://github.com/SearchApi/wreq-ruby
88
+ source_code_uri: https://github.com/SearchApi/wreq-ruby
89
+ rubygems_mfa_required: 'true'
90
+ rdoc_options:
91
+ - "--exclude"
92
+ - Cargo\..*
93
+ - "--exclude"
94
+ - "\\.rs$"
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '3.3'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements:
108
+ - Rust >= 1.85
109
+ rubygems_version: 3.6.9
110
+ specification_version: 4
111
+ summary: Ruby bindings for wreq, an HTTP client with TLS/HTTP2 browser fingerprinting
112
+ test_files: []