wreq 1.2.11 → 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.
@@ -0,0 +1,51 @@
1
+ # Fork safety
2
+
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.
@@ -0,0 +1,134 @@
1
+ # Interrupt handling policy
2
+
3
+ wreq-ruby must not construct or raise Ruby's built-in `Interrupt` to report a
4
+ request cancellation. This rule applies to the Rust extension and to Ruby
5
+ wrappers in this repository. A pull request that turns a wreq-owned
6
+ cancellation into the built-in class must not be merged.
7
+
8
+ Represent native cancellation as a Rust value until the Ruby-owned calling
9
+ thread has reacquired the GVL. Then map a wreq-owned request cancellation to
10
+ `Wreq::InterruptError`:
11
+
12
+ ```ruby
13
+ Wreq::InterruptError < Interrupt
14
+ ```
15
+
16
+ Keep this class outside `StandardError`. A broad transport rescue such as
17
+ `rescue StandardError` must not swallow an interruption.
18
+
19
+ ## Why `Interrupt` is reserved
20
+
21
+ Ruby documents `Interrupt` as the exception raised for an interrupt signal,
22
+ usually when the user presses Control-C. Its hierarchy is:
23
+
24
+ ```text
25
+ Exception
26
+ └── SignalException
27
+ └── Interrupt
28
+ ```
29
+
30
+ `Interrupt` is not a `StandardError`. Ruby's default `rescue` catches
31
+ `StandardError`, so it does not catch `Interrupt` or `Wreq::InterruptError`.
32
+ Code that explicitly uses `rescue Interrupt` catches both because
33
+ `Wreq::InterruptError` is a subclass.
34
+
35
+ The exact built-in class therefore carries Ruby-level control-flow meaning. If
36
+ wreq creates that class for its own cancellation, callers cannot tell whether
37
+ Ruby delivered an interrupt or the HTTP library cancelled a request. A
38
+ library-specific subclass preserves that distinction while keeping the
39
+ interruption outside ordinary transport errors.
40
+
41
+ ## Required behavior
42
+
43
+ | Event | wreq-ruby behavior |
44
+ | --- | --- |
45
+ | Ruby raises its built-in `Interrupt`, including an exception supplied through `Thread#raise` | Propagate the original exception. Do not replace or wrap it. |
46
+ | `Thread#kill`, `Thread#terminate`, or `Thread#exit` stops a thread | Let Ruby perform the fatal thread termination. The native unblock callback may request cancellation, but wreq must not translate the event into `Interrupt`. |
47
+ | wreq's native cancellation path finishes without a pending Ruby exception | Raise `Wreq::InterruptError`. |
48
+ | A connection, timeout, protocol, or other transport operation fails | Raise the matching wreq transport error under `StandardError`. |
49
+
50
+ Ruby's implementation also makes an important distinction here.
51
+ `Thread#raise` queues the exception chosen by the caller. `Thread#kill` queues
52
+ Ruby's internal fatal thread-kill event instead of an `Interrupt` object, and
53
+ its termination is asynchronous. Once a no-GVL callback returns, Ruby handles
54
+ that fatal event after reacquiring the GVL and before the native call can return
55
+ normally to wreq's error mapper.
56
+
57
+ ## Native no-GVL boundary
58
+
59
+ There are two separate rules at this boundary:
60
+
61
+ 1. A Tokio worker, other Rust background thread, no-GVL callback, or UBF must
62
+ not construct or raise any Ruby exception.
63
+ 2. Rust code running on the Ruby-owned calling thread with the GVL may construct
64
+ Ruby exceptions, but it must not turn a wreq-owned cancellation into Ruby's
65
+ built-in `Interrupt`.
66
+
67
+ Requests run through `rb_thread_call_without_gvl`. Ruby's C API documents this
68
+ sequence:
69
+
70
+ 1. Handle pending interrupts.
71
+ 2. Release the GVL.
72
+ 3. Run the native callback.
73
+ 4. Reacquire the GVL.
74
+ 5. Handle interrupts received while the callback was running.
75
+
76
+ Ruby may call the unblock function, or UBF, when another thread interacts with
77
+ the blocked thread. The UBF is a request to stop the native operation. It does
78
+ not identify which Ruby exception, if any, is pending.
79
+
80
+ The UBF in [`src/gvl.rs`](../src/gvl.rs) must only signal cancellation. It must
81
+ not call Ruby APIs or raise an exception while the GVL is released. The request
82
+ future returns its result as a Rust value. Only after the no-GVL call returns
83
+ to the Ruby-owned thread with the GVL may [`src/rt.rs`](../src/rt.rs) map a
84
+ wreq-owned cancellation to the `Wreq::InterruptError` defined in
85
+ [`src/error.rs`](../src/error.rs).
86
+
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.
91
+
92
+ These forms are forbidden for wreq-owned cancellation:
93
+
94
+ ```rust
95
+ MagnusError::new(ruby.exception_interrupt(), "request interrupted")
96
+ ```
97
+
98
+ ```ruby
99
+ raise Interrupt, "request interrupted"
100
+ ```
101
+
102
+ Using `exception_interrupt` as the parent when defining
103
+ `Wreq::InterruptError` is still required. Using it as the class passed to
104
+ `MagnusError::new` is not.
105
+
106
+ ## Review checklist
107
+
108
+ - Reject direct construction or raising of Ruby's built-in `Interrupt` for a
109
+ wreq-owned cancellation.
110
+ - Keep `Wreq::InterruptError` as a direct subclass of `Interrupt`.
111
+ - Keep Ruby API calls and exception construction out of the no-GVL callback
112
+ and UBF.
113
+ - Preserve an exception supplied by Ruby through `Thread#raise`.
114
+ - Do not turn `Thread#kill`, `Thread#terminate`, or `Thread#exit` into a new
115
+ exception.
116
+ - Test the real cancellation path, the exception hierarchy, and the
117
+ `StandardError` boundary when changing this code.
118
+
119
+ ## Ruby references
120
+
121
+ - [Ruby `Interrupt`](https://docs.ruby-lang.org/en/3.4/Interrupt.html) explains
122
+ that the class represents an interrupt signal, usually Control-C, and
123
+ inherits from `SignalException`.
124
+ - [Ruby's built-in exception hierarchy](https://docs.ruby-lang.org/en/4.0/Exception.html#class-Exception-label-Built-In+Exception+Class+Hierarchy)
125
+ shows that `SignalException` and `StandardError` are separate branches.
126
+ - [`Thread#raise`](https://docs.ruby-lang.org/en/4.0/Thread.html#method-i-raise)
127
+ raises the caller-supplied exception in another thread.
128
+ - [`Thread#kill`](https://docs.ruby-lang.org/en/4.0/Thread.html#method-i-kill)
129
+ documents asynchronous termination and its `terminate` and `exit` aliases.
130
+ - [`rb_thread_call_without_gvl`](https://docs.ruby-lang.org/capi/en/master/d6/dfb/include_2ruby_2thread_8h.html)
131
+ documents interrupt checks, GVL reacquisition, UBF cancellation, and the
132
+ restriction on Ruby API calls from no-GVL callbacks.
133
+ - [Issue #111](https://github.com/SearchApi/wreq-ruby/issues/111) contains the
134
+ original error-hierarchy discussion.
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "openssl"
5
+ require_relative "../lib/wreq"
6
+
7
+ url = ARGV.fetch(0, "https://example.com")
8
+ client = Wreq::Client.new(tls_info: true)
9
+ response = client.get(url)
10
+ tls_info = response.tls_info
11
+ response.close
12
+
13
+ abort "TLS information is unavailable for #{url}" unless tls_info
14
+
15
+ p tls_info
16
+
17
+ if (der = tls_info.peer_certificate)
18
+ certificate = OpenSSL::X509::Certificate.new(der)
19
+ puts "Subject: #{certificate.subject}"
20
+ puts "Issuer: #{certificate.issuer}"
21
+ puts "Valid from: #{certificate.not_before}"
22
+ puts "Valid until: #{certificate.not_after}"
23
+ end
24
+
25
+ chain = tls_info.peer_certificate_chain
26
+ chain_size = chain ? chain.length : "unavailable"
27
+ puts "Certificate chain: #{chain_size}"
data/lib/wreq.rb CHANGED
@@ -12,12 +12,40 @@ require_relative "wreq_ruby/http"
12
12
  require_relative "wreq_ruby/emulate"
13
13
  require_relative "wreq_ruby/client"
14
14
  require_relative "wreq_ruby/response"
15
+ require_relative "wreq_ruby/tls"
15
16
  require_relative "wreq_ruby/body"
16
17
  require_relative "wreq_ruby/header"
17
18
  require_relative "wreq_ruby/error"
18
19
  require_relative "wreq_ruby/cookie"
19
20
 
20
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
21
49
  module Wreq
22
50
  # Current wreq gem version.
23
51
  # @return [String]
@@ -60,6 +88,7 @@ unless defined?(Wreq)
60
88
  # @return [Wreq::Response] HTTP response
61
89
  # @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
62
90
  # value cannot be converted, validated, or built
91
+ # @raise [Wreq::ForkError] if the child inherited an initialized wreq-ruby runtime
63
92
  def self.request(method, url, **options)
64
93
  end
65
94
 
@@ -93,6 +122,7 @@ unless defined?(Wreq)
93
122
  # @return [Wreq::Response] HTTP response
94
123
  # @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
95
124
  # value cannot be converted, validated, or built
125
+ # @raise [Wreq::ForkError] if the child inherited an initialized wreq-ruby runtime
96
126
  def self.get(url, **options)
97
127
  end
98
128
 
@@ -126,6 +156,7 @@ unless defined?(Wreq)
126
156
  # @return [Wreq::Response] HTTP response
127
157
  # @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
128
158
  # value cannot be converted, validated, or built
159
+ # @raise [Wreq::ForkError] if the child inherited an initialized wreq-ruby runtime
129
160
  def self.head(url, **options)
130
161
  end
131
162
 
@@ -159,6 +190,7 @@ unless defined?(Wreq)
159
190
  # @return [Wreq::Response] HTTP response
160
191
  # @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
161
192
  # value cannot be converted, validated, or built
193
+ # @raise [Wreq::ForkError] if the child inherited an initialized wreq-ruby runtime
162
194
  def self.post(url, **options)
163
195
  end
164
196
 
@@ -192,6 +224,7 @@ unless defined?(Wreq)
192
224
  # @return [Wreq::Response] HTTP response
193
225
  # @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
194
226
  # value cannot be converted, validated, or built
227
+ # @raise [Wreq::ForkError] if the child inherited an initialized wreq-ruby runtime
195
228
  def self.put(url, **options)
196
229
  end
197
230
 
@@ -225,6 +258,7 @@ unless defined?(Wreq)
225
258
  # @return [Wreq::Response] HTTP response
226
259
  # @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
227
260
  # value cannot be converted, validated, or built
261
+ # @raise [Wreq::ForkError] if the child inherited an initialized wreq-ruby runtime
228
262
  def self.delete(url, **options)
229
263
  end
230
264
 
@@ -258,6 +292,7 @@ unless defined?(Wreq)
258
292
  # @return [Wreq::Response] HTTP response
259
293
  # @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
260
294
  # value cannot be converted, validated, or built
295
+ # @raise [Wreq::ForkError] if the child inherited an initialized wreq-ruby runtime
261
296
  def self.options(url, **options)
262
297
  end
263
298
 
@@ -291,6 +326,7 @@ unless defined?(Wreq)
291
326
  # @return [Wreq::Response] HTTP response
292
327
  # @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
293
328
  # value cannot be converted, validated, or built
329
+ # @raise [Wreq::ForkError] if the child inherited an initialized wreq-ruby runtime
294
330
  def self.trace(url, **options)
295
331
  end
296
332
 
@@ -324,6 +360,7 @@ unless defined?(Wreq)
324
360
  # @return [Wreq::Response] HTTP response
325
361
  # @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
326
362
  # value cannot be converted, validated, or built
363
+ # @raise [Wreq::ForkError] if the child inherited an initialized wreq-ruby runtime
327
364
  def self.patch(url, **options)
328
365
  end
329
366
  end
@@ -17,6 +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 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`.
20
26
  class BodySender
21
27
  # Create a bounded request-body sender.
22
28
  #
@@ -33,6 +39,7 @@ unless defined?(Wreq)
33
39
  # @param data [String] binary chunk
34
40
  # @return [nil]
35
41
  # @raise [IOError] if the sender or receiving side is closed
42
+ # @raise [Wreq::ForkError] if the sender or runtime belongs to the parent process
36
43
  def push(data)
37
44
  end
38
45
 
@@ -41,6 +48,7 @@ unless defined?(Wreq)
41
48
  # This operation is idempotent.
42
49
  #
43
50
  # @return [nil]
51
+ # @raise [Wreq::ForkError] if the sender belongs to the parent process
44
52
  def close
45
53
  end
46
54
 
@@ -50,6 +58,7 @@ unless defined?(Wreq)
50
58
  # the receiving side.
51
59
  #
52
60
  # @return [Boolean]
61
+ # @raise [Wreq::ForkError] if the sender belongs to the parent process
53
62
  def closed?
54
63
  end
55
64
  end
@@ -17,6 +17,15 @@ 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 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.
28
+ #
20
29
  # @example Basic usage
21
30
  # client = Wreq::Client.new
22
31
  # # Use client for HTTP requests
@@ -130,6 +139,11 @@ unless defined?(Wreq)
130
139
  # including self-signed or expired ones. Should only be disabled
131
140
  # for testing purposes.
132
141
  #
142
+ # @param tls_info [Boolean, nil] Retain peer certificate data for HTTPS
143
+ # responses. When true, {Wreq::Response#tls_info} may return a
144
+ # {Wreq::TlsInfo} object. Disabled by default because retaining
145
+ # certificate data uses additional memory.
146
+ #
133
147
  # @param no_proxy [Boolean, nil] Disable use of any configured proxy
134
148
  # for this client, even if proxy settings are detected from the
135
149
  # environment.
@@ -165,7 +179,7 @@ unless defined?(Wreq)
165
179
  # value cannot be converted or validated.
166
180
  # @raise [Wreq::BuilderError, Wreq::TlsError] if the native client cannot
167
181
  # be initialized.
168
- #
182
+ # @raise [Wreq::ForkError] if :cookie_provider belongs to a parent process.
169
183
  # @example Minimal client
170
184
  # client = Wreq::Client.new
171
185
  #
@@ -280,6 +294,7 @@ unless defined?(Wreq)
280
294
  # or unavailable on the current platform
281
295
  # @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
282
296
  # value cannot be converted, validated, or built
297
+ # @raise [Wreq::ForkError] if the client or runtime belongs to the parent process
283
298
  def request(method, url, **options)
284
299
  end
285
300
 
@@ -313,6 +328,7 @@ unless defined?(Wreq)
313
328
  # @return [Wreq::Response] HTTP response
314
329
  # @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
315
330
  # value cannot be converted, validated, or built
331
+ # @raise [Wreq::ForkError] if the client or runtime belongs to the parent process
316
332
  def get(url, **options)
317
333
  end
318
334
 
@@ -346,6 +362,7 @@ unless defined?(Wreq)
346
362
  # @return [Wreq::Response] HTTP response
347
363
  # @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
348
364
  # value cannot be converted, validated, or built
365
+ # @raise [Wreq::ForkError] if the client or runtime belongs to the parent process
349
366
  def head(url, **options)
350
367
  end
351
368
 
@@ -379,6 +396,7 @@ unless defined?(Wreq)
379
396
  # @return [Wreq::Response] HTTP response
380
397
  # @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
381
398
  # value cannot be converted, validated, or built
399
+ # @raise [Wreq::ForkError] if the client or runtime belongs to the parent process
382
400
  def post(url, **options)
383
401
  end
384
402
 
@@ -412,6 +430,7 @@ unless defined?(Wreq)
412
430
  # @return [Wreq::Response] HTTP response
413
431
  # @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
414
432
  # value cannot be converted, validated, or built
433
+ # @raise [Wreq::ForkError] if the client or runtime belongs to the parent process
415
434
  def put(url, **options)
416
435
  end
417
436
 
@@ -445,6 +464,7 @@ unless defined?(Wreq)
445
464
  # @return [Wreq::Response] HTTP response
446
465
  # @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
447
466
  # value cannot be converted, validated, or built
467
+ # @raise [Wreq::ForkError] if the client or runtime belongs to the parent process
448
468
  def delete(url, **options)
449
469
  end
450
470
 
@@ -478,6 +498,7 @@ unless defined?(Wreq)
478
498
  # @return [Wreq::Response] HTTP response
479
499
  # @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
480
500
  # value cannot be converted, validated, or built
501
+ # @raise [Wreq::ForkError] if the client or runtime belongs to the parent process
481
502
  def options(url, **options)
482
503
  end
483
504
 
@@ -511,6 +532,7 @@ unless defined?(Wreq)
511
532
  # @return [Wreq::Response] HTTP response
512
533
  # @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
513
534
  # value cannot be converted, validated, or built
535
+ # @raise [Wreq::ForkError] if the client or runtime belongs to the parent process
514
536
  def trace(url, **options)
515
537
  end
516
538
 
@@ -544,6 +566,7 @@ unless defined?(Wreq)
544
566
  # @return [Wreq::Response] HTTP response
545
567
  # @raise [TypeError, ArgumentError, Wreq::BuilderError] if a known option
546
568
  # value cannot be converted, validated, or built
569
+ # @raise [Wreq::ForkError] if the client or runtime belongs to the parent process
547
570
  def patch(url, **options)
548
571
  end
549
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,6 +11,21 @@ unless defined?(Wreq)
11
11
  # Memory allocation failed.
12
12
  class MemoryError < StandardError; end
13
13
 
14
+ # The child process tried to use native state created by its parent.
15
+ #
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.
20
+ #
21
+ # @example
22
+ # client = Wreq::Client.new
23
+ # Process.fork do
24
+ # client.get("https://example.com") # Raises in the child.
25
+ # end
26
+ # @see https://github.com/SearchApi/wreq-ruby/blob/main/docs/fork-safety.md
27
+ class ForkError < RuntimeError; end
28
+
14
29
  # Network connection errors
15
30
 
16
31
  # Connection to the server failed.