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.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/Cargo.lock +1687 -0
  3. data/Cargo.toml +54 -0
  4. data/Gemfile +18 -0
  5. data/LICENSE +201 -0
  6. data/README.md +153 -0
  7. data/Rakefile +90 -0
  8. data/build.rs +16 -0
  9. data/docs/windows-gnu-tokio-crash.md +70 -0
  10. data/examples/body.rb +42 -0
  11. data/examples/client.rb +33 -0
  12. data/examples/cookie.rb +24 -0
  13. data/examples/emulate_request.rb +37 -0
  14. data/examples/headers.rb +27 -0
  15. data/examples/proxy.rb +113 -0
  16. data/examples/send_stream.rb +85 -0
  17. data/examples/stream.rb +14 -0
  18. data/examples/thread_interrupt.rb +83 -0
  19. data/extconf.rb +7 -0
  20. data/lib/wreq.rb +303 -0
  21. data/lib/wreq_ruby/3.3/wreq_ruby.so +0 -0
  22. data/lib/wreq_ruby/3.4/wreq_ruby.so +0 -0
  23. data/lib/wreq_ruby/4.0/wreq_ruby.so +0 -0
  24. data/lib/wreq_ruby/body.rb +36 -0
  25. data/lib/wreq_ruby/client.rb +526 -0
  26. data/lib/wreq_ruby/cookie.rb +156 -0
  27. data/lib/wreq_ruby/emulate.rb +232 -0
  28. data/lib/wreq_ruby/error.rb +157 -0
  29. data/lib/wreq_ruby/header.rb +205 -0
  30. data/lib/wreq_ruby/http.rb +160 -0
  31. data/lib/wreq_ruby/response.rb +209 -0
  32. data/script/build_platform_gem.rb +34 -0
  33. data/script/build_windows_gnu.ps1 +257 -0
  34. data/src/arch.rs +33 -0
  35. data/src/client/body/form.rs +2 -0
  36. data/src/client/body/json.rs +16 -0
  37. data/src/client/body/stream.rs +146 -0
  38. data/src/client/body.rs +57 -0
  39. data/src/client/param.rs +19 -0
  40. data/src/client/query.rs +2 -0
  41. data/src/client/req.rs +274 -0
  42. data/src/client/resp.rs +248 -0
  43. data/src/client.rs +413 -0
  44. data/src/cookie.rs +312 -0
  45. data/src/emulate.rs +376 -0
  46. data/src/error.rs +163 -0
  47. data/src/extractor.rs +117 -0
  48. data/src/gvl.rs +154 -0
  49. data/src/header.rs +245 -0
  50. data/src/http.rs +142 -0
  51. data/src/lib.rs +98 -0
  52. data/src/macros.rs +123 -0
  53. data/src/rt.rs +46 -0
  54. data/test/client_cookie_test.rb +46 -0
  55. data/test/client_test.rb +136 -0
  56. data/test/cookie_test.rb +182 -0
  57. data/test/emulation_test.rb +21 -0
  58. data/test/error_handling_test.rb +92 -0
  59. data/test/header_test.rb +290 -0
  60. data/test/inspect_test.rb +125 -0
  61. data/test/module_methods_test.rb +75 -0
  62. data/test/orig_header_test.rb +115 -0
  63. data/test/request_parameters_test.rb +175 -0
  64. data/test/request_test.rb +244 -0
  65. data/test/response_test.rb +69 -0
  66. data/test/stream_test.rb +397 -0
  67. data/test/test_helper.rb +52 -0
  68. data/wreq.gemspec +68 -0
  69. metadata +123 -0
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/wreq"
4
+
5
+ # Make a client
6
+ client = Wreq::Client.new
7
+
8
+ # Send a GET request with cookies provided as a Hash.
9
+ # This form is serialized as multiple Cookie header fields (common in HTTP/2).
10
+ resp = client.get(
11
+ "https://tls.browserleaks.com",
12
+ cookies: {"foo" => "bar", "baz" => "qux"}
13
+ )
14
+
15
+ puts resp.text
16
+
17
+ # Send a GET request with cookies provided as a single Cookie header string.
18
+ # This form is common in HTTP/1.1: one Cookie header with '; ' separated pairs.
19
+ resp = client.get(
20
+ "https://tls.browserleaks.com",
21
+ cookies: "foo=bar; baz=qux"
22
+ )
23
+
24
+ puts resp.text
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/wreq"
4
+
5
+ # Example: How to use Emulation options in Wreq requests
6
+ #
7
+ # Emulation can be set globally on the client (applies to all requests),
8
+ # or per-request (overrides client default for that call).
9
+ #
10
+ # Global client emulation:
11
+ # Set when creating the Wreq::Client instance.
12
+ # All requests from this client will use the specified emulation unless overridden.
13
+ client = Wreq::Client.new(emulation: Wreq::Emulation.new(
14
+ profile: Wreq::Profile::Chrome142,
15
+ platform: Wreq::Platform::MacOS,
16
+ http2: true,
17
+ headers: true
18
+ ))
19
+
20
+ resp = client.get("https://tls.peet.ws/api/all")
21
+ puts resp.text
22
+
23
+ # Per-request emulation:
24
+ # Pass the emulation option to a single request method.
25
+ # This allows you to override the client default for specific calls.
26
+ resp = client.get(
27
+ "https://tls.peet.ws/api/all",
28
+ emulation: Wreq::Emulation.new(
29
+ profile: Wreq::Profile::Safari26,
30
+ platform: Wreq::Platform::MacOS,
31
+ http2: true,
32
+ headers: true
33
+ ),
34
+ # Skip client default headers for this request
35
+ default_headers: false
36
+ )
37
+ puts resp.text
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/wreq"
4
+
5
+ # Make a request
6
+ client = Wreq::Client.new
7
+ response = client.get("https://httpbin.io/headers")
8
+
9
+ puts "\n=== Iterating Over All Headers ==="
10
+ response.headers.each do |name, value|
11
+ puts "#{name}: #{value}"
12
+ end
13
+
14
+ puts "\n=== Response Summary ==="
15
+ puts "Status: #{response.code}"
16
+ puts "Version: #{response.version}"
17
+ puts "URI: #{response.url}"
18
+ puts "Content Length: #{response.content_length || "Unknown"}"
19
+ puts response.headers
20
+
21
+ if response.local_addr
22
+ puts "Local Address: #{response.local_addr}"
23
+ end
24
+
25
+ if response.remote_addr
26
+ puts "Remote Address: #{response.remote_addr}"
27
+ end
data/examples/proxy.rb ADDED
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/wreq"
4
+
5
+ # Proxy templates:
6
+ # HTTP/HTTPS proxy: http://127.0.0.1:6152
7
+ # SOCKS5 proxy: socks5://127.0.0.1:6153
8
+
9
+ puts "=" * 60
10
+ puts "Wreq Ruby Proxy Examples"
11
+ puts "=" * 60
12
+
13
+ # ==============================================================================
14
+ # Example 1: Basic HTTP Proxy
15
+ # ==============================================================================
16
+ puts "\n--- Example 1: Basic HTTP Proxy ---"
17
+ begin
18
+ client = Wreq::Client.new(
19
+ user_agent: "WreqClient/1.0",
20
+ proxy: "http://127.0.0.1:6152"
21
+ )
22
+ puts "✅ Client with HTTP proxy created"
23
+
24
+ response = client.get("https://httpbin.io/ip")
25
+ puts "Status: #{response.code}"
26
+ puts "Response: #{response.text}"
27
+ rescue Wreq::RequestError => e
28
+ puts "❌ Request failed: #{e.message}"
29
+ rescue => e
30
+ puts "❌ Error: #{e.class} - #{e.message}"
31
+ end
32
+
33
+ # ==============================================================================
34
+ # Example 2: SOCKS5 Proxy
35
+ # ==============================================================================
36
+ puts "\n--- Example 2: SOCKS5 Proxy ---"
37
+ begin
38
+ client = Wreq::Client.new(
39
+ user_agent: "WreqClient/1.0",
40
+ proxy: "socks5://127.0.0.1:6153"
41
+ )
42
+ puts "✅ Client with SOCKS5 proxy created"
43
+
44
+ response = client.get("https://httpbin.io/ip")
45
+ puts "Status: #{response.code}"
46
+ puts "Response: #{response.text}"
47
+ rescue Wreq::RequestError => e
48
+ puts "❌ Request failed: #{e.message}"
49
+ rescue => e
50
+ puts "❌ Error: #{e.class} - #{e.message}"
51
+ end
52
+
53
+ # ==============================================================================
54
+ # Example 3: SOCKS5h Proxy (DNS resolution through proxy)
55
+ # ==============================================================================
56
+ puts "\n--- Example 3: SOCKS5h Proxy (Remote DNS) ---"
57
+ begin
58
+ client = Wreq::Client.new(
59
+ user_agent: "WreqClient/1.0",
60
+ proxy: "socks5h://127.0.0.1:6153"
61
+ )
62
+ puts "✅ Client with SOCKS5h proxy created (DNS resolved by proxy)"
63
+
64
+ response = client.get("https://httpbin.io/ip")
65
+ puts "Status: #{response.code}"
66
+ puts "Response: #{response.text}"
67
+ rescue Wreq::RequestError => e
68
+ puts "❌ Request failed: #{e.message}"
69
+ rescue => e
70
+ puts "❌ Error: #{e.class} - #{e.message}"
71
+ end
72
+
73
+ # ==============================================================================
74
+ # Example 4: HTTP Proxy with Authentication
75
+ # ==============================================================================
76
+ puts "\n--- Example 4: HTTP Proxy with Authentication ---"
77
+ begin
78
+ # Format: http://username:password@host:port
79
+ client = Wreq::Client.new(
80
+ user_agent: "WreqClient/1.0",
81
+ proxy: "http://user:pass@127.0.0.1:6152"
82
+ )
83
+ puts "✅ Client with authenticated HTTP proxy created"
84
+
85
+ response = client.get("https://httpbin.io/ip")
86
+ puts "Status: #{response.code}"
87
+ puts "Response: #{response.text}"
88
+ rescue Wreq::RequestError => e
89
+ puts "❌ Request failed: #{e.message}"
90
+ rescue => e
91
+ puts "❌ Error: #{e.class} - #{e.message}"
92
+ end
93
+
94
+ # ==============================================================================
95
+ # Example 5: SOCKS5 Proxy with Authentication
96
+ # ==============================================================================
97
+ puts "\n--- Example 5: SOCKS5 Proxy with Authentication ---"
98
+ begin
99
+ # Format: socks5://username:password@host:port
100
+ client = Wreq::Client.new(
101
+ user_agent: "WreqClient/1.0",
102
+ proxy: "socks5://user:pass@127.0.0.1:6153"
103
+ )
104
+ puts "✅ Client with authenticated SOCKS5 proxy created"
105
+
106
+ response = client.get("https://httpbin.io/ip")
107
+ puts "Status: #{response.code}"
108
+ puts "Response: #{response.text}"
109
+ rescue Wreq::RequestError => e
110
+ puts "❌ Request failed: #{e.message}"
111
+ rescue => e
112
+ puts "❌ Error: #{e.class} - #{e.message}"
113
+ end
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/wreq"
4
+ require "json"
5
+ require "tmpdir"
6
+
7
+ # Helpers
8
+ ENDPOINT = ENV["WREQ_ECHO_URL"] || "https://httpbin.io/post"
9
+ CHUNK = 64 * 1024
10
+
11
+ def example1_simple_push
12
+ puts "\n=== Example 1: Simple push ==="
13
+ client = Wreq::Client.new
14
+ us = Wreq::BodySender.new(8)
15
+
16
+ producer = Thread.new do
17
+ 5.times do |i|
18
+ us.push("chunk-#{i}\n")
19
+ sleep 0.05
20
+ end
21
+ us.close
22
+ end
23
+
24
+ resp = client.post(ENDPOINT, body: us, headers: {"Content-Type" => "text/plain"})
25
+ body = resp.json
26
+ data = body["data"] || body["json"].to_json
27
+ puts "Echoed bytes: #{data.bytesize}"
28
+ producer.join
29
+ end
30
+
31
+ def example2_file_stream
32
+ puts "\n=== Example 2: File streaming ==="
33
+ client = Wreq::Client.new
34
+ us = Wreq::BodySender.new(16)
35
+
36
+ # Prepare a temp file (~250KB)
37
+ path = File.join(Dir.tmpdir, "wreq_upload_sample.bin")
38
+ File.binwrite(path, Random.new.bytes(2_500_00))
39
+
40
+ total = 0
41
+ producer = Thread.new do
42
+ File.open(path, "rb") do |f|
43
+ while (chunk = f.read(CHUNK))
44
+ total += chunk.bytesize
45
+ us.push(chunk)
46
+ end
47
+ end
48
+ us.close
49
+ end
50
+
51
+ resp = client.post(ENDPOINT, body: us, headers: {"Content-Type" => "application/octet-stream"})
52
+ json = resp.json
53
+ echoed = (json["headers"] && json["headers"]["Content-Length"]) || (json["data"] || "").bytesize
54
+ puts "Sent bytes ~#{total}, echoed length: #{echoed}"
55
+ producer.join
56
+ ensure
57
+ File.delete(path) if path && File.exist?(path)
58
+ end
59
+
60
+ def example3_background_producer
61
+ puts "\n=== Example 3: Background thread producer ==="
62
+ client = Wreq::Client.new
63
+ us = Wreq::BodySender.new(4)
64
+
65
+ # Simulate streaming generation
66
+ producer = Thread.new do
67
+ (1..20).each do |i|
68
+ us.push("data-#{i},")
69
+ sleep 0.01
70
+ end
71
+ us.close
72
+ end
73
+
74
+ resp = client.post(ENDPOINT, body: us)
75
+ json = resp.json
76
+ data = json["data"] || ""
77
+ puts "Received length: #{data.bytesize}"
78
+ producer.join
79
+ end
80
+
81
+ if __FILE__ == $0
82
+ example1_simple_push
83
+ example2_file_stream
84
+ example3_background_producer
85
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/wreq"
4
+
5
+ # Make a request
6
+ client = Wreq::Client.new
7
+ response = client.get("https://httpbin.io/stream/20")
8
+
9
+ # Get the streaming body receiver
10
+ puts "\n=== Streaming Response Body ==="
11
+ response.chunks do |chunk|
12
+ puts chunk
13
+ sleep 0.1 # Simulate processing time
14
+ end
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Tests Thread.kill interrupt support for wreq HTTP operations.
4
+ # All blocking operations should respond to thread interruption.
5
+
6
+ require_relative "../lib/wreq"
7
+
8
+ HANGING_URL = "http://10.255.255.1:12345/" # Non-routable, hangs forever
9
+ SLOW_BODY_URL = "https://httpbin.io/drip?duration=5&numbytes=5" # 5s slow body
10
+
11
+ def test_interrupt(name, kill_after: 2, max_wait: 5)
12
+ print "#{name}: "
13
+
14
+ start = Time.now
15
+ thread = Thread.new { yield }
16
+
17
+ sleep kill_after
18
+ thread.kill
19
+ killed = thread.join(max_wait)
20
+
21
+ elapsed = Time.now - start
22
+
23
+ if killed && elapsed < (kill_after + max_wait)
24
+ puts "PASS (killed in #{elapsed.round(1)}s)"
25
+ :pass
26
+ else
27
+ puts "FAIL (still alive after #{elapsed.round(1)}s)"
28
+ begin
29
+ Thread.kill(thread)
30
+ rescue
31
+ nil
32
+ end
33
+ :fail
34
+ end
35
+ end
36
+
37
+ puts "Thread Interrupt Test"
38
+ puts "=" * 40
39
+
40
+ results = []
41
+
42
+ # Test 1: Connection phase (non-routable IP)
43
+ results << test_interrupt("Connect phase") {
44
+ begin
45
+ Wreq.get(HANGING_URL)
46
+ rescue
47
+ nil
48
+ end
49
+ }
50
+
51
+ # Test 2: Connection phase with timeout
52
+ results << test_interrupt("Connect + timeout") {
53
+ begin
54
+ Wreq.get(HANGING_URL, timeout: 60)
55
+ rescue
56
+ nil
57
+ end
58
+ }
59
+
60
+ # Test 3: Body reading phase (slow drip endpoint)
61
+ results << test_interrupt("Body reading") {
62
+ resp = Wreq.get(SLOW_BODY_URL)
63
+ begin
64
+ resp.text
65
+ rescue
66
+ nil
67
+ end
68
+ }
69
+
70
+ # Test 4: Body streaming phase
71
+ results << test_interrupt("Body streaming") {
72
+ resp = Wreq.get(SLOW_BODY_URL)
73
+ begin
74
+ resp.chunks { |chunk| chunk }
75
+ rescue
76
+ nil
77
+ end
78
+ }
79
+
80
+ puts "=" * 40
81
+ passed = results.count(:pass)
82
+ puts "#{passed}/#{results.size} tests passed"
83
+ exit(results.all?(:pass) ? 0 : 1)
data/extconf.rb ADDED
@@ -0,0 +1,7 @@
1
+ require "mkmf"
2
+ # We use rb_sys for makefile generation only.
3
+ # We can use `RB_SYS_CARGO_PROFILE` to choose Cargo profile
4
+ # Read more https://github.com/oxidize-rb/rb-sys/blob/main/gem/README.md
5
+ require "rb_sys/mkmf"
6
+
7
+ create_rust_makefile("wreq_ruby/wreq_ruby")