curb 1.3.2 → 1.3.3
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/ext/curb.h +3 -3
- data/ext/curb_easy.c +114 -45
- data/ext/curb_multi.c +185 -20
- data/ext/curb_multi.h +3 -0
- data/tests/bug_curb_easy_blocks_ruby_threads.rb +3 -3
- data/tests/bug_follow_redirect_288.rb +7 -7
- data/tests/bug_raise_on_callback.rb +4 -3
- data/tests/helper.rb +7 -0
- data/tests/tc_curl_easy.rb +57 -0
- data/tests/tc_curl_easy_resolve.rb +32 -0
- data/tests/tc_curl_multi.rb +56 -0
- data/tests/tc_ftp_options.rb +14 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 363701a514e690ec0d3daab68555267a6640caa7242583941b674b2712eac9e4
|
|
4
|
+
data.tar.gz: 9cc165c1079d65dbff540ccdf10b4e456da09d21b81da52ecc9038b414e2c906
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 04cd7fece28ee5166ccf853d73686ff083b8ffac74c225f09f7795a39db27753a523736cfe604ad9880816f750774ba7f03f2ac0b2771276e8a3cefb75c60cf1
|
|
7
|
+
data.tar.gz: 7cf816b75ca9cafdd36bd9a7138735f19d609b9d2287066aff29f541f9581c5c10dcd70ac915a962e001d745ee5e8b7f37b2328819ac455f680f835225b64a75
|
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.
|
|
32
|
-
#define CURB_VER_NUM
|
|
31
|
+
#define CURB_VERSION "1.3.3"
|
|
32
|
+
#define CURB_VER_NUM 1033
|
|
33
33
|
#define CURB_VER_MAJ 1
|
|
34
34
|
#define CURB_VER_MIN 3
|
|
35
|
-
#define CURB_VER_MIC
|
|
35
|
+
#define CURB_VER_MIC 3
|
|
36
36
|
#define CURB_VER_PATCH 0
|
|
37
37
|
|
|
38
38
|
|
data/ext/curb_easy.c
CHANGED
|
@@ -829,6 +829,10 @@ static VALUE ruby_curl_easy_clone(VALUE self) {
|
|
|
829
829
|
|
|
830
830
|
/* now deep copy */
|
|
831
831
|
newrbce->curl = curl_easy_duphandle(rbce->curl);
|
|
832
|
+
if (!newrbce->curl) {
|
|
833
|
+
free(newrbce);
|
|
834
|
+
rb_raise(rb_eNoMemError, "Failed to duplicate Curl::Easy handle");
|
|
835
|
+
}
|
|
832
836
|
newrbce->curl_headers = (rbce->curl_headers) ? duplicate_curl_slist(rbce->curl_headers) : NULL;
|
|
833
837
|
newrbce->curl_proxy_headers = (rbce->curl_proxy_headers) ? duplicate_curl_slist(rbce->curl_proxy_headers) : NULL;
|
|
834
838
|
newrbce->curl_ftp_commands = (rbce->curl_ftp_commands) ? duplicate_curl_slist(rbce->curl_ftp_commands) : NULL;
|
|
@@ -1367,35 +1371,15 @@ static VALUE ruby_curl_easy_put_data_set(VALUE self, VALUE data) {
|
|
|
1367
1371
|
ruby_curl_easy *rbce;
|
|
1368
1372
|
CURL *curl;
|
|
1369
1373
|
VALUE upload;
|
|
1374
|
+
VALUE upload_stream = data;
|
|
1370
1375
|
VALUE headers;
|
|
1376
|
+
VALUE infile_size = Qnil;
|
|
1371
1377
|
|
|
1372
1378
|
TypedData_Get_Struct(self, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
1373
1379
|
|
|
1374
|
-
upload = ruby_curl_upload_new(cCurlUpload);
|
|
1375
|
-
ruby_curl_upload_stream_set(upload,data);
|
|
1376
|
-
|
|
1377
|
-
curl = rbce->curl;
|
|
1378
|
-
rb_easy_set("upload", upload); /* keep the upload object alive as long as
|
|
1379
|
-
the easy handle is active or until the upload
|
|
1380
|
-
is complete or terminated... */
|
|
1381
|
-
|
|
1382
|
-
curl_easy_setopt(curl, CURLOPT_NOBODY, 0);
|
|
1383
|
-
curl_easy_setopt(curl, CURLOPT_POST, 0);
|
|
1384
|
-
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL);
|
|
1385
|
-
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0);
|
|
1386
|
-
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
|
|
1387
|
-
curl_easy_setopt(curl, CURLOPT_READFUNCTION, (curl_read_callback)read_data_handler);
|
|
1388
|
-
#ifdef HAVE_CURLOPT_SEEKFUNCTION
|
|
1389
|
-
curl_easy_setopt(curl, CURLOPT_SEEKFUNCTION, (curl_seek_callback)seek_data_handler);
|
|
1390
|
-
#endif
|
|
1391
|
-
curl_easy_setopt(curl, CURLOPT_READDATA, rbce);
|
|
1392
|
-
#ifdef HAVE_CURLOPT_SEEKDATA
|
|
1393
|
-
curl_easy_setopt(curl, CURLOPT_SEEKDATA, rbce);
|
|
1394
|
-
#endif
|
|
1395
|
-
|
|
1396
1380
|
/*
|
|
1397
|
-
*
|
|
1398
|
-
*
|
|
1381
|
+
* Validate and prepare Ruby-visible state before mutating the CURL handle.
|
|
1382
|
+
* Several branches below can raise (header type, stat, size, to_s).
|
|
1399
1383
|
*/
|
|
1400
1384
|
if (!rb_easy_nil("headers")) {
|
|
1401
1385
|
if (rb_easy_type_check("headers", T_ARRAY) || rb_easy_type_check("headers", T_STRING)) {
|
|
@@ -1403,43 +1387,80 @@ static VALUE ruby_curl_easy_put_data_set(VALUE self, VALUE data) {
|
|
|
1403
1387
|
}
|
|
1404
1388
|
}
|
|
1405
1389
|
|
|
1406
|
-
|
|
1407
|
-
|
|
1390
|
+
if (!NIL_P(data) && !rb_respond_to(data, rb_intern("read"))) {
|
|
1391
|
+
if (rb_respond_to(data, rb_intern("to_s"))) {
|
|
1392
|
+
upload_stream = rb_obj_as_string(data);
|
|
1393
|
+
} else {
|
|
1394
|
+
rb_raise(rb_eRuntimeError, "PUT data must respond to read or to_s");
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1408
1397
|
|
|
1409
1398
|
headers = rb_easy_get("headers");
|
|
1410
1399
|
if( headers == Qnil ) {
|
|
1411
1400
|
headers = rb_hash_new();
|
|
1412
1401
|
}
|
|
1413
1402
|
|
|
1414
|
-
if (rb_respond_to(data, rb_intern("read"))) {
|
|
1415
|
-
VALUE stat =
|
|
1416
|
-
if
|
|
1403
|
+
if (!NIL_P(data) && rb_respond_to(data, rb_intern("read"))) {
|
|
1404
|
+
VALUE stat = Qnil;
|
|
1405
|
+
if (rb_respond_to(data, rb_intern("stat"))) {
|
|
1406
|
+
stat = rb_funcall(data, rb_intern("stat"), 0);
|
|
1407
|
+
}
|
|
1408
|
+
if(!NIL_P(stat) && stat != Qfalse && rb_hash_aref(headers, rb_str_new2("Content-Length")) == Qnil) {
|
|
1417
1409
|
VALUE size;
|
|
1418
1410
|
if( rb_hash_aref(headers, rb_str_new2("Expect")) == Qnil ) {
|
|
1419
1411
|
rb_hash_aset(headers, rb_str_new2("Expect"), rb_str_new2(""));
|
|
1420
1412
|
}
|
|
1421
1413
|
size = rb_funcall(stat, rb_intern("size"), 0);
|
|
1422
|
-
|
|
1414
|
+
infile_size = size;
|
|
1423
1415
|
}
|
|
1424
1416
|
else if( rb_hash_aref(headers, rb_str_new2("Content-Length")) == Qnil && rb_hash_aref(headers, rb_str_new2("Transfer-Encoding")) == Qnil ) {
|
|
1425
1417
|
rb_hash_aset(headers, rb_str_new2("Transfer-Encoding"), rb_str_new2("chunked"));
|
|
1426
1418
|
}
|
|
1427
1419
|
else if( rb_hash_aref(headers, rb_str_new2("Content-Length")) ) {
|
|
1428
1420
|
VALUE size = rb_funcall(rb_hash_aref(headers, rb_str_new2("Content-Length")), rb_intern("to_i"), 0);
|
|
1429
|
-
|
|
1421
|
+
infile_size = size;
|
|
1430
1422
|
}
|
|
1431
1423
|
}
|
|
1432
|
-
else if (rb_respond_to(data, rb_intern("to_s"))) {
|
|
1433
|
-
|
|
1424
|
+
else if (!NIL_P(data) && rb_respond_to(data, rb_intern("to_s"))) {
|
|
1425
|
+
infile_size = LONG2NUM(RSTRING_LEN(upload_stream));
|
|
1434
1426
|
if( rb_hash_aref(headers, rb_str_new2("Expect")) == Qnil ) {
|
|
1435
1427
|
rb_hash_aset(headers, rb_str_new2("Expect"), rb_str_new2(""));
|
|
1436
1428
|
}
|
|
1437
1429
|
}
|
|
1430
|
+
else if (NIL_P(data)) {
|
|
1431
|
+
/* Preserve legacy nil handling: configure an upload with no payload. */
|
|
1432
|
+
}
|
|
1438
1433
|
else {
|
|
1439
1434
|
rb_raise(rb_eRuntimeError, "PUT data must respond to read or to_s");
|
|
1440
1435
|
}
|
|
1441
1436
|
rb_easy_set("headers",headers);
|
|
1442
1437
|
|
|
1438
|
+
upload = ruby_curl_upload_new(cCurlUpload);
|
|
1439
|
+
ruby_curl_upload_stream_set(upload, upload_stream);
|
|
1440
|
+
|
|
1441
|
+
curl = rbce->curl;
|
|
1442
|
+
rb_easy_set("upload", upload); /* keep the upload object alive as long as
|
|
1443
|
+
the easy handle is active or until the upload
|
|
1444
|
+
is complete or terminated... */
|
|
1445
|
+
|
|
1446
|
+
curl_easy_setopt(curl, CURLOPT_NOBODY, 0);
|
|
1447
|
+
curl_easy_setopt(curl, CURLOPT_POST, 0);
|
|
1448
|
+
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL);
|
|
1449
|
+
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0);
|
|
1450
|
+
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
|
|
1451
|
+
curl_easy_setopt(curl, CURLOPT_READFUNCTION, (curl_read_callback)read_data_handler);
|
|
1452
|
+
#ifdef HAVE_CURLOPT_SEEKFUNCTION
|
|
1453
|
+
curl_easy_setopt(curl, CURLOPT_SEEKFUNCTION, (curl_seek_callback)seek_data_handler);
|
|
1454
|
+
#endif
|
|
1455
|
+
curl_easy_setopt(curl, CURLOPT_READDATA, rbce);
|
|
1456
|
+
#ifdef HAVE_CURLOPT_SEEKDATA
|
|
1457
|
+
curl_easy_setopt(curl, CURLOPT_SEEKDATA, rbce);
|
|
1458
|
+
#endif
|
|
1459
|
+
|
|
1460
|
+
if (!NIL_P(infile_size)) {
|
|
1461
|
+
curl_easy_setopt(curl, CURLOPT_INFILESIZE, NUM2LONG(infile_size));
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1443
1464
|
// if we made it this far, all should be well.
|
|
1444
1465
|
return data;
|
|
1445
1466
|
}
|
|
@@ -2754,7 +2775,7 @@ static VALUE cb_each_ftp_command(VALUE ftp_command, VALUE wrap, int _c, const VA
|
|
|
2754
2775
|
TypedData_Get_Struct(wrap, struct curl_slist *, &curl_slist_ptr_type, list);
|
|
2755
2776
|
|
|
2756
2777
|
ftp_command_string = rb_obj_as_string(ftp_command);
|
|
2757
|
-
struct curl_slist *new_list = curl_slist_append(*list, StringValuePtr(
|
|
2778
|
+
struct curl_slist *new_list = curl_slist_append(*list, StringValuePtr(ftp_command_string));
|
|
2758
2779
|
if (!new_list) {
|
|
2759
2780
|
rb_raise(rb_eNoMemError, "Failed to append to FTP command list");
|
|
2760
2781
|
}
|
|
@@ -2772,7 +2793,7 @@ static VALUE cb_each_resolve(VALUE resolve, VALUE wrap, int _c, const VALUE *_pt
|
|
|
2772
2793
|
TypedData_Get_Struct(wrap, struct curl_slist *, &curl_slist_ptr_type, list);
|
|
2773
2794
|
|
|
2774
2795
|
resolve_string = rb_obj_as_string(resolve);
|
|
2775
|
-
struct curl_slist *new_list = curl_slist_append(*list, StringValuePtr(
|
|
2796
|
+
struct curl_slist *new_list = curl_slist_append(*list, StringValuePtr(resolve_string));
|
|
2776
2797
|
if (!new_list) {
|
|
2777
2798
|
rb_raise(rb_eNoMemError, "Failed to append to resolve list");
|
|
2778
2799
|
}
|
|
@@ -3153,6 +3174,13 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
|
|
|
3153
3174
|
if (rb_easy_type_check("resolve", T_ARRAY)) {
|
|
3154
3175
|
VALUE wrap = TypedData_Wrap_Struct(rb_cObject, &curl_slist_ptr_type, rslv);
|
|
3155
3176
|
rb_block_call(rb_easy_get("resolve"), rb_intern("each"), 0, NULL, cb_each_resolve, wrap);
|
|
3177
|
+
} else {
|
|
3178
|
+
VALUE resolve_str = rb_obj_as_string(rb_easy_get("resolve"));
|
|
3179
|
+
struct curl_slist *new_list = curl_slist_append(*rslv, StringValuePtr(resolve_str));
|
|
3180
|
+
if (!new_list) {
|
|
3181
|
+
rb_raise(rb_eNoMemError, "Failed to append to resolve list");
|
|
3182
|
+
}
|
|
3183
|
+
*rslv = new_list;
|
|
3156
3184
|
}
|
|
3157
3185
|
|
|
3158
3186
|
if (*rslv) {
|
|
@@ -3209,6 +3237,44 @@ VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
|
|
|
3209
3237
|
return Qnil;
|
|
3210
3238
|
}
|
|
3211
3239
|
|
|
3240
|
+
struct easy_perform_request_restore_args {
|
|
3241
|
+
VALUE self;
|
|
3242
|
+
CURL *curl;
|
|
3243
|
+
ruby_curl_easy *rbce;
|
|
3244
|
+
int clear_customrequest;
|
|
3245
|
+
int clear_nobody;
|
|
3246
|
+
int clear_postfields;
|
|
3247
|
+
};
|
|
3248
|
+
|
|
3249
|
+
static VALUE perform_with_request_restore_body(VALUE argp) {
|
|
3250
|
+
struct easy_perform_request_restore_args *args = (struct easy_perform_request_restore_args *)argp;
|
|
3251
|
+
return rb_funcall(args->self, rb_intern("perform"), 0);
|
|
3252
|
+
}
|
|
3253
|
+
|
|
3254
|
+
static VALUE perform_with_request_restore_ensure(VALUE argp) {
|
|
3255
|
+
struct easy_perform_request_restore_args *args = (struct easy_perform_request_restore_args *)argp;
|
|
3256
|
+
|
|
3257
|
+
if (args->curl) {
|
|
3258
|
+
if (args->clear_nobody) {
|
|
3259
|
+
curl_easy_setopt(args->curl, CURLOPT_NOBODY, 0L);
|
|
3260
|
+
}
|
|
3261
|
+
if (args->clear_customrequest) {
|
|
3262
|
+
curl_easy_setopt(args->curl, CURLOPT_CUSTOMREQUEST, NULL);
|
|
3263
|
+
}
|
|
3264
|
+
if (args->clear_postfields) {
|
|
3265
|
+
curl_easy_setopt(args->curl, CURLOPT_POST, 0L);
|
|
3266
|
+
curl_easy_setopt(args->curl, CURLOPT_POSTFIELDS, NULL);
|
|
3267
|
+
curl_easy_setopt(args->curl, CURLOPT_POSTFIELDSIZE, 0L);
|
|
3268
|
+
curl_easy_setopt(args->curl, CURLOPT_HTTPGET, 1L);
|
|
3269
|
+
if (args->rbce && !NIL_P(args->rbce->opts)) {
|
|
3270
|
+
rb_hash_delete(args->rbce->opts, rb_easy_hkey("postdata_buffer"));
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
|
|
3275
|
+
return Qnil;
|
|
3276
|
+
}
|
|
3277
|
+
|
|
3212
3278
|
/*
|
|
3213
3279
|
* Common implementation of easy.http(verb) and easy.http_delete
|
|
3214
3280
|
*/
|
|
@@ -3241,13 +3307,9 @@ static VALUE ruby_curl_easy_perform_verb_str(VALUE self, const char *verb) {
|
|
|
3241
3307
|
curl_easy_setopt(curl, CURLOPT_POST, 0L);
|
|
3242
3308
|
}
|
|
3243
3309
|
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
if (is_head) {
|
|
3248
|
-
curl_easy_setopt(curl, CURLOPT_NOBODY, 0L);
|
|
3249
|
-
}
|
|
3250
|
-
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, NULL);
|
|
3310
|
+
struct easy_perform_request_restore_args restore_args = { self, curl, rbce, 1, is_head, 0 };
|
|
3311
|
+
retval = rb_ensure(perform_with_request_restore_body, (VALUE)&restore_args,
|
|
3312
|
+
perform_with_request_restore_ensure, (VALUE)&restore_args);
|
|
3251
3313
|
|
|
3252
3314
|
return retval;
|
|
3253
3315
|
}
|
|
@@ -3405,7 +3467,9 @@ static VALUE ruby_curl_easy_perform_post(int argc, VALUE *argv, VALUE self) {
|
|
|
3405
3467
|
ruby_curl_easy_post_body_set(self, post_body);
|
|
3406
3468
|
}
|
|
3407
3469
|
|
|
3408
|
-
|
|
3470
|
+
struct easy_perform_request_restore_args restore_args = { self, curl, rbce, 1, 0, 0 };
|
|
3471
|
+
return rb_ensure(perform_with_request_restore_body, (VALUE)&restore_args,
|
|
3472
|
+
perform_with_request_restore_ensure, (VALUE)&restore_args);
|
|
3409
3473
|
}
|
|
3410
3474
|
}
|
|
3411
3475
|
}
|
|
@@ -3459,7 +3523,9 @@ static VALUE ruby_curl_easy_perform_patch(int argc, VALUE *argv, VALUE self) {
|
|
|
3459
3523
|
if (rb_easy_nil("postdata_buffer")) {
|
|
3460
3524
|
ruby_curl_easy_post_body_set(self, patch_body);
|
|
3461
3525
|
}
|
|
3462
|
-
|
|
3526
|
+
struct easy_perform_request_restore_args restore_args = { self, curl, rbce, 1, 0, 1 };
|
|
3527
|
+
return rb_ensure(perform_with_request_restore_body, (VALUE)&restore_args,
|
|
3528
|
+
perform_with_request_restore_ensure, (VALUE)&restore_args);
|
|
3463
3529
|
}
|
|
3464
3530
|
}
|
|
3465
3531
|
}
|
|
@@ -3510,7 +3576,9 @@ static VALUE ruby_curl_easy_perform_put(int argc, VALUE *argv, VALUE self) {
|
|
|
3510
3576
|
ruby_curl_easy_put_data_set(self, post_body);
|
|
3511
3577
|
}
|
|
3512
3578
|
}
|
|
3513
|
-
|
|
3579
|
+
struct easy_perform_request_restore_args restore_args = { self, curl, rbce, 1, 0, 0 };
|
|
3580
|
+
return rb_ensure(perform_with_request_restore_body, (VALUE)&restore_args,
|
|
3581
|
+
perform_with_request_restore_ensure, (VALUE)&restore_args);
|
|
3514
3582
|
}
|
|
3515
3583
|
|
|
3516
3584
|
|
|
@@ -4577,6 +4645,7 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
|
|
|
4577
4645
|
}
|
|
4578
4646
|
/* Save the list pointer in the ruby_curl_easy structure for cleanup later */
|
|
4579
4647
|
rbce->curl_resolve = list;
|
|
4648
|
+
rb_hash_aset(rbce->opts, rb_easy_hkey("resolve"), val);
|
|
4580
4649
|
curl_easy_setopt(rbce->curl, CURLOPT_RESOLVE, list);
|
|
4581
4650
|
} break;
|
|
4582
4651
|
#endif
|
data/ext/curb_multi.c
CHANGED
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
#include <errno.h>
|
|
30
30
|
#include <fcntl.h>
|
|
31
|
+
#include <stdint.h>
|
|
31
32
|
#include <stdarg.h>
|
|
32
33
|
|
|
33
34
|
/*
|
|
@@ -52,9 +53,7 @@
|
|
|
52
53
|
#include <fcntl.h>
|
|
53
54
|
#endif
|
|
54
55
|
|
|
55
|
-
#if
|
|
56
|
-
#include <stdint.h> /* for intptr_t */
|
|
57
|
-
|
|
56
|
+
#if defined(HAVE_CURL_MULTI_WAIT) && !defined(HAVE_RB_THREAD_FD_SELECT)
|
|
58
57
|
struct wait_args {
|
|
59
58
|
CURLM *handle;
|
|
60
59
|
long timeout_ms;
|
|
@@ -95,6 +94,34 @@ static void rb_curl_multi_remove_request_reference(VALUE self, VALUE easy);
|
|
|
95
94
|
static VALUE ruby_curl_multi_mark_closed(VALUE self);
|
|
96
95
|
static VALUE ruby_curl_multi_alloc(VALUE klass);
|
|
97
96
|
static VALUE ruby_curl_multi_initialize(VALUE self);
|
|
97
|
+
static VALUE ruby_curl_multi_perform_impl(int argc, VALUE *argv, VALUE self);
|
|
98
|
+
#if defined(HAVE_CURL_MULTI_SOCKET_ACTION) && defined(HAVE_CURLMOPT_SOCKETFUNCTION) && defined(HAVE_CURLMOPT_TIMERFUNCTION) && defined(HAVE_RB_THREAD_FD_SELECT) && !defined(_WIN32)
|
|
99
|
+
static VALUE ruby_curl_multi_socket_perform_impl(int argc, VALUE *argv, VALUE self);
|
|
100
|
+
#endif
|
|
101
|
+
|
|
102
|
+
static ruby_curl_multi *ruby_curl_multi_pointer_if_compatible(VALUE multi_val) {
|
|
103
|
+
if (NIL_P(multi_val) || !RB_TYPE_P(multi_val, T_DATA)) {
|
|
104
|
+
return NULL;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
#if defined(RTYPEDDATA_P) && defined(RTYPEDDATA_TYPE) && defined(RTYPEDDATA_DATA)
|
|
108
|
+
if (!RTYPEDDATA_P(multi_val)) {
|
|
109
|
+
return NULL;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (RTYPEDDATA_TYPE(multi_val) != &ruby_curl_multi_data_type) {
|
|
113
|
+
return NULL;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return (ruby_curl_multi *)RTYPEDDATA_DATA(multi_val);
|
|
117
|
+
#else
|
|
118
|
+
if (!rb_typeddata_is_kind_of(multi_val, &ruby_curl_multi_data_type)) {
|
|
119
|
+
return NULL;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return DATA_PTR(multi_val);
|
|
123
|
+
#endif
|
|
124
|
+
}
|
|
98
125
|
|
|
99
126
|
static VALUE callback_exception(VALUE did_raise, VALUE exception) {
|
|
100
127
|
// TODO: we could have an option to enable exception reporting
|
|
@@ -342,6 +369,9 @@ static void ruby_curl_multi_init(ruby_curl_multi *rbcm) {
|
|
|
342
369
|
rbcm->active = 0;
|
|
343
370
|
rbcm->running = 0;
|
|
344
371
|
rbcm->closed = 0;
|
|
372
|
+
rbcm->perform_active = 0;
|
|
373
|
+
rbcm->callback_active = 0;
|
|
374
|
+
rbcm->allow_close_during_perform = 0;
|
|
345
375
|
|
|
346
376
|
if (rbcm->attached) {
|
|
347
377
|
st_free_table(rbcm->attached);
|
|
@@ -537,11 +567,21 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
|
|
537
567
|
CURLMcode mcode;
|
|
538
568
|
ruby_curl_easy *rbce;
|
|
539
569
|
ruby_curl_multi *rbcm;
|
|
570
|
+
ruby_curl_multi *existing_rbcm;
|
|
540
571
|
|
|
541
572
|
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
542
573
|
TypedData_Get_Struct(easy, ruby_curl_easy, &ruby_curl_easy_data_type, rbce);
|
|
543
574
|
ruby_curl_multi_ensure_handle(rbcm);
|
|
544
575
|
|
|
576
|
+
if (rb_curl_multi_has_easy(rbcm, rbce)) {
|
|
577
|
+
return self;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
existing_rbcm = ruby_curl_multi_pointer_if_compatible(rbce->multi);
|
|
581
|
+
if (existing_rbcm && existing_rbcm != rbcm && rb_curl_multi_has_easy(existing_rbcm, rbce)) {
|
|
582
|
+
rb_raise(rb_eRuntimeError, "Cannot add an active Curl::Easy handle to another Curl::Multi");
|
|
583
|
+
}
|
|
584
|
+
|
|
545
585
|
/* setup the easy handle */
|
|
546
586
|
ruby_curl_easy_setup( rbce );
|
|
547
587
|
|
|
@@ -797,6 +837,8 @@ static VALUE rb_curl_multi_run_completion_callbacks(VALUE argp) {
|
|
|
797
837
|
VALUE did_raise = rb_hash_new();
|
|
798
838
|
long redirect_count;
|
|
799
839
|
|
|
840
|
+
args->rbcm->callback_active = 1;
|
|
841
|
+
|
|
800
842
|
easy_callback_error = take_easy_callback_error_if_any(args->easy);
|
|
801
843
|
if (!NIL_P(easy_callback_error)) {
|
|
802
844
|
stash_multi_exception_if_unset(args->self, easy_callback_error, args->easy);
|
|
@@ -877,6 +919,10 @@ static VALUE rb_curl_multi_finish_completion_callbacks(VALUE argp) {
|
|
|
877
919
|
args->rbce->callback_active = 0;
|
|
878
920
|
}
|
|
879
921
|
|
|
922
|
+
if (args->rbcm) {
|
|
923
|
+
args->rbcm->callback_active = 0;
|
|
924
|
+
}
|
|
925
|
+
|
|
880
926
|
if (args->rbce->multi == args->self && !rb_curl_multi_has_easy(args->rbcm, args->rbce)) {
|
|
881
927
|
args->rbce->multi = Qnil;
|
|
882
928
|
}
|
|
@@ -1072,6 +1118,41 @@ static VALUE fiber_io_wait_protected(VALUE argp) {
|
|
|
1072
1118
|
}
|
|
1073
1119
|
#endif
|
|
1074
1120
|
|
|
1121
|
+
#if defined(RB_INTEGER_TYPE_P)
|
|
1122
|
+
#define CURB_INTEGER_P(value) RB_INTEGER_TYPE_P(value)
|
|
1123
|
+
#else
|
|
1124
|
+
#define CURB_INTEGER_P(value) (FIXNUM_P(value) || RB_TYPE_P((value), T_BIGNUM))
|
|
1125
|
+
#endif
|
|
1126
|
+
|
|
1127
|
+
static int multi_socket_wait_events_for_curl_poll(int what) {
|
|
1128
|
+
if (what == CURL_POLL_IN) return RB_WAITFD_IN;
|
|
1129
|
+
if (what == CURL_POLL_OUT) return RB_WAITFD_OUT;
|
|
1130
|
+
if (what == CURL_POLL_INOUT) return RB_WAITFD_IN | RB_WAITFD_OUT;
|
|
1131
|
+
return RB_WAITFD_IN | RB_WAITFD_OUT;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
static int multi_socket_cselect_flags_for_curl_poll(int what) {
|
|
1135
|
+
int flags = 0;
|
|
1136
|
+
|
|
1137
|
+
if (what == CURL_POLL_IN || what == CURL_POLL_INOUT) flags |= CURL_CSELECT_IN;
|
|
1138
|
+
if (what == CURL_POLL_OUT || what == CURL_POLL_INOUT) flags |= CURL_CSELECT_OUT;
|
|
1139
|
+
if (flags == 0) flags = CURL_CSELECT_IN | CURL_CSELECT_OUT;
|
|
1140
|
+
|
|
1141
|
+
return flags;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
static int multi_socket_cselect_flags_for_wait_events(int events) {
|
|
1145
|
+
int flags = 0;
|
|
1146
|
+
|
|
1147
|
+
if (events & RB_WAITFD_IN) flags |= CURL_CSELECT_IN;
|
|
1148
|
+
if (events & RB_WAITFD_OUT) flags |= CURL_CSELECT_OUT;
|
|
1149
|
+
#ifdef RB_WAITFD_PRI
|
|
1150
|
+
if (events & RB_WAITFD_PRI) flags |= CURL_CSELECT_ERR;
|
|
1151
|
+
#endif
|
|
1152
|
+
|
|
1153
|
+
return flags;
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1075
1156
|
static void multi_socket_forget_fd(multi_socket_ctx *ctx, int fd) {
|
|
1076
1157
|
st_data_t key = (st_data_t)fd;
|
|
1077
1158
|
st_data_t rec;
|
|
@@ -1236,6 +1317,7 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
1236
1317
|
|
|
1237
1318
|
int did_timeout = 0;
|
|
1238
1319
|
int any_ready = 0;
|
|
1320
|
+
int ready_flags = 0;
|
|
1239
1321
|
|
|
1240
1322
|
int handled_wait = 0;
|
|
1241
1323
|
if (count_tracked > 1) {
|
|
@@ -1270,10 +1352,7 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
1270
1352
|
if (scheduler != Qnil) {
|
|
1271
1353
|
int events = 0;
|
|
1272
1354
|
if (wait_fd >= 0) {
|
|
1273
|
-
|
|
1274
|
-
else if (wait_what == CURL_POLL_OUT) events = RB_WAITFD_OUT;
|
|
1275
|
-
else if (wait_what == CURL_POLL_INOUT) events = RB_WAITFD_IN|RB_WAITFD_OUT;
|
|
1276
|
-
else events = RB_WAITFD_IN|RB_WAITFD_OUT;
|
|
1355
|
+
events = multi_socket_wait_events_for_curl_poll(wait_what);
|
|
1277
1356
|
}
|
|
1278
1357
|
double timeout_s = (double)tv.tv_sec + ((double)tv.tv_usec / 1e6);
|
|
1279
1358
|
VALUE timeout = rb_float_new(timeout_s);
|
|
@@ -1305,8 +1384,21 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
1305
1384
|
did_timeout = 1;
|
|
1306
1385
|
any_ready = 0;
|
|
1307
1386
|
} else {
|
|
1308
|
-
any_ready = (ready != Qfalse);
|
|
1387
|
+
any_ready = (ready != Qfalse && !NIL_P(ready));
|
|
1309
1388
|
did_timeout = !any_ready;
|
|
1389
|
+
if (any_ready) {
|
|
1390
|
+
if (ready == Qtrue) {
|
|
1391
|
+
ready_flags = multi_socket_cselect_flags_for_curl_poll(wait_what);
|
|
1392
|
+
} else if (CURB_INTEGER_P(ready)) {
|
|
1393
|
+
ready_flags = multi_socket_cselect_flags_for_wait_events(NUM2INT(ready));
|
|
1394
|
+
if (ready_flags == 0) {
|
|
1395
|
+
any_ready = 0;
|
|
1396
|
+
did_timeout = 1;
|
|
1397
|
+
}
|
|
1398
|
+
} else {
|
|
1399
|
+
ready_flags = multi_socket_cselect_flags_for_curl_poll(wait_what);
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1310
1402
|
}
|
|
1311
1403
|
}
|
|
1312
1404
|
}
|
|
@@ -1316,10 +1408,7 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
1316
1408
|
#endif
|
|
1317
1409
|
#if defined(HAVE_RB_WAIT_FOR_SINGLE_FD)
|
|
1318
1410
|
if (!handled_wait && wait_fd >= 0) {
|
|
1319
|
-
int ev =
|
|
1320
|
-
if (wait_what == CURL_POLL_IN) ev = RB_WAITFD_IN;
|
|
1321
|
-
else if (wait_what == CURL_POLL_OUT) ev = RB_WAITFD_OUT;
|
|
1322
|
-
else if (wait_what == CURL_POLL_INOUT) ev = RB_WAITFD_IN|RB_WAITFD_OUT;
|
|
1411
|
+
int ev = multi_socket_wait_events_for_curl_poll(wait_what);
|
|
1323
1412
|
int rc = rb_wait_for_single_fd(wait_fd, ev, &tv);
|
|
1324
1413
|
curb_debugf("[curb.socket] rb_wait_for_single_fd rc=%d fd=%d ev=%d", rc, wait_fd, ev);
|
|
1325
1414
|
if (rc < 0) {
|
|
@@ -1328,6 +1417,7 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
1328
1417
|
}
|
|
1329
1418
|
any_ready = (rc != 0);
|
|
1330
1419
|
did_timeout = (rc == 0);
|
|
1420
|
+
if (any_ready) ready_flags = multi_socket_cselect_flags_for_wait_events(rc);
|
|
1331
1421
|
handled_wait = 1;
|
|
1332
1422
|
}
|
|
1333
1423
|
#endif
|
|
@@ -1351,6 +1441,11 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
1351
1441
|
}
|
|
1352
1442
|
any_ready = (rc > 0);
|
|
1353
1443
|
did_timeout = (rc == 0);
|
|
1444
|
+
if (any_ready && wait_fd >= 0) {
|
|
1445
|
+
if (rb_fd_isset(wait_fd, &rfds)) ready_flags |= CURL_CSELECT_IN;
|
|
1446
|
+
if (rb_fd_isset(wait_fd, &wfds)) ready_flags |= CURL_CSELECT_OUT;
|
|
1447
|
+
if (rb_fd_isset(wait_fd, &efds)) ready_flags |= CURL_CSELECT_ERR;
|
|
1448
|
+
}
|
|
1354
1449
|
rb_fd_term(&rfds); rb_fd_term(&wfds); rb_fd_term(&efds);
|
|
1355
1450
|
}
|
|
1356
1451
|
} else { /* count_tracked == 0 */
|
|
@@ -1368,10 +1463,8 @@ static void rb_curl_multi_socket_drive(VALUE self, ruby_curl_multi *rbcm, multi_
|
|
|
1368
1463
|
if (mrc != CURLM_OK) raise_curl_multi_error_exception(mrc);
|
|
1369
1464
|
} else if (any_ready) {
|
|
1370
1465
|
if (count_tracked == 1 && wait_fd >= 0) {
|
|
1371
|
-
int flags =
|
|
1372
|
-
if (
|
|
1373
|
-
if (wait_what == CURL_POLL_OUT || wait_what == CURL_POLL_INOUT) flags |= CURL_CSELECT_OUT;
|
|
1374
|
-
flags |= CURL_CSELECT_ERR;
|
|
1466
|
+
int flags = ready_flags;
|
|
1467
|
+
if (flags == 0) flags = multi_socket_cselect_flags_for_curl_poll(wait_what);
|
|
1375
1468
|
#if CURB_SOCKET_DEBUG
|
|
1376
1469
|
{
|
|
1377
1470
|
char b[32];
|
|
@@ -1415,7 +1508,7 @@ static VALUE ruby_curl_multi_socket_drive_ensure(VALUE argp) {
|
|
|
1415
1508
|
return Qnil;
|
|
1416
1509
|
}
|
|
1417
1510
|
|
|
1418
|
-
VALUE
|
|
1511
|
+
static VALUE ruby_curl_multi_socket_perform_impl(int argc, VALUE *argv, VALUE self) {
|
|
1419
1512
|
ruby_curl_multi *rbcm;
|
|
1420
1513
|
VALUE block = Qnil;
|
|
1421
1514
|
rb_scan_args(argc, argv, "0&", &block);
|
|
@@ -1445,7 +1538,11 @@ VALUE ruby_curl_multi_socket_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1445
1538
|
/* finalize */
|
|
1446
1539
|
rb_curl_multi_read_info(self, rbcm->handle);
|
|
1447
1540
|
rb_curl_multi_yield_if_given(self, block);
|
|
1448
|
-
if (cCurlMutiAutoClose == 1)
|
|
1541
|
+
if (cCurlMutiAutoClose == 1) {
|
|
1542
|
+
rbcm->allow_close_during_perform = 1;
|
|
1543
|
+
rb_funcall(self, rb_intern("_autoclose"), 0);
|
|
1544
|
+
rbcm->allow_close_during_perform = 0;
|
|
1545
|
+
}
|
|
1449
1546
|
|
|
1450
1547
|
return Qtrue;
|
|
1451
1548
|
}
|
|
@@ -1493,6 +1590,14 @@ static VALUE curb_select(void *args) {
|
|
|
1493
1590
|
int rc = select(set->maxfd, set->fdread, set->fdwrite, set->fdexcep, set->tv);
|
|
1494
1591
|
return INT2FIX(rc);
|
|
1495
1592
|
}
|
|
1593
|
+
|
|
1594
|
+
#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
|
|
1595
|
+
static void *curb_select_without_gvl(void *args) {
|
|
1596
|
+
struct _select_set* set = args;
|
|
1597
|
+
int rc = select(set->maxfd, set->fdread, set->fdwrite, set->fdexcep, set->tv);
|
|
1598
|
+
return (void *)(intptr_t)rc;
|
|
1599
|
+
}
|
|
1600
|
+
#endif
|
|
1496
1601
|
#endif
|
|
1497
1602
|
|
|
1498
1603
|
/*
|
|
@@ -1510,7 +1615,7 @@ static VALUE curb_select(void *args) {
|
|
|
1510
1615
|
*
|
|
1511
1616
|
* Run multi handles, looping selecting when data can be transfered
|
|
1512
1617
|
*/
|
|
1513
|
-
VALUE
|
|
1618
|
+
static VALUE ruby_curl_multi_perform_impl(int argc, VALUE *argv, VALUE self) {
|
|
1514
1619
|
CURLMcode mcode;
|
|
1515
1620
|
ruby_curl_multi *rbcm;
|
|
1516
1621
|
int maxfd, rc = -1;
|
|
@@ -1691,7 +1796,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1691
1796
|
rb_fd_term(&efds);
|
|
1692
1797
|
}
|
|
1693
1798
|
#elif defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
|
|
1694
|
-
rc = (int)(
|
|
1799
|
+
rc = (int)(intptr_t) rb_thread_call_without_gvl(curb_select_without_gvl, &fdset_args, RUBY_UBF_IO, 0);
|
|
1695
1800
|
#elif HAVE_RB_THREAD_BLOCKING_REGION
|
|
1696
1801
|
rc = rb_thread_blocking_region(curb_select, &fdset_args, RUBY_UBF_IO, 0);
|
|
1697
1802
|
#else
|
|
@@ -1725,11 +1830,66 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1725
1830
|
rb_curl_multi_read_info( self, rbcm->handle );
|
|
1726
1831
|
rb_curl_multi_yield_if_given(self, block);
|
|
1727
1832
|
if (cCurlMutiAutoClose == 1) {
|
|
1833
|
+
rbcm->allow_close_during_perform = 1;
|
|
1728
1834
|
rb_funcall(self, rb_intern("_autoclose"), 0);
|
|
1835
|
+
rbcm->allow_close_during_perform = 0;
|
|
1729
1836
|
}
|
|
1730
1837
|
return Qtrue;
|
|
1731
1838
|
}
|
|
1732
1839
|
|
|
1840
|
+
struct multi_perform_call_args {
|
|
1841
|
+
int argc;
|
|
1842
|
+
VALUE *argv;
|
|
1843
|
+
VALUE self;
|
|
1844
|
+
VALUE result;
|
|
1845
|
+
VALUE (*func)(int, VALUE *, VALUE);
|
|
1846
|
+
};
|
|
1847
|
+
|
|
1848
|
+
static VALUE ruby_curl_multi_perform_guard_body(VALUE argp) {
|
|
1849
|
+
struct multi_perform_call_args *args = (struct multi_perform_call_args *)argp;
|
|
1850
|
+
args->result = args->func(args->argc, args->argv, args->self);
|
|
1851
|
+
return args->result;
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
static VALUE ruby_curl_multi_perform_guard_ensure(VALUE self) {
|
|
1855
|
+
ruby_curl_multi *rbcm;
|
|
1856
|
+
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
1857
|
+
rbcm->perform_active = 0;
|
|
1858
|
+
rbcm->callback_active = 0;
|
|
1859
|
+
rbcm->allow_close_during_perform = 0;
|
|
1860
|
+
return Qnil;
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
static VALUE ruby_curl_multi_with_perform_guard(int argc, VALUE *argv, VALUE self, VALUE (*func)(int, VALUE *, VALUE)) {
|
|
1864
|
+
ruby_curl_multi *rbcm;
|
|
1865
|
+
struct multi_perform_call_args args;
|
|
1866
|
+
|
|
1867
|
+
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
1868
|
+
if (rbcm->perform_active) {
|
|
1869
|
+
rb_raise(rb_eRuntimeError, "Cannot recursively perform an active Curl::Multi handle");
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
rbcm->perform_active = 1;
|
|
1873
|
+
args.argc = argc;
|
|
1874
|
+
args.argv = argv;
|
|
1875
|
+
args.self = self;
|
|
1876
|
+
args.result = Qnil;
|
|
1877
|
+
args.func = func;
|
|
1878
|
+
|
|
1879
|
+
return rb_ensure(ruby_curl_multi_perform_guard_body, (VALUE)&args,
|
|
1880
|
+
ruby_curl_multi_perform_guard_ensure, self);
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
1884
|
+
return ruby_curl_multi_with_perform_guard(argc, argv, self, ruby_curl_multi_perform_impl);
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
#if defined(HAVE_CURL_MULTI_SOCKET_ACTION) && defined(HAVE_CURLMOPT_SOCKETFUNCTION) && defined(HAVE_CURLMOPT_TIMERFUNCTION) && defined(HAVE_RB_THREAD_FD_SELECT) && !defined(_WIN32)
|
|
1888
|
+
VALUE ruby_curl_multi_socket_perform(int argc, VALUE *argv, VALUE self) {
|
|
1889
|
+
return ruby_curl_multi_with_perform_guard(argc, argv, self, ruby_curl_multi_socket_perform_impl);
|
|
1890
|
+
}
|
|
1891
|
+
#endif
|
|
1892
|
+
|
|
1733
1893
|
/*
|
|
1734
1894
|
* call-seq:
|
|
1735
1895
|
*
|
|
@@ -1740,6 +1900,11 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
1740
1900
|
VALUE ruby_curl_multi_close(VALUE self) {
|
|
1741
1901
|
ruby_curl_multi *rbcm;
|
|
1742
1902
|
TypedData_Get_Struct(self, ruby_curl_multi, &ruby_curl_multi_data_type, rbcm);
|
|
1903
|
+
|
|
1904
|
+
if ((rbcm->perform_active || rbcm->callback_active) && !rbcm->allow_close_during_perform) {
|
|
1905
|
+
rb_raise(rb_eRuntimeError, "Cannot close an active Curl::Multi handle during perform");
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1743
1908
|
rb_curl_multi_detach_all(rbcm);
|
|
1744
1909
|
|
|
1745
1910
|
if (rbcm->handle) {
|
data/ext/curb_multi.h
CHANGED
|
@@ -4,7 +4,7 @@ class BugTestInstancePostDiffersFromClassPost < Test::Unit::TestCase
|
|
|
4
4
|
include BugTestServerSetupTeardown
|
|
5
5
|
|
|
6
6
|
def setup
|
|
7
|
-
@port =
|
|
7
|
+
@port = unused_local_port
|
|
8
8
|
@response_proc = lambda do|res|
|
|
9
9
|
sleep 0.5
|
|
10
10
|
res.body = "hi"
|
|
@@ -19,7 +19,7 @@ class BugTestInstancePostDiffersFromClassPost < Test::Unit::TestCase
|
|
|
19
19
|
|
|
20
20
|
5.times do |i|
|
|
21
21
|
t = Thread.new do
|
|
22
|
-
c = Curl::Easy.perform(
|
|
22
|
+
c = Curl::Easy.perform("http://127.0.0.1:#{@port}/test")
|
|
23
23
|
c.header_str
|
|
24
24
|
end
|
|
25
25
|
threads << t
|
|
@@ -35,7 +35,7 @@ class BugTestInstancePostDiffersFromClassPost < Test::Unit::TestCase
|
|
|
35
35
|
timer = Time.now
|
|
36
36
|
single_responses = []
|
|
37
37
|
5.times do |i|
|
|
38
|
-
c = Curl::Easy.perform(
|
|
38
|
+
c = Curl::Easy.perform("http://127.0.0.1:#{@port}/test")
|
|
39
39
|
single_responses << c.header_str
|
|
40
40
|
end
|
|
41
41
|
|
|
@@ -4,7 +4,7 @@ class BugFollowRedirect288 < Test::Unit::TestCase
|
|
|
4
4
|
include BugTestServerSetupTeardown
|
|
5
5
|
|
|
6
6
|
def setup
|
|
7
|
-
@port =
|
|
7
|
+
@port = unused_local_port
|
|
8
8
|
super
|
|
9
9
|
@server.mount_proc("/redirect_to_test") do|req,res|
|
|
10
10
|
res.set_redirect(WEBrick::HTTPStatus::TemporaryRedirect, "/test")
|
|
@@ -13,7 +13,7 @@ class BugFollowRedirect288 < Test::Unit::TestCase
|
|
|
13
13
|
|
|
14
14
|
def test_follow_redirect_with_no_redirect
|
|
15
15
|
|
|
16
|
-
c = Curl::Easy.new(
|
|
16
|
+
c = Curl::Easy.new("http://127.0.0.1:#{@port}/test")
|
|
17
17
|
did_call_redirect = false
|
|
18
18
|
c.on_redirect do|x|
|
|
19
19
|
did_call_redirect = true
|
|
@@ -22,7 +22,7 @@ class BugFollowRedirect288 < Test::Unit::TestCase
|
|
|
22
22
|
|
|
23
23
|
assert !did_call_redirect, "should reach this point redirect should not have been called"
|
|
24
24
|
|
|
25
|
-
c = Curl::Easy.new(
|
|
25
|
+
c = Curl::Easy.new("http://127.0.0.1:#{@port}/test")
|
|
26
26
|
did_call_redirect = false
|
|
27
27
|
c.on_redirect do|x|
|
|
28
28
|
did_call_redirect = true
|
|
@@ -33,7 +33,7 @@ class BugFollowRedirect288 < Test::Unit::TestCase
|
|
|
33
33
|
assert_equal 0, c.redirect_count
|
|
34
34
|
assert !did_call_redirect, "should reach this point redirect should not have been called"
|
|
35
35
|
|
|
36
|
-
c = Curl::Easy.new(
|
|
36
|
+
c = Curl::Easy.new("http://127.0.0.1:#{@port}/redirect_to_test")
|
|
37
37
|
did_call_redirect = false
|
|
38
38
|
c.on_redirect do|x|
|
|
39
39
|
did_call_redirect = true
|
|
@@ -43,7 +43,7 @@ class BugFollowRedirect288 < Test::Unit::TestCase
|
|
|
43
43
|
|
|
44
44
|
assert did_call_redirect, "we should have called on_redirect"
|
|
45
45
|
|
|
46
|
-
c = Curl::Easy.new(
|
|
46
|
+
c = Curl::Easy.new("http://127.0.0.1:#{@port}/redirect_to_test")
|
|
47
47
|
did_call_redirect = false
|
|
48
48
|
c.follow_location = true
|
|
49
49
|
# NOTE: while this API is not supported by libcurl e.g. there is no redirect function callback in libcurl we could
|
|
@@ -57,7 +57,7 @@ class BugFollowRedirect288 < Test::Unit::TestCase
|
|
|
57
57
|
|
|
58
58
|
assert did_call_redirect, "we should have called on_redirect"
|
|
59
59
|
|
|
60
|
-
c.url =
|
|
60
|
+
c.url = "http://127.0.0.1:#{@port}/test"
|
|
61
61
|
c.perform
|
|
62
62
|
assert_equal 0, c.redirect_count
|
|
63
63
|
assert_equal 200, c.response_code
|
|
@@ -65,7 +65,7 @@ class BugFollowRedirect288 < Test::Unit::TestCase
|
|
|
65
65
|
puts "checking for raise support"
|
|
66
66
|
did_raise = false
|
|
67
67
|
begin
|
|
68
|
-
c = Curl::Easy.new(
|
|
68
|
+
c = Curl::Easy.new("http://127.0.0.1:#{@port}/redirect_to_test")
|
|
69
69
|
did_call_redirect = false
|
|
70
70
|
c.on_redirect do|x|
|
|
71
71
|
raise "raise"
|
|
@@ -4,16 +4,17 @@ class BugRaiseOnCallback < Test::Unit::TestCase
|
|
|
4
4
|
include BugTestServerSetupTeardown
|
|
5
5
|
|
|
6
6
|
def setup
|
|
7
|
-
@port =
|
|
7
|
+
@port = unused_local_port
|
|
8
8
|
super
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def test_on_complte
|
|
12
|
-
|
|
12
|
+
url = "http://127.0.0.1:#{@port}/test"
|
|
13
|
+
c = Curl::Easy.new(url)
|
|
13
14
|
did_raise = false
|
|
14
15
|
begin
|
|
15
16
|
c.on_complete do|x|
|
|
16
|
-
assert_equal
|
|
17
|
+
assert_equal url, x.url
|
|
17
18
|
raise "error complete" # this will get swallowed
|
|
18
19
|
end
|
|
19
20
|
c.perform
|
data/tests/helper.rb
CHANGED
|
@@ -206,6 +206,13 @@ class TestServlet < WEBrick::HTTPServlet::AbstractServlet
|
|
|
206
206
|
end
|
|
207
207
|
|
|
208
208
|
module BugTestServerSetupTeardown
|
|
209
|
+
def unused_local_port
|
|
210
|
+
socket = TCPServer.new('127.0.0.1', 0)
|
|
211
|
+
socket.addr[1]
|
|
212
|
+
ensure
|
|
213
|
+
socket.close if socket
|
|
214
|
+
end
|
|
215
|
+
|
|
209
216
|
def setup
|
|
210
217
|
@port ||= 9992
|
|
211
218
|
@server = WEBrick::HTTPServer.new(:Port => @port, :Logger => WEBRICK_TEST_LOG, :AccessLog => [])
|
data/tests/tc_curl_easy.rb
CHANGED
|
@@ -123,6 +123,50 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
123
123
|
assert_equal "", easy.body_str.to_s
|
|
124
124
|
end
|
|
125
125
|
|
|
126
|
+
def test_head_request_restores_easy_state_after_callback_exception
|
|
127
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
128
|
+
easy.on_complete { raise "head complete blew up" }
|
|
129
|
+
|
|
130
|
+
error = assert_raise(Curl::Err::AbortedByCallbackError) { easy.http("HEAD") }
|
|
131
|
+
assert_equal "head complete blew up", error.message
|
|
132
|
+
|
|
133
|
+
easy.on_complete { |_curl| }
|
|
134
|
+
easy.perform
|
|
135
|
+
|
|
136
|
+
assert_equal "GET", easy.body_str
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def test_patch_request_does_not_leak_custom_method_to_next_perform
|
|
140
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
141
|
+
|
|
142
|
+
easy.http_patch("a=b")
|
|
143
|
+
assert_equal "PATCH\na=b", easy.body_str
|
|
144
|
+
|
|
145
|
+
easy.perform
|
|
146
|
+
assert_equal "GET", easy.body_str
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def test_put_request_does_not_leak_custom_method_to_next_perform
|
|
150
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
151
|
+
|
|
152
|
+
easy.http_put("payload")
|
|
153
|
+
assert_equal "PUT\npayload", easy.body_str
|
|
154
|
+
|
|
155
|
+
easy.perform
|
|
156
|
+
assert_equal "GET", easy.body_str
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def test_failed_put_data_setup_does_not_leave_easy_in_upload_mode
|
|
160
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
161
|
+
easy.headers = ["X-Test: true"]
|
|
162
|
+
|
|
163
|
+
assert_raise(RuntimeError) { easy.put_data = "payload" }
|
|
164
|
+
|
|
165
|
+
easy.headers = {}
|
|
166
|
+
easy.perform
|
|
167
|
+
assert_equal "GET", easy.body_str
|
|
168
|
+
end
|
|
169
|
+
|
|
126
170
|
def test_perform_releases_failed_implicit_multi_without_follow_up_easy_perform
|
|
127
171
|
Curl::Easy.flush_deferred_multi_closes
|
|
128
172
|
assert_equal 0, Curl::Easy.deferred_multi_closes.length
|
|
@@ -1376,6 +1420,15 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
1376
1420
|
assert_match(/message$/, curl.body_str)
|
|
1377
1421
|
end
|
|
1378
1422
|
|
|
1423
|
+
def test_put_data_from_to_s_object
|
|
1424
|
+
curl = Curl::Easy.new(TestServlet.url)
|
|
1425
|
+
curl.put_data = :message
|
|
1426
|
+
|
|
1427
|
+
curl.perform
|
|
1428
|
+
|
|
1429
|
+
assert_equal "PUT\nmessage", curl.body_str
|
|
1430
|
+
end
|
|
1431
|
+
|
|
1379
1432
|
def test_put_data_read_exception_sets_result_and_releases_callback_state
|
|
1380
1433
|
error_class = Class.new(StandardError)
|
|
1381
1434
|
reader_class = Class.new do
|
|
@@ -1507,6 +1560,10 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
|
1507
1560
|
# header value and encoding; skip the NTLM-specific assertion.
|
|
1508
1561
|
return
|
|
1509
1562
|
end
|
|
1563
|
+
# curl 8.20.0 disables NTLM by default and plans to remove it in
|
|
1564
|
+
# September 2026; libcurl silently downgrades to Basic when NTLM is
|
|
1565
|
+
# unavailable, so skip the NTLM-specific assertion in that case.
|
|
1566
|
+
return unless Curl.ntlm?
|
|
1510
1567
|
curl = Curl::Easy.new(TestServlet.url)
|
|
1511
1568
|
curl.username = "foo"
|
|
1512
1569
|
curl.password = "bar"
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
|
|
2
2
|
|
|
3
3
|
class TestCurbCurlEasyResolve < Test::Unit::TestCase
|
|
4
|
+
include TestServerMethods
|
|
5
|
+
|
|
4
6
|
def setup
|
|
7
|
+
server_setup
|
|
5
8
|
@easy = Curl::Easy.new
|
|
6
9
|
end
|
|
7
10
|
|
|
@@ -13,4 +16,33 @@ class TestCurbCurlEasyResolve < Test::Unit::TestCase
|
|
|
13
16
|
def test_empty_resolve
|
|
14
17
|
assert_equal @easy.resolve, nil
|
|
15
18
|
end
|
|
19
|
+
|
|
20
|
+
def test_setopt_resolve_persists_across_performs
|
|
21
|
+
host = "curb-setopt-resolve.invalid"
|
|
22
|
+
mapping = "#{host}:#{TestServlet.port}:127.0.0.1"
|
|
23
|
+
|
|
24
|
+
@easy.url = "http://#{host}:#{TestServlet.port}#{TestServlet.path}"
|
|
25
|
+
@easy.dns_cache_timeout = 0
|
|
26
|
+
@easy.set(:resolve, [mapping])
|
|
27
|
+
|
|
28
|
+
@easy.perform
|
|
29
|
+
assert_match(/GET/, @easy.body_str)
|
|
30
|
+
|
|
31
|
+
@easy.perform
|
|
32
|
+
assert_match(/GET/, @easy.body_str)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_resolve_entries_can_be_objects_that_convert_to_string
|
|
36
|
+
host = "curb-resolve-object.invalid"
|
|
37
|
+
mapping = "#{host}:#{TestServlet.port}:127.0.0.1"
|
|
38
|
+
entry = Object.new
|
|
39
|
+
entry.define_singleton_method(:to_s) { mapping }
|
|
40
|
+
|
|
41
|
+
@easy.url = "http://#{host}:#{TestServlet.port}#{TestServlet.path}"
|
|
42
|
+
@easy.dns_cache_timeout = 0
|
|
43
|
+
@easy.resolve = [entry]
|
|
44
|
+
|
|
45
|
+
@easy.perform
|
|
46
|
+
assert_match(/GET/, @easy.body_str)
|
|
47
|
+
end
|
|
16
48
|
end
|
data/tests/tc_curl_multi.rb
CHANGED
|
@@ -133,6 +133,62 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
|
133
133
|
Curl::Multi.autoclose = false
|
|
134
134
|
end
|
|
135
135
|
|
|
136
|
+
def test_close_inside_completion_callback_is_rejected_without_invalidating_multi
|
|
137
|
+
multi = Curl::Multi.new
|
|
138
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
139
|
+
|
|
140
|
+
easy.on_complete { multi.close }
|
|
141
|
+
multi.add(easy)
|
|
142
|
+
|
|
143
|
+
error = assert_raise(Curl::Err::AbortedByCallbackError) { multi.perform }
|
|
144
|
+
assert_match(/Cannot close an active Curl::Multi handle/, error.message)
|
|
145
|
+
assert multi.idle?
|
|
146
|
+
assert_equal({}, multi.requests)
|
|
147
|
+
|
|
148
|
+
easy.on_complete { |_curl| }
|
|
149
|
+
multi.add(easy)
|
|
150
|
+
multi.perform
|
|
151
|
+
|
|
152
|
+
assert_equal "GET", easy.body_str
|
|
153
|
+
ensure
|
|
154
|
+
multi.close if defined?(multi) && multi
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def test_close_inside_perform_block_is_rejected_without_invalidating_multi
|
|
158
|
+
multi = Curl::Multi.new
|
|
159
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
160
|
+
multi.add(easy)
|
|
161
|
+
|
|
162
|
+
error = assert_raise(RuntimeError) do
|
|
163
|
+
multi.perform { multi.close }
|
|
164
|
+
end
|
|
165
|
+
assert_match(/Cannot close an active Curl::Multi handle/, error.message)
|
|
166
|
+
|
|
167
|
+
assert_nothing_raised { multi.close }
|
|
168
|
+
multi = nil
|
|
169
|
+
ensure
|
|
170
|
+
multi.close if defined?(multi) && multi
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def test_active_easy_cannot_be_added_to_another_multi
|
|
174
|
+
first_multi = Curl::Multi.new
|
|
175
|
+
second_multi = Curl::Multi.new
|
|
176
|
+
easy = Curl::Easy.new(TestServlet.url)
|
|
177
|
+
|
|
178
|
+
first_multi.add(easy)
|
|
179
|
+
|
|
180
|
+
error = assert_raise(RuntimeError) { second_multi.add(easy) }
|
|
181
|
+
assert_match(/active Curl::Easy/, error.message)
|
|
182
|
+
assert_same first_multi, easy.multi
|
|
183
|
+
assert_equal easy, first_multi.requests[easy.object_id]
|
|
184
|
+
|
|
185
|
+
assert_nothing_raised { first_multi.close }
|
|
186
|
+
first_multi = nil
|
|
187
|
+
ensure
|
|
188
|
+
first_multi.close if defined?(first_multi) && first_multi
|
|
189
|
+
second_multi.close if defined?(second_multi) && second_multi
|
|
190
|
+
end
|
|
191
|
+
|
|
136
192
|
def test_close_makes_multi_unusable
|
|
137
193
|
multi = Curl::Multi.new
|
|
138
194
|
multi.close
|
data/tests/tc_ftp_options.rb
CHANGED
|
@@ -22,5 +22,18 @@ class TestCurbFtpOptions < Test::Unit::TestCase
|
|
|
22
22
|
assert_kind_of(Array, c.ftp_commands)
|
|
23
23
|
assert_equal ["PWD", "CWD /"], c.ftp_commands
|
|
24
24
|
end
|
|
25
|
-
end
|
|
26
25
|
|
|
26
|
+
def test_ftp_command_entries_can_be_objects_that_convert_to_string
|
|
27
|
+
command = Object.new
|
|
28
|
+
command.define_singleton_method(:to_s) { "PWD" }
|
|
29
|
+
|
|
30
|
+
c = Curl::Easy.new($TEST_URL)
|
|
31
|
+
c.ftp_commands = [command]
|
|
32
|
+
m = Curl::Multi.new
|
|
33
|
+
|
|
34
|
+
assert_nothing_raised { m.add(c) }
|
|
35
|
+
ensure
|
|
36
|
+
m.remove(c) if defined?(m) && m && m.requests[c.object_id]
|
|
37
|
+
m.close if defined?(m) && m
|
|
38
|
+
end
|
|
39
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: curb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.3.
|
|
4
|
+
version: 1.3.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ross Bamford
|
|
8
8
|
- Todd A. Fisher
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-05-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Curb (probably CUrl-RuBy or something) provides Ruby-language bindings
|
|
14
14
|
for the libcurl(3), a fully-featured client-side URL transfer library. cURL and
|