curb 1.3.5 → 1.3.7

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: a502fb4afad21a24a20302b7522b616b08afbbe3d4b5b2a3eeee46d83b53c50d
4
- data.tar.gz: 5e68fd3f3380f2045dfc6d4e8e157ebbb9fc56e4b76601574c92a755665eeef2
3
+ metadata.gz: 12fe9b3726ab9e6bb2a7369ebae117d8a2da19db382acd1b182af4b8cd19d4cb
4
+ data.tar.gz: 05a07a992383c5c956d037770215bb2bb8795cc3062bad6ca2ec84c0389dd880
5
5
  SHA512:
6
- metadata.gz: 940f343ad1d926ddcf27b994bca191e131176e766dce3d7f211f9ed5b28917ffb9ebfa5b8f71247ac8ca8a0b916e6565b60fb2ae57a964770b3273d1bd7d5d69
7
- data.tar.gz: 77a44dd831eea50e786d4b4871c2f1702e3bee2c74d0484f7b0812a91d19d4bcdd79c12e20dce7cd8ffcbaf0e06ae77cc6cc7734b7c71fa2feabc636af82b0d8
6
+ metadata.gz: 3f0016de5762e46f6b1f1ef3a588c82a41a8a3b1f8a3d3f351626279fa053539ec7d6ca35a8a9792ed5c9dd6e06c550d2b9ba7b900cffbb60af09f260e603662
7
+ data.tar.gz: c26cfe8e0c03bf30dda075c6abb916e4e1d66205f61ebe54d2db73c1359e3c8def512163393280874730769ffad6fc96c24aad8e5f79a5d586e1b43640ed6b9f
data/README.md CHANGED
@@ -1,10 +1,9 @@
1
1
  # Curb - Libcurl bindings for Ruby
2
2
 
