curb 1.4.0 → 1.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9f3088ba29db67ea56c9587898267648a704c8acbcb9e95cb58318c3464deb12
4
- data.tar.gz: 40134cd41f0704c2aaa3b65ca6507987f6e8bc755c4530c9361ccd25f4831c17
3
+ metadata.gz: 0d59fbe309c871b35fe03022151783ee2f88a2a348847758034c4ef83034e868
4
+ data.tar.gz: 3c143d3940a09679b69bd9d975342bbdbe1a200d1cf24fa6756714570d977d1b
5
5
  SHA512:
6
- metadata.gz: 29ffbf5bb4301db5729aff433a938251622c8483a29f5fa0f5344b52573048964c9863d00d254b42d01ff172291a1b469ea10f1fa9757b58fe4743a66c7078a5
7
- data.tar.gz: d2e21e48891f398af31a7b2dd8d99a2465f82bb97d9caecda8ac4cb69fb599e894c3172bc56a6106a28d7761b03743fc78ee80701b3051ac673b7b6f1ed27ea9
6
+ metadata.gz: 41ecf3ceed954e7a79b19db4b4969e2186407cf9d071b570471e4867d1a20521adf1a0091fa2c2c98902817f3c6c2ed435d11d0f9c3b9137ffd9923a11792468
7
+ data.tar.gz: 04cdc459106867e381df0c89db22448636cca5dbe6f7073ba0f7b70d9124030bd5a909b3c90de6242d3e2fe865ea6a296fcbd1fd35960baba5d6ca51f241c6c0
data/ChangeLog.md ADDED
@@ -0,0 +1,447 @@
1
+ # ChangeLog
2
+ ## 1.5.0
3
+ * Behavior changes (may affect code that does not use the safety features): redirects are now capped at 30 when `max_redirects` is unset, where curb previously passed unlimited (set `max_redirects = -1` to restore unlimited); CR, LF, and NUL in headers, proxy headers, `useragent`, `cookies`, custom request methods, and `CURLOPT_REQUEST_TARGET` now raise `ArgumentError` instead of being sent; `easy.cookies=` is no longer sent to other hosts after a cross-host redirect on libcurl 7.83+; and replacing `post_body`/`put_data` or calling `http_post`/`http_put`/`http_patch`/`http_delete`/`http(verb)` while a transfer is active now raises `RuntimeError`. Under `network_policy = :public` (including `Curl.safe!`), `file://` URLs without a protocol allowlist, non-IP-literal DoH URLs, and Unix sockets without `allow_unix_socket` are refused, and safe mode now defaults to a 60s timeout and 20 redirects. Details for each are below.
4
+ * Read `Curl::Multi.default_timeout` once per `Curl::Multi#perform` instead of on every wait-loop pass. The setting lives in Ractor-local storage, so each read cost two Ruby method calls, and the polling loop read it up to twice per pass (50 MB download: 132–166 `Ractor.current` calls per perform → 14, independent of transfer length). Applies to both the polling and Fiber-scheduler socket-action loops. Changing `default_timeout` from a callback or perform block now takes effect on the next `perform`.
5
+ * Fix a use-after-free when request state is replaced while a transfer is active: `post_body=`, `put_data=`, `autoreferer=`, `http_post`, `http_put`, `http_patch`, `http_delete`, and `http(verb)` called from a callback on the same handle, or on a sibling handle in the same performing `Curl::Multi`, freed the body buffer libcurl was still sending and could crash or send freed memory. These now raise `RuntimeError` like `setopt` does, and re-running native setup on an active handle is rejected the same way. Reconfiguring a handle from completion callbacks or once its transfer finishes is unchanged. Add regression coverage for own-handle and sibling-handle mutation.
6
+ * Test suite: fix intermittent "Could not connect" failures in the per-test WEBrick fixture (seen in the macOS Fiber scheduler tests). It reserved a free port, released it, and re-bound it without a bind address; if the IPv4 bind then lost a race for that ephemeral port, WEBrick silently kept only its IPv6 listener and `127.0.0.1` refused connections. The fixture now binds `127.0.0.1` explicitly and lets the kernel choose the port at bind time.
7
+ * Make `CURLOPT_STDERR` safer against programmer mistakes such as closing the IO too early: previously libcurl wrote verbose output through the IO's own `FILE*`, so closing the IO (including the block form of `File.open`, or closing it after `Curl::Multi#add`) and performing again wrote through freed memory and could corrupt the heap. curb now hands libcurl a stream it owns on a duplicate descriptor, so verbose output keeps reaching the file after the caller closes their IO. The descriptor is released on replacement, `reset`, `close`, and GC; clones get their own. Non-writable IOs now raise `IOError` instead of silently dropping output. Add regression coverage for early close, multi transfers, clones, and descriptor release.
8
+ * Fix a `network_policy = :public` bypass through non-IP sockets: a Unix socket path set with `setopt(CURLOPT_UNIX_SOCKET_PATH, ...)` after `Curl::Multi#add` was used without any destination check. The socket-open policy check now refuses every non-IP socket unless Unix sockets were explicitly allowed (`allow_unix_socket`), regardless of when the option was set. Add regression coverage for late Unix socket configuration.
9
+ * Require public IP-literal DoH endpoints under `network_policy = :public`: libcurl's DoH lookups do not go through the socket policy, so a DoH URL such as `https://localhost/dns-query` (or `127.1`, `[::1]`, a private address) reached local services even with `allow_doh` enabled, and the per-handle policy did not check DoH URLs at all. DoH hostnames are now rejected because they resolve outside the policy; use an address such as `https://1.1.1.1/dns-query`. The host is taken from libcurl's own URL parser, so numeric shorthand forms are normalized before the check. Add regression coverage for loopback, private, shorthand, IPv6, and hostname endpoints.
10
+ * Reject `file://` URLs under `network_policy = :public` when no protocol allowlist is configured, since reading a local file opens no socket for the policy to check. An explicit allowlist that includes `:file` is still honored, and `Curl.safe!`/`safe_*` keep raising `UnsupportedProtocolError` through their HTTP/HTTPS allowlist. Add regression coverage.
11
+ * Stop sending `easy.cookies=` to other hosts on redirect: cookies were passed as `CURLOPT_COOKIE`, which libcurl sends to every host a redirect reaches. They are now sent as a `Cookie` header, which libcurl 7.83+ drops on cross-host redirects. An explicit `Cookie` header still takes precedence, and clearing `cookies` now stops sending them on a reused handle. With the cookie engine enabled (`enable_cookies` or `cookielist`), `cookies=` is still passed as `CURLOPT_COOKIE` so libcurl merges both into one `Cookie` line, and is still forwarded on redirect; use the cookie engine's domain-scoped cookies there. Add regression coverage for cross-host and same-host redirects.
12
+ * Cap redirects by default: curb passed `-1` (unlimited) as `CURLOPT_MAXREDIRS` whenever `max_redirects` was unset, overriding libcurl's own limit. Unset now means at most 30 redirects (libcurl 8.3+'s default, applied on every libcurl version); `max_redirects = -1` still allows unlimited redirects. `Curl.safe!`, `safe_*` helpers, and `Easy#safe_http!` additionally default to a 60-second timeout and at most 20 redirects, configurable through `SafetyConfig#timeout`/`#max_redirects` and the `:timeout`/`:max_redirects` helper options, and applied only when a handle has not set its own (unlimited redirects are capped). Add regression coverage for redirect loops and safe-mode limits.
13
+ * Reject CR, LF, and NUL characters in request headers, proxy headers, `useragent`, `cookies`, custom request methods (including `http(verb)`), and `CURLOPT_REQUEST_TARGET`, raising `ArgumentError`. libcurl sends these values verbatim, so a value built from user input could inject extra headers or split the request. Add regression coverage.
14
+ * Stop reserving 32 KB/16 KB body and header buffers on every perform: that capacity stayed attached to each returned `body_str`/`header_str`, so a 2 KB response held 32 KB. The buffers now start small, grow as data arrives, and are trimmed to fit once the transfer completes (5,000 retained 2 KB bodies: 164 MB → 10.4 MB; 20,000 reused-handle GETs: 29 minor GCs → 2). Request time and large downloads are unchanged. Add regression coverage for retained capacity and large bodies.
15
+ * Cut fiber-scheduler overhead for `Curl::Easy#perform`: the scheduler driver yielded on every `Curl::Multi#perform` pass, about five yields per request, although the socket-action loop already waits through the scheduler on each pass. It now yields only on the fallback wait loop (Windows and builds without socket-action support), which blocks the thread. Under Async, a sequential request drops from ~42 µs/53 allocations to ~33 µs/41, and 100 concurrent fibers from ~30 µs to ~19 µs per request. Add regression coverage that sibling fibers keep running while a large body streams.
16
+
17
+ ## 1.4.1
18
+ * Fix CI fixtures for Windows file-URL normalization and libcurl/libpsl localhost cookie loading. Keep parallel multipart Ractor coverage on Windows without triggering Ruby VM bug #21537 through explicit worker GC; retain multipart compaction coverage in the PostField tests. Include this changelog and the transfer-guard regression tests in the gem package.
19
+ * Make `Curl.safe!` default to `network_policy = :public`, including calls without a configuration block. Global safe mode, `safe_*` helpers, and `Easy#safe_http!` now default to a configurable 10 MiB response-body cap, enforced for buffered responses and body callbacks. Explicit helper limits cannot weaken a tighter global cap. Trusted private destinations require an explicit global policy opt-out; raw requests outside safe mode retain their existing defaults. Add regression coverage for default SSRF blocking, multi policy refresh, body limits, and explicit overrides.
20
+ * Complete idle easy-handle detachment during repeated `initialize`, `close`, and `reset`: clear native attachment counts and Ruby request bookkeeping so the handle can be re-added immediately. Check failed libcurl escape/unescape allocations before accessing their results, raising `Curl::Err::CurlError`, and free returned buffers even if Ruby string allocation raises.
21
+ * Guard active multi transfers across GVL-releasing waits: reject access to attached easy handles and native multi mutations from other threads, including close/reset/clone, option changes, and removal. Reject destructive easy lifecycle operations and `setopt` while a transfer is active, while preserving cloning from callbacks on the performing thread. Release ownership on exceptions and interruption, and preserve it when reusing an autoclosed multi. Add concurrency regression tests for polling and socket-action performs.
22
+ * Protect multipart form construction from reentrant `Curl::Easy` access during field callbacks and string coercion, and keep the native lifecycle guard active through form cleanup. Use private native snapshots for multipart payloads so later callbacks, threads, or GC compaction cannot invalidate earlier fields. Avoid per-field VM-global GC root registration and an extra libcurl form copy; release temporary metadata snapshots immediately after form construction. Add regression coverage for POST/PATCH/PUT, exception recovery, string mutation, and compaction.
23
+ * Fix compiler-dependent crashes in multipart `Curl::PostField` uploads by coercing direct and block-provided content to strings before reading their pointers and lengths. Preserve `TypeError` for values without valid `to_str` conversion. Add regression coverage for content and buffered file fields, optional content types, and embedded NUL bytes.
24
+ * Fix SSRF protection in `Curl.safe_*` and `Curl::Easy#safe_http!`: enforce `network_policy = :public` alongside HTTP/HTTPS protocol limits, blocking loopback, private, and link-local destinations by default. Apply existing proxy and destination-override controls, and prevent weaker global or per-handle network policies from disabling protection. Safe helpers now reject trusted local services too; use raw helpers with explicit protocol limits for those requests. Response-body limits use the safe-mode default and can be configured through `max_body_bytes`. Add regression coverage for safe helpers, policy reapplication, and multi transfers.
25
+ * Fix crashes and uninitialized-memory reads when native operations are called on `Curl::Easy.allocate` or subclasses whose initializer omits `super`: reject uninitialized handles with `Curl::Err::FailedInitError`, initialize connection-info outputs, and check libcurl errors. Repeated `initialize` now detaches and frees the previous handle and rejects reinitialization during callbacks or native operations. Add regression coverage for allocation, subclasses, getters, lifecycle operations, and repeated initialization.
26
+ * Fix a use-after-free when `Curl::Easy#clone` or `#dup` is called from a callback during a multipart request: detach the temporary form from the clone before the original request frees it, and reset the clone's callback state. Supply new `PostField` objects to send a multipart request with the clone. Add regression coverage for body, header, progress, debug, and completion callbacks.
27
+
28
+ ## 1.4.0
29
+ * Fix crashes when a user-supplied value's `#to_s` returns a non-String (e.g. a Fixnum): `Curl::PostField#to_s` (name and content coercion), `Curl::Easy#http_auth_types=`, and `Curl::Easy#escape` now use `rb_obj_as_string` instead of trusting a raw `#to_s` result, and `Curl::Easy#unescape` coerces its argument before use. Previously these passed the unchecked result straight to raw C string macros (`RSTRING_PTR`/`RSTRING_LEN`), which could segfault instead of raising.
30
+ * Fix `Curl::Easy#http_auth_types = [:basic, :digest]` (the documented array form), which previously stringified the whole Array and set no auth types.
31
+ * `Curl::Easy#proxy_auth_types=` now accepts auth type symbols (`:basic`, `[:basic, :ntlm]`, etc.) like `http_auth_types=`, in addition to an Integer mask.
32
+ * Fix a curl handle leak in `Curl::PostField#to_s` when the content proc, `#to_s`, or UTF-8 conversion raised.
33
+ * Add guarded Ractor support on Ruby 3.0+ with thread-safe libcurl builds: isolate mutable configuration and cleanup queues per Ractor, freeze exported string constants, default Easy handles to `CURLOPT_NOSIGNAL`, and cover concurrent Easy requests with regression tests.
34
+ * `Curl::Multi#perform` now waits with `curl_multi_poll` (libcurl 7.68+) instead of `curl_multi_fdset`/`select`. Sockets numbered above `FD_SETSIZE` (1024), common in long-running servers, no longer fall back to fixed 100ms sleeps (20 sequential GETs: 4.0s → 12ms); `Thread#raise`, `Thread#kill` and signals interrupt the wait immediately through `curl_multi_wakeup`; and idle waits follow libcurl's timeout instead of a fixed 100ms sleep. Older libcurl keeps the previous wait loop.
35
+ * Under a Fiber scheduler, the socket-action loop now mirrors libcurl's sockets into one epoll (Linux) or kqueue (macOS/BSD) descriptor and waits on it with the scheduler's `io_wait` hook. This avoids the optional `io_select` hook, which Async implements by starting a thread per call (50 concurrent downloads with a CPU-busy Ruby thread: 637ms → 131ms), and fixes schedulers without `io_select` waiting up to `Curl::Multi.default_timeout` behind an idle socket. Platforms without epoll/kqueue keep the previous paths.
36
+ * Make completion processing in `Curl::Multi` O(1) per finished transfer instead of copying every attached handle (16,000 transfers: 6.5s → 2.6s).
37
+ * Scheduler-driven `Curl::Easy#perform` now queues completions instead of scanning every waiter on each drive-loop pass.
38
+ * Fix an invalid `timeval` in the fallback wait loop when `Curl::Multi.default_timeout` exceeds 1000, and drop a retry loop for `CURLM_CALL_MULTI_PERFORM`, which libcurl has not returned since 7.20.
39
+ * Test suite: fix cookie-list tests on libcurl 8.22+ with libpsl (#483), fix a WEBrick teardown race that could hang CI (notably the valgrind job), space fiber stacks apart in `rake test:valgrind` so valgrind tracks fiber switches instead of reporting false invalid reads, skip wall-clock bounds under valgrind, and add coverage for the new wait paths, poller cleanup, and completion/waiter bookkeeping.
40
+
41
+ ## 1.3.7
42
+ * Pin `Curl::Easy` wrappers during GC compaction so libcurl completion dispatch cannot follow a stale Ruby object reference (#482).
43
+ * Fix FTP option configuration (#481): add `Curl::Easy#ftp_create_missing_dirs` and its setter, export the available `CURLFTP_CREATE_DIR_*` mode constants, and route detected scalar/string FTP options through `Curl::Easy#set`.
44
+ * Make `Curl::Multi#perform` and scheduler-driven `Curl::Easy#perform` nonblocking under Fiber schedulers while preserving cancellation and final-yield behavior, including schedulers without `io_select`.
45
+
46
+ ## 1.3.6
47
+ * Removing long standing potential for mis-use with the Thread.current caching of easy handles (potentially a breaking change for some but more likely a fix)
48
+ * Add safe request mode for fetching untrusted HTTP(S) URLs: `Curl.safe!`, `Curl.safe_get`, and `Curl::Easy#safe_http!`.
49
+ * Add `CURLOPT_CONNECT_TO`, `Curl::Easy#connect_to`, `CURLOPT_DOH_URL`, `Curl::Easy#doh_url`, and DoH SSL verification options.
50
+ * Restore Ruby 2.6 CI coverage and fix `Curl::Easy#perform` on Ruby 2.6 by avoiding finalizer-backed `ObjectSpace::WeakMap` entries for idle `Curl::Easy` references, including frozen easy handles.
51
+ * Extend Ruby 4.0.x valgrind test handling to Ruby 4.0.5 by filtering Ruby VM Fiber/block-handler noise while preserving reports for errors originating in `curb_core`.
52
+ * Harden `Curl::Easy`/`Curl::Multi` cleanup against re-entrant close during coercion and stale multi attachments.
53
+
54
+ ## 1.3.5
55
+ * Improve Ruby 4.0.4 valgrind test handling by filtering Ruby VM Fiber/block-handler noise while preserving reports for errors originating in `curb_core`.
56
+ * Fix `Curl::Multi` socket-action Fiber scheduler handling with libcurl 8.20.0 by honoring libcurl timer deadlines, matching Ruby IO wrapper modes to socket interest, and using scheduler `io_select` for multi-fd waits.
57
+ * Fix `Curl::Multi#perform` so work added during the final idle yield is processed before autoclose, avoiding dropped queued requests.
58
+
59
+ ## 1.3.4
60
+ * Fix `Curl::Multi` socket-action dispatch so ready sockets are collected before calling back into libcurl, avoiding invalid access when socket interest changes during iteration.
61
+ * Keep the Fiber scheduler socket-action IO cache registered with Ruby GC during `_socket_perform` and clear it during cleanup, avoiding stale Ruby object references under valgrind.
62
+ * Make Fiber scheduler tests avoid hardcoded port `9993` so local listeners or concurrent valgrind runs do not break the release suite.
63
+
64
+ ## 1.3.3
65
+ * Skip the NTLM-specific username/password assertion when libcurl was built without NTLM support so newer libcurl builds do not fail by falling back to Basic auth.
66
+ * Fix `Curl::Easy#put_data=` with non-String `to_s` payloads so upload length calculation does not read non-String objects as Ruby strings.
67
+ * Guard `Curl::Easy#clone` against `curl_easy_duphandle` allocation failure so clone raises `NoMemError` instead of dereferencing a NULL handle.
68
+ * Guard `Curl::Multi` lifecycle during active `perform` calls so closing a multi handle from callbacks or perform blocks raises instead of freeing an in-use libcurl multi handle.
69
+ * Reject adding an active `Curl::Easy` handle to a second `Curl::Multi` before setup mutates request state.
70
+ * Restore one-shot request state for `HEAD`, `PATCH`, and `PUT` requests after success or callback/error unwinds so later requests do not inherit stale method/body options.
71
+ * Validate `Curl::Easy#put_data=` before mutating libcurl upload options, preventing failed setup paths from leaving stale upload callbacks installed.
72
+ * Preserve `CURLOPT_RESOLVE` values set through `Easy#set` across repeated performs, and allow resolve/FTP command entries that convert to strings.
73
+ * Fix older build fallback paths for `curl_multi_wait` and no-GVL `select`.
74
+ * Fix the Fiber scheduler socket-action path so single-socket waits pass the actual ready events back to libcurl, avoiding incomplete HTTP responses on macOS CI.
75
+ * Make legacy bug tests avoid hardcoded port `9999` so local SSH tunnels or other listeners do not break the release suite.
76
+ * Add regression coverage for multi lifecycle guards, active easy reuse, request state cleanup, upload setup rollback, resolve persistence, string-convertible resolve/FTP command entries, and scheduler socket waits.
77
+
78
+ ## 1.3.2
79
+ * Fix `Curl::PostField` GC marking so block-backed content fields remain valid across GC and compaction.
80
+ * Fix upload read/seek callbacks to preserve Ruby exceptions, avoid unsafe unwinds through libcurl, and reject reads larger than libcurl's supplied buffer.
81
+ * Fix multipart POST/PATCH/PUT cleanup so partially built native forms are freed when form construction raises.
82
+ * Restore `Curl::Easy#last_error` after `Easy#close` by reattaching the libcurl error buffer to the reinitialized handle.
83
+ * Fix `Curl::Easy#clone` with `put_data` so upload callbacks and upload state belong to the clone instead of the original handle.
84
+ * Add regression coverage for native callback, multipart cleanup, clone upload, close/error-buffer, and `PostField` GC safety.
85
+
86
+ ## 1.3.1
87
+ * Fix Ruby 4.x compatibility in `Curl::Multi`
88
+
89
+ ## 1.3.0
90
+ ### Breaking Changes
91
+ * `Curl::Multi#close` now permanently closes the multi handle. Code that previously called `close` and then reused the same `Curl::Multi` instance must now allocate a new multi handle instead. `Curl::Multi.autoclose` continues to use the internal reusable cleanup path for implicit autoclose and fiber-scheduler reuse.
92
+ * Callback exceptions raised during `Multi#perform` and scheduler-driven `Easy#perform` are now deferred until in-flight sibling transfers finish draining, and queued replacement work is no longer started once a deferred callback exception is pending. Applications that relied on immediate aborts or that enqueue new work from completion callbacks after a sibling failure may observe different control flow.
93
+ * `post_body=` and raw `CURLOPT_POSTFIELDS` assignments now snapshot the assigned Ruby string instead of aliasing later mutations to the caller's buffer. In addition, `easy.setopt(Curl::CURLOPT_POSTFIELDS, nil)` now clears the request body without implicitly switching the request back to GET; callers that relied on the old method-reset behavior must now set the method explicitly.
94
+ * `close` and `reset` are now blocked while progress, debug, and upload-read callbacks are active, matching the existing restriction for body and header callbacks. Code that performed cleanup directly inside those callbacks must move that work outside the callback.
95
+
96
+ ### Changes
97
+ * Improve Ruby 4.x and newer libcurl compatibility by addressing typed-data conversion and deprecation issues in the extension build.
98
+ * Fix `post_body`, `CURLOPT_POSTFIELDS`, multipart form reuse, and `resolve` cleanup so request buffers and per-request native state stay valid across setup, mutation, and reset.
99
+ * Tighten `Curl::Easy` and `Curl::Multi` lifecycle cleanup to reduce leaks and stale handle references, including explicit clearing of idle `easy.multi` back-references when a multi is closed.
100
+ * Rework deferred callback exception handling for `Easy` and `Multi` so callback failures are re-raised on the originating request after sibling work drains, queued replacement work does not start once a deferred exception is pending, and deferred implicit-multi cleanup stays thread-affine.
101
+ * Preserve the original callback exception for frozen `Curl::Easy` handles instead of raising `FrozenError`.
102
+ * Improve Fiber scheduler integration so scheduler-driven `Easy#perform` reuses the shared multi correctly, keeps yielding timers/perform blocks while deferred completions drain, and wakes the originating waiter when deferred callback errors surface.
103
+ * Add leak-trace tooling plus broader regression coverage for cleanup, callback, scheduler, GC.compact, and test-server stale-lock edge cases; CI now runs the valgrind suite on Linux Ruby 4.0.
104
+
105
+ ## 1.2.2
106
+ * Add `Curl::Easy#http_version` getter/setter along with HTTP/2 TLS/prior-knowledge constants so clients can force a specific HTTP protocol.
107
+ * Fix rare `Multi#perform` segfault with GC.compact by recovering the Easy when `CURLINFO_PRIVATE` is stale and pruning unknown handles.
108
+ * Store `ruby_curl_easy*` in `CURLOPT_PRIVATE` and use it for all libcurl callbacks (write/header/progress/debug) so GC.compact can’t invalidate stored VALUE pointers.
109
+ * Pass `ruby_curl_easy` as libcurl userdata for default body/header handlers so GC.compact can’t invalidate stored VALUE pointers.
110
+ * Safer GC: detach `Easy` from `Multi` during free to avoid dangling references.
111
+ * add work around for libcurl 8.16.0 max redirects regression
112
+
113
+ ## 1.2.1
114
+ * Clarify cookie behavior in README and RDoc:
115
+ - Differentiate between manual `cookies` header and the libcurl cookie engine
116
+ - Add `cookielist=` helper and examples for manipulating engine cookies (ALL/SESS/FLUSH/RELOAD)
117
+ - Note that `cookies = ''` clears the manual Cookie header; `nil` has no effect
118
+ * Add support for `CURLOPT_REQUEST_TARGET` (libcurl ≥ 7.55), including `easy.request_target=` setter, tests, and README usage notes
119
+ * Clarify that instance `easy.http_post` does not accept a URL argument; use `Curl::Easy.http_post(url, ...)` or set `easy.url` first
120
+ * Use after free fix
121
+
122
+ ## 1.2.0
123
+ * Fix on_redirect callback being triggered when follow_location=false and max_redirects=0
124
+ * Add support for CURLOPT_NOPROXY option (#441)
125
+ * Fix Curl.spnego? to check for both CURL_VERSION_SPNEGO and CURL_VERSION_GSSNEGOTIATE (#227)
126
+ * Add Fiber scheduler awareness for Curl::Multi: prefer scheduler-friendly socket-action path and `rb_thread_fd_select` when available (non-Windows)
127
+ - Add compile-time CURB_SOCKET_DEBUG flag to enable optional socket-action debug logging in curb_multi.c (default off; set CURB_SOCKET_DEBUG=1 when running extconf)
128
+ * When using request method HEAD ensure CURLOPT_NOBODY is set to 1 automatically
129
+ * Compiler checks run in parallel when building the extension JOBS=n to adjust concurrency of configure checks and build concurrency
130
+ * CI tests now run on Windows and MacOS
131
+
132
+ ## 1.1.0
133
+ * Add support for CURLOPT_RESOLVE
134
+ ```ruby
135
+ require 'resolv'
136
+ uri = URI.parse("https://google.com/")
137
+ resolved_ip = Resolv.getaddress(uri.host) # perform DNS lookup once
138
+ mapping = "#{uri.host}:#{uri.port}:#{resolved_ip}"
139
+
140
+ http = Curl::Easy.new("https://google.com/")
141
+ http.setopt(Curl::CURLOPT_RESOLVE, [mapping])
142
+
143
+ http.get
144
+
145
+ assert_match(/GET/, http.body)
146
+ ```
147
+ * allow configuration curl multi MAX_HOST_CONNECTIONS (#460)
148
+
149
+ https://curl.se/libcurl/c/CURLMOPT_MAX_HOST_CONNECTIONS.html
150
+ ## 1.0.9
151
+ * Fix for proxy headers see #457
152
+ * Add CI for Ruby 2.7, Ruby 3.3 and 3.4
153
+ ## 1.0.8
154
+ * PUT and PATCH requests now behave like POST requests with multipart form data supporting http_post, http_put, and http_patch with PostField arguments
155
+ ## 1.0.7
156
+ * Clean up easy handle after failing to add to a multi handle
157
+ * With newer versions of libcurl we can use curl_easy_escape instead of curl_escape, this should improve character encoding support
158
+ * When available use curl_multi_wait for greater system compatibility, users of libcurl 7.28.0 and later should benefit from this change
159
+ * Improve easy handle cloning to deep copy related lists, headers, cookies , proxy_headers, ftp_commands and resolve list.
160
+
161
+ * FTP: Document correct way to perform NLST/LIST (don’t use `ftp_commands` for data-transfer commands). Add README section for HTTP proxy tunnel usage and examples, including full LIST snippets. Map the following libcurl options through `Easy#set`:
162
+ - `:dirlistonly` -> `CURLOPT_DIRLISTONLY`
163
+ - `:ftp_use_epsv` -> `CURLOPT_FTP_USE_EPSV`
164
+ - `:ftp_use_eprt` -> `CURLOPT_FTP_USE_EPRT`
165
+ - `:ftp_skip_pasv_ip` -> `CURLOPT_FTP_SKIP_PASV_IP`
166
+ Also expand `ftp_commands` RDoc with guidance to avoid LIST/NLST/PASV/EPSV there.
167
+ Add a basic regression test in `tests/tc_ftp_options.rb` ensuring these options can be set without raising.
168
+ * Updating test suite and internal tools for ruby 3.4
169
+ * CURLOPT_COOKIELIST support see examples:
170
+ ```ruby
171
+ easy.setopt(Curl::CURLOPT_COOKIELIST, "Set-Cookie: c1=v1; domain=localhost; expires=#{expires.httpdate};")
172
+ easy.setopt(Curl::CURLOPT_COOKIELIST, [ ['.localhost', 'TRUE', '/', 'FALSE', 0, 'session', '42'].join("\t"),
173
+ ['.localhost', 'TRUE', '/', 'FALSE', 0, 'session2', '84'].join("\t"), '', ].join("\n") )
174
+ ```
175
+ * Add :on_missing to callbacks supported by Curl::Multi.http
176
+
177
+ ## 1.0.6
178
+ * Fix for PUT request length calculation
179
+ - see: #451
180
+ * Fix a double quote issue when evaluating cert's path
181
+ - see: #454
182
+
183
+ ## 1.0.5
184
+ * Add shorter response_code alias -> code
185
+ ```
186
+ Curl.get("https://github.com/taf2/curb").code # -> 200
187
+ ```
188
+ ## 1.0.4
189
+ * Improve exception handling from callback functions to ensure correct error line number is reported
190
+ * Resolve many compiler warnings
191
+ ## 1.0.3
192
+ * Adjust how we handle exceptions to handle more cases
193
+ ## 1.0.2
194
+ * Address alloc function T_DATA see: https://bugs.ruby-lang.org/issues/18007
195
+ * Add CURLOPT_PROXY_SSL_VERIFYHOST support
196
+ * Fix for exception handling in non standard callback functions on_redirect, on_complete
197
+ * Fix missing response code when using Curl::Multi.http with a block
198
+ * Fix to ensure redirect handler is called even when follow location is true
199
+ ## 1.0.1
200
+ * Fix nested Curl.get/post inside of a block
201
+ ```ruby
202
+ r1 = Curl.get("http://example.com/1") {|http|
203
+ r2 = Curl.get("http://example.com/2") {|h|
204
+ }
205
+ }
206
+ r1.body # previously was r2's body... this is now fixed
207
+ ```
208
+ * Restore compatibility with older libcurl installs
209
+ * Correctly set empty headers e.g. http.headers['Accept'] = '' # to remove the header from the request
210
+ ## 1.0.0
211
+ * Add Ruby 3.1 to tests
212
+ * Support curlopt_protocols and curlopt_redir_protocols.
213
+ * Improved error handling via last_error tracking
214
+ * Added support for CURLOPT_RANGE
215
+
216
+ ## 0.9.11
217
+ * Fixes for haproxy protocol via CURLOPT_HAPROXYPROTOCOL
218
+ * Ensure dev environments don't use older versions of rake when working on curb (not a production dependency)
219
+ * Many readmen updates - more windows support documentation
220
+ * Support added for CURLOPT_STDERR
221
+ ## 0.9.8
222
+ ### Fixes
223
+ * Fix build with curl `7.62.0`
224
+ * Support building against Ruby `1.8` to help people migrate to the future and beyond
225
+ ### Enhancements
226
+ * Improve `timeout=` to allow Floating point values which will automatically switch to `timeout_ms`
227
+ * Add `SOCKS5_HOSTNAME` support
228
+ * Added `Curl::Multi.autoclose` and `Curl::Multi#close` to improve connection handling. In past releases connection
229
+ clean up only ever happens when GC runs. Now in this release you can explicitly control connections via
230
+ `multi.close` or have it always close your connections with `Curl::Multi.autoclose=true`
231
+ ### Breaking Changes
232
+ * Request handling reduces C (lines of code), and might resolve segfault when mixing curb with ruby timeout
233
+ * Timeout change for anyone who expected `0.9` to equal `0` or `infinity`
234
+
235
+ ## 0.9.7
236
+ ### Breaking Changes
237
+ ### Fixes
238
+ * Guard use of `CURLOPT_RESOLVE` in `#if HAVE_CURLOPT_RESOLVE`
239
+ * Prevent `NoMethodError` when no HTTP Status
240
+ * [`Curl.urlalize` will miss raw url's query params](https://github.com/taf2/curb/issues/330)
241
+ * [Fix SSL versions constant names](https://github.com/taf2/curb/pull/333)
242
+ ### Enhancements
243
+ * Add `Curl::Easy#resolve` method to implement static IP overrides, bypassing DNS
244
+ * [Add new curl features](https://github.com/taf2/curb/pull/331)
245
+ * [Allow extending `Curl::Easy` and having a different number of constructor arguments
246
+ than `Curl::Easy` allows](https://github.com/taf2/curb/pull/329)
247
+
248
+ ## 0.9.4
249
+ ### Fixes
250
+ * [Fix `multi.pipeline`: `HAVE_CURLMOPT_PIPELINING` was never defined](https://github.com/taf2/curb/issues/298)
251
+ * Eliminate warnings on Ruby `2.4.1`
252
+ * [Hang in `select()`](https://github.com/taf2/curb/issues/323)
253
+ * Revert 'Experimenting with adding multi handle cleanup after every request, normally would be cleaned up with GC
254
+ (problem addressed by fix hang in `select()`)
255
+ * Prevent errant callbacks once the handle is cleaned up
256
+ ### Enhancements
257
+ * Add `PATCH` support?
258
+ * Add support for `CURLOPT_PATH_AS_IS`
259
+ * Add `.cookielist` method to `Curb::Easy`
260
+ * Improved Windows compatibility
261
+
262
+ ## 0.9.3
263
+ ### Fixes
264
+ * Correctly check for constants that may not be present in older versions of Curl
265
+
266
+ ## 0.9.2
267
+ * **Breaking change** - Change base class for exceptions from `Exception` to `StandardError`
268
+ ### Fixes
269
+ * [Segmentation fault (introduced with commit 50ab567)](https://github.com/taf2/curb/issues/240)
270
+ * Use 64-bit int for key in hash of active easy handles, to avoid clobbering on 32-bit systems
271
+ * [Fix `http_auth_types` for `:any` and `:anysafe`](https://github.com/taf2/curb/issues/291)
272
+ * [Use `LONG2NUM` and `NUM2LONG` to support all values of the `long` type](https://github.com/taf2/curb/issues/295)
273
+ * [Support empty reason phrases in the HTTP status line](https://github.com/taf2/curb/issues/292)
274
+ ### Enhancements
275
+ * Implement setting of `CURLOPT_MAXCONNECTS`
276
+ * Implement setting of `CURLOPT_FORBID_REUSE`
277
+ * Ability to specify specific allowed TLS versions when connecting
278
+ * Add support for `HTTP/2.0` and multiplexing
279
+ * Experimenting with adding multi handle cleanup after every request, normally would be cleaned up with GC
280
+
281
+ ## 0.9.1
282
+ ### Fixes
283
+ * Fix regression in `timeout`/`timeout_ms` handling
284
+
285
+ ## 0.9.0
286
+ ### Fixes
287
+ * Remove `CURLAUTH_ANY` as the default for authentication setting, as too many services in the world don't
288
+ respond correctly with a `401` but instead `403` so the option rarely works as we'd hope
289
+ * Use the memory address as the key to correctly keep a hash of active easy handles alive in the multi handle -- should
290
+ resolve existing reports of memory leaks as well as crashes under some conditions
291
+
292
+ ## 0.8.9
293
+ ### Fixes
294
+ * Only append query string if it exists
295
+ * Setting any of `CURLOPT_HEADER`, `CURLOPT_NOPROGRESS`, `CURLOPT_NOSIGNAL`, `CURLOPT_HTTPGET` did not work
296
+ (introduced in commits `4358d04` and `14e9b53`)
297
+ * Compilation for curl versions before `7.40.0`
298
+ * [`Curl::Multi.http` call of the `on_complete` callback expects unsupplied
299
+ parameter](https://github.com/taf2/curb/issues/270)
300
+ ### Enhancements
301
+ * Add `Curl::HTTP_2_0`, and `Curl.http2?`
302
+ * Support `CURLOPT_UNIX_SOCKET_PATH` in `Curl::Easy`
303
+ * Add `timeout_ms`/`connect_timeout_ms` options
304
+ * Add `on_redirect` callback handling to `.http`
305
+
306
+ ## 0.8.8
307
+ ### Fixes
308
+ * [`Curl::Easy.http_get` broken on macOS](https://github.com/taf2/curb/pull/242)
309
+
310
+ ## 0.8.7
311
+ ### Breaking Changes
312
+ ### Fixes
313
+ * Restore support for older, `pre-7.17` versions of curl (fixes CentOS 5 compatibility)
314
+ * Improve Ruby `2.2.x` support (use `rb_thread_fd_select` instead of `rb_thread_select`)
315
+ ### Enhancements
316
+ * Add SOCKS4A support
317
+ * Allow custom ssl cipher lists
318
+ * Add install instructions for Windows
319
+ * Make it possible easily reset the Thread current handle (`Curl.reset`)
320
+
321
+ ## 0.8.6
322
+ ### Breaking Changes
323
+ ### Fixes
324
+ * Add support for the new error `CURLM_ADDED_ALREADY` introduced at curl `7.33.0`
325
+ * Add conditionals for new `URLM_ADDED_ALREADY` error
326
+ * `Curl#urlalize` - `CGI.escape` for param values
327
+ * Use correct HTTP verb for DELETE requests (was `delete`, and some servers are apparently case sensitive)
328
+ * Typo in `ext/curb_errors.c`: `BadBasswordError` -> `BadPasswordError`
329
+ * Status method now returns the last status in the response header
330
+ ### Enhancements
331
+ * Support Ruby `2.2.0`
332
+ * Support `CURLOPT_TCP_NODELAY` in `Curl::Easy`
333
+ * Raise an exception if `curb` doesn't know how to handle a `CURLOPT`
334
+ * Expose license via gemspec
335
+ * Implement seek function for multi-pass authentication uploads
336
+
337
+ ## 0.8.5
338
+ ### Fixes
339
+ * Guard against a curl without GSSAPI delegation compiled in
340
+ * Added `extconf.rb` entry for `appconnect`
341
+ * Reset handle before each call when using "super simple API"
342
+ ### Enhancements
343
+ * Added support for `app_connect_time` from `libcurl`
344
+ * Alias `body_str` to `body` and `header_str` to `head`
345
+
346
+ ## 0.8.4
347
+ ### Fixes
348
+ * [Setting `post_body` to `nil` does not reset data posted in `http(:GET)`](https://github.com/taf2/curb/issues/133)
349
+ * Various spurious/inconsequential warnings
350
+ * `Curl.head` uses `OPTIONS` method instead of `HEAD` method
351
+ ### Enhancements
352
+ * Make "super simple API" more efficient by storing `Curl::Easy` instance in `Thread.current`
353
+ * GSSAPI: Add required flags for GSSAPI delegation support
354
+ * GSSAPI: Allow setting of the new delegation flag
355
+ * GSSAPI: Implement support for libcurl SPNEGO flags in curb
356
+
357
+ ## 0.8.3
358
+ ### Fixes
359
+ * [Exception raised when process is signaled](https://github.com/taf2/curb/issues/117)
360
+ * [Curb always appends a query to request uris](https://github.com/taf2/curb/issues/137)
361
+
362
+ ## 0.8.2
363
+ ### Fixes
364
+ * Clang compatibility
365
+ ### Enhancements
366
+ * Allow string data with simpler interface e.g. `Curl.post('...', 'data')`
367
+
368
+ ## 0.8.1
369
+ ### Fixes
370
+ * [Error installing on Ubuntu](https://github.com/taf2/curb/issues/106)
371
+ * Protect callbacks from reset/close
372
+ * `Curl::Multi` not actually performing HTTP requests in cases when the `:max_connects >= number of requests`
373
+ * Build error when compiled with `-Werror=format-security`
374
+ * `postalize` parameter escaping
375
+ ### Enhancements
376
+ * Support `CURLINFO_REDIRECT_URL`
377
+ * Support `CURLOPT_RESUME_FROM` in `Curl::Easy`
378
+ * Add `.status` to `Curl::Easy` instances
379
+ * Support `CURLOPT_FAILONERROR`
380
+ * Add even easier interface: `Curl.get` / `Curl.post` / e.t.c
381
+
382
+ ## 0.8.0
383
+ * **Breaking change** - `on_failure` only fires for 5xx responses
384
+ ### Fixes
385
+ * Error in README section for HTTP POST file upload.
386
+ ### Enhancements
387
+ * Add a default for `resolve_mode` to `CURL_IPRESOLVE_WHATEVER` e.g. `:auto`
388
+ * Add `on_redirect` and `on_missing` callbacks for 3xx/4xx responses
389
+ * Allow `http_post(array_of_fields)` when using multipart form posts
390
+
391
+ ## 0.7.18
392
+ ### Fixes
393
+ * Check for obsolete, since it appears to be gone from later versions of libcurl per issue #100
394
+ * Regression in PUT requests with NULL bytes (caused by `RSTRING_PTR` => `StringValueCStr` change)
395
+
396
+ ## 0.7.17
397
+ ### Fixes
398
+ * Restore compatibility with 0.7.16 (e.g. suport for building without `CURLOPT_SEEKDATA`, etc)
399
+ * Use `rb_thread_blocking_region` when available in place of `rb_thread_select` for ruby 1.9.x
400
+ * Copy-paste mistake which made it impossible to set `CURLOPT_SSL_VERIFYHOST` to `2`
401
+ * Copy-paste mistake that caused `CURLOPT_SSL_VERIFYPEER` to be overwritten by the value of `use_netrc` setting
402
+ ### Enhancements
403
+ * Warn on exceptions in success/failure/complete callbacks
404
+ * Use `StringValueCStr` more instead of the less safe `RSTRING_PTR`
405
+ * Defaulting `CURLOPT_SSL_VERIFYHOST` to `2` to make it consistent with cURLs default value
406
+
407
+ ## 0.7.16
408
+ * Add better support for timeouts - issue 48
409
+ * Improve stability, avoid bus errors when raising in callbacks
410
+ * Allow Content-Length header when sending a PUT request
411
+ * Better handling of cacert
412
+ * Support `CURLOPT_IPRESOLVE`
413
+ * Allow `PostField.file#to_s`
414
+ * Minor refactoring to how `PostField.file` handles strings
415
+ * Accept symbol and any object that `responds_to?(:to_s)` for `easy.http(:verb)`
416
+ * Free memory sooner after a request finishes instead of waiting for garbage collection
417
+ * Fix crash when sending nil for the `http_put` body
418
+ * Fix inspect for `Curl::Easy`, thanks Hongli Lai
419
+ * Fix unit test assertions for `Curl::Multi`, thanks Hongli Lai
420
+
421
+ ## 0.7.7
422
+ * `Curl::Multi` perform is more robust process callbacks before exiting the loop - issue 24.
423
+ Thanks to igrigorik and ramsingla
424
+ * improve `Curl::Multi` idle callback robustness, trigger callbacks more often - issue 24.
425
+ Thanks to igrigorik and ramsingla
426
+
427
+ ## 0.7.6
428
+ * fix bug in http_* when http_delete or http("verb") raises an exception
429
+ * add support for autoreferrer e.g. if follow_location=true it will pass that along
430
+
431
+ ## 0.7.5
432
+ * minor fix for no signal instead of INT2FIX Check boolean value
433
+
434
+ ## 0.7.4
435
+ * add support to set the http version e.g. `easy.version = Curl::HTTP_1_1`
436
+ * add support to disable libcurls internal use of signals.
437
+
438
+ ## 0.7.3
439
+ * Support rubinius
440
+ * allow Multi.download to be called without a block
441
+ * add additional `Multi.download` examples
442
+
443
+ ## 0.7.2
444
+ * Start tracking high level changes
445
+ * Add `Curl::Easy#close` - hard close an easy handle
446
+ * Add `Curl::Easy#reset` - reset all settings but keep connections alive
447
+ * Maintain persistent connections for single easy handles
data/README.md CHANGED
@@ -205,12 +205,20 @@ end
205
205
  `curb` is a libcurl binding and intentionally supports protocols beyond HTTP.
206
206
  Do not pass untrusted URLs to `Curl.get`, `Curl::Easy.new`, or related raw
207
207
  helpers without application-level validation. For user-supplied URLs, enable the
208
- safety policy for the current Ractor before making requests:
208
+ safety policy for the current Ractor before making requests. `Curl.safe!` defaults
209
+ to HTTP/HTTPS and `network_policy = :public`, blocking local/private destinations
210
+ even when called without a block. Safe mode also defaults to a 10 MiB
211
+ (10,485,760-byte) response-body limit, a 60-second transfer timeout, and at most
212
+ 20 redirects. The timeout and redirect cap only fill in limits a handle has not
213
+ set itself (an unlimited `max_redirects = -1` is capped); set either to `nil` to
214
+ disable it:
209
215
 
210
216
  ```ruby
211
217
  Curl.safe! do |config|
212
- config.network_policy = :public # block local/private destination IPs
213
- config.max_body_bytes = 1_000_000 # cap buffered/callback response bytes
218
+ config.network_policy = :public # default; block local/private destination IPs
219
+ config.max_body_bytes = 1_000_000 # optional tighter cap; default is 10 MiB
220
+ config.timeout = 30 # seconds; default is 60
221
+ config.max_redirects = 5 # default is 20
214
222
  end
215
223
 
216
224
  curl = Curl.get(user_url) # allows only http/https, including redirects
@@ -226,14 +234,37 @@ Curl.safe! do |config|
226
234
  end
227
235
  ```
228
236
 
229
- For local per-handle policy instead of process-wide policy, use
230
- `easy.safe_http!` and `easy.max_body_bytes = ...` before `perform`.
237
+ For local per-handle policy, use `easy.safe_http!` before `perform`, or use
238
+ `Curl.safe_get`, `Curl.safe_post`, and the other `safe_*` helpers. These enforce
239
+ HTTP/HTTPS for requests and redirects and enable `network_policy = :public`,
240
+ even when the global policy permits private destinations. This rejects local
241
+ services as well as private and link-local addresses, including metadata endpoints.
242
+ The proxy and destination-override controls below apply, with any explicit
243
+ exceptions from `Curl.safe!` honored. Unsupported libcurl builds fail closed.
244
+ Safe helpers and `easy.safe_http!` default to the global safe-mode body limit,
245
+ or 10 MiB when global safe mode is not enabled. Override it explicitly with
246
+ `Curl.safe_get(user_url, :max_body_bytes => 1_000_000)` or
247
+ `easy.safe_http!(:max_body_bytes => 20 * 1024 * 1024)`; `:timeout` and
248
+ `:max_redirects` are accepted the same way. A tighter global limit
249
+ always takes precedence. Set `config.max_body_bytes = nil` (or `0`) to disable
250
+ the global cap; a helper's explicit `:max_body_bytes => nil` or `0` disables its
251
+ own cap, but cannot bypass a global cap or an existing tighter handle limit.
252
+ Raw requests outside safe mode have no body limit or timeout unless configured
253
+ otherwise, and follow at most 30 redirects when `max_redirects` is unset.
254
+ These limits cover body bytes, not response headers.
255
+ For trusted local services, use the raw helpers with explicit protocol limits.
256
+ If global safe mode is enabled, explicitly set `config.network_policy = :none`
257
+ to opt out of peer-address filtering; `safe_*` helpers still enforce `:public`.
231
258
 
232
259
  With `network_policy = :public`, curb checks peer addresses when libcurl opens
233
- the socket and blocks local/private destinations. Proxies, `resolve`,
260
+ the socket and blocks local/private destinations, `file://` URLs (unless an
261
+ explicit protocol allowlist includes `:file`), and any non-IP socket such as a
262
+ Unix socket path set after the policy was applied. Proxies, `resolve`,
234
263
  `connect_to`, DoH URL overrides, and Unix socket paths are disabled by default
235
- under this policy unless explicitly allowed in the safety config. Custom DNS
236
- server overrides are rejected. To use a trusted explicit proxy without
264
+ under this policy unless explicitly allowed in the safety config. When DoH is
265
+ allowed, the DoH URL must use a public IP address (for example
266
+ `https://1.1.1.1/dns-query`): libcurl resolves a DoH hostname outside the
267
+ policy, so hostnames are rejected. Custom DNS server overrides are rejected. To use a trusted explicit proxy without
237
268
  re-enabling environment proxies, set `allowed_proxy_hosts` and configure
238
269
  `easy.proxy_url` on the request.
239
270
 
data/ext/curb.h CHANGED
@@ -28,10 +28,10 @@
28
28
  #include "curb_macros.h"
29
29
 
30
30
  // These should be managed from the Rake 'release' task.
31
- #define CURB_VERSION "1.4.0"
32
- #define CURB_VER_NUM 1040
31
+ #define CURB_VERSION "1.5.0"
32
+ #define CURB_VER_NUM 1050
33
33
  #define CURB_VER_MAJ 1
34
- #define CURB_VER_MIN 4
34
+ #define CURB_VER_MIN 5
35
35
  #define CURB_VER_MIC 0
36
36
  #define CURB_VER_PATCH 0
37
37