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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dfff227b1aca526697d055128ca828a911403c337796d840e45667554ece669a
4
- data.tar.gz: ce36c588548df0a31e769fa54b226561d2e5b84cf0dd681c0102575503803e86
3
+ metadata.gz: 45253084ba060aa37efcdd71fdea4302d55a8ca658b9945385dd632a241452f9
4
+ data.tar.gz: 817dd6fd547b6f1f5dcf074f707487cf1efb6b7f42324c24a27a8afa444f2868
5
5
  SHA512:
6
- metadata.gz: 77c99a185649091bc2c537efbdf4490404ef113c250a733ab47c3f49ac574659b455e78a3a77efe2f95ca9739b88b0f8ed6452b51d8583b01de7aaf4d88a1d0c
7
- data.tar.gz: df326eb44d85324d981fb9a624f52a87d30871836f093dbde2ecd9c8b7b9b76cb786e8f2c2cd15aed1d2237fe8b9fc33a34803bf74972fb15a1bb9e8296071e5
6
+ metadata.gz: 1af6ea1ce815b1b64ff19ea238831b57176df435b2d307f3050a4b33b977370b56891d1c56c334a173fc5e11e694527fe30a7ebb418720033ccffd484dc5381c
7
+ data.tar.gz: 88a15b449a5e6fbeb62a57d73fa28d8734d5453c0a5ecfca0d295f2b33f4d23481ad34bdae0acf8d78d622c24a98f5ed2f43c35fb0d24d1d2752aaa88467d0f5
data/Cargo.lock CHANGED
@@ -1489,7 +1489,7 @@ dependencies = [
1489
1489
 
1490
1490
  [[package]]
1491
1491
  name = "wreq-ruby"
1492
- version = "1.2.12"
1492
+ version = "1.2.13"
1493
1493
  dependencies = [
1494
1494
  "arc-swap",
1495
1495
  "bytes",
data/Cargo.toml CHANGED
@@ -5,7 +5,7 @@ authors = ["SearchApi <support@searchapi.io>"]
5
5
  homepage = "https://github.com/SearchApi/wreq-ruby"
6
6
  repository = "https://github.com/SearchApi/wreq-ruby"
7
7
  edition = "2024"
8
- version = "1.2.12"
8
+ version = "1.2.13"
9
9
  rust-version = "1.95"
10
10
 
11
11
  [lib]
data/docs/fork-safety.md CHANGED
@@ -1,34 +1,51 @@
1
1
  # Fork safety
2
2
 
3
- ## Why inherited clients are rejected
4
-
5
- wreq-ruby uses a process-wide Tokio runtime and connection pool. `fork` copies
6
- the parent's memory, but only the thread that called `fork` continues in the
7
- child. Tokio's worker threads are gone, and its inherited tasks, locks, and
8
- connections are not safe to reuse.
9
-
10
- If the parent has already loaded wreq-ruby, native HTTP operations in the child
11
- raise `Wreq::ForkError`. This applies to new and existing clients, module
12
- request methods, streaming request bodies, and response methods backed by native
13
- state. Retrying the operation in the same child raises the same error. Read-only
14
- response metadata such as status, headers, and captured TLS information remains
15
- available.
16
-
17
- The parent can continue using its clients. When inherited Ruby objects are
18
- collected in the child, their native runtime state is left for the operating
19
- system to reclaim when the process exits.
20
-
21
- ## HTTP work in forked children is unsupported
22
-
23
- A process created with `fork` must not start or continue HTTP work through
24
- wreq-ruby, even when it first loads the extension after the fork. If the parent
25
- loaded wreq-ruby, native HTTP operations in the child raise `Wreq::ForkError`.
26
-
27
- When the extension was not present in the parent, no wreq-ruby state or fork
28
- marker reaches the child. The extension cannot reliably distinguish that child
29
- from a newly started process, so this unsupported path cannot guarantee a Ruby
30
- error and may fail inside platform libraries.
31
-
32
- Prefork servers should use an `exec`- or spawn-based worker model when workers
33
- need wreq-ruby. Requiring the extension again does not reset inherited runtime
34
- state, and there is no `after_fork!` hook.
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.
@@ -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 this conversion centralized in `rt::try_block_on`. Request, response, and
88
- body operations may call `try_block_on`, but they must not construct their own
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 from its parent
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 from its parent
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 from its parent
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 from its parent
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 from its parent
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 from its parent
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 from its parent
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 from its parent
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 from its parent
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
@@ -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 or using a sender raises Wreq::ForkError if the child inherited
21
- # wreq-ruby from its parent.
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 child inherited wreq-ruby from its parent
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 child inherited wreq-ruby from its parent
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 child inherited wreq-ruby from its parent
61
+ # @raise [Wreq::ForkError] if the sender belongs to the parent process
59
62
  def closed?
60
63
  end
61
64
  end
@@ -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 child process cannot create or use a client if it inherited wreq-ruby
21
- # from its parent. These calls raise Wreq::ForkError before accessing the
22
- # native runtime. Require wreq after the worker has been forked.
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 the child inherited wreq-ruby from its parent
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 child inherited wreq-ruby from its parent
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 child inherited wreq-ruby from its parent
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 child inherited wreq-ruby from its parent
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 child inherited wreq-ruby from its parent
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 child inherited wreq-ruby from its parent
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 child inherited wreq-ruby from its parent
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 child inherited wreq-ruby from its parent
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 child inherited wreq-ruby from its parent
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 child inherited wreq-ruby from its parent
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
@@ -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
@@ -11,15 +11,17 @@ unless defined?(Wreq)
11
11
  # Memory allocation failed.
12
12
  class MemoryError < StandardError; end
13
13
 
14
- # The child process inherited wreq-ruby from its parent.
14
+ # The child process tried to use native state created by its parent.
15
15
  #
16
- # Tokio worker threads do not survive fork, and inherited pooled
17
- # connections are not safe to reuse. This error is raised before a child
18
- # can access that state.
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
- # Wreq::Client.new # Raises if the parent loaded wreq-ruby.
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
@@ -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
- # Body methods raise Wreq::ForkError if the child inherited wreq-ruby from
12
- # its parent.
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 child inherited wreq-ruby from its parent
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 child inherited wreq-ruby from its parent
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 child inherited wreq-ruby from its parent
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 child inherited wreq-ruby from its parent
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 child inherited wreq-ruby from its parent
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>