wreq 1.2.12 → 1.2.13
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 +4 -4
- data/Cargo.lock +1 -1
- data/Cargo.toml +1 -1
- data/docs/fork-safety.md +49 -32
- data/docs/interrupt-handling.md +4 -3
- data/lib/wreq.rb +36 -12
- data/lib/wreq_ruby/body.rb +9 -6
- data/lib/wreq_ruby/client.rb +18 -14
- data/lib/wreq_ruby/cookie.rb +9 -0
- data/lib/wreq_ruby/error.rb +7 -5
- data/lib/wreq_ruby/response.rb +23 -7
- data/src/arch.rs +83 -37
- data/src/client/body/stream.rs +15 -24
- data/src/client/req.rs +119 -122
- data/src/client/resp.rs +101 -55
- data/src/client.rs +78 -36
- data/src/cookie.rs +47 -11
- data/src/error.rs +1 -1
- data/src/lib.rs +0 -2
- data/src/macros.rs +0 -1
- data/src/rt.rs +20 -36
- data/test/fork_test.rb +30 -6
- data/test/scripts/fork_safety.rb +66 -43
- data/test/scripts/prefork_runtime.rb +95 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 45253084ba060aa37efcdd71fdea4302d55a8ca658b9945385dd632a241452f9
|
|
4
|
+
data.tar.gz: 817dd6fd547b6f1f5dcf074f707487cf1efb6b7f42324c24a27a8afa444f2868
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1af6ea1ce815b1b64ff19ea238831b57176df435b2d307f3050a4b33b977370b56891d1c56c334a173fc5e11e694527fe30a7ebb418720033ccffd484dc5381c
|
|
7
|
+
data.tar.gz: 88a15b449a5e6fbeb62a57d73fa28d8734d5453c0a5ecfca0d295f2b33f4d23481ad34bdae0acf8d78d622c24a98f5ed2f43c35fb0d24d1d2752aaa88467d0f5
|
data/Cargo.lock
CHANGED
data/Cargo.toml
CHANGED
data/docs/fork-safety.md
CHANGED
|
@@ -1,34 +1,51 @@
|
|
|
1
1
|
# Fork safety
|
|
2
2
|
|
|
3
|
-
##
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
3
|
+
## Prefork checklist
|
|
4
|
+
|
|
5
|
+
- `require "wreq"` may run in the parent before workers fork.
|
|
6
|
+
- Create each `Wreq::Client`, `Wreq::Jar`, and `Wreq::BodySender` in the worker
|
|
7
|
+
that will use it.
|
|
8
|
+
- Keep each `Wreq::Response` in the process that received it.
|
|
9
|
+
- Do not start requests or push streaming body data in the parent before workers
|
|
10
|
+
fork.
|
|
11
|
+
- If the parent must use wreq-ruby first, start workers with `spawn` or `exec`
|
|
12
|
+
instead of `fork`.
|
|
13
|
+
|
|
14
|
+
wreq-ruby checks process ownership whenever it exposes guarded native state. It
|
|
15
|
+
does not copy, reset, or rebuild inherited objects.
|
|
16
|
+
|
|
17
|
+
## Loading before fork
|
|
18
|
+
|
|
19
|
+
wreq-ruby creates its process-wide Tokio runtime on the first operation that
|
|
20
|
+
needs it. Requiring the gem does not initialize the runtime, so a prefork server
|
|
21
|
+
may load wreq-ruby during boot. Each worker can then create its own runtime on
|
|
22
|
+
its first request without an `after_fork!` hook.
|
|
23
|
+
|
|
24
|
+
Create clients and other native-backed objects inside the worker. Each client,
|
|
25
|
+
response, body sender, and cookie jar belongs to the process that created it.
|
|
26
|
+
Using an inherited object raises `Wreq::ForkError`, even when the parent never
|
|
27
|
+
started the runtime. wreq-ruby does not rebuild these objects.
|
|
28
|
+
|
|
29
|
+
## Forking after runtime initialization
|
|
30
|
+
|
|
31
|
+
Once the parent starts an HTTP operation or otherwise uses the Tokio runtime, a
|
|
32
|
+
forked child must not reuse it. Tokio's worker threads do not survive `fork`, and
|
|
33
|
+
the inherited connection pool may refer to those missing threads.
|
|
34
|
+
|
|
35
|
+
Operations that need the inherited runtime raise `Wreq::ForkError`. This
|
|
36
|
+
includes requests through new or existing clients, module request methods, and
|
|
37
|
+
streaming request writes. Constructing a new client, body sender, or cookie jar
|
|
38
|
+
does not use the runtime, but runtime-backed operations remain unavailable in
|
|
39
|
+
that child. Retrying them raises the same error.
|
|
40
|
+
|
|
41
|
+
An inherited `Wreq::Response` cannot be used at all. This includes status,
|
|
42
|
+
headers, socket addresses, TLS information, and body methods. Values copied out
|
|
43
|
+
before the fork, such as a `Wreq::StatusCode` or `Wreq::TlsInfo`, are separate
|
|
44
|
+
objects and do not retain access to the response.
|
|
45
|
+
|
|
46
|
+
The parent remains usable. Native objects collected in the child do not destroy
|
|
47
|
+
state owned by the parent process.
|
|
48
|
+
|
|
49
|
+
Use a spawn- or exec-based worker when the parent must perform HTTP work before
|
|
50
|
+
workers start. Requiring the extension again cannot replace an inherited
|
|
51
|
+
runtime.
|
data/docs/interrupt-handling.md
CHANGED
|
@@ -84,9 +84,10 @@ to the Ruby-owned thread with the GVL may [`src/rt.rs`](../src/rt.rs) map a
|
|
|
84
84
|
wreq-owned cancellation to the `Wreq::InterruptError` defined in
|
|
85
85
|
[`src/error.rs`](../src/error.rs).
|
|
86
86
|
|
|
87
|
-
Keep
|
|
88
|
-
body operations may call `
|
|
89
|
-
Ruby cancellation exception.
|
|
87
|
+
Keep cancellation conversion centralized in `rt::block_on`. Request, response,
|
|
88
|
+
and body operations may call `block_on`, but they must not construct their own
|
|
89
|
+
Ruby cancellation exception. `block_on` returns a future's native error
|
|
90
|
+
unchanged so the caller can convert it after the GVL has been reacquired.
|
|
90
91
|
|
|
91
92
|
These forms are forbidden for wreq-owned cancellation:
|
|
92
93
|
|
data/lib/wreq.rb
CHANGED
|
@@ -19,6 +19,33 @@ require_relative "wreq_ruby/error"
|
|
|
19
19
|
require_relative "wreq_ruby/cookie"
|
|
20
20
|
|
|
21
21
|
unless defined?(Wreq)
|
|
22
|
+
# An HTTP client backed by a lazily initialized, process-wide Tokio runtime.
|
|
23
|
+
#
|
|
24
|
+
# Loading wreq-ruby before `fork` is supported. The parent must not send a
|
|
25
|
+
# request or perform another operation that starts the runtime before workers
|
|
26
|
+
# are forked. Create clients and begin HTTP work inside each worker so it gets
|
|
27
|
+
# its own runtime and connection pool. Clients, responses, body senders, and
|
|
28
|
+
# cookie jars belong to the process that created them and must be recreated
|
|
29
|
+
# in the worker. wreq-ruby does not rebuild inherited objects.
|
|
30
|
+
#
|
|
31
|
+
# Accessing an inherited native-backed object raises Wreq::ForkError even if
|
|
32
|
+
# the parent did not start the runtime. If the parent did start it, the child
|
|
33
|
+
# also cannot perform new runtime-backed operations. Retrying does not replace
|
|
34
|
+
# either kind of inherited state. Use `spawn` or `exec`, or move the parent's
|
|
35
|
+
# HTTP work until after the workers have been forked.
|
|
36
|
+
#
|
|
37
|
+
# @example Preload the extension, then start HTTP work in the worker
|
|
38
|
+
# require "wreq"
|
|
39
|
+
#
|
|
40
|
+
# Process.fork do
|
|
41
|
+
# client = Wreq::Client.new
|
|
42
|
+
# response = client.get("https://example.com")
|
|
43
|
+
# puts response.status
|
|
44
|
+
# end
|
|
45
|
+
#
|
|
46
|
+
# @note Fork safety Create clients, cookie jars, and body senders inside the
|
|
47
|
+
# worker that uses them. Do not carry responses across `fork`.
|
|
48
|
+
# @see https://github.com/SearchApi/wreq-ruby/blob/main/docs/fork-safety.md
|
|
22
49
|
module Wreq
|
|
23
50
|
# Current wreq gem version.
|
|
24
51
|
# @return [String]
|
|
@@ -29,9 +56,6 @@ unless defined?(Wreq)
|
|
|
29
56
|
# raise ArgumentError. Known values retain the error class from their Ruby
|
|
30
57
|
# or native conversion, such as TypeError or Wreq::BuilderError. Validation
|
|
31
58
|
# finishes before network I/O.
|
|
32
|
-
#
|
|
33
|
-
# If a child process inherits wreq-ruby from its parent, requests raise
|
|
34
|
-
# Wreq::ForkError. Require wreq after the worker has been forked.
|
|
35
59
|
|
|
36
60
|
# Send an HTTP request.
|
|
37
61
|
#
|
|
@@ -64,7 +88,7 @@ unless defined?(Wreq)
|
|
|
64
88
|
# @return [Wreq::Response] HTTP response
|
|
65
89
|
# @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
|
|
66
90
|
# value cannot be converted, validated, or built
|
|
67
|
-
# @raise [Wreq::ForkError] if the child inherited wreq-ruby
|
|
91
|
+
# @raise [Wreq::ForkError] if the child inherited an initialized wreq-ruby runtime
|
|
68
92
|
def self.request(method, url, **options)
|
|
69
93
|
end
|
|
70
94
|
|
|
@@ -98,7 +122,7 @@ unless defined?(Wreq)
|
|
|
98
122
|
# @return [Wreq::Response] HTTP response
|
|
99
123
|
# @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
|
|
100
124
|
# value cannot be converted, validated, or built
|
|
101
|
-
# @raise [Wreq::ForkError] if the child inherited wreq-ruby
|
|
125
|
+
# @raise [Wreq::ForkError] if the child inherited an initialized wreq-ruby runtime
|
|
102
126
|
def self.get(url, **options)
|
|
103
127
|
end
|
|
104
128
|
|
|
@@ -132,7 +156,7 @@ unless defined?(Wreq)
|
|
|
132
156
|
# @return [Wreq::Response] HTTP response
|
|
133
157
|
# @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
|
|
134
158
|
# value cannot be converted, validated, or built
|
|
135
|
-
# @raise [Wreq::ForkError] if the child inherited wreq-ruby
|
|
159
|
+
# @raise [Wreq::ForkError] if the child inherited an initialized wreq-ruby runtime
|
|
136
160
|
def self.head(url, **options)
|
|
137
161
|
end
|
|
138
162
|
|
|
@@ -166,7 +190,7 @@ unless defined?(Wreq)
|
|
|
166
190
|
# @return [Wreq::Response] HTTP response
|
|
167
191
|
# @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
|
|
168
192
|
# value cannot be converted, validated, or built
|
|
169
|
-
# @raise [Wreq::ForkError] if the child inherited wreq-ruby
|
|
193
|
+
# @raise [Wreq::ForkError] if the child inherited an initialized wreq-ruby runtime
|
|
170
194
|
def self.post(url, **options)
|
|
171
195
|
end
|
|
172
196
|
|
|
@@ -200,7 +224,7 @@ unless defined?(Wreq)
|
|
|
200
224
|
# @return [Wreq::Response] HTTP response
|
|
201
225
|
# @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
|
|
202
226
|
# value cannot be converted, validated, or built
|
|
203
|
-
# @raise [Wreq::ForkError] if the child inherited wreq-ruby
|
|
227
|
+
# @raise [Wreq::ForkError] if the child inherited an initialized wreq-ruby runtime
|
|
204
228
|
def self.put(url, **options)
|
|
205
229
|
end
|
|
206
230
|
|
|
@@ -234,7 +258,7 @@ unless defined?(Wreq)
|
|
|
234
258
|
# @return [Wreq::Response] HTTP response
|
|
235
259
|
# @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
|
|
236
260
|
# value cannot be converted, validated, or built
|
|
237
|
-
# @raise [Wreq::ForkError] if the child inherited wreq-ruby
|
|
261
|
+
# @raise [Wreq::ForkError] if the child inherited an initialized wreq-ruby runtime
|
|
238
262
|
def self.delete(url, **options)
|
|
239
263
|
end
|
|
240
264
|
|
|
@@ -268,7 +292,7 @@ unless defined?(Wreq)
|
|
|
268
292
|
# @return [Wreq::Response] HTTP response
|
|
269
293
|
# @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
|
|
270
294
|
# value cannot be converted, validated, or built
|
|
271
|
-
# @raise [Wreq::ForkError] if the child inherited wreq-ruby
|
|
295
|
+
# @raise [Wreq::ForkError] if the child inherited an initialized wreq-ruby runtime
|
|
272
296
|
def self.options(url, **options)
|
|
273
297
|
end
|
|
274
298
|
|
|
@@ -302,7 +326,7 @@ unless defined?(Wreq)
|
|
|
302
326
|
# @return [Wreq::Response] HTTP response
|
|
303
327
|
# @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
|
|
304
328
|
# value cannot be converted, validated, or built
|
|
305
|
-
# @raise [Wreq::ForkError] if the child inherited wreq-ruby
|
|
329
|
+
# @raise [Wreq::ForkError] if the child inherited an initialized wreq-ruby runtime
|
|
306
330
|
def self.trace(url, **options)
|
|
307
331
|
end
|
|
308
332
|
|
|
@@ -336,7 +360,7 @@ unless defined?(Wreq)
|
|
|
336
360
|
# @return [Wreq::Response] HTTP response
|
|
337
361
|
# @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
|
|
338
362
|
# value cannot be converted, validated, or built
|
|
339
|
-
# @raise [Wreq::ForkError] if the child inherited wreq-ruby
|
|
363
|
+
# @raise [Wreq::ForkError] if the child inherited an initialized wreq-ruby runtime
|
|
340
364
|
def self.patch(url, **options)
|
|
341
365
|
end
|
|
342
366
|
end
|
data/lib/wreq_ruby/body.rb
CHANGED
|
@@ -17,8 +17,12 @@ unless defined?(Wreq)
|
|
|
17
17
|
#
|
|
18
18
|
# A sender can be attached to one request. Closing it prevents further writes but
|
|
19
19
|
# retains queued chunks so a request attached afterward can still drain them.
|
|
20
|
-
# Creating
|
|
21
|
-
#
|
|
20
|
+
# Creating a sender does not initialize Tokio. An inherited sender raises
|
|
21
|
+
# Wreq::ForkError before its channel is accessed. A new sender can be
|
|
22
|
+
# created in a child, but pushing data also requires a usable runtime.
|
|
23
|
+
#
|
|
24
|
+
# @note Fork safety Create each sender in the worker that writes to it.
|
|
25
|
+
# Do not pass a sender through `fork`.
|
|
22
26
|
class BodySender
|
|
23
27
|
# Create a bounded request-body sender.
|
|
24
28
|
#
|
|
@@ -27,7 +31,6 @@ unless defined?(Wreq)
|
|
|
27
31
|
# @return [Wreq::BodySender] A streaming request body sender
|
|
28
32
|
# @raise [ArgumentError] if capacity is zero, negative, or too large
|
|
29
33
|
# @raise [TypeError] if capacity is not an Integer
|
|
30
|
-
# @raise [Wreq::ForkError] if the child inherited wreq-ruby from its parent
|
|
31
34
|
def self.new(capacity = 8)
|
|
32
35
|
end
|
|
33
36
|
|
|
@@ -36,7 +39,7 @@ unless defined?(Wreq)
|
|
|
36
39
|
# @param data [String] binary chunk
|
|
37
40
|
# @return [nil]
|
|
38
41
|
# @raise [IOError] if the sender or receiving side is closed
|
|
39
|
-
# @raise [Wreq::ForkError] if the
|
|
42
|
+
# @raise [Wreq::ForkError] if the sender or runtime belongs to the parent process
|
|
40
43
|
def push(data)
|
|
41
44
|
end
|
|
42
45
|
|
|
@@ -45,7 +48,7 @@ unless defined?(Wreq)
|
|
|
45
48
|
# This operation is idempotent.
|
|
46
49
|
#
|
|
47
50
|
# @return [nil]
|
|
48
|
-
# @raise [Wreq::ForkError] if the
|
|
51
|
+
# @raise [Wreq::ForkError] if the sender belongs to the parent process
|
|
49
52
|
def close
|
|
50
53
|
end
|
|
51
54
|
|
|
@@ -55,7 +58,7 @@ unless defined?(Wreq)
|
|
|
55
58
|
# the receiving side.
|
|
56
59
|
#
|
|
57
60
|
# @return [Boolean]
|
|
58
|
-
# @raise [Wreq::ForkError] if the
|
|
61
|
+
# @raise [Wreq::ForkError] if the sender belongs to the parent process
|
|
59
62
|
def closed?
|
|
60
63
|
end
|
|
61
64
|
end
|
data/lib/wreq_ruby/client.rb
CHANGED
|
@@ -17,9 +17,14 @@ unless defined?(Wreq)
|
|
|
17
17
|
# native conversion, such as TypeError or Wreq::BuilderError. Request
|
|
18
18
|
# validation finishes before network I/O.
|
|
19
19
|
#
|
|
20
|
-
# A
|
|
21
|
-
#
|
|
22
|
-
#
|
|
20
|
+
# A client belongs to the process that created it. An inherited client
|
|
21
|
+
# raises Wreq::ForkError before its connection pool is accessed. Loading
|
|
22
|
+
# the gem before fork is supported, but clients must be created inside the
|
|
23
|
+
# worker. If the parent already started the runtime, new clients can be
|
|
24
|
+
# constructed in the child but cannot send requests.
|
|
25
|
+
#
|
|
26
|
+
# @note Fork safety Create each client in the worker that uses it. An
|
|
27
|
+
# inherited client is never rebuilt automatically.
|
|
23
28
|
#
|
|
24
29
|
# @example Basic usage
|
|
25
30
|
# client = Wreq::Client.new
|
|
@@ -174,8 +179,7 @@ unless defined?(Wreq)
|
|
|
174
179
|
# value cannot be converted or validated.
|
|
175
180
|
# @raise [Wreq::BuilderError, Wreq::TlsError] if the native client cannot
|
|
176
181
|
# be initialized.
|
|
177
|
-
# @raise [Wreq::ForkError] if
|
|
178
|
-
#
|
|
182
|
+
# @raise [Wreq::ForkError] if :cookie_provider belongs to a parent process.
|
|
179
183
|
# @example Minimal client
|
|
180
184
|
# client = Wreq::Client.new
|
|
181
185
|
#
|
|
@@ -290,7 +294,7 @@ unless defined?(Wreq)
|
|
|
290
294
|
# or unavailable on the current platform
|
|
291
295
|
# @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
|
|
292
296
|
# value cannot be converted, validated, or built
|
|
293
|
-
# @raise [Wreq::ForkError] if the
|
|
297
|
+
# @raise [Wreq::ForkError] if the client or runtime belongs to the parent process
|
|
294
298
|
def request(method, url, **options)
|
|
295
299
|
end
|
|
296
300
|
|
|
@@ -324,7 +328,7 @@ unless defined?(Wreq)
|
|
|
324
328
|
# @return [Wreq::Response] HTTP response
|
|
325
329
|
# @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
|
|
326
330
|
# value cannot be converted, validated, or built
|
|
327
|
-
# @raise [Wreq::ForkError] if the
|
|
331
|
+
# @raise [Wreq::ForkError] if the client or runtime belongs to the parent process
|
|
328
332
|
def get(url, **options)
|
|
329
333
|
end
|
|
330
334
|
|
|
@@ -358,7 +362,7 @@ unless defined?(Wreq)
|
|
|
358
362
|
# @return [Wreq::Response] HTTP response
|
|
359
363
|
# @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
|
|
360
364
|
# value cannot be converted, validated, or built
|
|
361
|
-
# @raise [Wreq::ForkError] if the
|
|
365
|
+
# @raise [Wreq::ForkError] if the client or runtime belongs to the parent process
|
|
362
366
|
def head(url, **options)
|
|
363
367
|
end
|
|
364
368
|
|
|
@@ -392,7 +396,7 @@ unless defined?(Wreq)
|
|
|
392
396
|
# @return [Wreq::Response] HTTP response
|
|
393
397
|
# @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
|
|
394
398
|
# value cannot be converted, validated, or built
|
|
395
|
-
# @raise [Wreq::ForkError] if the
|
|
399
|
+
# @raise [Wreq::ForkError] if the client or runtime belongs to the parent process
|
|
396
400
|
def post(url, **options)
|
|
397
401
|
end
|
|
398
402
|
|
|
@@ -426,7 +430,7 @@ unless defined?(Wreq)
|
|
|
426
430
|
# @return [Wreq::Response] HTTP response
|
|
427
431
|
# @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
|
|
428
432
|
# value cannot be converted, validated, or built
|
|
429
|
-
# @raise [Wreq::ForkError] if the
|
|
433
|
+
# @raise [Wreq::ForkError] if the client or runtime belongs to the parent process
|
|
430
434
|
def put(url, **options)
|
|
431
435
|
end
|
|
432
436
|
|
|
@@ -460,7 +464,7 @@ unless defined?(Wreq)
|
|
|
460
464
|
# @return [Wreq::Response] HTTP response
|
|
461
465
|
# @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
|
|
462
466
|
# value cannot be converted, validated, or built
|
|
463
|
-
# @raise [Wreq::ForkError] if the
|
|
467
|
+
# @raise [Wreq::ForkError] if the client or runtime belongs to the parent process
|
|
464
468
|
def delete(url, **options)
|
|
465
469
|
end
|
|
466
470
|
|
|
@@ -494,7 +498,7 @@ unless defined?(Wreq)
|
|
|
494
498
|
# @return [Wreq::Response] HTTP response
|
|
495
499
|
# @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
|
|
496
500
|
# value cannot be converted, validated, or built
|
|
497
|
-
# @raise [Wreq::ForkError] if the
|
|
501
|
+
# @raise [Wreq::ForkError] if the client or runtime belongs to the parent process
|
|
498
502
|
def options(url, **options)
|
|
499
503
|
end
|
|
500
504
|
|
|
@@ -528,7 +532,7 @@ unless defined?(Wreq)
|
|
|
528
532
|
# @return [Wreq::Response] HTTP response
|
|
529
533
|
# @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
|
|
530
534
|
# value cannot be converted, validated, or built
|
|
531
|
-
# @raise [Wreq::ForkError] if the
|
|
535
|
+
# @raise [Wreq::ForkError] if the client or runtime belongs to the parent process
|
|
532
536
|
def trace(url, **options)
|
|
533
537
|
end
|
|
534
538
|
|
|
@@ -562,7 +566,7 @@ unless defined?(Wreq)
|
|
|
562
566
|
# @return [Wreq::Response] HTTP response
|
|
563
567
|
# @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
|
|
564
568
|
# value cannot be converted, validated, or built
|
|
565
|
-
# @raise [Wreq::ForkError] if the
|
|
569
|
+
# @raise [Wreq::ForkError] if the client or runtime belongs to the parent process
|
|
566
570
|
def patch(url, **options)
|
|
567
571
|
end
|
|
568
572
|
end
|
data/lib/wreq_ruby/cookie.rb
CHANGED
|
@@ -158,6 +158,11 @@ unless defined?(Wreq)
|
|
|
158
158
|
# Stores cookies for reuse across requests.
|
|
159
159
|
#
|
|
160
160
|
# Pass a Jar to Wreq::Client as `cookie_provider` to share its cookies.
|
|
161
|
+
# A jar belongs to the process that created it and cannot be inherited
|
|
162
|
+
# across `fork`.
|
|
163
|
+
#
|
|
164
|
+
# @note Fork safety Create a new jar in each worker. wreq-ruby does not
|
|
165
|
+
# copy cookies from an inherited jar.
|
|
161
166
|
class Jar
|
|
162
167
|
# Creates an empty cookie jar.
|
|
163
168
|
# @return [Wreq::Jar]
|
|
@@ -166,6 +171,7 @@ unless defined?(Wreq)
|
|
|
166
171
|
|
|
167
172
|
# Returns all stored cookies.
|
|
168
173
|
# @return [Array<Wreq::Cookie>]
|
|
174
|
+
# @raise [Wreq::ForkError] if the jar belongs to the parent process
|
|
169
175
|
def get_all
|
|
170
176
|
end
|
|
171
177
|
|
|
@@ -174,6 +180,7 @@ unless defined?(Wreq)
|
|
|
174
180
|
# @param url [String] URL that scopes the cookie
|
|
175
181
|
# @return [void]
|
|
176
182
|
# @raise [TypeError] if cookie is neither a String nor Wreq::Cookie
|
|
183
|
+
# @raise [Wreq::ForkError] if the jar belongs to the parent process
|
|
177
184
|
def add(cookie, url)
|
|
178
185
|
end
|
|
179
186
|
|
|
@@ -181,11 +188,13 @@ unless defined?(Wreq)
|
|
|
181
188
|
# @param name [String]
|
|
182
189
|
# @param url [String]
|
|
183
190
|
# @return [void]
|
|
191
|
+
# @raise [Wreq::ForkError] if the jar belongs to the parent process
|
|
184
192
|
def remove(name, url)
|
|
185
193
|
end
|
|
186
194
|
|
|
187
195
|
# Clear all cookies from the jar.
|
|
188
196
|
# @return [void]
|
|
197
|
+
# @raise [Wreq::ForkError] if the jar belongs to the parent process
|
|
189
198
|
def clear
|
|
190
199
|
end
|
|
191
200
|
end
|
data/lib/wreq_ruby/error.rb
CHANGED
|
@@ -11,15 +11,17 @@ unless defined?(Wreq)
|
|
|
11
11
|
# Memory allocation failed.
|
|
12
12
|
class MemoryError < StandardError; end
|
|
13
13
|
|
|
14
|
-
# The child process
|
|
14
|
+
# The child process tried to use native state created by its parent.
|
|
15
15
|
#
|
|
16
|
-
# Tokio worker threads do not survive fork
|
|
17
|
-
#
|
|
18
|
-
#
|
|
16
|
+
# Tokio worker threads do not survive fork. Inherited connection pools,
|
|
17
|
+
# locks, channels, and response state are also unsafe to use. wreq-ruby
|
|
18
|
+
# raises this error before exposing them. Loading the gem before fork is
|
|
19
|
+
# supported, but native-backed objects must be created in each worker.
|
|
19
20
|
#
|
|
20
21
|
# @example
|
|
22
|
+
# client = Wreq::Client.new
|
|
21
23
|
# Process.fork do
|
|
22
|
-
#
|
|
24
|
+
# client.get("https://example.com") # Raises in the child.
|
|
23
25
|
# end
|
|
24
26
|
# @see https://github.com/SearchApi/wreq-ruby/blob/main/docs/fork-safety.md
|
|
25
27
|
class ForkError < RuntimeError; end
|
data/lib/wreq_ruby/response.rb
CHANGED
|
@@ -8,8 +8,12 @@ unless defined?(Wreq)
|
|
|
8
8
|
# access to HTTP response data including status codes, headers, body
|
|
9
9
|
# content, and streaming capabilities.
|
|
10
10
|
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
11
|
+
# A response belongs to the process that received it. Accessing its metadata
|
|
12
|
+
# or body after inheriting it from a parent raises Wreq::ForkError.
|
|
13
|
+
#
|
|
14
|
+
# @note Fork safety Keep each response in the process that received it.
|
|
15
|
+
# Issue a new request in the worker instead of carrying a response through
|
|
16
|
+
# `fork`.
|
|
13
17
|
#
|
|
14
18
|
# @example Basic response handling
|
|
15
19
|
# response = client.get("https://api.example.com")
|
|
@@ -29,6 +33,7 @@ unless defined?(Wreq)
|
|
|
29
33
|
# Get the HTTP status code as an integer.
|
|
30
34
|
#
|
|
31
35
|
# @return [Integer] Status code (e.g., 200, 404, 500)
|
|
36
|
+
# @raise [Wreq::ForkError] if the response belongs to the parent process
|
|
32
37
|
# @example
|
|
33
38
|
# response.code # => 200
|
|
34
39
|
def code
|
|
@@ -37,6 +42,7 @@ unless defined?(Wreq)
|
|
|
37
42
|
# Get the HTTP status code object.
|
|
38
43
|
#
|
|
39
44
|
# @return [Wreq::StatusCode] Status code wrapper with helper methods
|
|
45
|
+
# @raise [Wreq::ForkError] if the response belongs to the parent process
|
|
40
46
|
# @example
|
|
41
47
|
# status = response.status
|
|
42
48
|
# status.success? # => true
|
|
@@ -46,6 +52,7 @@ unless defined?(Wreq)
|
|
|
46
52
|
# Get the HTTP protocol version used.
|
|
47
53
|
#
|
|
48
54
|
# @return [Wreq::Version] HTTP version (HTTP/1.1, HTTP/2, etc.)
|
|
55
|
+
# @raise [Wreq::ForkError] if the response belongs to the parent process
|
|
49
56
|
# @example
|
|
50
57
|
# response.version # => Wreq::Version::HTTP_11
|
|
51
58
|
def version
|
|
@@ -54,6 +61,7 @@ unless defined?(Wreq)
|
|
|
54
61
|
# Get the final URL after redirects.
|
|
55
62
|
#
|
|
56
63
|
# @return [String] The final URL
|
|
64
|
+
# @raise [Wreq::ForkError] if the response belongs to the parent process
|
|
57
65
|
# @example
|
|
58
66
|
# response.url # => "https://example.com/final-page"
|
|
59
67
|
def url
|
|
@@ -62,6 +70,7 @@ unless defined?(Wreq)
|
|
|
62
70
|
# Get the content length if known.
|
|
63
71
|
#
|
|
64
72
|
# @return [Integer, nil] Content length in bytes, or nil if unknown
|
|
73
|
+
# @raise [Wreq::ForkError] if the response belongs to the parent process
|
|
65
74
|
# @example
|
|
66
75
|
# response.content_length # => 1024
|
|
67
76
|
def content_length
|
|
@@ -75,6 +84,7 @@ unless defined?(Wreq)
|
|
|
75
84
|
# response or a later snapshot, and object identity is not guaranteed.
|
|
76
85
|
#
|
|
77
86
|
# @return [Wreq::Headers] Response headers
|
|
87
|
+
# @raise [Wreq::ForkError] if the response belongs to the parent process
|
|
78
88
|
# @example
|
|
79
89
|
# response.headers.get("content-type") # => "application/json"
|
|
80
90
|
def headers
|
|
@@ -83,6 +93,7 @@ unless defined?(Wreq)
|
|
|
83
93
|
# Get the local socket address.
|
|
84
94
|
#
|
|
85
95
|
# @return [String, nil] Local address (e.g., "127.0.0.1:54321"), or nil
|
|
96
|
+
# @raise [Wreq::ForkError] if the response belongs to the parent process
|
|
86
97
|
# @example
|
|
87
98
|
# response.local_addr # => "192.168.1.100:54321"
|
|
88
99
|
def local_addr
|
|
@@ -91,6 +102,7 @@ unless defined?(Wreq)
|
|
|
91
102
|
# Get the remote socket address.
|
|
92
103
|
#
|
|
93
104
|
# @return [String, nil] Remote address (e.g., "93.184.216.34:443"), or nil
|
|
105
|
+
# @raise [Wreq::ForkError] if the response belongs to the parent process
|
|
94
106
|
# @example
|
|
95
107
|
# response.remote_addr # => "93.184.216.34:443"
|
|
96
108
|
def remote_addr
|
|
@@ -101,6 +113,7 @@ unless defined?(Wreq)
|
|
|
101
113
|
# Invalid `Set-Cookie` values are skipped.
|
|
102
114
|
#
|
|
103
115
|
# @return [Array<Wreq::Cookie>] Parsed response cookies
|
|
116
|
+
# @raise [Wreq::ForkError] if the response belongs to the parent process
|
|
104
117
|
# @example
|
|
105
118
|
# response.cookies.each do |cookie|
|
|
106
119
|
# puts "#{cookie.name}=#{cookie.value}"
|
|
@@ -110,7 +123,7 @@ unless defined?(Wreq)
|
|
|
110
123
|
|
|
111
124
|
# Get the response bytes as a binary string.
|
|
112
125
|
# @return [String] Response body as binary data
|
|
113
|
-
# @raise [Wreq::ForkError] if the
|
|
126
|
+
# @raise [Wreq::ForkError] if the response belongs to the parent process
|
|
114
127
|
# @example
|
|
115
128
|
# binary_data = response.bytes
|
|
116
129
|
# puts binary_data.size # => 1024
|
|
@@ -126,7 +139,7 @@ unless defined?(Wreq)
|
|
|
126
139
|
# html = response.text("ISO-8859-1")
|
|
127
140
|
# puts html
|
|
128
141
|
# @raise [Wreq::DecodingError] if body cannot be decoded with the specified encoding
|
|
129
|
-
# @raise [Wreq::ForkError] if the
|
|
142
|
+
# @raise [Wreq::ForkError] if the response belongs to the parent process
|
|
130
143
|
def text(default_encoding = "UTF-8")
|
|
131
144
|
end
|
|
132
145
|
|
|
@@ -137,7 +150,7 @@ unless defined?(Wreq)
|
|
|
137
150
|
#
|
|
138
151
|
# @return [Object] Parsed JSON (Hash, Array, String, Integer, Float, Boolean, nil)
|
|
139
152
|
# @raise [Wreq::DecodingError] if body is not valid JSON
|
|
140
|
-
# @raise [Wreq::ForkError] if the
|
|
153
|
+
# @raise [Wreq::ForkError] if the response belongs to the parent process
|
|
141
154
|
# @example
|
|
142
155
|
# data = response.json
|
|
143
156
|
# puts data["key"]
|
|
@@ -155,7 +168,7 @@ unless defined?(Wreq)
|
|
|
155
168
|
# @raise [LocalJumpError] if called without a block
|
|
156
169
|
# @raise [Wreq::TimeoutError, Wreq::BodyError, Wreq::ConnectionResetError, Wreq::RequestError]
|
|
157
170
|
# if streaming fails while reading the response body
|
|
158
|
-
# @raise [Wreq::ForkError] if the
|
|
171
|
+
# @raise [Wreq::ForkError] if the response belongs to the parent process
|
|
159
172
|
# @example Save response to file
|
|
160
173
|
# File.open("output.bin", "wb") do |f|
|
|
161
174
|
# response.chunks { |chunk| f.write(chunk) }
|
|
@@ -172,7 +185,7 @@ unless defined?(Wreq)
|
|
|
172
185
|
# Close the response and free associated resources.
|
|
173
186
|
#
|
|
174
187
|
# @return [void]
|
|
175
|
-
# @raise [Wreq::ForkError] if the
|
|
188
|
+
# @raise [Wreq::ForkError] if the response belongs to the parent process
|
|
176
189
|
# @example
|
|
177
190
|
# response.close
|
|
178
191
|
def close
|
|
@@ -186,6 +199,7 @@ unless defined?(Wreq)
|
|
|
186
199
|
#
|
|
187
200
|
# @return [Wreq::TlsInfo, nil] TLS information for this response, or +nil+
|
|
188
201
|
# when unavailable
|
|
202
|
+
# @raise [Wreq::ForkError] if the response belongs to the parent process
|
|
189
203
|
# @example
|
|
190
204
|
# client = Wreq::Client.new(tls_info: true)
|
|
191
205
|
# response = client.get("https://example.com")
|
|
@@ -208,6 +222,7 @@ module Wreq
|
|
|
208
222
|
# Returns the response body as a string.
|
|
209
223
|
#
|
|
210
224
|
# @return [String] Response body text
|
|
225
|
+
# @raise [Wreq::ForkError] if the response belongs to the parent process
|
|
211
226
|
# @example
|
|
212
227
|
# puts response.to_s
|
|
213
228
|
# puts response
|
|
@@ -221,6 +236,7 @@ module Wreq
|
|
|
221
236
|
# Format: #<Wreq::Response STATUS content-type="..." body=SIZE>
|
|
222
237
|
#
|
|
223
238
|
# @return [String] Compact formatted response information
|
|
239
|
+
# @raise [Wreq::ForkError] if the response belongs to the parent process
|
|
224
240
|
# @example
|
|
225
241
|
# p response
|
|
226
242
|
# # => #<Wreq::Response 200 content-type="application/json" body=456B>
|