ethon 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +698 -2
  3. data/README.md +183 -40
  4. data/ethon.gemspec +9 -1
  5. data/lib/ethon/curl.rb +22 -0
  6. data/lib/ethon/curls/classes.rb +284 -12
  7. data/lib/ethon/curls/codes.rb +28 -9
  8. data/lib/ethon/curls/constants.rb +61 -4
  9. data/lib/ethon/curls/form_options.rb +2 -1
  10. data/lib/ethon/curls/functions.rb +60 -11
  11. data/lib/ethon/curls/infos.rb +104 -5
  12. data/lib/ethon/curls/introspection.rb +139 -0
  13. data/lib/ethon/curls/introspection_constants.rb +42 -0
  14. data/lib/ethon/curls/mime.rb +187 -0
  15. data/lib/ethon/curls/optional.rb +14 -0
  16. data/lib/ethon/curls/options.rb +298 -71
  17. data/lib/ethon/curls/settings.rb +12 -3
  18. data/lib/ethon/curls/share.rb +165 -0
  19. data/lib/ethon/curls/share_constants.rb +47 -0
  20. data/lib/ethon/curls/url.rb +171 -0
  21. data/lib/ethon/curls/url_constants.rb +78 -0
  22. data/lib/ethon/easy/callbacks.rb +178 -46
  23. data/lib/ethon/easy/features.rb +4 -1
  24. data/lib/ethon/easy/files.rb +113 -0
  25. data/lib/ethon/easy/form.rb +65 -6
  26. data/lib/ethon/easy/header.rb +57 -15
  27. data/lib/ethon/easy/headers_api.rb +79 -0
  28. data/lib/ethon/easy/http/actionable.rb +25 -3
  29. data/lib/ethon/easy/http/get.rb +5 -1
  30. data/lib/ethon/easy/http/post.rb +1 -0
  31. data/lib/ethon/easy/http/postable.rb +9 -2
  32. data/lib/ethon/easy/http/putable.rb +19 -4
  33. data/lib/ethon/easy/http.rb +16 -4
  34. data/lib/ethon/easy/informations.rb +51 -2
  35. data/lib/ethon/easy/mime.rb +29 -0
  36. data/lib/ethon/easy/mirror.rb +17 -4
  37. data/lib/ethon/easy/operations.rb +259 -11
  38. data/lib/ethon/easy/options.rb +327 -8
  39. data/lib/ethon/easy/queryable.rb +59 -14
  40. data/lib/ethon/easy/response_callbacks.rb +33 -12
  41. data/lib/ethon/easy/share.rb +28 -0
  42. data/lib/ethon/easy/url.rb +32 -0
  43. data/lib/ethon/easy/util.rb +19 -0
  44. data/lib/ethon/easy.rb +185 -10
  45. data/lib/ethon/errors/fork_safety.rb +10 -0
  46. data/lib/ethon/errors/form_add.rb +12 -0
  47. data/lib/ethon/errors/global_init.rb +1 -1
  48. data/lib/ethon/errors/header.rb +12 -0
  49. data/lib/ethon/errors/invalid_option.rb +3 -1
  50. data/lib/ethon/errors/invalid_value.rb +3 -1
  51. data/lib/ethon/errors/mime.rb +17 -0
  52. data/lib/ethon/errors/multi_add.rb +1 -1
  53. data/lib/ethon/errors/multi_assign.rb +12 -0
  54. data/lib/ethon/errors/multi_fdset.rb +1 -1
  55. data/lib/ethon/errors/multi_perform.rb +12 -0
  56. data/lib/ethon/errors/multi_poll.rb +12 -0
  57. data/lib/ethon/errors/multi_remove.rb +1 -1
  58. data/lib/ethon/errors/multi_timeout.rb +2 -2
  59. data/lib/ethon/errors/multi_wait.rb +12 -0
  60. data/lib/ethon/errors/multi_wakeup.rb +12 -0
  61. data/lib/ethon/errors/select.rb +1 -1
  62. data/lib/ethon/errors/set_option.rb +27 -0
  63. data/lib/ethon/errors/share.rb +17 -0
  64. data/lib/ethon/errors/unsupported_option.rb +23 -0
  65. data/lib/ethon/errors/url.rb +17 -0
  66. data/lib/ethon/errors.rb +13 -0
  67. data/lib/ethon/libc.rb +30 -1
  68. data/lib/ethon/loggable.rb +1 -2
  69. data/lib/ethon/multi/operations.rb +269 -25
  70. data/lib/ethon/multi/options.rb +86 -12
  71. data/lib/ethon/multi/stack.rb +175 -10
  72. data/lib/ethon/multi.rb +44 -12
  73. data/lib/ethon/version.rb +1 -1
  74. data/lib/ethon.rb +8 -9
  75. metadata +52 -4
data/CHANGELOG.md CHANGED
@@ -2,7 +2,703 @@
2
2
 
3
3
  ## Master
4
4
 
