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,205 @@
1
+ # frozen_string_literal: true
2
+
3
+ unless defined?(Wreq)
4
+ module Wreq
5
+ # HTTP headers collection.
6
+ #
7
+ # Provides efficient access to HTTP headers with case-insensitive lookups.
8
+ # Headers are created by the native extension and cannot be directly instantiated.
9
+ #
10
+ # All header names are case-insensitive. Multiple values for the same header
11
+ # are supported through the `get_all` method.
12
+ #
13
+ # @example Accessing response headers
14
+ # response = Wreq.get("https://example.com")
15
+ # headers = response.headers
16
+ # content_type = headers["Content-Type"]
17
+ # content_type = headers.get("content-type") # Same, case-insensitive
18
+ #
19
+ # @example Getting all values for a header
20
+ # accept_values = headers.get_all("Accept")
21
+ # # => ["application/json", "text/html"]
22
+ #
23
+ # @example Modifying headers
24
+ # headers.set("X-Custom-Header", "value")
25
+ # headers["Authorization"] = "Bearer token"
26
+ # headers.append("Accept", "application/xml")
27
+ #
28
+ # @example Iterating headers
29
+ # headers.each do |name, value|
30
+ # puts "#{name}: #{value}"
31
+ # end
32
+ #
33
+ # @example Converting to hash
34
+ # hash = headers.to_h
35
+ # hash["content-type"] # => "text/html"
36
+ class Headers
37
+ # Create a new empty Headers collection.
38
+ #
39
+ # @return [Wreq::Headers] New headers instance
40
+ # @example
41
+ # headers = Wreq::Headers.new
42
+ # headers.set("Content-Type", "application/json")
43
+ def self.new
44
+ end
45
+
46
+ # Get a header value by name (case-insensitive).
47
+ #
48
+ # Returns the first value if multiple values exist for the same header.
49
+ #
50
+ # @param name [String] Header name (case-insensitive)
51
+ # @return [String, nil] Header value, or nil if not found
52
+ # @example
53
+ # headers.get("Content-Type") # => "application/json"
54
+ # headers.get("content-type") # => "application/json" (same)
55
+ # headers.get("X-Nonexistent") # => nil
56
+ def get(name)
57
+ end
58
+
59
+ # Get all values for a header name (case-insensitive).
60
+ #
61
+ # Useful when a header can have multiple values (e.g., Accept, Set-Cookie).
62
+ #
63
+ # @param name [String] Header name (case-insensitive)
64
+ # @return [Array<String>] All values for this header (empty array if not found)
65
+ # @example
66
+ # headers.get_all("Accept")
67
+ # # => ["application/json", "text/html", "application/xml"]
68
+ # headers.get_all("X-Nonexistent") # => []
69
+ def get_all(name)
70
+ end
71
+
72
+ # Set a header value, replacing any existing values.
73
+ #
74
+ # @param name [String] Header name
75
+ # @param value [String] Header value
76
+ # @return [void]
77
+ # @raise [Wreq::BuilderError] if name or value contains invalid characters
78
+ # @example
79
+ # headers.set("Content-Type", "application/json")
80
+ # headers.set("X-Custom-Header", "my-value")
81
+ def set(name, value)
82
+ end
83
+
84
+ # Append a header value without replacing existing values.
85
+ #
86
+ # Adds a new value for the header, preserving any existing values.
87
+ # Useful for headers that can have multiple values.
88
+ #
89
+ # @param name [String] Header name
90
+ # @param value [String] Header value to append
91
+ # @return [void]
92
+ # @raise [Wreq::BuilderError] if name or value contains invalid characters
93
+ # @example
94
+ # headers.set("Accept", "application/json")
95
+ # headers.append("Accept", "text/html")
96
+ # headers.get_all("Accept") # => ["application/json", "text/html"]
97
+ def append(name, value)
98
+ end
99
+
100
+ # Remove all values for a header name.
101
+ #
102
+ # @param name [String] Header name (case-insensitive)
103
+ # @return [String, nil] The removed value (first one if multiple), or nil
104
+ # @example
105
+ # headers.remove("Authorization") # => "Bearer token"
106
+ # headers.remove("X-Nonexistent") # => nil
107
+ def remove(name)
108
+ end
109
+
110
+ # Check if a header exists (case-insensitive).
111
+ #
112
+ # @param name [String] Header name
113
+ # @return [Boolean] true if the header exists
114
+ # @example
115
+ # headers.contains?("Content-Type") # => true
116
+ # headers.contains?("X-Missing") # => false
117
+ def contains?(name)
118
+ end
119
+
120
+ # Check if a header key exists (alias for {#contains?}).
121
+ #
122
+ # @param name [String] Header name
123
+ # @return [Boolean] true if the header exists
124
+ # @example
125
+ # headers.key?("Accept") # => true
126
+ def key?(name)
127
+ end
128
+
129
+ # Get the number of headers.
130
+ #
131
+ # @return [Integer] Total number of unique header names
132
+ # @example
133
+ # headers.length # => 12
134
+ def length
135
+ end
136
+
137
+ # Check if there are no headers.
138
+ #
139
+ # @return [Boolean] true if no headers exist
140
+ # @example
141
+ # headers.empty? # => false
142
+ def empty?
143
+ end
144
+
145
+ # Remove all headers.
146
+ #
147
+ # @return [void]
148
+ # @example
149
+ # headers.clear
150
+ # headers.empty? # => true
151
+ def clear
152
+ end
153
+
154
+ # Get all header names.
155
+ #
156
+ # @return [Array<String>] Array of header names (lowercase)
157
+ # @example
158
+ # headers.keys
159
+ # # => ["content-type", "accept", "user-agent", "authorization"]
160
+ def keys
161
+ end
162
+
163
+ # Get all header values.
164
+ #
165
+ # Returns one value per header (the first if multiple values exist).
166
+ #
167
+ # @return [Array<String>] Array of header values
168
+ # @example
169
+ # headers.values
170
+ # # => ["application/json", "text/html", "Mozilla/5.0", "Bearer token"]
171
+ def values
172
+ end
173
+
174
+ # Iterate over headers.
175
+ #
176
+ # Yields each header name and value pair. If a header has multiple values,
177
+ # only the first is yielded.
178
+ #
179
+ # @yieldparam name [String] Header name (lowercase)
180
+ # @yieldparam value [String] Header value
181
+ # @return [Enumerator, self] Returns enumerator if no block given, self otherwise
182
+ # @example With block
183
+ # headers.each do |name, value|
184
+ # puts "#{name}: #{value}"
185
+ # end
186
+ # @example Without block
187
+ # enum = headers.each
188
+ # enum.to_a # => [["content-type", "text/html"], ...]
189
+ def each
190
+ end
191
+
192
+ # Convert headers to a string representation.
193
+ def to_s
194
+ end
195
+ end
196
+ end
197
+ end
198
+
199
+ module Wreq
200
+ class Headers
201
+ def inspect
202
+ "#<Wreq::Headers [#{length} headers]>"
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wreq
4
+ # HTTP method enumeration backed by Rust.
5
+ #
6
+ # Variants are exposed as constants under this class.
7
+ # Each constant is an instance of {Wreq::Method}.
8
+ #
9
+ # @example Using predefined constants
10
+ # method = Wreq::Method::GET
11
+ # method.class #=> Wreq::Method
12
+ #
13
+ # @example In request context
14
+ # Wreq.request(url: "https://api.example.com", method: Wreq::Method::POST)
15
+ class Method
16
+ # Constants are set by the native extension at initialization.
17
+ # These stubs are for documentation only.
18
+ unless const_defined?(:GET)
19
+ GET = nil # @return [Wreq::Method] HTTP GET method
20
+ HEAD = nil # @return [Wreq::Method] HTTP HEAD method
21
+ POST = nil # @return [Wreq::Method] HTTP POST method
22
+ PUT = nil # @return [Wreq::Method] HTTP PUT method
23
+ DELETE = nil # @return [Wreq::Method] HTTP DELETE method
24
+ OPTIONS = nil # @return [Wreq::Method] HTTP OPTIONS method
25
+ TRACE = nil # @return [Wreq::Method] HTTP TRACE method
26
+ PATCH = nil # @return [Wreq::Method] HTTP PATCH method
27
+ end
28
+ end
29
+
30
+ # HTTP version enumeration backed by Rust.
31
+ #
32
+ # @example Using predefined constants
33
+ # version = Wreq::Version::HTTP_11
34
+ # version.class #=> Wreq::Version
35
+ class Version
36
+ # Constants are set by the native extension at initialization.
37
+ # These stubs are for documentation only.
38
+ unless const_defined?(:HTTP_11)
39
+ HTTP_09 = nil # @return [Wreq::Version] HTTP/0.9
40
+ HTTP_10 = nil # @return [Wreq::Version] HTTP/1.0
41
+ HTTP_11 = nil # @return [Wreq::Version] HTTP/1.1
42
+ HTTP_2 = nil # @return [Wreq::Version] HTTP/2
43
+ HTTP_3 = nil # @return [Wreq::Version] HTTP/3
44
+ end
45
+
46
+ # Returns a string representation of the HTTP version.
47
+ # @return [String] HTTP version as string
48
+ unless method_defined?(:to_s)
49
+ def to_s
50
+ end
51
+ end
52
+
53
+ # Compares HTTP versions by semantic value, not object identity.
54
+ #
55
+ # This method is implemented by the native extension.
56
+ # When comparing with non-{Wreq::Version} objects, it returns false.
57
+ #
58
+ # @param other [Object] object to compare against
59
+ # @return [Boolean] true when both represent the same HTTP version
60
+ # @example
61
+ # Wreq::Version::HTTP_11 == response.version
62
+ unless method_defined?(:==)
63
+ def ==(other)
64
+ end
65
+ end
66
+ end
67
+
68
+ # HTTP status code wrapper.
69
+ #
70
+ # This class wraps standard HTTP status codes and provides
71
+ # convenient methods to check the response category.
72
+ #
73
+ # The actual implementation is provided by Rust for performance.
74
+ #
75
+ # @example Check if response is successful
76
+ # status = response.status
77
+ # if status.success?
78
+ # puts "Request succeeded with code: #{status.as_int}"
79
+ # end
80
+ #
81
+ # @example Check different status categories
82
+ # status.informational? # 1xx
83
+ # status.success? # 2xx
84
+ # status.redirection? # 3xx
85
+ # status.client_error? # 4xx
86
+ # status.server_error? # 5xx
87
+ unless const_defined?(:StatusCode)
88
+ class StatusCode
89
+ # Returns the status code as an integer.
90
+ #
91
+ # @return [Integer] the numeric HTTP status code (100-599)
92
+ def as_int
93
+ end
94
+
95
+ # Checks if status code is informational (1xx).
96
+ #
97
+ # Informational responses indicate that the request was received
98
+ # and the process is continuing.
99
+ #
100
+ # @return [Boolean] true if status is 100-199
101
+ def informational?
102
+ end
103
+
104
+ # Checks if status code indicates success (2xx).
105
+ #
106
+ # Success responses indicate that the request was successfully
107
+ # received, understood, and accepted.
108
+ #
109
+ # @return [Boolean] true if status is 200-299
110
+ def success?
111
+ end
112
+
113
+ # Checks if status code indicates redirection (3xx).
114
+ #
115
+ # Redirection responses indicate that further action needs to be
116
+ # taken to complete the request.
117
+ #
118
+ # @return [Boolean] true if status is 300-399
119
+ def redirection?
120
+ end
121
+
122
+ # Checks if status code indicates client error (4xx).
123
+ #
124
+ # Client error responses indicate that the request contains bad
125
+ # syntax or cannot be fulfilled.
126
+ #
127
+ # @return [Boolean] true if status is 400-499
128
+ def client_error?
129
+ end
130
+
131
+ # Checks if status code indicates server error (5xx).
132
+ #
133
+ # Server error responses indicate that the server failed to
134
+ # fulfill a valid request.
135
+ #
136
+ # @return [Boolean] true if status is 500-599
137
+ def server_error?
138
+ end
139
+
140
+ # Returns a string representation of the status code.
141
+ # @return [String] Status code as string
142
+ def to_s
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ module Wreq
149
+ class StatusCode
150
+ def inspect
151
+ "#<Wreq::StatusCode #{self}>"
152
+ end
153
+ end
154
+
155
+ class Version
156
+ def inspect
157
+ "#<Wreq::Version #{self}>"
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,209 @@
1
+ # frozen_string_literal: true
2
+
3
+ unless defined?(Wreq)
4
+ module Wreq
5
+ # HTTP response object containing status, headers, and body.
6
+ #
7
+ # This class wraps a native Rust implementation providing efficient
8
+ # access to HTTP response data including status codes, headers, body
9
+ # content, and streaming capabilities.
10
+ #
11
+ # @example Basic response handling
12
+ # response = client.get("https://api.example.com")
13
+ # puts response.status.as_int # => 200
14
+ # puts response.text
15
+ #
16
+ # @example JSON response
17
+ # response = client.get("https://api.example.com/data")
18
+ # data = response.json
19
+ #
20
+ # @example Streaming response
21
+ # response = client.get("https://example.com/large-file")
22
+ # response.stream.each do |chunk|
23
+ # # Process chunk
24
+ # end
25
+ class Response
26
+ # Get the HTTP status code as an integer.
27
+ #
28
+ # @return [Integer] Status code (e.g., 200, 404, 500)
29
+ # @example
30
+ # response.code # => 200
31
+ def code
32
+ end
33
+
34
+ # Get the HTTP status code object.
35
+ #
36
+ # @return [Wreq::StatusCode] Status code wrapper with helper methods
37
+ # @example
38
+ # status = response.status
39
+ # status.success? # => true
40
+ def status
41
+ end
42
+
43
+ # Get the HTTP protocol version used.
44
+ #
45
+ # @return [Wreq::Version] HTTP version (HTTP/1.1, HTTP/2, etc.)
46
+ # @example
47
+ # response.version # => Wreq::Version::HTTP_11
48
+ def version
49
+ end
50
+
51
+ # Get the final URL after redirects.
52
+ #
53
+ # @return [String] The final URL
54
+ # @example
55
+ # response.url # => "https://example.com/final-page"
56
+ def url
57
+ end
58
+
59
+ # Get the content length if known.
60
+ #
61
+ # @return [Integer, nil] Content length in bytes, or nil if unknown
62
+ # @example
63
+ # response.content_length # => 1024
64
+ def content_length
65
+ end
66
+
67
+ # Get the local socket address.
68
+ #
69
+ # @return [String, nil] Local address (e.g., "127.0.0.1:54321"), or nil
70
+ # @example
71
+ # response.local_addr # => "192.168.1.100:54321"
72
+ def local_addr
73
+ end
74
+
75
+ # Get the remote socket address.
76
+ #
77
+ # @return [String, nil] Remote address (e.g., "93.184.216.34:443"), or nil
78
+ # @example
79
+ # response.remote_addr # => "93.184.216.34:443"
80
+ def remote_addr
81
+ end
82
+
83
+ # Get the response bytes as a binary string.
84
+ # @return [String] Response body as binary data
85
+ # @example
86
+ # binary_data = response.bytes
87
+ # puts binary_data.size # => 1024
88
+ def bytes
89
+ end
90
+
91
+ # Get the response body as text with a specific charset.
92
+ # This method allows you to specify a default encoding
93
+ # to use when decoding the response body.
94
+ # # @param default_encoding [String] Default encoding to use (e.g., "UTF-8")
95
+ # # @return [String] Response body decoded as text using the specified encoding
96
+ # @example
97
+ # html = response.text("ISO-8859-1")
98
+ # puts html
99
+ # @raise [Wreq::DecodingError] if body cannot be decoded with the specified encoding
100
+ def text(default_encoding: "UTF-8")
101
+ end
102
+
103
+ # Parse the response body as JSON.
104
+ #
105
+ # @return [Object] Parsed JSON (Hash, Array, String, Integer, Float, Boolean, nil)
106
+ # @raise [Wreq::DecodingError] if body is not valid JSON
107
+ # @example
108
+ # data = response.json
109
+ # puts data["key"]
110
+ def json
111
+ end
112
+
113
+ # Stream the response body, yielding each chunk to the given block.
114
+ #
115
+ # This method allows you to process large HTTP responses efficiently,
116
+ # by yielding each chunk of the body as it arrives, without loading
117
+ # the entire response into memory.
118
+ #
119
+ # @return [nil]
120
+ # @yield [chunk] Each chunk of the response body as a binary String
121
+ # @raise [LocalJumpError] if called without a block
122
+ # @raise [Wreq::TimeoutError, Wreq::BodyError, Wreq::ConnectionResetError, Wreq::RequestError]
123
+ # if streaming fails while reading the response body
124
+ # @example Save response to file
125
+ # File.open("output.bin", "wb") do |f|
126
+ # response.chunks { |chunk| f.write(chunk) }
127
+ # end
128
+ # @example Count total bytes streamed
129
+ # total = 0
130
+ # response.chunks { |chunk| total += chunk.bytesize }
131
+ # puts "Downloaded #{total} bytes"
132
+ #
133
+ # Exceptions raised inside the block are propagated to the caller.
134
+ def chunks
135
+ end
136
+
137
+ # Close the response and free associated resources.
138
+ #
139
+ # @return [void]
140
+ # @example
141
+ # response.close
142
+ def close
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ # ======================== Ruby API Extensions ========================
149
+
150
+ module Wreq
151
+ class Response
152
+ # Returns the response body as a string.
153
+ #
154
+ # @return [String] Response body text
155
+ # @example
156
+ # puts response.to_s
157
+ # puts response
158
+ # File.write("page.html", response)
159
+ def to_s
160
+ text
161
+ end
162
+
163
+ # Returns a compact string representation for debugging.
164
+ #
165
+ # Format: #<Wreq::Response STATUS content-type="..." body=SIZE>
166
+ #
167
+ # @return [String] Compact formatted response information
168
+ # @example
169
+ # p response
170
+ # # => #<Wreq::Response 200 content-type="application/json" body=456B>
171
+ def inspect
172
+ parts = ["#<Wreq::Response"]
173
+
174
+ parts << code.to_s
175
+
176
+ if headers.respond_to?(:get)
177
+ content_type = headers.get("content-type")
178
+ parts << "content-type=#{content_type.inspect}" if content_type
179
+ end
180
+
181
+ if content_length
182
+ parts << "body=#{format_bytes(content_length)}"
183
+ end
184
+
185
+ parts.join(" ") + ">"
186
+ end
187
+
188
+ private
189
+
190
+ def format_bytes(bytes)
191
+ return "0B" if bytes.zero?
192
+
193
+ units = ["B", "KB", "MB", "GB"]
194
+ size = bytes.to_f
195
+ unit_index = 0
196
+
197
+ while size >= 1024 && unit_index < units.length - 1
198
+ size /= 1024.0
199
+ unit_index += 1
200
+ end
201
+
202
+ if unit_index == 0
203
+ "#{size.to_i}#{units[unit_index]}"
204
+ else
205
+ "#{size.round(1)}#{units[unit_index]}"
206
+ end
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Build a platform-specific gem with pre-compiled native extensions.
5
+ #
6
+ # Usage: ruby script/build_platform_gem.rb PLATFORM
7
+ # Example: ruby script/build_platform_gem.rb arm64-darwin
8
+ #
9
+ # Expects compiled .bundle/.so files in version-specific directories:
10
+ # lib/wreq_ruby/3.3/wreq_ruby.bundle
11
+ # lib/wreq_ruby/3.4/wreq_ruby.bundle
12
+ # lib/wreq_ruby/4.0/wreq_ruby.bundle
13
+
14
+ require "rubygems/package"
15
+ require "fileutils"
16
+
17
+ platform = ARGV.fetch(0) { abort "Usage: #{$0} PLATFORM" }
18
+
19
+ spec = Gem::Specification.load("wreq.gemspec")
20
+ spec.platform = Gem::Platform.new(platform)
21
+ spec.extensions = []
22
+ # Keep in sync with Rakefile cross_compiling block
23
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.3", "< 4.1.dev")
24
+
25
+ # Add version-specific compiled extensions
26
+ binaries = Dir.glob("lib/wreq_ruby/[0-9]*/*.{bundle,so}")
27
+ abort "No compiled binaries found in lib/wreq_ruby/*/. Did compilation succeed?" if binaries.empty?
28
+ spec.files += binaries
29
+
30
+ FileUtils.mkdir_p("pkg")
31
+ gem_file = Gem::Package.build(spec)
32
+ FileUtils.mv(gem_file, "pkg/")
33
+
34
+ puts "Built: pkg/#{File.basename(gem_file)}"