3
- [![CI](https://github.com/taf2/curb/actions/workflows/ci.yml/badge.svg)](https://github.com/taf2/curb/actions/workflows/ci.yml)
4
- [![codecov](https://codecov.io/gh/taf2/curb/branch/master/graph/badge.svg)](https://codecov.io/gh/taf2/curb)
3
+ [![CI](https://github.com/taf2/curb/actions/workflows/CI.yml/badge.svg)](https://github.com/taf2/curb/actions/workflows/CI.yml)
5
4
  [![Gem Version](https://badge.fury.io/rb/curb.svg)](https://badge.fury.io/rb/curb)
6
5
 
7
- * [CI Build Status](https://github.com/taf2/curb/actions/workflows/ci.yml)
6
+ * [CI Build Status](https://github.com/taf2/curb/actions/workflows/CI.yml)
8
7
  * [rubydoc rdoc](http://www.rubydoc.info/github/taf2/curb/)
9
8
  * [github project](http://github.com/taf2/curb/tree/master)
10
9
 
@@ -23,8 +22,8 @@ Ruby license. See the LICENSE file for the gory details.
23
22
 
24
23
  ## Easy mode
25
24
 
26
- GET request
27
- ```
25
+ ### GET request
26
+ ```ruby
28
27
  res = Curl.get("https://www.google.com/") {|http|
29
28
  http.timeout = 10 # raise exception if request/response not handled within 10 seconds
30
29
  }
@@ -33,8 +32,8 @@ GET request
33
32
  puts res.body
34
33
  ```
35
34
 
36
- POST request
37
- ```
35
+ ### POST request
36
+ ```ruby
38
37
  res = Curl.post("https://your-server.com/endpoint", {post: "this"}.to_json) {|http|
39
38
  http.headers["Content-Type"] = "application/json"
40
39
  }
@@ -45,7 +44,9 @@ POST request
45
44
 
46
45
  ## FTP Support
47
46
 
47
+ ```ruby
48
48
  require 'curb'
49
+ ```
49
50
 
50
51
  ### Basic FTP Download
51
52
  ```ruby
@@ -139,7 +140,7 @@ puts list.body
139
140
  ```
140
141
 
141
142
  ### Advanced FTP Usage with Various Options
142
- ```
143
+ ```ruby
143
144
  puts "\n=== Advanced FTP Example ==="
144
145
  advanced = Curl::Easy.new do |curl|
145
146
  curl.url = 'ftp://ftp.example.com/remote/file.txt'
@@ -175,7 +176,7 @@ advanced.perform
175
176
  ```
176
177
 
177
178
  ### Parallel FTP Downloads
178
- ```
179
+ ```ruby
179
180
  puts "\n=== Parallel FTP Downloads Example ==="
180
181
  urls = [
181
182
  'ftp://ftp.example.com/file1.txt',
@@ -185,7 +186,7 @@ urls = [
185
186
  ```
186
187
 
187
188
  ### Common options for all connections
188
- ```
189
+ ```ruby
189
190
  options = {
190
191
  :username => 'user',
191
192
  :password => 'password',
@@ -199,11 +200,68 @@ Curl::Multi.download(urls, options) do |curl, file_path|
199
200
  end
200
201
  ```
201
202
 
203
+ ## Security considerations
204
+
205
+ `curb` is a libcurl binding and intentionally supports protocols beyond HTTP.
206
+ Do not pass untrusted URLs to `Curl.get`, `Curl::Easy.new`, or related raw
207
+ helpers without application-level validation. For user-supplied URLs, enable the
208
+ process-wide safety policy before making requests:
209
+
210
+ ```ruby
211
+ 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
214
+ end
215
+
216
+ curl = Curl.get(user_url) # allows only http/https, including redirects
217
+ ```
218
+
219
+ To allow a different protocol set, configure it explicitly. Redirects default to
220
+ the same protocol list:
221
+
222
+ ```ruby
223
+ Curl.safe! do |config|
224
+ config.protocols = [:http, :ftp]
225
+ config.max_body_bytes = 1_000_000
226
+ end
227
+ ```
228
+
229
+ For local per-handle policy instead of process-wide policy, use
230
+ `easy.safe_http!` and `easy.max_body_bytes = ...` before `perform`.
231
+
232
+ With `network_policy = :public`, curb checks peer addresses when libcurl opens
233
+ the socket and blocks local/private destinations. Proxies, `resolve`,
234
+ `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
237
+ re-enabling environment proxies, set `allowed_proxy_hosts` and configure
238
+ `easy.proxy_url` on the request.
239
+
240
+ For stricter egress, combine the public network policy with host and CIDR
241
+ allowlists. Host allowlists gate the configured request URL and, when supported
242
+ by libcurl, each followed redirect before the request is sent. CIDR allowlists
243
+ are checked against the resolved peer address at socket-open time:
244
+
245
+ ```ruby
246
+ Curl.safe! do |config|
247
+ config.network_policy = :public
248
+ config.allowed_hosts = ["api.example.com"]
249
+ config.allowed_proxy_hosts = ["proxy.example.com"]
250
+ config.allowed_cidrs = ["93.184.216.0/24", "2606:2800:220::/48"]
251
+ end
252
+ ```
253
+
254
+ By default, responses are buffered into `body` when no `on_body` callback is
255
+ configured. For untrusted or large responses, use `on_body`, `download`, and/or
256
+ `max_body_bytes` so a remote endpoint cannot force unbounded memory growth.
257
+ `max_body_bytes` is enforced for downloads as well as buffered responses and
258
+ custom body callbacks.
259
+
202
260
  ## You will need
203
261
 
204
262
  * A working Ruby installation (`2.0.0+` will work but `2.1+` preferred) (it's possible it still works with 1.8.7 but you'd have to tell me if not...)
205
263
  * A working libcurl development installation
206
- (Ideally one of the versions listed in the compatibility chart below that maps to your `curb` version)
264
+ (Ideally one of the versions listed in the compatibility chart below that maps to your `curb` version)
207
265
  * A sane build environment (e.g. gcc, make)
208
266
 
209
267
  ## Version Compatibility chart
@@ -212,8 +270,27 @@ A **non-exhaustive** set of compatibility versions of the libcurl library
212
270
  with this gem are as follows. (Note that these are only the ones that have been
213
271
  tested and reported to work across a variety of platforms / rubies)
214
272
 
273
+ The upper bounds for recent releases reflect the compatibility work documented
274
+ in the changelog, including the libcurl 8.16.0 regression workaround in 1.2.2
275
+ and the libcurl 8.20.0 scheduler fixes in 1.3.5. The 1.3.7 upper bound also
276
+ includes expected compatibility with libcurl 8.21.0; this version is not yet
277
+ an explicit CI target.
278
+
215
279
  | Gem Version | Release Date | libcurl versions |
216
280
  | ----------- | -------------- | ----------------- |
281
+ | 1.3.7 | Jul 10, 2026 | 7.58 – 8.21.0 |
282
+ | 1.3.6 | Jun 17, 2026 | 7.58 – 8.20.0 |
283
+ | 1.3.5 | May 14, 2026 | 7.58 – 8.20.0 |
284
+ | 1.3.4 | May 12, 2026 | 7.58 – 8.16.0 |
285
+ | 1.3.3 | May 11, 2026 | 7.58 – 8.16.0 |
286
+ | 1.3.2 | Apr 23, 2026 | 7.58 – 8.16.0 |
287
+ | 1.3.1 | Apr 05, 2026 | 7.58 – 8.16.0 |
288
+ | 1.3.0 | Apr 02, 2026 | 7.58 – 8.16.0 |
289
+ | 1.2.2 | Sep 18, 2025 | 7.58 – 8.16.0 |
290
+ | 1.2.1 | Sep 17, 2025 | 7.58 – 8.12.1 |
291
+ | 1.2.0 | Aug 31, 2025 | 7.58 – 8.12.1 |
292
+ | 1.1.0 | Jul 24, 2025 | 7.58 – 8.12.1 |
293
+ | 1.0.9 | Feb 15, 2025 | 7.58 – 8.12.1 |
217
294
  | 1.0.8 | Feb 10, 2025 | 7.58 – 8.12.1 |
218
295
  | 1.0.7 | Feb 09, 2025 | 7.58 – 8.12.1 |
219
296
  | 1.0.6 | Aug 23, 2024 | 7.58 – 8.12.1 |
@@ -230,7 +307,7 @@ tested and reported to work across a variety of platforms / rubies)
230
307
  | 0.9.4 | Aug 2017 | 7.41 – 7.58 |
231
308
  | 0.9.3 | Apr 2016 | 7.26 – 7.58 |
232
309
 
233
- ```*avoid using these version are known to have issues with segmentation faults```
310
+ *Avoid using these versions; they are known to have issues with segmentation faults.*
234
311
 
235
312
  ## Installation...
236
313
 
@@ -400,6 +477,7 @@ c = Curl::Easy.http_post("http://my.rails.box/thing/create",
400
477
  c = Curl::Easy.new("http://my.rails.box/files/upload")
401
478
  c.multipart_form_post = true
402
479
  c.http_post(Curl::PostField.file('thing[file]', 'myfile.rb'))
480
+ ```
403
481
 
404
482
  ### Custom request target
405
483
 
@@ -413,7 +491,6 @@ c.perform
413
491
  ```
414
492
 
415
493
  For HTTPS, prefer `easy.resolve = ["host:443:IP"]` to keep Host/SNI/certificates aligned.
416
- ```
417
494
 
418
495
  ### Using HTTP/2
419
496
 
data/Rakefile CHANGED
@@ -107,9 +107,10 @@ else
107
107
  end
108
108
 
109
109
  ruby_memcheck_config = { binary_name: 'curb_core' }
110
+ ruby_version = Gem::Version.new(RUBY_VERSION)
110
111
 
111
- if RUBY_ENGINE == 'ruby' && RUBY_VERSION == '4.0.4'
112
- # Ruby 4.0.4 reports fiber/block-handler VM stack accesses under Valgrind.
112
+ if RUBY_ENGINE == 'ruby' && ruby_version >= Gem::Version.new('4.0.4') && ruby_version < Gem::Version.new('4.1.0')
113
+ # Ruby 4.0.4+ reports fiber/block-handler VM stack accesses under Valgrind.
113
114
  # Keep reporting errors that originate in curb_core, but filter Ruby-side noise.
114
115
  ruby_memcheck_config[:filter_all_errors] = true
115
116
  if RubyMemcheck::Configuration.instance_method(:initialize).parameters.any? { |type, name|
@@ -183,7 +184,11 @@ end
183
184
  # RDoc Tasks ---------------------------------------------------------
184
185
  desc "Create the RDOC documentation"
185
186
  task :doc do
186
- ruby "doc.rb #{ENV['DOC_OPTS']}"
187
+ doc_opts = Shellwords.split(ENV.fetch('DOC_OPTS', ''))
188
+ unsupported_opts = doc_opts - ['--cpp']
189
+ fail "Unsupported DOC_OPTS: #{unsupported_opts.join(' ')}" unless unsupported_opts.empty?
190
+
191
+ ruby 'doc.rb', *doc_opts
187
192
  end
188
193
 
189
194
  desc "Publish the RDoc documentation to project web site"
data/doc.rb CHANGED
@@ -1,10 +1,35 @@
1
1
  require 'fileutils'
2
+ require 'open3'
3
+ require 'shellwords'
2
4
  include FileUtils
3
5
 
4
- begin
5
- incflags = File.read('ext/Makefile')[/INCFLAGS\s*=\s*(.*)$/,1]
6
+ def makefile_variables(path)
7
+ variables = {}
8
+
9
+ File.foreach(path) do |line|
10
+ line = line.chomp
11
+ variables[$1] = $2.strip if line =~ /\A([A-Za-z_]\w*)\s*=\s*(.*)\z/
12
+ end
13
+
14
+ variables
15
+ end
16
+
17
+ def expand_make_variables(value, variables, seen = [])
18
+ value.gsub(/\$\(([^)]+)\)/) do
19
+ key = Regexp.last_match(1)
20
+ raise ArgumentError, "unsupported Makefile variable in INCFLAGS: #{key}" unless variables.key?(key)
21
+ raise ArgumentError, "recursive Makefile variable in INCFLAGS: #{key}" if seen.include?(key)
22
+
23
+ expand_make_variables(variables[key], variables, seen + [key])
24
+ end
25
+ end
26
+
27
+ def makefile_incflags
28
+ variables = makefile_variables('ext/Makefile')
29
+ Shellwords.split(expand_make_variables(variables.fetch('INCFLAGS', ''), variables))
6
30
  rescue Errno::ENOENT
7
31
  $stderr.puts("No makefile found; run `rake ext/Makefile' first.")
32
+ []
8
33
  end
9
34
 
10
35
  pp_srcdir = 'ext'
@@ -15,26 +40,41 @@ mkdir(tmpdir)
15
40
  begin
16
41
  if ARGV.include?('--cpp')
17
42
  begin
18
- if `cpp --version` =~ /\(GCC\)/
43
+ cpp_version, status = Open3.capture2e('cpp', '--version')
44
+
45
+ if status.success? && cpp_version =~ /\(GCC\)/
19
46
  # gnu cpp
20
47
  $stderr.puts "Running GNU cpp over source"
48
+ incflags = makefile_incflags
21
49
 
22
- Dir['ext/*.c'].each do |fn|
23
- system("cpp -DRDOC_NEVER_DEFINED -C #{incflags} -o " +
24
- "#{File.join(tmpdir, File.basename(fn))} #{fn}")
50
+ Dir['ext/*.c'].sort.each do |fn|
51
+ out = File.join(tmpdir, File.basename(fn))
52
+ abort "cpp failed for #{fn}" unless system('cpp', '-DRDOC_NEVER_DEFINED', '-C', *incflags, '-o', out, fn)
25
53
  end
26
54
 
27
55
  pp_srcdir = tmpdir
28
56
  else
29
57
  $stderr.puts "Not running cpp (non-GNU)"
30
58
  end
31
- rescue
59
+ rescue Errno::ENOENT
32
60
  # no cpp
33
61
  $stderr.puts "No cpp found"
62
+ rescue ArgumentError => e
63
+ abort e.message
34
64
  end
35
65
  end
36
66
 
37
- system("rdoc --title='Curb - libcurl bindings for ruby' --main=README #{pp_srcdir}/*.c README LICENSE lib/curb.rb")
67
+ main = File.exist?('README.md') ? 'README.md' : 'README'
68
+ sources = Dir[File.join(pp_srcdir, '*.c')].sort
69
+ abort 'rdoc failed' unless system(
70
+ 'rdoc',
71
+ '--title', 'Curb - libcurl bindings for ruby',
72
+ '--main', main,
73
+ *sources,
74
+ main,
75
+ 'LICENSE',
76
+ 'lib/curb.rb'
77
+ )
38
78
  ensure
39
79
  rm_rf(tmpdir)
40
80
  end
data/ext/curb.c CHANGED
@@ -457,6 +457,12 @@ void Init_curb_core() {
457
457
  #endif
458
458
  #ifdef HAVE_CURLOPT_OPENSOCKETDATA
459
459
  CURB_DEFINE(CURLOPT_OPENSOCKETDATA);
460
+ #endif
461
+ #ifdef HAVE_CURLOPT_PREREQFUNCTION
462
+ CURB_DEFINE(CURLOPT_PREREQFUNCTION);
463
+ #endif
464
+ #ifdef HAVE_CURLOPT_PREREQDATA
465
+ CURB_DEFINE(CURLOPT_PREREQDATA);
460
466
  #endif
461
467
  /* CURLOPT_PROGRESSFUNCTION deprecated since 7.32.0, use XFERINFOFUNCTION */
462
468
  #ifdef HAVE_CURLOPT_PROGRESSFUNCTION
@@ -557,6 +563,9 @@ void Init_curb_core() {
557
563
  CURB_DEFINE(CURLOPT_LOCALPORT);
558
564
  #endif
559
565
  CURB_DEFINE(CURLOPT_DNS_CACHE_TIMEOUT);
566
+ #ifdef HAVE_CURLOPT_DNS_SERVERS
567
+ CURB_DEFINE(CURLOPT_DNS_SERVERS);
568
+ #endif
560
569
  /* CURLOPT_DNS_USE_GLOBAL_CACHE deprecated since 7.11.1, does nothing since 7.62.0 */
561
570
  #ifdef HAVE_CURLOPT_DNS_USE_GLOBAL_CACHE
562
571
  CURB_DEFINE(CURLOPT_DNS_USE_GLOBAL_CACHE);
@@ -739,6 +748,15 @@ void Init_curb_core() {
739
748
  #ifdef HAVE_CURLOPT_FTP_CREATE_MISSING_DIRS
740
749
  CURB_DEFINE(CURLOPT_FTP_CREATE_MISSING_DIRS);
741
750
  #endif
751
+ #ifdef HAVE_CURLFTP_CREATE_DIR_NONE
752
+ CURB_DEFINE(CURLFTP_CREATE_DIR_NONE);
753
+ #endif
754
+ #ifdef HAVE_CURLFTP_CREATE_DIR
755
+ CURB_DEFINE(CURLFTP_CREATE_DIR);
756
+ #endif
757
+ #ifdef HAVE_CURLFTP_CREATE_DIR_RETRY
758
+ CURB_DEFINE(CURLFTP_CREATE_DIR_RETRY);
759
+ #endif
742
760
  #ifdef HAVE_CURLOPT_FTP_RESPONSE_TIMEOUT
743
761
  CURB_DEFINE(CURLOPT_FTP_RESPONSE_TIMEOUT);
744
762
  #endif
@@ -955,6 +973,9 @@ void Init_curb_core() {
955
973
  #ifdef HAVE_CURLUSESSL_ALL
956
974
  CURB_DEFINE(CURLUSESSL_ALL);
957
975
  #endif
976
+ #ifdef HAVE_CURLOPT_CONNECT_TO
977
+ CURB_DEFINE(CURLOPT_CONNECT_TO);
978
+ #endif
958
979
  #ifdef HAVE_CURLOPT_RESOLVE
959
980
  CURB_DEFINE(CURLOPT_RESOLVE);
960
981
  #endif
@@ -1011,6 +1032,18 @@ void Init_curb_core() {
1011
1032
  #ifdef HAVE_CURLOPT_SSL_VERIFYPEER
1012
1033
  CURB_DEFINE(CURLOPT_SSL_VERIFYPEER);
1013
1034
  #endif
1035
+ #ifdef HAVE_CURLOPT_DOH_URL
1036
+ CURB_DEFINE(CURLOPT_DOH_URL);
1037
+ #endif
1038
+ #ifdef HAVE_CURLOPT_DOH_SSL_VERIFYPEER
1039
+ CURB_DEFINE(CURLOPT_DOH_SSL_VERIFYPEER);
1040
+ #endif
1041
+ #ifdef HAVE_CURLOPT_DOH_SSL_VERIFYHOST
1042
+ CURB_DEFINE(CURLOPT_DOH_SSL_VERIFYHOST);
1043
+ #endif
1044
+ #ifdef HAVE_CURLOPT_DOH_SSL_VERIFYSTATUS
1045
+ CURB_DEFINE(CURLOPT_DOH_SSL_VERIFYSTATUS);
1046
+ #endif
1014
1047
  #ifdef HAVE_CURLOPT_CAINFO
1015
1048
  CURB_DEFINE(CURLOPT_CAINFO);
1016
1049
  #endif
data/ext/curb.h CHANGED
@@ -28,11 +28,11 @@
28
28
  #include "curb_macros.h"
29
29
 
30
30
  // These should be managed from the Rake 'release' task.
31
- #define CURB_VERSION "1.3.5"
32
- #define CURB_VER_NUM 1035
31
+ #define CURB_VERSION "1.3.7"
32
+ #define CURB_VER_NUM 1037
33
33
  #define CURB_VER_MAJ 1
34
34
  #define CURB_VER_MIN 3
35
- #define CURB_VER_MIC 5
35
+ #define CURB_VER_MIC 7
36
36
  #define CURB_VER_PATCH 0
37
37
 
38
38