5
- [Full Changelog](https://github.com/typhoeus/ethon/compare/v0.17.0...master)
5
+ [Full Changelog](https://github.com/typhoeus/ethon/compare/v0.19.0...master)
6
+
7
+ ## 0.19.0
8
+
9
+ [Full Changelog](https://github.com/typhoeus/ethon/compare/v0.18.0...v0.19.0)
10
+
11
+ Before 1.0, a minor release may include incompatible API changes; review the
12
+ breaking changes before upgrading.
13
+
14
+ ### Breaking changes
15
+
16
+ * Unsupported libcurl callback ABIs now raise `NotImplementedError` instead of
17
+ being registered with the generic read/write callback signature. This affects
18
+ low-level setters such as `ioctlfunction=`, `seekfunction=`, and
19
+ `opensocketfunction=`. ([#281](https://github.com/typhoeus/ethon/pull/281))
20
+ * Fatal errors returned by `curl_multi_perform` now raise
21
+ `Ethon::Errors::MultiPerform` instead of allowing Multi execution to continue
22
+ with invalid state. ([#286](https://github.com/typhoeus/ethon/pull/286))
23
+ * Easy and Multi handles created before a `fork` can no longer be used or
24
+ cleaned up in the child process. Ethon now raises
25
+ `Ethon::Errors::ForkSafety`; if libcurl was initialized before forking, use
26
+ `fork` plus `exec`, otherwise create handles after forking.
27
+ ([#295](https://github.com/typhoeus/ethon/pull/295))
28
+ * Unknown named enum and bitmask option values now raise
29
+ `Ethon::Errors::InvalidValue` instead of silently becoming `0`. Strings and
30
+ symbols are accepted equivalently, and direct integer values remain
31
+ supported. ([#300](https://github.com/typhoeus/ethon/pull/300))
32
+ * Exceptions raised by `on_body` callbacks are now consistently re-raised for
33
+ both Easy and Multi transfers, including when verbose mode is enabled.
34
+ Applications that relied on these exceptions being swallowed must handle
35
+ them explicitly. ([#294](https://github.com/typhoeus/ethon/pull/294))
36
+ * Errors from `curl_easy_setopt` and `curl_multi_setopt` are no longer ignored.
37
+ An option the loaded libcurl does not know raises
38
+ `Ethon::Errors::UnsupportedOption`, and other rejected values raise
39
+ `Ethon::Errors::SetOption`. Setting an option such as
40
+ `Easy#http_version = :httpv3` on a libcurl built without HTTP/3 now raises
41
+ instead of silently running with a different configuration.
42
+ `Curl.supports_option?` and `Curl.version_at_least?` can be used to guard
43
+ optional settings.
44
+ ([#343](https://github.com/typhoeus/ethon/pull/343))
45
+ * `require 'ethon'` no longer loads mime-types: the registry is loaded on the
46
+ first file upload instead, saving about 2.4 MB and 100 ms per process. Code
47
+ relying on ethon to load `MIME::Types` must now require it itself.
48
+ ([Clément Joubert](https://github.com/czj),
49
+ [#320](https://github.com/typhoeus/ethon/pull/320))
50
+ * `Easy#cleanup` and `Multi#cleanup` are terminal, and `Easy#perform` and
51
+ `Easy#reset` raise while the handle is attached to a `Multi`. Applications
52
+ that reused cleaned-up or attached handles must create new ones.
53
+ ([#326](https://github.com/typhoeus/ethon/pull/326),
54
+ [#339](https://github.com/typhoeus/ethon/pull/339),
55
+ [#346](https://github.com/typhoeus/ethon/pull/346),
56
+ [#382](https://github.com/typhoeus/ethon/pull/382))
57
+
58
+ ### Behavior changes
59
+
60
+ * Verbose debug output is written through `Ethon.logger` at the debug level
61
+ instead of directly to stdout, so request and response headers no longer
62
+ bypass the application's logging configuration. Set
63
+ `Ethon.logger.level = Logger::DEBUG` to keep seeing the messages; response
64
+ body data is not logged.
65
+ ([#390](https://github.com/typhoeus/ethon/pull/390))
66
+ * Assigning a new native handle to an `Easy`, including `Easy.new(handle: ...)`
67
+ and `Easy#handle = nil`, now installs Ethon's response callbacks and replays
68
+ the callbacks retained by the object. Constructor options are applied to a
69
+ supplied handle instead of the handle Ethon created and discarded.
70
+ ([#402](https://github.com/typhoeus/ethon/pull/402))
71
+ * `Easy#postfields=` now sets `CURLOPT_POSTFIELDSIZE` from the retained bytes
72
+ and restores libcurl's default (`-1`) when the body is cleared. This keeps a
73
+ previous, larger body size from being applied to a shorter body. Set
74
+ `postfieldsize` after `postfields` when sending a prefix or another length.
75
+ ([#403](https://github.com/typhoeus/ethon/pull/403))
76
+ * `Easy#postfields=` and `Easy#copypostfields=` now send binary bodies without
77
+ truncating them at the first NUL. A `postfieldsize` larger than the retained
78
+ body raises `Ethon::Errors::InvalidValue` instead of making libcurl read
79
+ past the buffer. Copying a new body without an explicit size sets the exact
80
+ byte length, and clearing the copied body clears the size as well.
81
+ ([#436](https://github.com/typhoeus/ethon/pull/436))
82
+ * `Multi.new` now accepts `max_host_connections`, `max_pipeline_length`,
83
+ `content_length_penalty_size`, and `chunk_length_penalty_size`, which were
84
+ already in the multi option table but had no setters.
85
+ ([#444](https://github.com/typhoeus/ethon/pull/444))
86
+ * `Share#unlockfunction` callbacks now receive `(handle, data, userp)`, the
87
+ arguments defined by `curl_unlock_function`. They previously used the lock
88
+ callback signature, so the user pointer was read as an access mode and the
89
+ callback received a garbage fourth argument.
90
+ ([#405](https://github.com/typhoeus/ethon/pull/405))
91
+ * `Easy#header` and `Easy#each_header` now default to the last request in a
92
+ transfer (`request = -1`), so structured headers describe the response after
93
+ a redirect instead of the first request. Pass `0` to read the first request.
94
+ ([#409](https://github.com/typhoeus/ethon/pull/409))
95
+ * `Easy#url=` now stores a frozen snapshot of the string libcurl accepted, and
96
+ `Easy#request_headers` returns a fresh copy of the headers and header values
97
+ that were accepted. Mutating the object passed to a setter no longer makes
98
+ the accessor describe a URL or header set the handle never received.
99
+ ([#413](https://github.com/typhoeus/ethon/pull/413),
100
+ [#418](https://github.com/typhoeus/ethon/pull/418))
101
+ * Multipart POST and PUT bodies now use the MIME API when the loaded libcurl
102
+ supports it, and fall back to the deprecated form API on older builds. Part
103
+ names, files, filenames, content types, and regular fields are preserved.
104
+ ([#318](https://github.com/typhoeus/ethon/pull/318))
105
+ * Explicit `writefunction`, `headerfunction`, and `debugfunction` options
106
+ passed to `Easy.new` are no longer overwritten by Ethon's internal
107
+ callbacks. A response that aborts from `on_headers` also leaves the buffered
108
+ body empty.
109
+ ([#345](https://github.com/typhoeus/ethon/pull/345))
110
+ * Empty HTTP header values now follow libcurl semantics: `"Accept" => ""`
111
+ suppresses libcurl's default header, while `"X-Empty;" => ""` sends an empty
112
+ header. ([gonubana](https://github.com/gonubana),
113
+ [#225](https://github.com/typhoeus/ethon/pull/225))
114
+
115
+ ### Enhancements
116
+
117
+ * Added opt-in `CURLOPT_ERRORBUFFER` support through `Easy#errorbuffer=` and
118
+ `Easy#error_message`, including reset, duplication, Multi, and Mirror support.
119
+ ([Adrien Rey-Jarthon](https://github.com/jarthod),
120
+ [#277](https://github.com/typhoeus/ethon/pull/277))
121
+ * Added `Easy#file=` and `Easy#writedata=` to stream response bodies to a path
122
+ or writable Ruby object without accumulating the body in memory. The original
123
+ implementation and authorship were preserved from PR #100.
124
+ ([Seth Madison](https://github.com/baconz),
125
+ [#298](https://github.com/typhoeus/ethon/pull/298))
126
+ * Added block-based `Easy#debugfunction` observers while preserving Ethon's
127
+ internal debug collection and safely propagating callback errors.
128
+ ([Kai Lieth](https://github.com/klieth),
129
+ [#154](https://github.com/typhoeus/ethon/pull/154))
130
+ * Added `Easy#ssl_engines`, `Easy#cookielist`, and `Easy#certinfo`. Slist
131
+ results are copied and freed according to libcurl ownership rules, while
132
+ certificate lists are copied without freeing libcurl-owned memory. The
133
+ relevant implementation and authorship were preserved from PR #74.
134
+ ([Jethro Beekman](https://github.com/jethrogb),
135
+ [#299](https://github.com/typhoeus/ethon/pull/299))
136
+ * Added multipart PUT support. ([#292](https://github.com/typhoeus/ethon/pull/292))
137
+ * Released the Ruby GVL around `curl_multi_perform` and native handle cleanup.
138
+ ([Vladimir Shteinman](https://github.com/Bobochka),
139
+ [#278](https://github.com/typhoeus/ethon/pull/278))
140
+ * Added libcurl error codes 89 through 101.
141
+ ([Adrien Rey-Jarthon](https://github.com/jarthod),
142
+ [#276](https://github.com/typhoeus/ethon/pull/276))
143
+ * Added the remaining libcurl 8.7.1 options, info values, blob options, enum
144
+ table entries, and symbolic option values, including DoH, HSTS, Alt-Svc,
145
+ AWS SigV4, SASL, HTTP/3, TLS 1.3 ciphers, proxy headers, and the modern
146
+ `_t`, socket, and pointer info getters.
147
+ ([#307](https://github.com/typhoeus/ethon/pull/307),
148
+ [#308](https://github.com/typhoeus/ethon/pull/308),
149
+ [#309](https://github.com/typhoeus/ethon/pull/309))
150
+ * `Multi` now prefers `curl_multi_poll` and `curl_multi_wait` over `select`,
151
+ allocating fd sets only for the fallback. This reduces `Multi.new` memory
152
+ from roughly 199 KB to 7 KB on hosts with a large fd limit and avoids 1ms
153
+ busy-waiting when no descriptor is ready.
154
+ ([#330](https://github.com/typhoeus/ethon/pull/330))
155
+ * Added support for current Windows libcurl DLL names: `libcurl-4`,
156
+ `libcurl-x64`, and `libcurl-arm64`.
157
+ ([Shizuo Fujita](https://github.com/Watson1978),
158
+ [#271](https://github.com/typhoeus/ethon/pull/271))
159
+ * Added `ETHON_CURL_LIBS` to override the native libcurl library names.
160
+ ([Sergey Tarasov](https://github.com/dzirtusss),
161
+ [#235](https://github.com/typhoeus/ethon/pull/235))
162
+
163
+ ### New libcurl bindings
164
+
165
+ * Added a URL API wrapper (`Ethon::Curl::Url`) with `get`, `set`, `dup`, and
166
+ `to_s`, plus the `CURLU_*` part, code, and flag enums.
167
+ ([#311](https://github.com/typhoeus/ethon/pull/311))
168
+ * Added a MIME API wrapper (`Ethon::Curl::Mime`, `Ethon::Curl::MimePart`) and
169
+ `Easy#mime=`/`#mimepost=` for multipart bodies, including per-part headers.
170
+ ([#312](https://github.com/typhoeus/ethon/pull/312))
171
+ * Added a share API wrapper (`Ethon::Curl::Share`) with `Easy#share=` and
172
+ lock/unlock callbacks for multi-threaded sharing.
173
+ ([#313](https://github.com/typhoeus/ethon/pull/313))
174
+ * Added structured response headers (`Easy#header`, `Easy#each_header`,
175
+ `Easy#headers_list`), option introspection (`Curl.easy_option`,
176
+ `Curl.option_by_id`, `Curl.each_easy_option`), and version introspection
177
+ (`Curl.protocols`, `Curl.feature_names`, `Curl.version_info_full`).
178
+ ([#314](https://github.com/typhoeus/ethon/pull/314))
179
+ * Added `Easy#unescape`, `Easy#pause`, `Easy#upkeep`, `Multi#wait`,
180
+ `Multi#poll`, and `Multi#wakeup`.
181
+ ([#310](https://github.com/typhoeus/ethon/pull/310))
182
+ * Added `Multi#assign`, `Multi#native_handles`, the `Curl::SslBackend` enum,
183
+ and optional `curl_global_sslset`/`curl_global_trace` bindings.
184
+ ([#315](https://github.com/typhoeus/ethon/pull/315))
185
+
186
+ Functions that are not exported by the loaded libcurl are attached
187
+ conditionally. Wrappers raise `NotImplementedError` when the function they need
188
+ is unavailable; introspection helpers return `nil` or an empty array.
189
+
190
+ ### Fixes and hardening
191
+
192
+ * Callback errors are now raised after the transfer has completed, so
193
+ `on_complete` callbacks still run and an `Easy` is removed from its `Multi`
194
+ before the error reaches the caller.
195
+ ([#302](https://github.com/typhoeus/ethon/pull/302))
196
+ * A failed `curl_easy_init` or `curl_multi_init` now raises `NoMemoryError`
197
+ immediately instead of wrapping address `0` and deferring the failure.
198
+ ([#305](https://github.com/typhoeus/ethon/pull/305))
199
+ * `Easy#headers=` now rejects CR and LF in header names and values with
200
+ `Ethon::Errors::InvalidValue`, so custom headers cannot inject additional
201
+ request headers.
202
+ ([#327](https://github.com/typhoeus/ethon/pull/327))
203
+ * PUT bodies now honor `escape: false` instead of always percent-encoding the
204
+ form, matching POST behavior.
205
+ ([#328](https://github.com/typhoeus/ethon/pull/328))
206
+ * Numeric option values are now checked against the signed range of the
207
+ native type before being passed to libcurl. Values outside the range raise
208
+ `Ethon::Errors::InvalidValue` instead of a later `RangeError` or FFI error.
209
+ ([#329](https://github.com/typhoeus/ethon/pull/329))
210
+ * Request header lists are freed when a later header is invalid or
211
+ `curl_slist_append` fails, and unknown header origins raise
212
+ `Ethon::Errors::InvalidValue` instead of reaching libcurl as `0`.
213
+ ([#347](https://github.com/typhoeus/ethon/pull/347))
214
+ * Response stream teardown now only finishes the destination that the completed
215
+ transfer wrote to. A completion callback can schedule the next transfer on
216
+ the same `Easy` (including adding it to a `Multi` with a new or reused
217
+ response file); that destination is no longer closed before the next transfer
218
+ runs. `Multi#add` finishes the previous transfer's stream before preparing
219
+ the next one, and no longer clears a pending callback error while the Easy is
220
+ completing, so the error from the finished transfer is still raised. A
221
+ failing close or flush also no longer replaces the callback or completion
222
+ error, and `Multi#perform` runs `on_complete` before raising a teardown error
223
+ for a handle libcurl already detached.
224
+ ([#431](https://github.com/typhoeus/ethon/pull/431))
225
+ * `Multi#assign` rejects a nil or non-integer socket descriptor with
226
+ `Ethon::Errors::InvalidValue` instead of letting FFI raise `TypeError`.
227
+ ([#432](https://github.com/typhoeus/ethon/pull/432))
228
+ * `Curl::Url#clone` now duplicates the native URL handle through
229
+ `curl_url_dup`, like `#dup`, instead of raising
230
+ `cannot duplicate FFI::AutoPointer`.
231
+ ([#433](https://github.com/typhoeus/ethon/pull/433))
232
+ * Response bodies written to a Ruby IO now retry short writes. Wrapper and
233
+ nonblocking destinations could accept fewer bytes than a chunk without
234
+ raising, and the rest of the chunk was silently lost. A write that reports
235
+ zero progress now fails the transfer, while destinations returning `nil`
236
+ keep their previous behavior.
237
+ ([#443](https://github.com/typhoeus/ethon/pull/443))
238
+ * `Easy#file=` now opens a new path before releasing the previous response
239
+ destination. A bad path raises without discarding the file that was already
240
+ configured, so the next `perform` still writes to it.
241
+ ([#439](https://github.com/typhoeus/ethon/pull/439))
242
+ * A duplicated `Easy` keeps a configured response file path and opens its own
243
+ stream for the next transfer. A caller-owned IO is still dropped so the two
244
+ handles cannot write into the same object.
245
+ ([#435](https://github.com/typhoeus/ethon/pull/435))
246
+ * Multipart forms no longer treat every `Array` value as a file upload. With
247
+ `params_encoding: :none`, ordinary arrays stay data, and file values are
248
+ marked by `Queryable::FileInfo`. Raw `String` bodies are rejected when
249
+ `multipart: true` is requested instead of producing an unnamed part or a
250
+ `NoMethodError`.
251
+ ([#441](https://github.com/typhoeus/ethon/pull/441),
252
+ [#438](https://github.com/typhoeus/ethon/pull/438))
253
+ * `InvalidOption`, `UnsupportedOption`, and `SetOption` now inspect the option
254
+ name in their messages, so control characters in an untrusted option key
255
+ cannot inject lines into logs.
256
+ ([#442](https://github.com/typhoeus/ethon/pull/442))
257
+ * Corrected API annotations for the `Easy#perform` and `Easy#handle` return
258
+ types, the `Multi` option setter parameter types, and `Multi#socket_action`.
259
+ ([#445](https://github.com/typhoeus/ethon/pull/445))
260
+ * Assigning a replacement handle now clears the Ruby-side URL, headers,
261
+ postfields, URL/MIME/slist/blob/share state, response, and return code,
262
+ because the new handle does not have them. `Easy#dup` keeps that state
263
+ because the native duplicate already carries it.
264
+ ([#448](https://github.com/typhoeus/ethon/pull/448))
265
+ * `Ethon::Errors::InvalidValue` now escapes the value in its message, so a
266
+ CR or LF in a rejected URL or request-line value can no longer be injected
267
+ into logs that record the exception.
268
+ ([#429](https://github.com/typhoeus/ethon/pull/429))
269
+ * The `haproxy_client_ip` option now rejects whitespace. libcurl writes the
270
+ value into the space-separated PROXY protocol line without validating it, so
271
+ a value could add fields or lines to the header.
272
+ ([#428](https://github.com/typhoeus/ethon/pull/428))
273
+ * URL values now reject CR and LF before libcurl stores them. libcurl's URL
274
+ API wrote line breaks in paths and queries into the request line verbatim,
275
+ so a URL handle attached with `Easy#curlu=` could inject headers. The string
276
+ setter accepted the same input into `Easy#url` and debug logs before the
277
+ transfer failed. Percent-encoded line breaks remain valid.
278
+ ([#419](https://github.com/typhoeus/ethon/pull/419),
279
+ [#425](https://github.com/typhoeus/ethon/pull/425))
280
+ * `Curl::Url.new(existing_url)` now duplicates the handle instead of re-parsing
281
+ its string form. Partial handles can be copied, complete handles are not
282
+ normalized, and non-zero `CURLU_*` flags are rejected for a copy because
283
+ there is no parse step to apply them to.
284
+ ([#420](https://github.com/typhoeus/ethon/pull/420))
285
+ * `Multi#add` and `#delete` now roll back their Ruby-side stack when the native
286
+ add or remove raises, `Easy#cleanup` refuses to free an attached handle, and
287
+ `Multi#cleanup` detaches every easy handle and is terminal. Socket and timer
288
+ callbacks are kept until the native multi is cleaned up.
289
+ ([#326](https://github.com/typhoeus/ethon/pull/326))
290
+ * `Multi#delete` and `Multi#cleanup` now clear the running count when the stack
291
+ becomes empty, so `Multi#ongoing?` is false after the last handle is removed.
292
+ A handle that libcurl detached before a socket callback raised is no longer
293
+ put back on the stack, so it remains usable.
294
+ ([#421](https://github.com/typhoeus/ethon/pull/421))
295
+ * `Easy#reset_transfer_state` clears `return_code`, so adding a used Easy to a
296
+ Multi no longer exposes the previous transfer's result until the new transfer
297
+ completes.
298
+ ([#422](https://github.com/typhoeus/ethon/pull/422))
299
+ * Streamed response destinations are flushed before `on_complete`, and
300
+ caller-owned response IOs are flushed when the transfer finishes. Previously
301
+ completion callbacks could read a partial body, and caller-owned file writers
302
+ needed an explicit flush after `perform`.
303
+ ([#424](https://github.com/typhoeus/ethon/pull/424))
304
+ * `Easy#log_inspect` redacts credentials in `effective_url` so debug logs do not
305
+ contain URL passwords. The same redaction applies to
306
+ `Mirror#log_informations`; `Easy#effective_url` and `Mirror#options` still
307
+ return the exact URL.
308
+ ([#423](https://github.com/typhoeus/ethon/pull/423))
309
+ * Corrected the mirror and query-parameter specs so they assert the constants
310
+ and null-byte cases they describe.
311
+ ([#426](https://github.com/typhoeus/ethon/pull/426))
312
+ * Clearing `writefunction`, `headerfunction`, or `debugfunction` now restores
313
+ Ethon's internal callback. libcurl's default callback previously wrote
314
+ response bodies to stdout, stopped collecting response headers, or sent
315
+ verbose output to stderr.
316
+ ([#410](https://github.com/typhoeus/ethon/pull/410))
317
+ * `Easy#handle=` now frees the native handle it replaces when this Easy owned
318
+ it, and reattaches the error buffer and `noprogress` setting to the
319
+ replacement. Replacing a handle no longer leaks it, and `#error_message` and
320
+ progress callbacks keep working.
321
+ ([#411](https://github.com/typhoeus/ethon/pull/411))
322
+ * `Multi#cleanup` now continues detaching the remaining easy handles after one
323
+ native detach or streamed-response close fails, instead of leaving them
324
+ attached to a cleaned-up Multi. The first error is still raised after
325
+ teardown.
326
+ ([#412](https://github.com/typhoeus/ethon/pull/412))
327
+ * `Curl::Mime.new` now rejects handles that are not an `Ethon::Easy`, an
328
+ `Ethon::Curl::EasyHandle`, or a non-null raw easy pointer. Passing another
329
+ owned handle, such as a `Multi`, previously reached `curl_mime_init` and
330
+ could crash the process. Assigning `nil` to a MIME part field now clears it
331
+ instead of setting an empty value or raising.
332
+ ([#414](https://github.com/typhoeus/ethon/pull/414))
333
+ * `Curl::Header#origin` now masks every undocumented origin bit, not only the
334
+ single reserved bit current libcurl sets, so the value only contains the
335
+ documented `CURLH_*` bits.
336
+ ([#417](https://github.com/typhoeus/ethon/pull/417))
337
+ * `Curl::Slist.build` now rejects CR, LF, and NUL in list lines, so lists used
338
+ for FTP quotes, SMTP recipients, and other verbatim protocol commands cannot
339
+ inject additional commands.
340
+ ([#415](https://github.com/typhoeus/ethon/pull/415))
341
+ * `:none` options no longer interpret `false` as enabled, `Ethon.logger = nil`
342
+ falls back to the default logger, and `ETHON_FD_SIZE` is validated and
343
+ rounded up to the native fd_set word size.
344
+ ([#366](https://github.com/typhoeus/ethon/pull/366))
345
+ * HTTP helper actions are resolved through an explicit class map, so custom
346
+ verbs that collide with `Ethon::Easy::Http` constants such as `:custom` or
347
+ `:actionable` are sent as custom requests instead of raising.
348
+ ([#363](https://github.com/typhoeus/ethon/pull/363))
349
+ * The `select` fallback in `Multi#set_fds` now sleeps for the libcurl timeout
350
+ when no file descriptors are ready instead of busy-waiting in 1ms steps.
351
+ ([#364](https://github.com/typhoeus/ethon/pull/364))
352
+ * `Easy#file=` treats writable path-like objects such as `Pathname` as paths
353
+ instead of caller-owned IOs, so the response is no longer truncated on every
354
+ write. ([#360](https://github.com/typhoeus/ethon/pull/360))
355
+ * MIME part headers now reject CR, LF, and NUL bytes instead of passing them
356
+ verbatim to libcurl. Empty header lines are dropped, so assigning an empty
357
+ string clears the part headers instead of adding an empty header line.
358
+ ([#362](https://github.com/typhoeus/ethon/pull/362))
359
+ * `CURLOPT_COPYPOSTFIELDS` values are now NUL-terminated, and assigning `nil`
360
+ passes a NULL pointer instead of a zero-length buffer.
361
+ ([#359](https://github.com/typhoeus/ethon/pull/359))
362
+ * `Easy#url=` and `Easy#postfields=` only update the Ruby-side value after
363
+ libcurl accepts the option, so a rejected value cannot leave the accessors
364
+ describing state the handle never received. `Easy#postfields=` also retains
365
+ a frozen snapshot of the string libcurl points at, so caller mutations cannot
366
+ change the request or leave libcurl reading a reallocated buffer.
367
+ ([#350](https://github.com/typhoeus/ethon/pull/350),
368
+ [#374](https://github.com/typhoeus/ethon/pull/374))
369
+ * `Easy#cleanup` drops references to native-backed strings, lists, MIME/URL
370
+ handles, blobs, and callbacks after freeing the handle. It also drops the
371
+ response, progress, and debug callback closures, and `Multi#cleanup` drops
372
+ its retained socket and timer callbacks, so terminal cleanup does not keep
373
+ application state alive.
374
+ ([#365](https://github.com/typhoeus/ethon/pull/365),
375
+ [#381](https://github.com/typhoeus/ethon/pull/381))
376
+ * `Multi#cleanup` now releases the native multi handle even when detaching an
377
+ easy handle or closing a streamed response fails, and keeps multi ownership
378
+ on an easy handle whose native detach failed so it cannot be reused.
379
+ ([#352](https://github.com/typhoeus/ethon/pull/352))
380
+ * `Multi#handle` and `Multi#cleanup` now enforce fork safety before allocating
381
+ or releasing a native multi handle, and `Easy#reset`/`#perform` validate the
382
+ handle before clearing response state, so a cleaned up or inherited handle
383
+ cannot truncate a response file or discard the previous result while raising.
384
+ ([#358](https://github.com/typhoeus/ethon/pull/358))
385
+ * `Easy#handle=` now wraps a raw `FFI::Pointer` in an owned
386
+ `Ethon::Curl::EasyHandle`, so the documented setter produces a usable handle
387
+ that fork safety tracks and `#cleanup` frees. Other owned pointer types and
388
+ null pointers are rejected.
389
+ ([#380](https://github.com/typhoeus/ethon/pull/380))
390
+ * `Easy#cleanup` frees the native handle even when closing a streamed response
391
+ fails, and `Multi#add` closes an open response stream when the easy handle is
392
+ unusable or the native add fails.
393
+ ([#358](https://github.com/typhoeus/ethon/pull/358))
394
+ * Owned handles now record an explicit `free` and raise
395
+ `Ethon::Errors::EthonError` when used afterwards, instead of dereferencing
396
+ the freed native pointer. This covers Easy and Multi handles, URL, MIME,
397
+ share, and slist wrappers.
398
+ ([#382](https://github.com/typhoeus/ethon/pull/382))
399
+ * Option setters, MIME part header assignment, and `Multi#assign` reject freed
400
+ owned pointers instead of handing libcurl a dangling address.
401
+ ([#382](https://github.com/typhoeus/ethon/pull/382),
402
+ [#408](https://github.com/typhoeus/ethon/pull/408))
403
+ * MIME part and request header lists detect a failed `curl_slist_append`
404
+ allocation (FFI reports a NULL pointer, not `nil`) and free the partially
405
+ built list before installing it.
406
+ ([#347](https://github.com/typhoeus/ethon/pull/347),
407
+ [#353](https://github.com/typhoeus/ethon/pull/353))
408
+ * Fixed the `:string_escape_null` option type to resolve
409
+ `Ethon::Easy::Util` instead of an undefined constant.
410
+ ([#351](https://github.com/typhoeus/ethon/pull/351))
411
+ * Added byte-exact binary transfer tests, a full option-table cross-check
412
+ against libcurl's own metadata, multipart round-trip tests, end-to-end
413
+ coverage for the `curl_multi_wait` and `select` fallbacks, and a multi
414
+ response-isolation test.
415
+ ([#354](https://github.com/typhoeus/ethon/pull/354),
416
+ [#355](https://github.com/typhoeus/ethon/pull/355),
417
+ [#356](https://github.com/typhoeus/ethon/pull/356))
418
+ * Reusing an `Easy` no longer leaks the previous custom HTTP method or a
419
+ stale `CURLOPT_CURLU` handle. `Easy#url=` clears a URL handle, `Easy#curlu=`
420
+ clears the string URL, and `Easy#reset` detaches the share before releasing
421
+ it.
422
+ ([#323](https://github.com/typhoeus/ethon/pull/323),
423
+ [#325](https://github.com/typhoeus/ethon/pull/325),
424
+ [#344](https://github.com/typhoeus/ethon/pull/344))
425
+ * GET requests with a body send the body again; the method reset only applies
426
+ to empty GET requests.
427
+ ([#344](https://github.com/typhoeus/ethon/pull/344))
428
+ * MIME parts keep a caller-provided header slist alive until libcurl replaces
429
+ it. ([#368](https://github.com/typhoeus/ethon/pull/368))
430
+ * `Curl::Share#share` and `#unshare` now reject unknown or non-symbol/string
431
+ data types with `Ethon::Errors::InvalidValue`.
432
+ ([#338](https://github.com/typhoeus/ethon/pull/338))
433
+ * `Easy#header` and `Easy#each_header` accept an array of header origins as
434
+ documented, and reject unknown origins. `Ethon::Curl::Header#origin` no
435
+ longer exposes libcurl's reserved bit, so it matches the documented
436
+ `CURLH_*` values in `Ethon::Curl::HeaderOrigin`.
437
+ ([#347](https://github.com/typhoeus/ethon/pull/347),
438
+ [#383](https://github.com/typhoeus/ethon/pull/383))
439
+ * `Easy#cleanup` is terminal; using the handle afterwards raises
440
+ `Ethon::Errors::EthonError` instead of silently sending requests without
441
+ callbacks. ([#382](https://github.com/typhoeus/ethon/pull/382))
442
+ * Exceptions raised while buffering response headers are now captured and
443
+ re-raised after the transfer, matching the body and debug callbacks.
444
+ ([#316](https://github.com/typhoeus/ethon/pull/316))
445
+ * `Easy.supports_timeout_ms?` now checks the libcurl version instead of async
446
+ DNS support. ([#319](https://github.com/typhoeus/ethon/pull/319))
447
+ * Requests no longer set `CURLOPT_HTTPPOST` on libcurl builds configured with
448
+ `--disable-form-api`, where the option does not exist.
449
+ ([#349](https://github.com/typhoeus/ethon/pull/349))
450
+ * Corrected the native option number for `tcp_fastopen` (`CURLOPT_TCP_FASTOPEN`,
451
+ 244) and the string ABI used by `tlsauth_type` and `proxy_tlsauth_type`.
452
+ `:srp` and `"srp"` are normalized to libcurl's required `"SRP"` value.
453
+ ([#307](https://github.com/typhoeus/ethon/pull/307))
454
+ * String options containing NUL bytes now raise
455
+ `Ethon::Errors::InvalidValue` instead of FFI's `ArgumentError`, and
456
+ fractional or out-of-range values for integer options are rejected. Binary
457
+ bodies still use `copypostfields`.
458
+ ([#329](https://github.com/typhoeus/ethon/pull/329),
459
+ [#340](https://github.com/typhoeus/ethon/pull/340))
460
+ * String options that libcurl writes verbatim into a request line, a protocol
461
+ header, or a protocol command now reject CR and LF. This covers
462
+ `customrequest`, `request_target`, `useragent`, `referer`, `cookie`,
463
+ `accept_encoding`, `range`, and the RTSP, SMTP, and FTP equivalents, which
464
+ could otherwise inject additional headers or commands.
465
+ ([#379](https://github.com/typhoeus/ethon/pull/379))
466
+ * Split the progress callback ABI into `CURLOPT_PROGRESSFUNCTION` (`double`) and
467
+ `CURLOPT_XFERINFOFUNCTION` (`curl_off_t`), and select it using libcurl's
468
+ numeric version instead of string comparison.
469
+ ([#307](https://github.com/typhoeus/ethon/pull/307))
470
+ * Changing `escape` or `params_encoding` after a query string was built now
471
+ rebuilds the cached pairs and string.
472
+ ([#332](https://github.com/typhoeus/ethon/pull/332))
473
+ * Read callbacks now report the number of bytes actually read, handle
474
+ short-reading IO bodies, and correctly start from an IO's current position.
475
+ ([#307](https://github.com/typhoeus/ethon/pull/307),
476
+ [#344](https://github.com/typhoeus/ethon/pull/344))
477
+ * Duplicating an `Easy` now rebinds its response, read, progress, and custom
478
+ callbacks to the copy instead of leaving them pointing at the original
479
+ handle, starts with a clean result/callback-error state, and no longer
480
+ aliases the original MIME structure.
481
+ ([#335](https://github.com/typhoeus/ethon/pull/335))
482
+ * Custom HTTP verbs that collide with top-level Ruby constants no longer
483
+ resolve to those constants.
484
+ ([#307](https://github.com/typhoeus/ethon/pull/307),
485
+ [#363](https://github.com/typhoeus/ethon/pull/363))
486
+ * `Easy#reset` now clears multipart state, request headers, retained read
487
+ callbacks, and pending callback errors. `Easy#request_headers` exposes the
488
+ request headers without colliding with the `on_headers` callback.
489
+ ([#307](https://github.com/typhoeus/ethon/pull/307),
490
+ [#323](https://github.com/typhoeus/ethon/pull/323))
491
+ * `Easy#perform` and `Easy#reset` now refuse to run while the handle is still
492
+ attached to a `Multi`, matching `Easy#cleanup`.
493
+ ([#339](https://github.com/typhoeus/ethon/pull/339),
494
+ [#346](https://github.com/typhoeus/ethon/pull/346))
495
+ * `Easy#perform`/`#reset`/`#cleanup`/`#dup` and `Multi#perform`/
496
+ `#socket_action`/`#cleanup` now reject reentrant calls made from callbacks
497
+ while the handle is still active.
498
+ ([#342](https://github.com/typhoeus/ethon/pull/342))
499
+ * `Easy#perform` now returns the result of the transfer it ran even when an
500
+ `on_complete` callback resets the Easy. Resetting clears `#return_code`, so
501
+ `perform` used to return `nil` after a successful transfer.
502
+ ([#404](https://github.com/typhoeus/ethon/pull/404))
503
+ * Reading `#on_body` without a block, or after clearing the callback list, no
504
+ longer disables response body buffering.
505
+ ([#334](https://github.com/typhoeus/ethon/pull/334))
506
+ * Multi no longer mutates the caller's options, validates `execution_mode`, and
507
+ no longer raises when `running_count` is nil. Finalizers no longer emit
508
+ `ThreadError`/`SignalException` warnings.
509
+ ([#307](https://github.com/typhoeus/ethon/pull/307))
510
+ * Windows `fd_set` now uses pointer-sized socket entries.
511
+ ([#307](https://github.com/typhoeus/ethon/pull/307))
512
+ * Added libcurl 8.23 options (`CURLOPT_ECH`, `CURLOPT_TCP_KEEPCNT`,
513
+ `CURLOPT_UPLOAD_FLAGS`, `CURLOPT_SSL_SIGNATURE_ALGORITHMS`, and the
514
+ `CURLOPT_HTTPSIG_*` family), the newer `CURLINFO_*` entries, and
515
+ `CURLUE_BACKSLASH`/`CURLU_GET_EMPTY`/`CURLU_NO_GUESS_SCHEME`.
516
+ ([#341](https://github.com/typhoeus/ethon/pull/341))
517
+ * `curl_global_sslset` results now use the `Curl::SslSetCode` enum. An unknown
518
+ backend is `:unknown_backend` instead of `:unsupported_protocol`, and a late
519
+ call is `:too_late` instead of `:failed_init`.
520
+ ([#406](https://github.com/typhoeus/ethon/pull/406))
521
+ * Fixed the test server's keep-alive limits, cutting the suite from ~35s to
522
+ ~5s, and added coverage for the option table, callbacks, reset behavior,
523
+ Multi message handling, and libcurl info ownership.
524
+ ([#324](https://github.com/typhoeus/ethon/pull/324))
525
+ * Removing an `Easy` that streams its response to a file from a `Multi`, or a
526
+ failed `Multi#add`, now closes the file instead of leaving it open until GC.
527
+ ([#336](https://github.com/typhoeus/ethon/pull/336))
528
+ * `Multi#dup` and `Multi#clone` now raise `TypeError` instead of returning a
529
+ shallow copy that shares the native multi handle and easy-handle stack with
530
+ the original.
531
+ ([#372](https://github.com/typhoeus/ethon/pull/372))
532
+ * `Easy#file=` validates the easy handle before opening a path, so a cleaned up
533
+ or fork-inherited handle cannot truncate the destination while raising.
534
+ ([#373](https://github.com/typhoeus/ethon/pull/373))
535
+ * `Easy#url=` applies a string URL before detaching a `CURLOPT_CURLU` handle,
536
+ so a rejected value leaves the existing URL handle usable.
537
+ ([#370](https://github.com/typhoeus/ethon/pull/370))
538
+ * `Multi#add` rejects an Easy that another Multi owns before resetting its
539
+ transfer state or preparing its response stream.
540
+ ([#369](https://github.com/typhoeus/ethon/pull/369))
541
+ * MIME part content types and encoders reject CR, LF, and NUL bytes, and the
542
+ other MIME part strings reject NUL bytes with `Ethon::Errors::InvalidValue`
543
+ instead of FFI's `ArgumentError`.
544
+ ([#371](https://github.com/typhoeus/ethon/pull/371))
545
+ * `Easy` retains managed `Curl::Slist` values assigned to list-backed options
546
+ such as `resolve`, `connect_to`, and `quote`, so garbage collection cannot
547
+ free a list while libcurl still uses it.
548
+ ([#368](https://github.com/typhoeus/ethon/pull/368))
549
+ * Owned native pointers now register in one shared registry, so
550
+ `Curl::OwnedPointer.owners` reports every live handle and the memory usage
551
+ test really checks for native handle leaks. Registration and release are
552
+ synchronized for concurrent handles.
553
+ ([#304](https://github.com/typhoeus/ethon/pull/304),
554
+ [#375](https://github.com/typhoeus/ethon/pull/375))
555
+ * `Easy#reset` closes a streamed response file before clearing state, so a
556
+ close failure leaves the handle usable instead of half-reset.
557
+ ([#376](https://github.com/typhoeus/ethon/pull/376))
558
+ * `Easy#complete` rejects reentrant calls from its own `on_headers` or
559
+ `on_complete` callbacks instead of recursing until the stack overflows.
560
+ ([#377](https://github.com/typhoeus/ethon/pull/377))
561
+ * `Multi#add` no longer opens the response file, resets the transfer state, and
562
+ clears callback errors before libcurl accepted the handle. A failed add
563
+ leaves the previous response and a streamed response file untouched, rolls
564
+ the native attach back when preparing the response stream fails or a socket
565
+ or timer callback raises, and keeps a handle it cannot detach tracked so
566
+ `Multi#cleanup` can retry. Non-`Ethon::Easy` arguments raise `TypeError`.
567
+ ([#386](https://github.com/typhoeus/ethon/pull/386))
568
+ * `Easy#dup` and `#clone` now raise `Ethon::Errors::EthonError` when the Easy
569
+ is still attached to a `Multi`, matching `#perform`, `#reset`, and
570
+ `#cleanup`. libcurl requires the source handle to be idle while it is
571
+ cloned, and the copy can never be part of the same multi.
572
+ ([#387](https://github.com/typhoeus/ethon/pull/387))
573
+ * `Easy#share=`, `#mime=`, and `#curlu=` now raise `TypeError` for values that
574
+ are not the matching `Ethon::Curl` wrapper and keep the previously assigned
575
+ handle. Other `FFI::Pointer` values were passed to libcurl as a share, MIME,
576
+ or URL handle, where the wrong native layout is undefined behavior.
577
+ ([#388](https://github.com/typhoeus/ethon/pull/388))
578
+ * `Curl::Url#set` and `#get` reject unknown part names and flags with
579
+ `Ethon::Errors::InvalidValue` instead of FFI's `ArgumentError`, and values
580
+ containing a NUL byte now raise `Ethon::Errors::InvalidValue` instead of
581
+ silently truncating the URL passed to libcurl. Other values are coerced with
582
+ `to_s`, and `nil` still clears a part.
583
+ ([#391](https://github.com/typhoeus/ethon/pull/391))
584
+ * `Multi#easy_handles` returns a frozen snapshot instead of the mutable array
585
+ the multi uses for its bookkeeping. Modifying the returned array previously
586
+ desynchronized the Ruby-side stack from the handles libcurl had attached.
587
+ ([#392](https://github.com/typhoeus/ethon/pull/392))
588
+ * `Easy#reset` is allowed from an `on_complete` callback again, restoring
589
+ `Typhoeus::Pool.release`, and a callback error raised during the transfer is
590
+ still raised after the reset. Resets from body, header, and progress
591
+ callbacks stay rejected.
592
+ ([#393](https://github.com/typhoeus/ethon/pull/393))
593
+ * MIME part names and filenames reject CR and LF, and the deprecated form API
594
+ validates its field name, filename, and content type, so they cannot inject
595
+ extra part headers on libcurl older than 7.81.
596
+ ([#394](https://github.com/typhoeus/ethon/pull/394))
597
+ * `Easy#mirror` omits pointer-valued informations such as
598
+ `CURLINFO_TLS_SSL_PTR`, so mirrored options stay marshalable for cached
599
+ responses. `Easy#tls_ssl_ptr` remains available.
600
+ ([#395](https://github.com/typhoeus/ethon/pull/395))
601
+ * The `select` fallback in `Multi#set_fds` releases the GVL while waiting, so
602
+ other Ruby threads can run during the wait.
603
+ ([#396](https://github.com/typhoeus/ethon/pull/396))
604
+ * Added `:https3` to `proxytype` and `:earlydata` to `ssl_options` and
605
+ `proxy_ssl_options`.
606
+ ([#397](https://github.com/typhoeus/ethon/pull/397))
607
+ * Duplicating an `Easy` keeps the progress callback that was effective on the
608
+ original: `on_progress` no longer disappears when a custom progress function
609
+ is also configured.
610
+ ([#398](https://github.com/typhoeus/ethon/pull/398))
611
+ * `Easy#handle=` rejects assignment while the Easy is performing, completing,
612
+ attached to a `Multi`, or cleaned up.
613
+ ([#399](https://github.com/typhoeus/ethon/pull/399))
614
+ * Wrapping a native pointer that another `Ethon::Curl` wrapper already owns
615
+ now raises instead of creating a second owner that can use the pointer after
616
+ it is freed. `Easy#handle=` also rejects a handle owned by another Easy.
617
+ ([#401](https://github.com/typhoeus/ethon/pull/401))
618
+ * Reworked Easy, Multi, slist, and multipart native ownership around typed,
619
+ PID-aware pointers; removed unsafe Ruby finalizers and prevented inherited
620
+ native cleanup after forks. ([#280](https://github.com/typhoeus/ethon/pull/280),
621
+ [#287](https://github.com/typhoeus/ethon/pull/287),
622
+ [#295](https://github.com/typhoeus/ethon/pull/295))
623
+ * Retained option callbacks and multipart forms for the full period in which
624
+ libcurl may call or read them. ([#282](https://github.com/typhoeus/ethon/pull/282),
625
+ [#284](https://github.com/typhoeus/ethon/pull/284))
626
+ * Kept Multi's Ruby stack consistent when native add/remove operations fail or
627
+ callbacks raise. ([#285](https://github.com/typhoeus/ethon/pull/285))
628
+ * Fixed the `curl_version_info` enum ABI and Multi's
629
+ `CURLMOPT_MAX_TOTAL_CONNECTIONS` option number.
630
+ ([#279](https://github.com/typhoeus/ethon/pull/279),
631
+ [#283](https://github.com/typhoeus/ethon/pull/283))
632
+ * Replaced deprecated `getdtablesize` usage with `Etc.sysconf`, with a resource
633
+ limit fallback and the existing `ETHON_FD_SIZE` override.
634
+ ([#296](https://github.com/typhoeus/ethon/pull/296))
635
+ * Skipped NTLM integration specs when the installed libcurl has no NTLM support.
636
+ ([Felix Yan](https://github.com/felixonmars),
637
+ [#275](https://github.com/typhoeus/ethon/pull/275))
638
+
639
+ ### Documentation and maintenance
640
+
641
+ * Documented thread, callback, signal, and fork safety requirements.
642
+ ([#297](https://github.com/typhoeus/ethon/pull/297),
643
+ [#303](https://github.com/typhoeus/ethon/pull/303))
644
+ * Documented the URL, MIME, share, header, and introspection APIs, rewrote the
645
+ README overview and usage sections, and fixed a stale documentation link and
646
+ parameter names.
647
+ ([#317](https://github.com/typhoeus/ethon/pull/317),
648
+ [#451](https://github.com/typhoeus/ethon/pull/451),
649
+ [#453](https://github.com/typhoeus/ethon/pull/453))
650
+ * Corrected `CURLMOPT_PIPELINING` documentation to describe its integer bitmask
651
+ values. ([Bernard Mak](https://github.com/b2mak),
652
+ [#239](https://github.com/typhoeus/ethon/pull/239))
653
+ * Updated curl documentation and repository links.
654
+ ([Olle Jonsson](https://github.com/olleolleolle),
655
+ [#237](https://github.com/typhoeus/ethon/pull/237))
656
+ * Removed stale test-server entry points.
657
+ ([Vít Ondruch](https://github.com/voxik),
658
+ [#256](https://github.com/typhoeus/ethon/pull/256))
659
+ * Corrected deprecation wording, warning text, and error message spelling.
660
+ ([#331](https://github.com/typhoeus/ethon/pull/331),
661
+ [#333](https://github.com/typhoeus/ethon/pull/333))
662
+ * Added regression coverage for uncovered Easy and Multi behavior, require-time
663
+ memory use, GVL scheduling, GC and native handle leaks, encoding properties,
664
+ cleanup/MIME/reuse paths, and libcurl 8.23 differences.
665
+ ([#306](https://github.com/typhoeus/ethon/pull/306),
666
+ [#321](https://github.com/typhoeus/ethon/pull/321),
667
+ [#322](https://github.com/typhoeus/ethon/pull/322),
668
+ [#337](https://github.com/typhoeus/ethon/pull/337),
669
+ [#348](https://github.com/typhoeus/ethon/pull/348),
670
+ [#361](https://github.com/typhoeus/ethon/pull/361),
671
+ [#385](https://github.com/typhoeus/ethon/pull/385),
672
+ [#430](https://github.com/typhoeus/ethon/pull/430),
673
+ [#449](https://github.com/typhoeus/ethon/pull/449))
674
+ * Expanded CI through Ruby 4.0, retained required Ruby 2.6 coverage, added a
675
+ master-only latest-libcurl build, added Windows coverage, and reduced matrix
676
+ setup time.
677
+ ([Geremia Taglialatela](https://github.com/tagliala),
678
+ [#274](https://github.com/typhoeus/ethon/pull/274),
679
+ [#288](https://github.com/typhoeus/ethon/pull/288),
680
+ [#289](https://github.com/typhoeus/ethon/pull/289),
681
+ [#290](https://github.com/typhoeus/ethon/pull/290),
682
+ [#291](https://github.com/typhoeus/ethon/pull/291),
683
+ [#293](https://github.com/typhoeus/ethon/pull/293),
684
+ [#450](https://github.com/typhoeus/ethon/pull/450))
685
+
686
+ ## 0.18.0
687
+
688
+ [Full Changelog](https://github.com/typhoeus/ethon/compare/v0.17.0...v0.18.0)
689
+
690
+ * Fixed verbose mode attempting to print binary SSL data by replacing it with a summary.
691
+ ([Felipe Mesquita](https://github.com/felipedmesquita), [#265](https://github.com/typhoeus/ethon/pull/265))
692
+ * Fixed `on_headers` callbacks being called once per header line instead of once for all headers.
693
+ ([Felipe Mesquita](https://github.com/felipedmesquita), [#264](https://github.com/typhoeus/ethon/pull/264))
694
+ * Stabilized test server boot on macOS by fixing IPv6/IPv4 localhost resolution and preventing orphaned processes.
695
+ ([Geremia Taglialatela](https://github.com/tagliala), [#261](https://github.com/typhoeus/ethon/pull/261))
696
+ * Added `logger` to runtime dependencies for Ruby 3.5+ compatibility.
697
+ ([Geremia Taglialatela](https://github.com/tagliala), [#259](https://github.com/typhoeus/ethon/pull/259))
698
+ * Opted-in for MFA requirement explicitly by adding `rubygems_mfa_required` metadata.
699
+ ([Geremia Taglialatela](https://github.com/tagliala), [#257](https://github.com/typhoeus/ethon/pull/257))
700
+ * Removed redundant encoding and require_relative checks.
701
+ ([Geremia Taglialatela](https://github.com/tagliala), [#258](https://github.com/typhoeus/ethon/pull/258))
6
702
 
7
703
  ## 0.17.0
8
704
 
@@ -211,7 +907,7 @@ Enhancements:
211
907
  Enhancements:
212
908
 
213
909
  * Add support for protocols and redir_protocols(
214
- [libcurl SASL buffer overflow vulnerability](http://curl.haxx.se/docs/adv_20130206.html)).
910
+ [libcurl SASL buffer overflow vulnerability](https://curl.se/docs/adv_20130206.html)).
215
911
  * Add max_send_speed_large and max_recv_speed_large([Paul Schuegraf](https://github.com/pschuegr), [\#33](https://github.com/typhoeus/ethon/pull/33))
216
912
 
217
913
  ## 0.5.7