wreq 1.2.3-x64-mingw-ucrt
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.lock +1687 -0
- data/Cargo.toml +54 -0
- data/Gemfile +18 -0
- data/LICENSE +201 -0
- data/README.md +153 -0
- data/Rakefile +90 -0
- data/build.rs +16 -0
- data/docs/windows-gnu-tokio-crash.md +70 -0
- data/examples/body.rb +42 -0
- data/examples/client.rb +33 -0
- data/examples/cookie.rb +24 -0
- data/examples/emulate_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 +303 -0
- data/lib/wreq_ruby/3.3/wreq_ruby.so +0 -0
- data/lib/wreq_ruby/3.4/wreq_ruby.so +0 -0
- data/lib/wreq_ruby/4.0/wreq_ruby.so +0 -0
- data/lib/wreq_ruby/body.rb +36 -0
- data/lib/wreq_ruby/client.rb +526 -0
- data/lib/wreq_ruby/cookie.rb +156 -0
- data/lib/wreq_ruby/emulate.rb +232 -0
- data/lib/wreq_ruby/error.rb +157 -0
- data/lib/wreq_ruby/header.rb +205 -0
- data/lib/wreq_ruby/http.rb +160 -0
- data/lib/wreq_ruby/response.rb +209 -0
- data/script/build_platform_gem.rb +34 -0
- data/script/build_windows_gnu.ps1 +257 -0
- data/src/arch.rs +33 -0
- data/src/client/body/form.rs +2 -0
- data/src/client/body/json.rs +16 -0
- data/src/client/body/stream.rs +146 -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 +274 -0
- data/src/client/resp.rs +248 -0
- data/src/client.rs +413 -0
- data/src/cookie.rs +312 -0
- data/src/emulate.rs +376 -0
- data/src/error.rs +163 -0
- data/src/extractor.rs +117 -0
- data/src/gvl.rs +154 -0
- data/src/header.rs +245 -0
- data/src/http.rs +142 -0
- data/src/lib.rs +98 -0
- data/src/macros.rs +123 -0
- data/src/rt.rs +46 -0
- data/test/client_cookie_test.rb +46 -0
- data/test/client_test.rb +136 -0
- data/test/cookie_test.rb +182 -0
- data/test/emulation_test.rb +21 -0
- data/test/error_handling_test.rb +92 -0
- data/test/header_test.rb +290 -0
- data/test/inspect_test.rb +125 -0
- data/test/module_methods_test.rb +75 -0
- data/test/orig_header_test.rb +115 -0
- data/test/request_parameters_test.rb +175 -0
- data/test/request_test.rb +244 -0
- data/test/response_test.rb +69 -0
- data/test/stream_test.rb +397 -0
- data/test/test_helper.rb +52 -0
- data/wreq.gemspec +68 -0
- metadata +123 -0
data/test/stream_test.rb
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
require "socket"
|
|
3
|
+
|
|
4
|
+
class StreamTest < Minitest::Test
|
|
5
|
+
def test_simple_push_stream
|
|
6
|
+
client = Wreq::Client.new
|
|
7
|
+
sender = Wreq::BodySender.new(4)
|
|
8
|
+
producer = Thread.new do
|
|
9
|
+
3.times { |i| sender.push("chunk-#{i}\n") }
|
|
10
|
+
sender.close
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
resp = client.post("#{HTTPBIN_URL}/post", body: sender, headers: {"Content-Type" => "text/plain"})
|
|
14
|
+
|
|
15
|
+
assert_equal 200, resp.code
|
|
16
|
+
|
|
17
|
+
echoed = resp.json["data"]
|
|
18
|
+
assert_includes echoed, "chunk-0"
|
|
19
|
+
assert_includes echoed, "chunk-1"
|
|
20
|
+
assert_includes echoed, "chunk-2"
|
|
21
|
+
|
|
22
|
+
producer.join
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def test_response_body_chunks_stream
|
|
26
|
+
client = Wreq::Client.new
|
|
27
|
+
resp = client.get("#{HTTPBIN_URL}/stream/5")
|
|
28
|
+
chunks = []
|
|
29
|
+
|
|
30
|
+
resp.chunks do |chunk|
|
|
31
|
+
chunks << chunk
|
|
32
|
+
assert_kind_of String, chunk, "Each yielded chunk must be a String"
|
|
33
|
+
assert_match(/\{.*\}/, chunk)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
assert_equal 5, chunks.size, "Should yield exactly 5 chunks from /stream/5"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def test_chunks_yields_binary_encoding
|
|
40
|
+
client = Wreq::Client.new
|
|
41
|
+
resp = client.get("#{HTTPBIN_URL}/stream/3")
|
|
42
|
+
|
|
43
|
+
resp.chunks do |chunk|
|
|
44
|
+
assert chunk.encoding == Encoding::BINARY || chunk.encoding == Encoding::ASCII_8BIT,
|
|
45
|
+
"Chunk should have binary encoding, got #{chunk.encoding}"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def test_chunks_with_single_chunk_body
|
|
50
|
+
client = Wreq::Client.new
|
|
51
|
+
resp = client.get("#{HTTPBIN_URL}/bytes/1024")
|
|
52
|
+
chunk_count = 0
|
|
53
|
+
total_bytes = 0
|
|
54
|
+
|
|
55
|
+
resp.chunks do |chunk|
|
|
56
|
+
chunk_count += 1
|
|
57
|
+
total_bytes += chunk.bytesize
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
assert chunk_count >= 1, "Should yield at least one chunk"
|
|
61
|
+
assert_equal 1024, total_bytes, "Total bytes should match content length"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def test_chunks_returns_nil
|
|
65
|
+
client = Wreq::Client.new
|
|
66
|
+
resp = client.get("#{HTTPBIN_URL}/stream/3")
|
|
67
|
+
|
|
68
|
+
result = resp.chunks { |_chunk| :processing }
|
|
69
|
+
|
|
70
|
+
assert_nil result, "chunks should return nil after completion"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def test_chunks_with_empty_body
|
|
74
|
+
client = Wreq::Client.new
|
|
75
|
+
resp = client.get("#{HTTPBIN_URL}/status/204")
|
|
76
|
+
chunk_count = 0
|
|
77
|
+
|
|
78
|
+
resp.chunks do |_chunk|
|
|
79
|
+
chunk_count += 1
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
assert_equal 0, chunk_count, "No chunks should be yielded for empty 204 response"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def test_chunks_without_block_raises_error
|
|
86
|
+
client = Wreq::Client.new
|
|
87
|
+
resp = client.get("#{HTTPBIN_URL}/stream/3")
|
|
88
|
+
|
|
89
|
+
assert_raises(LocalJumpError) do
|
|
90
|
+
resp.chunks
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def test_other_threads_run_during_streaming
|
|
95
|
+
client = Wreq::Client.new
|
|
96
|
+
resp = client.get("#{HTTPBIN_URL}/drip?duration=3&numbytes=3&delay=1")
|
|
97
|
+
|
|
98
|
+
counter = 0
|
|
99
|
+
tick_thread = Thread.new do
|
|
100
|
+
30.times do
|
|
101
|
+
counter += 1
|
|
102
|
+
sleep 0.1
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
chunks_received = 0
|
|
107
|
+
resp.chunks do |_chunk|
|
|
108
|
+
chunks_received += 1
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
tick_thread.join(10)
|
|
112
|
+
|
|
113
|
+
assert counter > 5,
|
|
114
|
+
"Counter only reached #{counter} - other threads may not be running during streaming. " \
|
|
115
|
+
"GVL should be released during I/O waits."
|
|
116
|
+
assert chunks_received >= 1, "Should have received at least one chunk"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def test_multiple_concurrent_streams
|
|
120
|
+
client = Wreq::Client.new
|
|
121
|
+
results = {}
|
|
122
|
+
done = {}
|
|
123
|
+
|
|
124
|
+
t1 = Thread.new do
|
|
125
|
+
resp = client.get("#{HTTPBIN_URL}/stream/3")
|
|
126
|
+
chunks = []
|
|
127
|
+
resp.chunks { |chunk| chunks << chunk }
|
|
128
|
+
results[:t1] = chunks.size
|
|
129
|
+
done[:t1] = true
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
t2 = Thread.new do
|
|
133
|
+
resp = client.get("#{HTTPBIN_URL}/stream/3")
|
|
134
|
+
chunks = []
|
|
135
|
+
resp.chunks { |chunk| chunks << chunk }
|
|
136
|
+
results[:t2] = chunks.size
|
|
137
|
+
done[:t2] = true
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
t1.join(15)
|
|
141
|
+
t2.join(15)
|
|
142
|
+
|
|
143
|
+
assert done[:t1], "Thread 1 should complete"
|
|
144
|
+
assert done[:t2], "Thread 2 should complete"
|
|
145
|
+
assert_equal 3, results[:t1], "Thread 1 should receive 3 chunks"
|
|
146
|
+
assert_equal 3, results[:t2], "Thread 2 should receive 3 chunks"
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def test_thread_interrupt_connect
|
|
150
|
+
url = "http://10.255.255.1:12345/"
|
|
151
|
+
thread = Thread.new do
|
|
152
|
+
Wreq.get(url)
|
|
153
|
+
rescue => _
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
sleep 2
|
|
157
|
+
thread.kill
|
|
158
|
+
killed = thread.join(5)
|
|
159
|
+
|
|
160
|
+
assert killed, "Connect phase should be interruptible"
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def test_thread_interrupt_connect_with_timeout
|
|
164
|
+
url = "http://10.255.255.1:12345/"
|
|
165
|
+
thread = Thread.new do
|
|
166
|
+
Wreq.get(url, timeout: 60)
|
|
167
|
+
rescue => _
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
sleep 2
|
|
171
|
+
thread.kill
|
|
172
|
+
killed = thread.join(5)
|
|
173
|
+
|
|
174
|
+
assert killed, "Connect+timeout phase should be interruptible"
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def test_thread_interrupt_body_reading
|
|
178
|
+
url = "#{HTTPBIN_URL}/drip?duration=5&numbytes=5"
|
|
179
|
+
thread = Thread.new do
|
|
180
|
+
resp = Wreq.get(url)
|
|
181
|
+
resp.text
|
|
182
|
+
rescue => _
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
sleep 2
|
|
186
|
+
thread.kill
|
|
187
|
+
killed = thread.join(5)
|
|
188
|
+
|
|
189
|
+
assert killed, "Body reading should be interruptible"
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def test_thread_interrupt_body_streaming
|
|
193
|
+
url = "#{HTTPBIN_URL}/drip?duration=5&numbytes=5"
|
|
194
|
+
thread = Thread.new do
|
|
195
|
+
resp = Wreq.get(url)
|
|
196
|
+
resp.chunks { |chunk| chunk }
|
|
197
|
+
rescue => _
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
sleep 2
|
|
201
|
+
thread.kill
|
|
202
|
+
killed = thread.join(5)
|
|
203
|
+
|
|
204
|
+
assert killed, "Body streaming should be interruptible"
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def test_thread_interrupt_during_slow_stream_with_block_processing
|
|
208
|
+
url = "#{HTTPBIN_URL}/drip?duration=5&numbytes=5&delay=1"
|
|
209
|
+
thread = Thread.new do
|
|
210
|
+
resp = Wreq.get(url)
|
|
211
|
+
resp.chunks do |_chunk|
|
|
212
|
+
sleep 0.5
|
|
213
|
+
end
|
|
214
|
+
rescue => _
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
sleep 2
|
|
218
|
+
thread.kill
|
|
219
|
+
killed = thread.join(5)
|
|
220
|
+
|
|
221
|
+
assert killed, "Streaming with slow block processing should be interruptible"
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def test_chunks_propagates_streaming_errors
|
|
225
|
+
client = Wreq::Client.new
|
|
226
|
+
|
|
227
|
+
with_truncated_chunked_response do |url|
|
|
228
|
+
resp = client.get(url)
|
|
229
|
+
error = assert_raises(Wreq::BodyError, Wreq::ConnectionResetError, Wreq::DecodingError) do
|
|
230
|
+
resp.chunks do |_chunk|
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
assert_match(/body|connection|decode|incomplete|closed|unexpected/i, error.message)
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def test_exception_in_block_propagates
|
|
239
|
+
client = Wreq::Client.new
|
|
240
|
+
resp = client.get("#{HTTPBIN_URL}/stream/5")
|
|
241
|
+
error_raised = false
|
|
242
|
+
chunks_before_error = 0
|
|
243
|
+
|
|
244
|
+
begin
|
|
245
|
+
resp.chunks do |_chunk|
|
|
246
|
+
chunks_before_error += 1
|
|
247
|
+
raise "intentional error in block" if chunks_before_error == 2
|
|
248
|
+
end
|
|
249
|
+
rescue RuntimeError => e
|
|
250
|
+
error_raised = true
|
|
251
|
+
assert_equal "intentional error in block", e.message
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
assert error_raised, "Exception raised inside the block should propagate out"
|
|
255
|
+
assert_equal 2, chunks_before_error, "Should have processed 2 chunks before the error"
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def test_chunks_called_twice_raises_error
|
|
259
|
+
client = Wreq::Client.new
|
|
260
|
+
resp = client.get("#{HTTPBIN_URL}/stream/3")
|
|
261
|
+
resp.chunks { |_chunk| }
|
|
262
|
+
error_raised = false
|
|
263
|
+
|
|
264
|
+
begin
|
|
265
|
+
resp.chunks { |_chunk| }
|
|
266
|
+
rescue => e
|
|
267
|
+
error_raised = true
|
|
268
|
+
assert_instance_of Wreq::MemoryError, e,
|
|
269
|
+
"Second chunks call should raise MemoryError, got #{e.class}: #{e.message}"
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
assert error_raised, "Second chunks call should raise an error"
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def test_text_after_chunks_raises_error
|
|
276
|
+
client = Wreq::Client.new
|
|
277
|
+
resp = client.get("#{HTTPBIN_URL}/stream/3")
|
|
278
|
+
resp.chunks { |_chunk| }
|
|
279
|
+
error_raised = false
|
|
280
|
+
|
|
281
|
+
begin
|
|
282
|
+
resp.text
|
|
283
|
+
rescue => e
|
|
284
|
+
error_raised = true
|
|
285
|
+
assert_instance_of Wreq::MemoryError, e,
|
|
286
|
+
"Calling text after chunks should raise MemoryError, got #{e.class}: #{e.message}"
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
assert error_raised, "Calling text after chunks should raise an error"
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def test_chunks_content_matches_full_body
|
|
293
|
+
client = Wreq::Client.new
|
|
294
|
+
resp_full = client.get("#{HTTPBIN_URL}/bytes/4096")
|
|
295
|
+
full_bytes = resp_full.bytes
|
|
296
|
+
|
|
297
|
+
resp_stream = client.get("#{HTTPBIN_URL}/bytes/4096")
|
|
298
|
+
streamed_bytes = "".b
|
|
299
|
+
resp_stream.chunks do |chunk|
|
|
300
|
+
streamed_bytes << chunk
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
assert_equal full_bytes.bytesize, streamed_bytes.bytesize,
|
|
304
|
+
"Streamed body size should match full body size"
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def test_chunks_json_stream_content
|
|
308
|
+
client = Wreq::Client.new
|
|
309
|
+
resp = client.get("#{HTTPBIN_URL}/stream/5")
|
|
310
|
+
chunks = []
|
|
311
|
+
|
|
312
|
+
resp.chunks do |chunk|
|
|
313
|
+
chunks << chunk
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
chunks.each_with_index do |chunk, i|
|
|
317
|
+
assert_match(/\{.*\}/, chunk,
|
|
318
|
+
"Chunk #{i} should contain a JSON object, got: #{chunk[0..80]}")
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def test_block_not_garbage_collected_during_streaming
|
|
323
|
+
client = Wreq::Client.new
|
|
324
|
+
resp = client.get("#{HTTPBIN_URL}/stream/3")
|
|
325
|
+
chunks_received = 0
|
|
326
|
+
|
|
327
|
+
resp.chunks do |_chunk|
|
|
328
|
+
chunks_received += 1
|
|
329
|
+
GC.start
|
|
330
|
+
GC.start
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
assert_equal 3, chunks_received,
|
|
334
|
+
"All 3 chunks should be received even with forced GC between yields"
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def test_close_after_streaming
|
|
338
|
+
client = Wreq::Client.new
|
|
339
|
+
resp = client.get("#{HTTPBIN_URL}/stream/3")
|
|
340
|
+
|
|
341
|
+
resp.chunks { |_chunk| }
|
|
342
|
+
resp.close
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def test_chunks_via_module_method
|
|
346
|
+
resp = Wreq.get("#{HTTPBIN_URL}/stream/3")
|
|
347
|
+
chunks = []
|
|
348
|
+
|
|
349
|
+
resp.chunks do |chunk|
|
|
350
|
+
chunks << chunk
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
assert_equal 3, chunks.size, "Module-level Wreq.get + chunks should work"
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def test_chunks_via_client_instance
|
|
357
|
+
client = Wreq::Client.new
|
|
358
|
+
resp = client.get("#{HTTPBIN_URL}/stream/3")
|
|
359
|
+
chunks = []
|
|
360
|
+
|
|
361
|
+
resp.chunks do |chunk|
|
|
362
|
+
chunks << chunk
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
assert_equal 3, chunks.size, "Client instance get + chunks should work"
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
private
|
|
369
|
+
|
|
370
|
+
def with_truncated_chunked_response
|
|
371
|
+
server = TCPServer.new("127.0.0.1", 0)
|
|
372
|
+
port = server.addr[1]
|
|
373
|
+
thread = Thread.new do
|
|
374
|
+
socket = server.accept
|
|
375
|
+
begin
|
|
376
|
+
socket.readpartial(4096) if IO.select([socket], nil, nil, 5)
|
|
377
|
+
socket.write "HTTP/1.1 200 OK\r\n"
|
|
378
|
+
socket.write "Content-Type: text/plain\r\n"
|
|
379
|
+
socket.write "Transfer-Encoding: chunked\r\n"
|
|
380
|
+
socket.write "\r\n"
|
|
381
|
+
socket.write "5\r\nhello\r\n"
|
|
382
|
+
socket.write "A\r\nshort"
|
|
383
|
+
ensure
|
|
384
|
+
socket.close unless socket.closed?
|
|
385
|
+
end
|
|
386
|
+
rescue IOError, SystemCallError
|
|
387
|
+
ensure
|
|
388
|
+
server.close unless server.closed?
|
|
389
|
+
end
|
|
390
|
+
thread.report_on_exception = false
|
|
391
|
+
|
|
392
|
+
yield "http://127.0.0.1:#{port}/"
|
|
393
|
+
ensure
|
|
394
|
+
server&.close unless server&.closed?
|
|
395
|
+
thread&.join(5)
|
|
396
|
+
end
|
|
397
|
+
end
|
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
4
|
+
require "wreq"
|
|
5
|
+
|
|
6
|
+
HTTPBIN_URL = ENV.fetch("HTTPBIN_URL", "https://httpbin.io").delete_suffix("/")
|
|
7
|
+
MINITEST_RETRY_COUNT = Integer(ENV.fetch("MINITEST_RETRY_COUNT", "3"))
|
|
8
|
+
MINITEST_RETRY_DISPLAY_FAILURE_MESSAGES =
|
|
9
|
+
ENV.fetch("MINITEST_RETRY_DISPLAY_FAILURE_MESSAGES", "true") != "false"
|
|
10
|
+
MINITEST_RETRY_SLEEP_INTERVAL = Float(ENV.fetch("MINITEST_RETRY_SLEEP_INTERVAL", "1"))
|
|
11
|
+
|
|
12
|
+
require "minitest/autorun"
|
|
13
|
+
require "minitest/retry"
|
|
14
|
+
|
|
15
|
+
# httpbin.io is a shared external service, so the integration tests can see
|
|
16
|
+
# occasional network failures even when the client behavior is correct.
|
|
17
|
+
Minitest::Retry.use!(
|
|
18
|
+
retry_count: MINITEST_RETRY_COUNT,
|
|
19
|
+
verbose: false,
|
|
20
|
+
io: $stderr
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
Minitest::Retry.on_retry do |klass, test_name, attempt, result|
|
|
24
|
+
$stderr.puts "[MinitestRetry] retry #{klass}##{test_name} (#{attempt}/#{MINITEST_RETRY_COUNT})"
|
|
25
|
+
|
|
26
|
+
if MINITEST_RETRY_DISPLAY_FAILURE_MESSAGES
|
|
27
|
+
result.failures.each do |failure|
|
|
28
|
+
message = failure.message.to_s.strip
|
|
29
|
+
$stderr.puts message unless message.empty?
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
sleep MINITEST_RETRY_SLEEP_INTERVAL if MINITEST_RETRY_SLEEP_INTERVAL.positive?
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
module HttpbinHelpers
|
|
37
|
+
def httpbin_value(value)
|
|
38
|
+
(value.is_a?(Array) && value.length == 1) ? value.first : value
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def httpbin_fetch(hash, key)
|
|
42
|
+
httpbin_value(hash.fetch(key))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def httpbin_cookies(json)
|
|
46
|
+
json.fetch("cookies", json)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
class Minitest::Test
|
|
51
|
+
include HttpbinHelpers
|
|
52
|
+
end
|
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,123 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: wreq
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.2.3
|
|
5
|
+
platform: x64-mingw-ucrt
|
|
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
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- Cargo.lock
|
|
22
|
+
- Cargo.toml
|
|
23
|
+
- Gemfile
|
|
24
|
+
- LICENSE
|
|
25
|
+
- README.md
|
|
26
|
+
- Rakefile
|
|
27
|
+
- build.rs
|
|
28
|
+
- docs/windows-gnu-tokio-crash.md
|
|
29
|
+
- examples/body.rb
|
|
30
|
+
- examples/client.rb
|
|
31
|
+
- examples/cookie.rb
|
|
32
|
+
- examples/emulate_request.rb
|
|
33
|
+
- examples/headers.rb
|
|
34
|
+
- examples/proxy.rb
|
|
35
|
+
- examples/send_stream.rb
|
|
36
|
+
- examples/stream.rb
|
|
37
|
+
- examples/thread_interrupt.rb
|
|
38
|
+
- extconf.rb
|
|
39
|
+
- lib/wreq.rb
|
|
40
|
+
- lib/wreq_ruby/3.3/wreq_ruby.so
|
|
41
|
+
- lib/wreq_ruby/3.4/wreq_ruby.so
|
|
42
|
+
- lib/wreq_ruby/4.0/wreq_ruby.so
|
|
43
|
+
- lib/wreq_ruby/body.rb
|
|
44
|
+
- lib/wreq_ruby/client.rb
|
|
45
|
+
- lib/wreq_ruby/cookie.rb
|
|
46
|
+
- lib/wreq_ruby/emulate.rb
|
|
47
|
+
- lib/wreq_ruby/error.rb
|
|
48
|
+
- lib/wreq_ruby/header.rb
|
|
49
|
+
- lib/wreq_ruby/http.rb
|
|
50
|
+
- lib/wreq_ruby/response.rb
|
|
51
|
+
- script/build_platform_gem.rb
|
|
52
|
+
- script/build_windows_gnu.ps1
|
|
53
|
+
- src/arch.rs
|
|
54
|
+
- src/client.rs
|
|
55
|
+
- src/client/body.rs
|
|
56
|
+
- src/client/body/form.rs
|
|
57
|
+
- src/client/body/json.rs
|
|
58
|
+
- src/client/body/stream.rs
|
|
59
|
+
- src/client/param.rs
|
|
60
|
+
- src/client/query.rs
|
|
61
|
+
- src/client/req.rs
|
|
62
|
+
- src/client/resp.rs
|
|
63
|
+
- src/cookie.rs
|
|
64
|
+
- src/emulate.rs
|
|
65
|
+
- src/error.rs
|
|
66
|
+
- src/extractor.rs
|
|
67
|
+
- src/gvl.rs
|
|
68
|
+
- src/header.rs
|
|
69
|
+
- src/http.rs
|
|
70
|
+
- src/lib.rs
|
|
71
|
+
- src/macros.rs
|
|
72
|
+
- src/rt.rs
|
|
73
|
+
- test/client_cookie_test.rb
|
|
74
|
+
- test/client_test.rb
|
|
75
|
+
- test/cookie_test.rb
|
|
76
|
+
- test/emulation_test.rb
|
|
77
|
+
- test/error_handling_test.rb
|
|
78
|
+
- test/header_test.rb
|
|
79
|
+
- test/inspect_test.rb
|
|
80
|
+
- test/module_methods_test.rb
|
|
81
|
+
- test/orig_header_test.rb
|
|
82
|
+
- test/request_parameters_test.rb
|
|
83
|
+
- test/request_test.rb
|
|
84
|
+
- test/response_test.rb
|
|
85
|
+
- test/stream_test.rb
|
|
86
|
+
- test/test_helper.rb
|
|
87
|
+
- wreq.gemspec
|
|
88
|
+
homepage: https://github.com/SearchApi/wreq-ruby
|
|
89
|
+
licenses:
|
|
90
|
+
- Apache-2.0
|
|
91
|
+
metadata:
|
|
92
|
+
bug_tracker_uri: https://github.com/SearchApi/wreq-ruby/issues
|
|
93
|
+
changelog_uri: https://github.com/SearchApi/wreq-ruby/releases
|
|
94
|
+
documentation_uri: https://github.com/SearchApi/wreq-ruby#readme
|
|
95
|
+
homepage_uri: https://github.com/SearchApi/wreq-ruby
|
|
96
|
+
source_code_uri: https://github.com/SearchApi/wreq-ruby
|
|
97
|
+
rubygems_mfa_required: 'true'
|
|
98
|
+
rdoc_options:
|
|
99
|
+
- "--exclude"
|
|
100
|
+
- Cargo\..*
|
|
101
|
+
- "--exclude"
|
|
102
|
+
- "\\.rs$"
|
|
103
|
+
require_paths:
|
|
104
|
+
- lib
|
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '3.3'
|
|
110
|
+
- - "<"
|
|
111
|
+
- !ruby/object:Gem::Version
|
|
112
|
+
version: 4.1.dev
|
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - ">="
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '0'
|
|
118
|
+
requirements:
|
|
119
|
+
- Rust >= 1.85
|
|
120
|
+
rubygems_version: 4.0.10
|
|
121
|
+
specification_version: 4
|
|
122
|
+
summary: Ruby bindings for wreq, an HTTP client with TLS/HTTP2 browser fingerprinting
|
|
123
|
+
test_files: []
|