curb 1.3.6 → 1.4.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 +4 -4
- data/README.md +71 -14
- data/Rakefile +12 -1
- data/ext/curb.c +26 -4
- data/ext/curb.h +4 -4
- data/ext/curb_easy.c +178 -77
- data/ext/curb_easy.h +1 -0
- data/ext/curb_multi.c +639 -225
- data/ext/curb_multi.h +4 -0
- data/ext/curb_postfield.c +31 -31
- data/ext/extconf.rb +10 -0
- data/lib/curl/easy.rb +18 -9
- data/lib/curl.rb +49 -12
- data/tests/helper.rb +300 -4
- data/tests/io_select_less_scheduler_probe.rb +105 -0
- data/tests/tc_curl_easy.rb +60 -4
- data/tests/tc_curl_easy_cookielist.rb +8 -4
- data/tests/tc_curl_easy_resolve.rb +2 -0
- data/tests/tc_curl_multi.rb +29 -0
- data/tests/tc_curl_multi_poll.rb +223 -0
- data/tests/tc_curl_multi_socket_poller.rb +374 -0
- data/tests/tc_curl_native_coverage.rb +1 -0
- data/tests/tc_curl_postfield.rb +39 -0
- data/tests/tc_fiber_scheduler.rb +433 -6
- data/tests/tc_ftp_options.rb +58 -0
- data/tests/tc_gc_compact.rb +53 -0
- data/tests/tc_ractor.rb +94 -0
- data/tests/tc_test_server_methods.rb +15 -0
- metadata +10 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9f3088ba29db67ea56c9587898267648a704c8acbcb9e95cb58318c3464deb12
|
|
4
|
+
data.tar.gz: 40134cd41f0704c2aaa3b65ca6507987f6e8bc755c4530c9361ccd25f4831c17
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 29ffbf5bb4301db5729aff433a938251622c8483a29f5fa0f5344b52573048964c9863d00d254b42d01ff172291a1b469ea10f1fa9757b58fe4743a66c7078a5
|
|
7
|
+
data.tar.gz: d2e21e48891f398af31a7b2dd8d99a2465f82bb97d9caecda8ac4cb69fb599e894c3172bc56a6106a28d7761b03743fc78ee80701b3051ac673b7b6f1ed27ea9
|
data/README.md
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
# Curb - Libcurl bindings for Ruby
|
|
2
2
|
|
|
3
|
-
[](https://codecov.io/gh/taf2/curb)
|
|
3
|
+
[](https://github.com/taf2/curb/actions/workflows/CI.yml)
|
|
5
4
|
[](https://badge.fury.io/rb/curb)
|
|
6
5
|
|
|
7
|
-
* [CI Build Status](https://github.com/taf2/curb/actions/workflows/
|
|
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',
|
|
@@ -204,7 +205,7 @@ end
|
|
|
204
205
|
`curb` is a libcurl binding and intentionally supports protocols beyond HTTP.
|
|
205
206
|
Do not pass untrusted URLs to `Curl.get`, `Curl::Easy.new`, or related raw
|
|
206
207
|
helpers without application-level validation. For user-supplied URLs, enable the
|
|
207
|
-
|
|
208
|
+
safety policy for the current Ractor before making requests:
|
|
208
209
|
|
|
209
210
|
```ruby
|
|
210
211
|
Curl.safe! do |config|
|
|
@@ -256,11 +257,47 @@ configured. For untrusted or large responses, use `on_body`, `download`, and/or
|
|
|
256
257
|
`max_body_bytes` is enforced for downloads as well as buffered responses and
|
|
257
258
|
custom body callbacks.
|
|
258
259
|
|
|
260
|
+
## Ractor support
|
|
261
|
+
|
|
262
|
+
On Ruby 3.0+, curb can perform requests from multiple Ractors when it is built
|
|
263
|
+
against a libcurl that reports the `CURL_VERSION_THREADSAFE` capability
|
|
264
|
+
(libcurl 7.84.0 or newer). Check `Curl::RACTOR_SAFE` at runtime. Builds that do
|
|
265
|
+
not meet those requirements continue to work in the main Ractor but do not
|
|
266
|
+
advertise native Ractor safety.
|
|
267
|
+
|
|
268
|
+
Create `Curl::Easy` and `Curl::Multi` handles inside the Ractor that uses them;
|
|
269
|
+
do not operate on the same native handle concurrently from different Ractors.
|
|
270
|
+
Safety configuration, `Curl::Multi.default_timeout`, `Curl::Multi.autoclose`,
|
|
271
|
+
and deferred cleanup queues are isolated per Ractor. Call `Curl.safe!` in each
|
|
272
|
+
Ractor that needs the safety policy:
|
|
273
|
+
|
|
274
|
+
```ruby
|
|
275
|
+
raise "this curb build is not Ractor-safe" unless Curl::RACTOR_SAFE
|
|
276
|
+
|
|
277
|
+
workers = 4.times.map do
|
|
278
|
+
Ractor.new do
|
|
279
|
+
Curl.safe! { |config| config.protocols = [:http, :https] }
|
|
280
|
+
easy = Curl::Easy.new("https://example.com/")
|
|
281
|
+
easy.perform
|
|
282
|
+
easy.response_code
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# Ruby 4.0 uses Ractor#value; earlier Ractor releases use Ractor#take.
|
|
287
|
+
statuses = workers.map do |worker|
|
|
288
|
+
worker.respond_to?(:value) ? worker.value : worker.take
|
|
289
|
+
end
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Curb enables `CURLOPT_NOSIGNAL` on newly initialized and reset Easy handles by
|
|
293
|
+
default so separate handles can be used safely from parallel Ruby execution.
|
|
294
|
+
Applications can still override the option explicitly when required.
|
|
295
|
+
|
|
259
296
|
## You will need
|
|
260
297
|
|
|
261
298
|
* 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...)
|
|
262
299
|
* A working libcurl development installation
|
|
263
|
-
(Ideally one of the versions listed in the compatibility chart below that maps to your `curb` version)
|
|
300
|
+
(Ideally one of the versions listed in the compatibility chart below that maps to your `curb` version)
|
|
264
301
|
* A sane build environment (e.g. gcc, make)
|
|
265
302
|
|
|
266
303
|
## Version Compatibility chart
|
|
@@ -269,8 +306,28 @@ A **non-exhaustive** set of compatibility versions of the libcurl library
|
|
|
269
306
|
with this gem are as follows. (Note that these are only the ones that have been
|
|
270
307
|
tested and reported to work across a variety of platforms / rubies)
|
|
271
308
|
|
|
309
|
+
The upper bounds for recent releases reflect the compatibility work documented
|
|
310
|
+
in the changelog, including the libcurl 8.16.0 regression workaround in 1.2.2
|
|
311
|
+
and the libcurl 8.20.0 scheduler fixes in 1.3.5. The 1.3.7 upper bound also
|
|
312
|
+
includes expected compatibility with libcurl 8.21.0; this version is not yet
|
|
313
|
+
an explicit CI target.
|
|
314
|
+
|
|
272
315
|
| Gem Version | Release Date | libcurl versions |
|
|
273
316
|
| ----------- | -------------- | ----------------- |
|
|
317
|
+
| 1.4.0 | Sep 27, 2026 | 7.58 – 8.21.0 |
|
|
318
|
+
| 1.3.7 | Jul 10, 2026 | 7.58 – 8.21.0 |
|
|
319
|
+
| 1.3.6 | Jun 17, 2026 | 7.58 – 8.20.0 |
|
|
320
|
+
| 1.3.5 | May 14, 2026 | 7.58 – 8.20.0 |
|
|
321
|
+
| 1.3.4 | May 12, 2026 | 7.58 – 8.16.0 |
|
|
322
|
+
| 1.3.3 | May 11, 2026 | 7.58 – 8.16.0 |
|
|
323
|
+
| 1.3.2 | Apr 23, 2026 | 7.58 – 8.16.0 |
|
|
324
|
+
| 1.3.1 | Apr 05, 2026 | 7.58 – 8.16.0 |
|
|
325
|
+
| 1.3.0 | Apr 02, 2026 | 7.58 – 8.16.0 |
|
|
326
|
+
| 1.2.2 | Sep 18, 2025 | 7.58 – 8.16.0 |
|
|
327
|
+
| 1.2.1 | Sep 17, 2025 | 7.58 – 8.12.1 |
|
|
328
|
+
| 1.2.0 | Aug 31, 2025 | 7.58 – 8.12.1 |
|
|
329
|
+
| 1.1.0 | Jul 24, 2025 | 7.58 – 8.12.1 |
|
|
330
|
+
| 1.0.9 | Feb 15, 2025 | 7.58 – 8.12.1 |
|
|
274
331
|
| 1.0.8 | Feb 10, 2025 | 7.58 – 8.12.1 |
|
|
275
332
|
| 1.0.7 | Feb 09, 2025 | 7.58 – 8.12.1 |
|
|
276
333
|
| 1.0.6 | Aug 23, 2024 | 7.58 – 8.12.1 |
|
|
@@ -287,7 +344,7 @@ tested and reported to work across a variety of platforms / rubies)
|
|
|
287
344
|
| 0.9.4 | Aug 2017 | 7.41 – 7.58 |
|
|
288
345
|
| 0.9.3 | Apr 2016 | 7.26 – 7.58 |
|
|
289
346
|
|
|
290
|
-
|
|
347
|
+
*Avoid using these versions; they are known to have issues with segmentation faults.*
|
|
291
348
|
|
|
292
349
|
## Installation...
|
|
293
350
|
|
|
@@ -457,6 +514,7 @@ c = Curl::Easy.http_post("http://my.rails.box/thing/create",
|
|
|
457
514
|
c = Curl::Easy.new("http://my.rails.box/files/upload")
|
|
458
515
|
c.multipart_form_post = true
|
|
459
516
|
c.http_post(Curl::PostField.file('thing[file]', 'myfile.rb'))
|
|
517
|
+
```
|
|
460
518
|
|
|
461
519
|
### Custom request target
|
|
462
520
|
|
|
@@ -470,7 +528,6 @@ c.perform
|
|
|
470
528
|
```
|
|
471
529
|
|
|
472
530
|
For HTTPS, prefer `easy.resolve = ["host:443:IP"]` to keep Host/SNI/certificates aligned.
|
|
473
|
-
```
|
|
474
531
|
|
|
475
532
|
### Using HTTP/2
|
|
476
533
|
|
data/Rakefile
CHANGED
|
@@ -126,7 +126,18 @@ end
|
|
|
126
126
|
|
|
127
127
|
RubyMemcheck.config(**ruby_memcheck_config)
|
|
128
128
|
namespace :test do
|
|
129
|
-
|
|
129
|
+
# Ruby pools fiber stacks next to each other, about 1MB apart. Valgrind
|
|
130
|
+
# treats a stack-pointer jump smaller than --max-stackframe (2MB) as an
|
|
131
|
+
# ordinary call or return rather than a switch to another stack, and marks
|
|
132
|
+
# the memory in between, which can hold another fiber's stack, as
|
|
133
|
+
# inaccessible. Later reads of that fiber's stack (turning a block into a
|
|
134
|
+
# Proc, GC marking suspended fibers) are then reported as invalid. Spacing
|
|
135
|
+
# fiber stacks wider than that lets valgrind recognize every fiber switch.
|
|
136
|
+
task :valgrind_env do
|
|
137
|
+
ENV['RUBY_FIBER_MACHINE_STACK_SIZE'] ||= (4 * 1024 * 1024).to_s
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
RubyMemcheck::TestTask.new(valgrind: [:compile, :valgrind_env]) do|t|
|
|
130
141
|
t.test_files = FileList['tests/tc_*.rb']
|
|
131
142
|
t.verbose = false
|
|
132
143
|
end
|
data/ext/curb.c
CHANGED
|
@@ -243,25 +243,38 @@ static void finalize_curb_core(VALUE data) {
|
|
|
243
243
|
|
|
244
244
|
void Init_curb_core() {
|
|
245
245
|
curl_version_info_data *ver;
|
|
246
|
-
VALUE curlver, curllongver, curlvernum;
|
|
246
|
+
VALUE curbver, curlver, curllongver, curlvernum;
|
|
247
|
+
int ractor_safe = 0;
|
|
247
248
|
|
|
248
249
|
curl_global_init(CURL_GLOBAL_ALL);
|
|
249
250
|
rb_set_end_proc(finalize_curb_core, Qnil);
|
|
250
251
|
ver = curl_version_info(CURLVERSION_NOW);
|
|
251
252
|
|
|
253
|
+
#if defined(HAVE_RB_EXT_RACTOR_SAFE) && defined(HAVE_CURL_VERSION_THREADSAFE)
|
|
254
|
+
/* Ractors execute native methods in parallel. Only opt in when both Ruby
|
|
255
|
+
* provides the safety marker and libcurl advertises thread-safe global
|
|
256
|
+
* initialization. Individual Curl handles remain Ractor-owned. */
|
|
257
|
+
if (ver && (ver->features & CURL_VERSION_THREADSAFE)) {
|
|
258
|
+
rb_ext_ractor_safe(1);
|
|
259
|
+
ractor_safe = 1;
|
|
260
|
+
}
|
|
261
|
+
#endif
|
|
262
|
+
|
|
252
263
|
mCurl = rb_define_module("Curl");
|
|
253
264
|
|
|
254
|
-
|
|
255
|
-
|
|
265
|
+
curbver = rb_obj_freeze(rb_str_new2(CURB_VERSION));
|
|
266
|
+
curlver = rb_obj_freeze(rb_str_new2(ver->version));
|
|
267
|
+
curllongver = rb_obj_freeze(rb_str_new2(curl_version()));
|
|
256
268
|
curlvernum = LONG2NUM(LIBCURL_VERSION_NUM);
|
|
257
269
|
|
|
258
|
-
rb_define_const(mCurl, "CURB_VERSION",
|
|
270
|
+
rb_define_const(mCurl, "CURB_VERSION", curbver);
|
|
259
271
|
rb_define_const(mCurl, "VERSION", curlver);
|
|
260
272
|
rb_define_const(mCurl, "CURL_VERSION", curlver);
|
|
261
273
|
rb_define_const(mCurl, "VERNUM", curlvernum);
|
|
262
274
|
rb_define_const(mCurl, "CURL_VERNUM", curlvernum);
|
|
263
275
|
rb_define_const(mCurl, "LONG_VERSION", curllongver);
|
|
264
276
|
rb_define_const(mCurl, "CURL_LONG_VERSION", curllongver);
|
|
277
|
+
rb_define_const(mCurl, "RACTOR_SAFE", ractor_safe ? Qtrue : Qfalse);
|
|
265
278
|
|
|
266
279
|
/* Passed to on_debug handler to indicate that the data is informational text. */
|
|
267
280
|
rb_define_const(mCurl, "CURLINFO_TEXT", LONG2NUM(CURLINFO_TEXT));
|
|
@@ -748,6 +761,15 @@ void Init_curb_core() {
|
|
|
748
761
|
#ifdef HAVE_CURLOPT_FTP_CREATE_MISSING_DIRS
|
|
749
762
|
CURB_DEFINE(CURLOPT_FTP_CREATE_MISSING_DIRS);
|
|
750
763
|
#endif
|
|
764
|
+
#ifdef HAVE_CURLFTP_CREATE_DIR_NONE
|
|
765
|
+
CURB_DEFINE(CURLFTP_CREATE_DIR_NONE);
|
|
766
|
+
#endif
|
|
767
|
+
#ifdef HAVE_CURLFTP_CREATE_DIR
|
|
768
|
+
CURB_DEFINE(CURLFTP_CREATE_DIR);
|
|
769
|
+
#endif
|
|
770
|
+
#ifdef HAVE_CURLFTP_CREATE_DIR_RETRY
|
|
771
|
+
CURB_DEFINE(CURLFTP_CREATE_DIR_RETRY);
|
|
772
|
+
#endif
|
|
751
773
|
#ifdef HAVE_CURLOPT_FTP_RESPONSE_TIMEOUT
|
|
752
774
|
CURB_DEFINE(CURLOPT_FTP_RESPONSE_TIMEOUT);
|
|
753
775
|
#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.
|
|
32
|
-
#define CURB_VER_NUM
|
|
31
|
+
#define CURB_VERSION "1.4.0"
|
|
32
|
+
#define CURB_VER_NUM 1040
|
|
33
33
|
#define CURB_VER_MAJ 1
|
|
34
|
-
#define CURB_VER_MIN
|
|
35
|
-
#define CURB_VER_MIC
|
|
34
|
+
#define CURB_VER_MIN 4
|
|
35
|
+
#define CURB_VER_MIC 0
|
|
36
36
|
#define CURB_VER_PATCH 0
|
|
37
37
|
|
|
38
38
|
|
data/ext/curb_easy.c
CHANGED
|
@@ -66,6 +66,17 @@ static struct curl_slist *duplicate_curl_slist(struct curl_slist *list);
|
|
|
66
66
|
static size_t proc_data_handler(char *stream, size_t size, size_t nmemb, VALUE proc);
|
|
67
67
|
static int curb_array_includes_string(VALUE list, VALUE value);
|
|
68
68
|
|
|
69
|
+
static void curb_easy_apply_thread_defaults(ruby_curl_easy *rbce) {
|
|
70
|
+
#ifdef HAVE_CURLOPT_NOSIGNAL
|
|
71
|
+
CURLcode ecode = curl_easy_setopt(rbce->curl, CURLOPT_NOSIGNAL, 1L);
|
|
72
|
+
if (ecode != CURLE_OK) {
|
|
73
|
+
raise_curl_easy_error_exception(ecode);
|
|
74
|
+
}
|
|
75
|
+
#else
|
|
76
|
+
(void)rbce;
|
|
77
|
+
#endif
|
|
78
|
+
}
|
|
79
|
+
|
|
69
80
|
/* ================== CURL HANDLER FUNCS ==============*/
|
|
70
81
|
|
|
71
82
|
static int curb_ipv4_is_unsafe_destination(const unsigned char *ip) {
|
|
@@ -1187,6 +1198,7 @@ static int proc_debug_handler(CURL *curl,
|
|
|
1187
1198
|
static void curl_easy_mark(void *ptr) {
|
|
1188
1199
|
ruby_curl_easy *rbce = (ruby_curl_easy *)ptr;
|
|
1189
1200
|
if (rbce) {
|
|
1201
|
+
if (!NIL_P(rbce->self)) { rb_gc_mark(rbce->self); }
|
|
1190
1202
|
if (!NIL_P(rbce->opts)) { rb_gc_mark(rbce->opts); }
|
|
1191
1203
|
if (!NIL_P(rbce->multi)) { rb_gc_mark(rbce->multi); }
|
|
1192
1204
|
if (!NIL_P(rbce->callback_error)) { rb_gc_mark(rbce->callback_error); }
|
|
@@ -1500,6 +1512,7 @@ static VALUE ruby_curl_easy_initialize(int argc, VALUE *argv, VALUE self) {
|
|
|
1500
1512
|
ruby_curl_easy_zero(rbce);
|
|
1501
1513
|
rbce->self = self;
|
|
1502
1514
|
|
|
1515
|
+
curb_easy_apply_thread_defaults(rbce);
|
|
1503
1516
|
curl_easy_setopt(rbce->curl, CURLOPT_ERRORBUFFER, &rbce->err_buf);
|
|
1504
1517
|
|
|
1505
1518
|
rb_easy_set("url", url);
|
|
@@ -1658,6 +1671,7 @@ static VALUE ruby_curl_easy_close(VALUE self) {
|
|
|
1658
1671
|
ruby_curl_easy_zero(rbce);
|
|
1659
1672
|
rbce->self = self;
|
|
1660
1673
|
|
|
1674
|
+
curb_easy_apply_thread_defaults(rbce);
|
|
1661
1675
|
curl_easy_setopt(rbce->curl, CURLOPT_ERRORBUFFER, rbce->err_buf);
|
|
1662
1676
|
|
|
1663
1677
|
/* give the new curl handle a reference back to the ruby object */
|
|
@@ -1702,6 +1716,7 @@ static VALUE ruby_curl_easy_reset(VALUE self) {
|
|
|
1702
1716
|
ruby_curl_easy_zero(rbce);
|
|
1703
1717
|
rbce->self = self;
|
|
1704
1718
|
|
|
1719
|
+
curb_easy_apply_thread_defaults(rbce);
|
|
1705
1720
|
curl_easy_setopt(rbce->curl, CURLOPT_ERRORBUFFER, &rbce->err_buf);
|
|
1706
1721
|
|
|
1707
1722
|
/* reset clobbers the private setting, so reset it to self */
|
|
@@ -2551,6 +2566,41 @@ static VALUE ruby_curl_easy_proxy_type_get(VALUE self) {
|
|
|
2551
2566
|
(!strncmp("anysafe",node,7)) ? CURLAUTH_ANYSAFE : \
|
|
2552
2567
|
(!strncmp("any",node,3)) ? CURLAUTH_ANY : 0
|
|
2553
2568
|
#endif
|
|
2569
|
+
/*
|
|
2570
|
+
* Convert the arguments to http_auth_types= / proxy_auth_types= into a
|
|
2571
|
+
* CURLAUTH bitmask. Accepts nil, an Integer mask, or one or more auth type
|
|
2572
|
+
* names (symbols/strings), either as separate args or as a single Array.
|
|
2573
|
+
*/
|
|
2574
|
+
static long ruby_curl_easy_auth_types_to_mask(VALUE args_ary) {
|
|
2575
|
+
long i, len;
|
|
2576
|
+
char* node = NULL;
|
|
2577
|
+
long mask = 0;
|
|
2578
|
+
VALUE first;
|
|
2579
|
+
|
|
2580
|
+
/* easy.http_auth_types = [:basic, :digest] arrives as a single Array arg */
|
|
2581
|
+
if (RARRAY_LEN(args_ary) == 1 && RB_TYPE_P(rb_ary_entry(args_ary,0), T_ARRAY)) {
|
|
2582
|
+
args_ary = rb_ary_entry(args_ary,0);
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2585
|
+
len = RARRAY_LEN(args_ary);
|
|
2586
|
+
first = rb_ary_entry(args_ary,0);
|
|
2587
|
+
|
|
2588
|
+
if (len == 1 && (first == Qnil || TYPE(first) == T_FIXNUM || TYPE(first) == T_BIGNUM)) {
|
|
2589
|
+
return first == Qnil ? 0 : NUM2LONG(first);
|
|
2590
|
+
}
|
|
2591
|
+
|
|
2592
|
+
// we could have multiple values, but they should be symbols
|
|
2593
|
+
// rb_obj_as_string always yields a String, even if to_s misbehaves;
|
|
2594
|
+
// RSTRING_PTR on a raw rb_funcall result would crash if #to_s returned
|
|
2595
|
+
// a non-String (e.g. a Fixnum, whose VALUE isn't a real pointer).
|
|
2596
|
+
for( i = 0; i < len; ++i ) {
|
|
2597
|
+
VALUE node_str = rb_obj_as_string(rb_ary_entry(args_ary,i));
|
|
2598
|
+
node = StringValuePtr(node_str);
|
|
2599
|
+
mask |= CURL_HTTPAUTH_STR_TO_NUM(node);
|
|
2600
|
+
}
|
|
2601
|
+
return mask;
|
|
2602
|
+
}
|
|
2603
|
+
|
|
2554
2604
|
/*
|
|
2555
2605
|
* call-seq:
|
|
2556
2606
|
* easy.http_auth_types = fixnum or nil => fixnum or nil
|
|
@@ -2560,37 +2610,14 @@ static VALUE ruby_curl_easy_proxy_type_get(VALUE self) {
|
|
|
2560
2610
|
* +perform+ calls. This is a bitmap made by ORing together the
|
|
2561
2611
|
* Curl::CURLAUTH constants.
|
|
2562
2612
|
*/
|
|
2563
|
-
static VALUE ruby_curl_easy_http_auth_types_set(int argc, VALUE *argv, VALUE self) {
|
|
2613
|
+
static VALUE ruby_curl_easy_http_auth_types_set(int argc, VALUE *argv, VALUE self) {
|
|
2564
2614
|
ruby_curl_easy *rbce;
|
|
2565
2615
|
VALUE args_ary;
|
|
2566
|
-
long i, len;
|
|
2567
|
-
char* node = NULL;
|
|
2568
|
-
long mask = 0;
|
|
2569
2616
|
|
|
2570
2617
|
rb_scan_args(argc, argv, "*", &args_ary);
|
|
2571
2618
|
TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
2572
2619
|
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
if (len == 1 && (rb_ary_entry(args_ary,0) == Qnil || TYPE(rb_ary_entry(args_ary,0)) == T_FIXNUM ||
|
|
2576
|
-
TYPE(rb_ary_entry(args_ary,0)) == T_BIGNUM)) {
|
|
2577
|
-
if (rb_ary_entry(args_ary,0) == Qnil) {
|
|
2578
|
-
rbce->http_auth_types = 0;
|
|
2579
|
-
}
|
|
2580
|
-
else {
|
|
2581
|
-
rbce->http_auth_types = NUM2LONG(rb_ary_entry(args_ary,0));
|
|
2582
|
-
}
|
|
2583
|
-
}
|
|
2584
|
-
else {
|
|
2585
|
-
// we could have multiple values, but they should be symbols
|
|
2586
|
-
node = RSTRING_PTR(rb_funcall(rb_ary_entry(args_ary,0),rb_intern("to_s"),0));
|
|
2587
|
-
mask = CURL_HTTPAUTH_STR_TO_NUM(node);
|
|
2588
|
-
for( i = 1; i < len; ++i ) {
|
|
2589
|
-
node = RSTRING_PTR(rb_funcall(rb_ary_entry(args_ary,i),rb_intern("to_s"),0));
|
|
2590
|
-
mask |= CURL_HTTPAUTH_STR_TO_NUM(node);
|
|
2591
|
-
}
|
|
2592
|
-
rbce->http_auth_types = mask;
|
|
2593
|
-
}
|
|
2620
|
+
rbce->http_auth_types = ruby_curl_easy_auth_types_to_mask(args_ary);
|
|
2594
2621
|
return LONG2NUM(rbce->http_auth_types);
|
|
2595
2622
|
}
|
|
2596
2623
|
|
|
@@ -2608,13 +2635,21 @@ static VALUE ruby_curl_easy_http_auth_types_get(VALUE self) {
|
|
|
2608
2635
|
/*
|
|
2609
2636
|
* call-seq:
|
|
2610
2637
|
* easy.proxy_auth_types = fixnum or nil => fixnum or nil
|
|
2638
|
+
* easy.proxy_auth_types = [:basic,:digest,:digest_ie,:gssnegotiate, :ntlm, :any, :anysafe]
|
|
2611
2639
|
*
|
|
2612
2640
|
* Set the proxy authentication types that may be used for the following
|
|
2613
2641
|
* +perform+ calls. This is a bitmap made by ORing together the
|
|
2614
2642
|
* Curl::CURLAUTH constants.
|
|
2615
2643
|
*/
|
|
2616
|
-
static VALUE ruby_curl_easy_proxy_auth_types_set(VALUE
|
|
2617
|
-
|
|
2644
|
+
static VALUE ruby_curl_easy_proxy_auth_types_set(int argc, VALUE *argv, VALUE self) {
|
|
2645
|
+
ruby_curl_easy *rbce;
|
|
2646
|
+
VALUE args_ary;
|
|
2647
|
+
|
|
2648
|
+
rb_scan_args(argc, argv, "*", &args_ary);
|
|
2649
|
+
TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
2650
|
+
|
|
2651
|
+
rbce->proxy_auth_types = ruby_curl_easy_auth_types_to_mask(args_ary);
|
|
2652
|
+
return LONG2NUM(rbce->proxy_auth_types);
|
|
2618
2653
|
}
|
|
2619
2654
|
|
|
2620
2655
|
/*
|
|
@@ -5581,6 +5616,17 @@ static VALUE ruby_curl_easy_last_error(VALUE self) {
|
|
|
5581
5616
|
* @note Some options - like url or cookie - aren't set directly throught +curl_easy_setopt+, but stored in the Ruby object state.
|
|
5582
5617
|
* @note When +curl_easy_setopt+ is called, return value is not checked here.
|
|
5583
5618
|
*/
|
|
5619
|
+
static long ruby_curl_easy_option_to_long(VALUE value) {
|
|
5620
|
+
if (value == Qtrue) {
|
|
5621
|
+
return 1L;
|
|
5622
|
+
}
|
|
5623
|
+
if (value == Qfalse) {
|
|
5624
|
+
return 0L;
|
|
5625
|
+
}
|
|
5626
|
+
|
|
5627
|
+
return NUM2LONG(rb_funcall(value, rb_intern("to_i"), 0));
|
|
5628
|
+
}
|
|
5629
|
+
|
|
5584
5630
|
static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
|
|
5585
5631
|
ruby_curl_easy *rbce;
|
|
5586
5632
|
long option = NUM2LONG(opt);
|
|
@@ -5696,59 +5742,76 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
|
|
|
5696
5742
|
/* FTP-specific toggles */
|
|
5697
5743
|
#ifdef HAVE_CURLOPT_DIRLISTONLY
|
|
5698
5744
|
case CURLOPT_DIRLISTONLY: {
|
|
5699
|
-
|
|
5700
|
-
VALUE value;
|
|
5701
|
-
if (type == T_TRUE) {
|
|
5702
|
-
value = rb_int_new(1);
|
|
5703
|
-
} else if (type == T_FALSE) {
|
|
5704
|
-
value = rb_int_new(0);
|
|
5705
|
-
} else {
|
|
5706
|
-
value = rb_funcall(val, rb_intern("to_i"), 0);
|
|
5707
|
-
}
|
|
5708
|
-
curl_easy_setopt(rbce->curl, CURLOPT_DIRLISTONLY, NUM2LONG(value));
|
|
5745
|
+
curl_easy_setopt(rbce->curl, CURLOPT_DIRLISTONLY, ruby_curl_easy_option_to_long(val));
|
|
5709
5746
|
} break;
|
|
5710
5747
|
#endif
|
|
5748
|
+
#ifdef HAVE_CURLOPT_FTPPORT
|
|
5749
|
+
case CURLOPT_FTPPORT:
|
|
5750
|
+
curl_easy_setopt(rbce->curl, CURLOPT_FTPPORT, NIL_P(val) ? NULL : StringValueCStr(val));
|
|
5751
|
+
break;
|
|
5752
|
+
#endif
|
|
5753
|
+
#ifdef HAVE_CURLOPT_APPEND
|
|
5754
|
+
case CURLOPT_APPEND:
|
|
5755
|
+
curl_easy_setopt(rbce->curl, CURLOPT_APPEND, ruby_curl_easy_option_to_long(val));
|
|
5756
|
+
break;
|
|
5757
|
+
#endif
|
|
5711
5758
|
#ifdef HAVE_CURLOPT_FTP_USE_EPSV
|
|
5712
|
-
case CURLOPT_FTP_USE_EPSV:
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
if (type == T_TRUE) {
|
|
5716
|
-
value = rb_int_new(1);
|
|
5717
|
-
} else if (type == T_FALSE) {
|
|
5718
|
-
value = rb_int_new(0);
|
|
5719
|
-
} else {
|
|
5720
|
-
value = rb_funcall(val, rb_intern("to_i"), 0);
|
|
5721
|
-
}
|
|
5722
|
-
curl_easy_setopt(rbce->curl, CURLOPT_FTP_USE_EPSV, NUM2LONG(value));
|
|
5723
|
-
} break;
|
|
5759
|
+
case CURLOPT_FTP_USE_EPSV:
|
|
5760
|
+
curl_easy_setopt(rbce->curl, CURLOPT_FTP_USE_EPSV, ruby_curl_easy_option_to_long(val));
|
|
5761
|
+
break;
|
|
5724
5762
|
#endif
|
|
5725
5763
|
#ifdef HAVE_CURLOPT_FTP_USE_EPRT
|
|
5726
|
-
case CURLOPT_FTP_USE_EPRT:
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5764
|
+
case CURLOPT_FTP_USE_EPRT:
|
|
5765
|
+
curl_easy_setopt(rbce->curl, CURLOPT_FTP_USE_EPRT, ruby_curl_easy_option_to_long(val));
|
|
5766
|
+
break;
|
|
5767
|
+
#endif
|
|
5768
|
+
#ifdef HAVE_CURLOPT_FTP_USE_PRET
|
|
5769
|
+
case CURLOPT_FTP_USE_PRET:
|
|
5770
|
+
curl_easy_setopt(rbce->curl, CURLOPT_FTP_USE_PRET, ruby_curl_easy_option_to_long(val));
|
|
5771
|
+
break;
|
|
5772
|
+
#endif
|
|
5773
|
+
#ifdef HAVE_CURLOPT_FTP_CREATE_MISSING_DIRS
|
|
5774
|
+
case CURLOPT_FTP_CREATE_MISSING_DIRS:
|
|
5775
|
+
rb_easy_set("ftp_create_missing_dirs", val);
|
|
5776
|
+
curl_easy_setopt(rbce->curl, CURLOPT_FTP_CREATE_MISSING_DIRS, ruby_curl_easy_option_to_long(val));
|
|
5777
|
+
break;
|
|
5778
|
+
#endif
|
|
5779
|
+
#ifdef HAVE_CURLOPT_FTP_RESPONSE_TIMEOUT
|
|
5780
|
+
case CURLOPT_FTP_RESPONSE_TIMEOUT:
|
|
5781
|
+
rbce->ftp_response_timeout = ruby_curl_easy_option_to_long(val);
|
|
5782
|
+
curl_easy_setopt(rbce->curl, CURLOPT_FTP_RESPONSE_TIMEOUT, rbce->ftp_response_timeout);
|
|
5783
|
+
break;
|
|
5784
|
+
#endif
|
|
5785
|
+
#ifdef HAVE_CURLOPT_FTP_ALTERNATIVE_TO_USER
|
|
5786
|
+
case CURLOPT_FTP_ALTERNATIVE_TO_USER:
|
|
5787
|
+
curl_easy_setopt(rbce->curl, CURLOPT_FTP_ALTERNATIVE_TO_USER, NIL_P(val) ? NULL : StringValueCStr(val));
|
|
5788
|
+
break;
|
|
5738
5789
|
#endif
|
|
5739
5790
|
#ifdef HAVE_CURLOPT_FTP_SKIP_PASV_IP
|
|
5740
|
-
case CURLOPT_FTP_SKIP_PASV_IP:
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5791
|
+
case CURLOPT_FTP_SKIP_PASV_IP:
|
|
5792
|
+
curl_easy_setopt(rbce->curl, CURLOPT_FTP_SKIP_PASV_IP, ruby_curl_easy_option_to_long(val));
|
|
5793
|
+
break;
|
|
5794
|
+
#endif
|
|
5795
|
+
#ifdef HAVE_CURLOPT_FTPSSLAUTH
|
|
5796
|
+
case CURLOPT_FTPSSLAUTH:
|
|
5797
|
+
curl_easy_setopt(rbce->curl, CURLOPT_FTPSSLAUTH, ruby_curl_easy_option_to_long(val));
|
|
5798
|
+
break;
|
|
5799
|
+
#endif
|
|
5800
|
+
#ifdef HAVE_CURLOPT_FTP_SSL_CCC
|
|
5801
|
+
case CURLOPT_FTP_SSL_CCC:
|
|
5802
|
+
curl_easy_setopt(rbce->curl, CURLOPT_FTP_SSL_CCC, ruby_curl_easy_option_to_long(val));
|
|
5803
|
+
break;
|
|
5804
|
+
#endif
|
|
5805
|
+
#ifdef HAVE_CURLOPT_FTP_ACCOUNT
|
|
5806
|
+
case CURLOPT_FTP_ACCOUNT:
|
|
5807
|
+
curl_easy_setopt(rbce->curl, CURLOPT_FTP_ACCOUNT, NIL_P(val) ? NULL : StringValueCStr(val));
|
|
5808
|
+
break;
|
|
5809
|
+
#endif
|
|
5810
|
+
#ifdef HAVE_CURLOPT_FTP_FILEMETHOD
|
|
5811
|
+
case CURLOPT_FTP_FILEMETHOD:
|
|
5812
|
+
rbce->ftp_filemethod = ruby_curl_easy_option_to_long(val);
|
|
5813
|
+
curl_easy_setopt(rbce->curl, CURLOPT_FTP_FILEMETHOD, rbce->ftp_filemethod);
|
|
5814
|
+
break;
|
|
5752
5815
|
#endif
|
|
5753
5816
|
case CURLOPT_RANGE: {
|
|
5754
5817
|
curl_easy_setopt(rbce->curl, CURLOPT_RANGE, StringValueCStr(val));
|
|
@@ -5955,6 +6018,34 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
|
|
|
5955
6018
|
return val;
|
|
5956
6019
|
}
|
|
5957
6020
|
|
|
6021
|
+
#ifdef HAVE_CURLOPT_FTP_CREATE_MISSING_DIRS
|
|
6022
|
+
/*
|
|
6023
|
+
* call-seq:
|
|
6024
|
+
* easy.ftp_create_missing_dirs = boolean_or_mode => boolean_or_mode
|
|
6025
|
+
*
|
|
6026
|
+
* Configure whether libcurl creates missing directories for an FTP or SFTP
|
|
6027
|
+
* transfer. In addition to true and false, this accepts the
|
|
6028
|
+
* Curl::CURLFTP_CREATE_DIR_* mode constants when provided by libcurl.
|
|
6029
|
+
*/
|
|
6030
|
+
static VALUE ruby_curl_easy_ftp_create_missing_dirs_set(VALUE self, VALUE value) {
|
|
6031
|
+
return ruby_curl_easy_set_opt(
|
|
6032
|
+
self,
|
|
6033
|
+
LONG2NUM(CURLOPT_FTP_CREATE_MISSING_DIRS),
|
|
6034
|
+
value
|
|
6035
|
+
);
|
|
6036
|
+
}
|
|
6037
|
+
|
|
6038
|
+
/*
|
|
6039
|
+
* call-seq:
|
|
6040
|
+
* easy.ftp_create_missing_dirs => boolean, number, or nil
|
|
6041
|
+
*
|
|
6042
|
+
* Return the configured missing-directory creation mode.
|
|
6043
|
+
*/
|
|
6044
|
+
static VALUE ruby_curl_easy_ftp_create_missing_dirs_get(VALUE self) {
|
|
6045
|
+
CURB_OBJECT_HGETTER(ruby_curl_easy, ftp_create_missing_dirs);
|
|
6046
|
+
}
|
|
6047
|
+
#endif
|
|
6048
|
+
|
|
5958
6049
|
/*
|
|
5959
6050
|
* call-seq:
|
|
5960
6051
|
* easy.getinfo(opt) => nil
|
|
@@ -6010,8 +6101,9 @@ static VALUE ruby_curl_easy_escape(VALUE self, VALUE svalue) {
|
|
|
6010
6101
|
|
|
6011
6102
|
TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
6012
6103
|
|
|
6013
|
-
/* NOTE: make sure the value is a string
|
|
6014
|
-
|
|
6104
|
+
/* NOTE: make sure the value is a string; rb_obj_as_string always yields a
|
|
6105
|
+
* String, even if the object's to_s misbehaves and returns a non-String. */
|
|
6106
|
+
if( rb_type(str) != T_STRING ) { str = rb_obj_as_string(str); }
|
|
6015
6107
|
|
|
6016
6108
|
#if (LIBCURL_VERSION_NUM >= 0x070f04)
|
|
6017
6109
|
result = (char*)curl_easy_escape(rbce->curl, StringValuePtr(str), (int)RSTRING_LEN(str));
|
|
@@ -6041,6 +6133,11 @@ static VALUE ruby_curl_easy_unescape(VALUE self, VALUE str) {
|
|
|
6041
6133
|
|
|
6042
6134
|
TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
6043
6135
|
|
|
6136
|
+
/* Coerce as its own statement before use below - StringValuePtr and
|
|
6137
|
+
* RSTRING_LEN are sibling args to the same call and C does not guarantee
|
|
6138
|
+
* left-to-right evaluation order between them. */
|
|
6139
|
+
StringValue(str);
|
|
6140
|
+
|
|
6044
6141
|
#if (LIBCURL_VERSION_NUM >= 0x070f04)
|
|
6045
6142
|
result = (char*)curl_easy_unescape(rbce->curl, StringValuePtr(str), (int)RSTRING_LEN(str), &rlen);
|
|
6046
6143
|
#else
|
|
@@ -6075,7 +6172,7 @@ void init_curb_easy() {
|
|
|
6075
6172
|
idNetworkPolicyNone = rb_intern("none");
|
|
6076
6173
|
idNetworkPolicyPublic = rb_intern("public");
|
|
6077
6174
|
|
|
6078
|
-
rbstrAmp = rb_str_new2("&");
|
|
6175
|
+
rbstrAmp = rb_obj_freeze(rb_str_new2("&"));
|
|
6079
6176
|
rb_global_variable(&rbstrAmp);
|
|
6080
6177
|
|
|
6081
6178
|
cCurlEasy = rb_define_class_under(mCurl, "Easy", rb_cObject);
|
|
@@ -6146,7 +6243,7 @@ void init_curb_easy() {
|
|
|
6146
6243
|
rb_define_method(cCurlEasy, "proxy_type", ruby_curl_easy_proxy_type_get, 0);
|
|
6147
6244
|
rb_define_method(cCurlEasy, "http_auth_types=", ruby_curl_easy_http_auth_types_set, -1);
|
|
6148
6245
|
rb_define_method(cCurlEasy, "http_auth_types", ruby_curl_easy_http_auth_types_get, 0);
|
|
6149
|
-
rb_define_method(cCurlEasy, "proxy_auth_types=", ruby_curl_easy_proxy_auth_types_set, 1);
|
|
6246
|
+
rb_define_method(cCurlEasy, "proxy_auth_types=", ruby_curl_easy_proxy_auth_types_set, -1);
|
|
6150
6247
|
rb_define_method(cCurlEasy, "proxy_auth_types", ruby_curl_easy_proxy_auth_types_get, 0);
|
|
6151
6248
|
rb_define_method(cCurlEasy, "max_redirects=", ruby_curl_easy_max_redirects_set, 1);
|
|
6152
6249
|
rb_define_method(cCurlEasy, "max_redirects", ruby_curl_easy_max_redirects_get, 0);
|
|
@@ -6162,6 +6259,10 @@ void init_curb_easy() {
|
|
|
6162
6259
|
rb_define_method(cCurlEasy, "dns_cache_timeout", ruby_curl_easy_dns_cache_timeout_get, 0);
|
|
6163
6260
|
rb_define_method(cCurlEasy, "ftp_response_timeout=", ruby_curl_easy_ftp_response_timeout_set, 1);
|
|
6164
6261
|
rb_define_method(cCurlEasy, "ftp_response_timeout", ruby_curl_easy_ftp_response_timeout_get, 0);
|
|
6262
|
+
#ifdef HAVE_CURLOPT_FTP_CREATE_MISSING_DIRS
|
|
6263
|
+
rb_define_method(cCurlEasy, "ftp_create_missing_dirs=", ruby_curl_easy_ftp_create_missing_dirs_set, 1);
|
|
6264
|
+
rb_define_method(cCurlEasy, "ftp_create_missing_dirs", ruby_curl_easy_ftp_create_missing_dirs_get, 0);
|
|
6265
|
+
#endif
|
|
6165
6266
|
rb_define_method(cCurlEasy, "low_speed_limit=", ruby_curl_easy_low_speed_limit_set, 1);
|
|
6166
6267
|
rb_define_method(cCurlEasy, "low_speed_limit", ruby_curl_easy_low_speed_limit_get, 0);
|
|
6167
6268
|
rb_define_method(cCurlEasy, "low_speed_time=", ruby_curl_easy_low_speed_time_set, 1);
|
data/ext/curb_easy.h
CHANGED
|
@@ -112,6 +112,7 @@ typedef struct {
|
|
|
112
112
|
curb_cidr_rule *network_allowed_cidr_rules;
|
|
113
113
|
char **network_allowed_hosts;
|
|
114
114
|
|
|
115
|
+
/* attachment_sequence of the multi at the time this easy was last added. */
|
|
115
116
|
unsigned long multi_attachment_generation;
|
|
116
117
|
curl_off_t downloaded_body_bytes;
|
|
117
118
|
size_t network_allowed_cidr_rule_count;
|