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
|
@@ -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)}"
|