curb 1.0.6 → 1.0.8
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 +192 -20
- data/ext/curb_multi.c +44 -1
- data/ext/curb_postfield.c +92 -55
- data/ext/extconf.rb +2 -0
- data/lib/curl/easy.rb +26 -3
- data/lib/curl/multi.rb +1 -1
- data/tests/helper.rb +16 -1
- data/tests/tc_curl_easy.rb +67 -19
- data/tests/tc_curl_easy_cookielist.rb +277 -0
- data/tests/tc_curl_multi.rb +9 -9
- metadata +6 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 26faca47e47561b5ab762a32f58e2771db9a39c2482152fa1116a7082a27de91
|
4
|
+
data.tar.gz: a138509513b023f12537a1bff7fa11e6ef563cc5c59af860b73f119705ee1023
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4686d9ad528ba0f1931c95fa95d25266c8ffcc2214c3a5f527e437f30977f94bde4a48b38d27ad6342cc868c620e2fd4bd214baeae435725a1746d0c93a22aef
|
7
|
+
data.tar.gz: 2e4ba49a5906448e0ad381dad6017cf01140166ee78f1b025b9245cc79c6ff20b37ffd18abc61a56289e8bdeee0750a68f3d84369da36d360f7a61775da10566
|
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.0.
|
32
|
-
#define CURB_VER_NUM
|
31
|
+
#define CURB_VERSION "1.0.8"
|
32
|
+
#define CURB_VER_NUM 1008
|
33
33
|
#define CURB_VER_MAJ 1
|
34
34
|
#define CURB_VER_MIN 0
|
35
|
-
#define CURB_VER_MIC
|
35
|
+
#define CURB_VER_MIC 8
|
36
36
|
#define CURB_VER_PATCH 0
|
37
37
|
|
38
38
|
|
data/ext/curb_easy.c
CHANGED
@@ -31,6 +31,7 @@ static FILE * rb_io_stdio_file(rb_io_t *fptr) {
|
|
31
31
|
return fptr->f;
|
32
32
|
}
|
33
33
|
#endif
|
34
|
+
static struct curl_slist *duplicate_curl_slist(struct curl_slist *list);
|
34
35
|
|
35
36
|
/* ================== CURL HANDLER FUNCS ==============*/
|
36
37
|
|
@@ -264,7 +265,7 @@ void curl_easy_free(ruby_curl_easy *rbce) {
|
|
264
265
|
static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
|
265
266
|
rbce->opts = rb_hash_new();
|
266
267
|
|
267
|
-
memset(rbce->err_buf, 0,
|
268
|
+
memset(rbce->err_buf, 0, CURL_ERROR_SIZE);
|
268
269
|
|
269
270
|
rbce->curl_headers = NULL;
|
270
271
|
rbce->curl_proxy_headers = NULL;
|
@@ -370,6 +371,16 @@ static VALUE ruby_curl_easy_initialize(int argc, VALUE *argv, VALUE self) {
|
|
370
371
|
return self;
|
371
372
|
}
|
372
373
|
|
374
|
+
/* Helper to duplicate a curl_slist */
|
375
|
+
static struct curl_slist *duplicate_curl_slist(struct curl_slist *list) {
|
376
|
+
struct curl_slist *dup = NULL;
|
377
|
+
struct curl_slist *tmp;
|
378
|
+
for (tmp = list; tmp; tmp = tmp->next) {
|
379
|
+
dup = curl_slist_append(dup, tmp->data);
|
380
|
+
}
|
381
|
+
return dup;
|
382
|
+
}
|
383
|
+
|
373
384
|
/*
|
374
385
|
* call-seq:
|
375
386
|
* easy.clone => <easy clone>
|
@@ -384,14 +395,22 @@ static VALUE ruby_curl_easy_clone(VALUE self) {
|
|
384
395
|
Data_Get_Struct(self, ruby_curl_easy, rbce);
|
385
396
|
|
386
397
|
newrbce = ALLOC(ruby_curl_easy);
|
398
|
+
/* shallow copy */
|
387
399
|
memcpy(newrbce, rbce, sizeof(ruby_curl_easy));
|
400
|
+
|
401
|
+
/* now deep copy */
|
388
402
|
newrbce->curl = curl_easy_duphandle(rbce->curl);
|
389
|
-
newrbce->curl_headers = NULL;
|
390
|
-
newrbce->curl_proxy_headers = NULL;
|
391
|
-
newrbce->curl_ftp_commands = NULL;
|
392
|
-
newrbce->curl_resolve = NULL;
|
403
|
+
newrbce->curl_headers = (rbce->curl_headers) ? duplicate_curl_slist(rbce->curl_headers) : NULL;
|
404
|
+
newrbce->curl_proxy_headers = (rbce->curl_proxy_headers) ? duplicate_curl_slist(rbce->curl_proxy_headers) : NULL;
|
405
|
+
newrbce->curl_ftp_commands = (rbce->curl_ftp_commands) ? duplicate_curl_slist(rbce->curl_ftp_commands) : NULL;
|
406
|
+
newrbce->curl_resolve = (rbce->curl_resolve) ? duplicate_curl_slist(rbce->curl_resolve) : NULL;
|
393
407
|
|
394
|
-
|
408
|
+
if (rbce->opts != Qnil) {
|
409
|
+
newrbce->opts = rb_funcall(rbce->opts, rb_intern("dup"), 0);
|
410
|
+
}
|
411
|
+
|
412
|
+
/* Set the error buffer on the new curl handle using the new err_buf */
|
413
|
+
curl_easy_setopt(newrbce->curl, CURLOPT_ERRORBUFFER, newrbce->err_buf);
|
395
414
|
|
396
415
|
return Data_Wrap_Struct(cCurlEasy, curl_easy_mark, curl_easy_free, newrbce);
|
397
416
|
}
|
@@ -2614,7 +2633,7 @@ static VALUE ruby_curl_easy_perform_verb_str(VALUE self, const char *verb) {
|
|
2614
2633
|
Data_Get_Struct(self, ruby_curl_easy, rbce);
|
2615
2634
|
curl = rbce->curl;
|
2616
2635
|
|
2617
|
-
memset(rbce->err_buf, 0,
|
2636
|
+
memset(rbce->err_buf, 0, CURL_ERROR_SIZE);
|
2618
2637
|
|
2619
2638
|
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, verb);
|
2620
2639
|
|
@@ -2681,7 +2700,7 @@ static VALUE ruby_curl_easy_perform_post(int argc, VALUE *argv, VALUE self) {
|
|
2681
2700
|
Data_Get_Struct(self, ruby_curl_easy, rbce);
|
2682
2701
|
curl = rbce->curl;
|
2683
2702
|
|
2684
|
-
memset(rbce->err_buf, 0,
|
2703
|
+
memset(rbce->err_buf, 0, CURL_ERROR_SIZE);
|
2685
2704
|
|
2686
2705
|
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, NULL);
|
2687
2706
|
|
@@ -2739,6 +2758,88 @@ static VALUE ruby_curl_easy_perform_post(int argc, VALUE *argv, VALUE self) {
|
|
2739
2758
|
}
|
2740
2759
|
}
|
2741
2760
|
|
2761
|
+
/*
|
2762
|
+
* call-seq:
|
2763
|
+
* easy.http_patch("url=encoded%20form%20data;and=so%20on") => true
|
2764
|
+
* easy.http_patch("url=encoded%20form%20data", "and=so%20on", ...) => true
|
2765
|
+
* easy.http_patch("url=encoded%20form%20data", Curl::PostField, "and=so%20on", ...) => true
|
2766
|
+
* easy.http_patch(Curl::PostField, Curl::PostField ..., Curl::PostField) => true
|
2767
|
+
*
|
2768
|
+
* PATCH the specified formdata to the currently configured URL using
|
2769
|
+
* the current options set for this Curl::Easy instance. This method
|
2770
|
+
* always returns true, or raises an exception (defined under
|
2771
|
+
* Curl::Err) on error.
|
2772
|
+
*
|
2773
|
+
* When multipart_form_post is true the multipart logic is used; otherwise,
|
2774
|
+
* the arguments are joined into a raw PATCH body.
|
2775
|
+
*/
|
2776
|
+
static VALUE ruby_curl_easy_perform_patch(int argc, VALUE *argv, VALUE self) {
|
2777
|
+
ruby_curl_easy *rbce;
|
2778
|
+
CURL *curl;
|
2779
|
+
int i;
|
2780
|
+
VALUE args_ary;
|
2781
|
+
|
2782
|
+
rb_scan_args(argc, argv, "*", &args_ary);
|
2783
|
+
Data_Get_Struct(self, ruby_curl_easy, rbce);
|
2784
|
+
curl = rbce->curl;
|
2785
|
+
|
2786
|
+
/* Clear the error buffer */
|
2787
|
+
memset(rbce->err_buf, 0, CURL_ERROR_SIZE);
|
2788
|
+
|
2789
|
+
/* Set the custom HTTP method to PATCH */
|
2790
|
+
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH");
|
2791
|
+
|
2792
|
+
if (rbce->multipart_form_post) {
|
2793
|
+
VALUE ret;
|
2794
|
+
struct curl_httppost *first = NULL, *last = NULL;
|
2795
|
+
|
2796
|
+
/* Build the multipart form (same logic as for POST) */
|
2797
|
+
for (i = 0; i < argc; i++) {
|
2798
|
+
if (rb_obj_is_instance_of(argv[i], cCurlPostField)) {
|
2799
|
+
append_to_form(argv[i], &first, &last);
|
2800
|
+
} else if (rb_type(argv[i]) == T_ARRAY) {
|
2801
|
+
long j, argv_len = RARRAY_LEN(argv[i]);
|
2802
|
+
for (j = 0; j < argv_len; ++j) {
|
2803
|
+
if (rb_obj_is_instance_of(rb_ary_entry(argv[i], j), cCurlPostField)) {
|
2804
|
+
append_to_form(rb_ary_entry(argv[i], j), &first, &last);
|
2805
|
+
} else {
|
2806
|
+
rb_raise(eCurlErrInvalidPostField,
|
2807
|
+
"You must use PostFields only with multipart form posts");
|
2808
|
+
return Qnil;
|
2809
|
+
}
|
2810
|
+
}
|
2811
|
+
} else {
|
2812
|
+
rb_raise(eCurlErrInvalidPostField,
|
2813
|
+
"You must use PostFields only with multipart form posts");
|
2814
|
+
return Qnil;
|
2815
|
+
}
|
2816
|
+
}
|
2817
|
+
/* Disable the POST flag */
|
2818
|
+
curl_easy_setopt(curl, CURLOPT_POST, 0);
|
2819
|
+
/* Use the built multipart form as the request body */
|
2820
|
+
curl_easy_setopt(curl, CURLOPT_HTTPPOST, first);
|
2821
|
+
ret = rb_funcall(self, rb_intern("perform"), 0);
|
2822
|
+
curl_formfree(first);
|
2823
|
+
return ret;
|
2824
|
+
} else {
|
2825
|
+
/* Join arguments into a raw PATCH body */
|
2826
|
+
VALUE patch_body = rb_funcall(args_ary, idJoin, 1, rbstrAmp);
|
2827
|
+
if (patch_body == Qnil) {
|
2828
|
+
rb_raise(eCurlErrError, "Failed to join arguments");
|
2829
|
+
return Qnil;
|
2830
|
+
} else {
|
2831
|
+
if (rb_type(patch_body) == T_STRING && RSTRING_LEN(patch_body) > 0) {
|
2832
|
+
ruby_curl_easy_post_body_set(self, patch_body);
|
2833
|
+
}
|
2834
|
+
/* If postdata_buffer is still nil, set it so that the PATCH header is enabled */
|
2835
|
+
if (rb_easy_nil("postdata_buffer")) {
|
2836
|
+
ruby_curl_easy_post_body_set(self, patch_body);
|
2837
|
+
}
|
2838
|
+
return rb_funcall(self, rb_intern("perform"), 0);
|
2839
|
+
}
|
2840
|
+
}
|
2841
|
+
}
|
2842
|
+
|
2742
2843
|
/*
|
2743
2844
|
* call-seq:
|
2744
2845
|
* easy.http_put(data) => true
|
@@ -2747,18 +2848,70 @@ static VALUE ruby_curl_easy_perform_post(int argc, VALUE *argv, VALUE self) {
|
|
2747
2848
|
* current options set for this Curl::Easy instance. This method always
|
2748
2849
|
* returns true, or raises an exception (defined under Curl::Err) on error.
|
2749
2850
|
*/
|
2750
|
-
static VALUE ruby_curl_easy_perform_put(VALUE
|
2851
|
+
static VALUE ruby_curl_easy_perform_put(int argc, VALUE *argv, VALUE self) {
|
2751
2852
|
ruby_curl_easy *rbce;
|
2752
2853
|
CURL *curl;
|
2854
|
+
VALUE args_ary;
|
2855
|
+
int i;
|
2753
2856
|
|
2857
|
+
rb_scan_args(argc, argv, "*", &args_ary);
|
2754
2858
|
Data_Get_Struct(self, ruby_curl_easy, rbce);
|
2755
2859
|
curl = rbce->curl;
|
2756
2860
|
|
2757
|
-
memset(rbce->err_buf, 0,
|
2758
|
-
|
2759
|
-
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, NULL);
|
2760
|
-
ruby_curl_easy_put_data_set(self, data);
|
2861
|
+
memset(rbce->err_buf, 0, CURL_ERROR_SIZE);
|
2862
|
+
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
|
2761
2863
|
|
2864
|
+
/* New: if no arguments were provided, treat as an empty PUT */
|
2865
|
+
if (RARRAY_LEN(args_ary) == 0) {
|
2866
|
+
/* Option 1: explicitly set an empty body */
|
2867
|
+
ruby_curl_easy_put_data_set(self, rb_str_new2(""));
|
2868
|
+
}
|
2869
|
+
/* If a single argument is given and it is a String or responds to read, use legacy behavior */
|
2870
|
+
else if (RARRAY_LEN(args_ary) == 1 &&
|
2871
|
+
(rb_type(rb_ary_entry(args_ary, 0)) == T_STRING ||
|
2872
|
+
rb_respond_to(rb_ary_entry(args_ary, 0), rb_intern("read")))) {
|
2873
|
+
ruby_curl_easy_put_data_set(self, rb_ary_entry(args_ary, 0));
|
2874
|
+
}
|
2875
|
+
/* Otherwise, if multipart_form_post is true, use multipart logic */
|
2876
|
+
else if (rbce->multipart_form_post) {
|
2877
|
+
VALUE ret;
|
2878
|
+
struct curl_httppost *first = NULL, *last = NULL;
|
2879
|
+
for (i = 0; i < RARRAY_LEN(args_ary); i++) {
|
2880
|
+
VALUE field = rb_ary_entry(args_ary, i);
|
2881
|
+
if (rb_obj_is_instance_of(field, cCurlPostField)) {
|
2882
|
+
append_to_form(field, &first, &last);
|
2883
|
+
} else if (rb_type(field) == T_ARRAY) {
|
2884
|
+
long j;
|
2885
|
+
for (j = 0; j < RARRAY_LEN(field); j++) {
|
2886
|
+
VALUE subfield = rb_ary_entry(field, j);
|
2887
|
+
if (rb_obj_is_instance_of(subfield, cCurlPostField)) {
|
2888
|
+
append_to_form(subfield, &first, &last);
|
2889
|
+
} else {
|
2890
|
+
rb_raise(eCurlErrInvalidPostField,
|
2891
|
+
"You must use PostFields only with multipart form posts");
|
2892
|
+
}
|
2893
|
+
}
|
2894
|
+
} else {
|
2895
|
+
rb_raise(eCurlErrInvalidPostField,
|
2896
|
+
"You must use PostFields only with multipart form posts");
|
2897
|
+
}
|
2898
|
+
}
|
2899
|
+
curl_easy_setopt(curl, CURLOPT_POST, 0);
|
2900
|
+
curl_easy_setopt(curl, CURLOPT_HTTPPOST, first);
|
2901
|
+
/* Set the method explicitly to PUT */
|
2902
|
+
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
|
2903
|
+
ret = rb_funcall(self, rb_intern("perform"), 0);
|
2904
|
+
curl_formfree(first);
|
2905
|
+
return ret;
|
2906
|
+
}
|
2907
|
+
/* Fallback: join all arguments */
|
2908
|
+
else {
|
2909
|
+
VALUE post_body = rb_funcall(args_ary, idJoin, 1, rbstrAmp);
|
2910
|
+
if (post_body != Qnil && rb_type(post_body) == T_STRING &&
|
2911
|
+
RSTRING_LEN(post_body) > 0) {
|
2912
|
+
ruby_curl_easy_put_data_set(self, post_body);
|
2913
|
+
}
|
2914
|
+
}
|
2762
2915
|
return rb_funcall(self, rb_intern("perform"), 0);
|
2763
2916
|
}
|
2764
2917
|
|
@@ -3364,14 +3517,16 @@ static VALUE ruby_curl_easy_num_connects_get(VALUE self) {
|
|
3364
3517
|
|
3365
3518
|
/*
|
3366
3519
|
* call-seq:
|
3367
|
-
* easy.cookielist =>
|
3520
|
+
* easy.cookielist => cookielist
|
3368
3521
|
*
|
3369
3522
|
* Retrieves the cookies curl knows in an array of strings.
|
3370
3523
|
* Returned strings are in Netscape cookiejar format or in Set-Cookie format.
|
3524
|
+
* Since 7.43.0 cookies in the Set-Cookie format without a domain name are not exported.
|
3371
3525
|
*
|
3372
|
-
*
|
3373
|
-
*
|
3374
|
-
*
|
3526
|
+
* @see https://curl.se/libcurl/c/CURLINFO_COOKIELIST.html option <code>CURLINFO_COOKIELIST</code> of
|
3527
|
+
* <code>curl_easy_getopt(3)</code> to see how libcurl behaves.
|
3528
|
+
* @note requires libcurl 7.14.1 or higher, otherwise +-1+ is always returned
|
3529
|
+
* @return [Array<String>, nil, -1] array of strings, or +nil+ if there are no cookies, or +-1+ if the libcurl version is too old
|
3375
3530
|
*/
|
3376
3531
|
static VALUE ruby_curl_easy_cookielist_get(VALUE self) {
|
3377
3532
|
#ifdef HAVE_CURLINFO_COOKIELIST
|
@@ -3482,9 +3637,16 @@ static VALUE ruby_curl_easy_last_error(VALUE self) {
|
|
3482
3637
|
|
3483
3638
|
/*
|
3484
3639
|
* call-seq:
|
3485
|
-
* easy.setopt
|
3640
|
+
* easy.setopt(opt, val) => val
|
3486
3641
|
*
|
3487
3642
|
* Initial access to libcurl curl_easy_setopt
|
3643
|
+
*
|
3644
|
+
* @param [Fixnum] opt The option to set, see +Curl::CURLOPT_*+ constants
|
3645
|
+
* @param [Object] val
|
3646
|
+
* @return [Object] val
|
3647
|
+
* @raise [TypeError] if the option is not supported
|
3648
|
+
* @note Some options - like url or cookie - aren't set directly throught +curl_easy_setopt+, but stored in the Ruby object state.
|
3649
|
+
* @note When +curl_easy_setopt+ is called, return value is not checked here.
|
3488
3650
|
*/
|
3489
3651
|
static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
|
3490
3652
|
ruby_curl_easy *rbce;
|
@@ -3650,6 +3812,11 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
|
|
3650
3812
|
curl_easy_setopt(rbce->curl, CURLOPT_SSL_SESSIONID_CACHE, NUM2LONG(val));
|
3651
3813
|
break;
|
3652
3814
|
#endif
|
3815
|
+
#if HAVE_CURLOPT_COOKIELIST
|
3816
|
+
case CURLOPT_COOKIELIST: {
|
3817
|
+
curl_easy_setopt(rbce->curl, CURLOPT_COOKIELIST, StringValueCStr(val));
|
3818
|
+
} break;
|
3819
|
+
#endif
|
3653
3820
|
#if HAVE_CURLOPT_PROXY_SSL_VERIFYHOST
|
3654
3821
|
case CURLOPT_PROXY_SSL_VERIFYHOST:
|
3655
3822
|
curl_easy_setopt(rbce->curl, CURLOPT_PROXY_SSL_VERIFYHOST, NUM2LONG(val));
|
@@ -3664,9 +3831,13 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
|
|
3664
3831
|
|
3665
3832
|
/*
|
3666
3833
|
* call-seq:
|
3667
|
-
* easy.getinfo
|
3834
|
+
* easy.getinfo(opt) => nil
|
3668
3835
|
*
|
3669
3836
|
* Iniital access to libcurl curl_easy_getinfo, remember getinfo doesn't return the same values as setopt
|
3837
|
+
*
|
3838
|
+
* @note This method is not implemented yet.
|
3839
|
+
* @param [Fixnum] code Constant +CURLINFO_*+ from libcurl
|
3840
|
+
* @return [nil]
|
3670
3841
|
*/
|
3671
3842
|
static VALUE ruby_curl_easy_get_opt(VALUE self, VALUE opt) {
|
3672
3843
|
return Qnil;
|
@@ -3909,7 +4080,8 @@ void init_curb_easy() {
|
|
3909
4080
|
|
3910
4081
|
rb_define_method(cCurlEasy, "http", ruby_curl_easy_perform_verb, 1);
|
3911
4082
|
rb_define_method(cCurlEasy, "http_post", ruby_curl_easy_perform_post, -1);
|
3912
|
-
rb_define_method(cCurlEasy, "http_put", ruby_curl_easy_perform_put, 1);
|
4083
|
+
rb_define_method(cCurlEasy, "http_put", ruby_curl_easy_perform_put, -1);
|
4084
|
+
rb_define_method(cCurlEasy, "http_patch", ruby_curl_easy_perform_patch, -1);
|
3913
4085
|
|
3914
4086
|
/* Post-perform info methods */
|
3915
4087
|
rb_define_method(cCurlEasy, "body_str", ruby_curl_easy_body_str_get, 0);
|
data/ext/curb_multi.c
CHANGED
@@ -27,6 +27,21 @@
|
|
27
27
|
#include <fcntl.h>
|
28
28
|
#endif
|
29
29
|
|
30
|
+
#ifdef HAVE_CURL_MULTI_WAIT
|
31
|
+
#include <stdint.h> /* for intptr_t */
|
32
|
+
|
33
|
+
struct wait_args {
|
34
|
+
CURLM *handle;
|
35
|
+
long timeout_ms;
|
36
|
+
int numfds;
|
37
|
+
};
|
38
|
+
static void *curl_multi_wait_wrapper(void *p) {
|
39
|
+
struct wait_args *args = p;
|
40
|
+
CURLMcode code = curl_multi_wait(args->handle, NULL, 0, args->timeout_ms, &args->numfds);
|
41
|
+
return (void *)(intptr_t)code;
|
42
|
+
}
|
43
|
+
#endif
|
44
|
+
|
30
45
|
extern VALUE mCurl;
|
31
46
|
static VALUE idCall;
|
32
47
|
|
@@ -226,6 +241,8 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
|
226
241
|
|
227
242
|
mcode = curl_multi_add_handle(rbcm->handle, rbce->curl);
|
228
243
|
if (mcode != CURLM_CALL_MULTI_PERFORM && mcode != CURLM_OK) {
|
244
|
+
ruby_curl_easy_cleanup(easy, rbce);
|
245
|
+
|
229
246
|
raise_curl_multi_error_exception(mcode);
|
230
247
|
}
|
231
248
|
|
@@ -264,6 +281,7 @@ VALUE ruby_curl_multi_remove(VALUE self, VALUE rb_easy_handle) {
|
|
264
281
|
|
265
282
|
return self;
|
266
283
|
}
|
284
|
+
|
267
285
|
static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
|
268
286
|
CURLMcode result;
|
269
287
|
ruby_curl_easy *rbce;
|
@@ -528,7 +546,6 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
528
546
|
|
529
547
|
do {
|
530
548
|
while (rbcm->running) {
|
531
|
-
|
532
549
|
#ifdef HAVE_CURL_MULTI_TIMEOUT
|
533
550
|
/* get the curl suggested time out */
|
534
551
|
mcode = curl_multi_timeout(rbcm->handle, &timeout_milliseconds);
|
@@ -552,6 +569,31 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
552
569
|
/* or buggy versions libcurl sometimes reports huge timeouts... let's cap it */
|
553
570
|
}
|
554
571
|
|
572
|
+
#ifdef HAVE_CURL_MULTI_WAIT
|
573
|
+
{
|
574
|
+
struct wait_args wait_args;
|
575
|
+
wait_args.handle = rbcm->handle;
|
576
|
+
wait_args.timeout_ms = timeout_milliseconds;
|
577
|
+
wait_args.numfds = 0;
|
578
|
+
#if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
|
579
|
+
CURLMcode wait_rc = (CURLMcode)(intptr_t)
|
580
|
+
rb_thread_call_without_gvl(curl_multi_wait_wrapper, &wait_args, RUBY_UBF_IO, NULL);
|
581
|
+
#else
|
582
|
+
CURLMcode wait_rc = curl_multi_wait(rbcm->handle, NULL, 0, timeout_milliseconds, &wait_args.numfds);
|
583
|
+
#endif
|
584
|
+
if (wait_rc != CURLM_OK) {
|
585
|
+
raise_curl_multi_error_exception(wait_rc);
|
586
|
+
}
|
587
|
+
if (wait_args.numfds == 0) {
|
588
|
+
rb_thread_wait_for(tv_100ms);
|
589
|
+
}
|
590
|
+
/* Process pending transfers after waiting */
|
591
|
+
rb_curl_multi_run(self, rbcm->handle, &(rbcm->running));
|
592
|
+
rb_curl_multi_read_info(self, rbcm->handle);
|
593
|
+
if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
|
594
|
+
}
|
595
|
+
#else
|
596
|
+
|
555
597
|
tv.tv_sec = 0; /* never wait longer than 1 second */
|
556
598
|
tv.tv_usec = (int)(timeout_milliseconds * 1000); /* XXX: int is the right type for OSX, what about linux? */
|
557
599
|
|
@@ -618,6 +660,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
618
660
|
if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
|
619
661
|
break;
|
620
662
|
}
|
663
|
+
#endif /* HAVE_CURL_MULTI_WAIT */
|
621
664
|
}
|
622
665
|
|
623
666
|
} while( rbcm->running );
|
data/ext/curb_postfield.c
CHANGED
@@ -421,71 +421,108 @@ static VALUE ruby_curl_postfield_content_proc_set(int argc, VALUE *argv, VALUE s
|
|
421
421
|
* Only content fields may be converted to strings.
|
422
422
|
*/
|
423
423
|
static VALUE ruby_curl_postfield_to_str(VALUE self) {
|
424
|
-
// FIXME This is using the deprecated curl_escape func
|
425
424
|
ruby_curl_postfield *rbcpf;
|
426
425
|
VALUE result = Qnil;
|
427
426
|
VALUE name = Qnil;
|
428
427
|
char *tmpchrs;
|
429
|
-
|
428
|
+
#ifdef HAVE_CURL_EASY_ESCAPE
|
429
|
+
CURL *curl_handle = NULL;
|
430
|
+
#endif
|
431
|
+
|
430
432
|
Data_Get_Struct(self, ruby_curl_postfield, rbcpf);
|
431
433
|
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
} else if (rb_respond_to(name,rb_intern("to_s"))) {
|
434
|
+
if (rbcpf->name != Qnil) {
|
435
|
+
name = rbcpf->name;
|
436
|
+
if (TYPE(name) != T_STRING) {
|
437
|
+
if (rb_respond_to(name, rb_intern("to_s")))
|
437
438
|
name = rb_funcall(name, rb_intern("to_s"), 0);
|
438
|
-
|
439
|
-
|
440
|
-
name = Qnil; // we can't handle this object
|
441
|
-
}
|
442
|
-
}
|
443
|
-
if (name == Qnil) {
|
444
|
-
rb_raise(eCurlErrInvalidPostField, "Cannot convert unnamed field to string %s:%d, make sure your field name responds_to :to_s", __FILE__, __LINE__);
|
439
|
+
else
|
440
|
+
name = Qnil;
|
445
441
|
}
|
442
|
+
}
|
443
|
+
if (name == Qnil) {
|
444
|
+
rb_raise(eCurlErrInvalidPostField,
|
445
|
+
"Cannot convert unnamed field to string %s:%d, make sure your field name responds_to :to_s",
|
446
|
+
__FILE__, __LINE__);
|
447
|
+
}
|
448
|
+
/* Force field name to UTF-8 before escaping */
|
449
|
+
VALUE name_utf8 = rb_str_export_to_enc(name, rb_utf8_encoding());
|
446
450
|
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
451
|
+
#ifdef HAVE_CURL_EASY_ESCAPE
|
452
|
+
curl_handle = curl_easy_init();
|
453
|
+
if (!curl_handle) {
|
454
|
+
rb_raise(eCurlErrInvalidPostField, "Failed to initialize curl handle for escaping");
|
455
|
+
}
|
456
|
+
tmpchrs = curl_easy_escape(curl_handle, StringValuePtr(name_utf8), (int)RSTRING_LEN(name_utf8));
|
457
|
+
if (!tmpchrs) {
|
458
|
+
curl_easy_cleanup(curl_handle);
|
459
|
+
rb_raise(eCurlErrInvalidPostField, "Failed to url-encode name");
|
460
|
+
}
|
461
|
+
#else
|
462
|
+
tmpchrs = curl_escape(StringValuePtr(name_utf8), (int)RSTRING_LEN(name_utf8));
|
463
|
+
if (!tmpchrs) {
|
464
|
+
rb_raise(eCurlErrInvalidPostField, "Failed to url-encode name");
|
465
|
+
}
|
466
|
+
#endif
|
467
|
+
|
468
|
+
VALUE escd_name = rb_str_new2(tmpchrs);
|
469
|
+
#ifdef HAVE_CURL_EASY_ESCAPE
|
470
|
+
curl_free(tmpchrs);
|
471
|
+
#else
|
472
|
+
curl_free(tmpchrs);
|
473
|
+
#endif
|
474
|
+
|
475
|
+
VALUE tmpcontent = Qnil;
|
476
|
+
if (rbcpf->content_proc != Qnil) {
|
477
|
+
tmpcontent = rb_funcall(rbcpf->content_proc, idCall, 1, self);
|
478
|
+
} else if (rbcpf->content != Qnil) {
|
479
|
+
tmpcontent = rbcpf->content;
|
480
|
+
} else if (rbcpf->local_file != Qnil) {
|
481
|
+
tmpcontent = rbcpf->local_file;
|
482
|
+
} else if (rbcpf->remote_file != Qnil) {
|
483
|
+
tmpcontent = rbcpf->remote_file;
|
484
|
+
} else {
|
485
|
+
tmpcontent = rb_str_new2("");
|
486
|
+
}
|
487
|
+
if (TYPE(tmpcontent) != T_STRING) {
|
488
|
+
if (rb_respond_to(tmpcontent, rb_intern("to_s")))
|
489
|
+
tmpcontent = rb_funcall(tmpcontent, rb_intern("to_s"), 0);
|
490
|
+
else {
|
491
|
+
#ifdef HAVE_CURL_EASY_ESCAPE
|
492
|
+
curl_easy_cleanup(curl_handle);
|
493
|
+
#endif
|
494
|
+
rb_raise(rb_eRuntimeError,
|
495
|
+
"postfield(%s) is not a string and does not respond_to to_s",
|
496
|
+
RSTRING_PTR(escd_name));
|
487
497
|
}
|
488
|
-
|
498
|
+
}
|
499
|
+
/* Force content to UTF-8 before escaping */
|
500
|
+
VALUE content_utf8 = rb_str_export_to_enc(tmpcontent, rb_utf8_encoding());
|
501
|
+
#ifdef HAVE_CURL_EASY_ESCAPE
|
502
|
+
tmpchrs = curl_easy_escape(curl_handle, StringValuePtr(content_utf8), (int)RSTRING_LEN(content_utf8));
|
503
|
+
if (!tmpchrs) {
|
504
|
+
curl_easy_cleanup(curl_handle);
|
505
|
+
rb_raise(eCurlErrInvalidPostField, "Failed to url-encode content");
|
506
|
+
}
|
507
|
+
#else
|
508
|
+
tmpchrs = curl_escape(StringValuePtr(content_utf8), (int)RSTRING_LEN(content_utf8));
|
509
|
+
if (!tmpchrs) {
|
510
|
+
rb_raise(eCurlErrInvalidPostField, "Failed to url-encode content");
|
511
|
+
}
|
512
|
+
#endif
|
513
|
+
|
514
|
+
VALUE escd_content = rb_str_new2(tmpchrs);
|
515
|
+
#ifdef HAVE_CURL_EASY_ESCAPE
|
516
|
+
curl_free(tmpchrs);
|
517
|
+
curl_easy_cleanup(curl_handle);
|
518
|
+
#else
|
519
|
+
curl_free(tmpchrs);
|
520
|
+
#endif
|
521
|
+
|
522
|
+
result = escd_name;
|
523
|
+
rb_str_cat(result, "=", 1);
|
524
|
+
rb_str_concat(result, escd_content);
|
525
|
+
|
489
526
|
return result;
|
490
527
|
}
|
491
528
|
|
data/ext/extconf.rb
CHANGED
@@ -467,6 +467,8 @@ have_func('rb_thread_blocking_region')
|
|
467
467
|
have_header('ruby/thread.h') && have_func('rb_thread_call_without_gvl', 'ruby/thread.h')
|
468
468
|
have_header('ruby/io.h')
|
469
469
|
have_func('rb_io_stdio_file')
|
470
|
+
have_func('curl_multi_wait')
|
471
|
+
have_func('curl_easy_duphandle')
|
470
472
|
|
471
473
|
create_header('curb_config.h')
|
472
474
|
create_makefile('curb_core')
|
data/lib/curl/easy.rb
CHANGED
@@ -374,13 +374,36 @@ module Curl
|
|
374
374
|
#
|
375
375
|
# call-seq:
|
376
376
|
# Curl::Easy.http_put(url, data) {|c| ... }
|
377
|
+
# Curl::Easy.http_put(url, "some=urlencoded%20form%20data&and=so%20on") => true
|
378
|
+
# Curl::Easy.http_put(url, "some=urlencoded%20form%20data", "and=so%20on", ...) => true
|
379
|
+
# Curl::Easy.http_put(url, "some=urlencoded%20form%20data", Curl::PostField, "and=so%20on", ...) => true
|
380
|
+
# Curl::Easy.http_put(url, Curl::PostField, Curl::PostField ..., Curl::PostField) => true
|
377
381
|
#
|
378
382
|
# see easy.http_put
|
379
383
|
#
|
380
|
-
def http_put(
|
381
|
-
|
384
|
+
def http_put(*args)
|
385
|
+
url = args.shift
|
386
|
+
c = Curl::Easy.new(url)
|
387
|
+
yield c if block_given?
|
388
|
+
c.http_put(*args)
|
389
|
+
c
|
390
|
+
end
|
391
|
+
|
392
|
+
#
|
393
|
+
# call-seq:
|
394
|
+
# Curl::Easy.http_patch(url, data) {|c| ... }
|
395
|
+
# Curl::Easy.http_patch(url, "some=urlencoded%20form%20data&and=so%20on") => true
|
396
|
+
# Curl::Easy.http_patch(url, "some=urlencoded%20form%20data", "and=so%20on", ...) => true
|
397
|
+
# Curl::Easy.http_patch(url, "some=urlencoded%20form%20data", Curl::PostField, "and=so%20on", ...) => true
|
398
|
+
# Curl::Easy.http_patch(url, Curl::PostField, Curl::PostField ..., Curl::PostField) => true
|
399
|
+
#
|
400
|
+
# see easy.http_patch
|
401
|
+
#
|
402
|
+
def http_patch(*args)
|
403
|
+
url = args.shift
|
404
|
+
c = Curl::Easy.new(url)
|
382
405
|
yield c if block_given?
|
383
|
-
c.
|
406
|
+
c.http_patch(*args)
|
384
407
|
c
|
385
408
|
end
|
386
409
|
|
data/lib/curl/multi.rb
CHANGED
@@ -95,7 +95,7 @@ module Curl
|
|
95
95
|
|
96
96
|
# configure the multi handle
|
97
97
|
multi_options.each { |k,v| m.send("#{k}=", v) }
|
98
|
-
callbacks = [:on_progress,:on_debug,:on_failure,:on_success,:on_redirect,:on_body,:on_header]
|
98
|
+
callbacks = [:on_progress,:on_debug,:on_failure,:on_success,:on_redirect,:on_missing,:on_body,:on_header]
|
99
99
|
|
100
100
|
add_free_handle = proc do|conf, easy|
|
101
101
|
c = conf.dup # avoid being destructive to input
|
data/tests/helper.rb
CHANGED
@@ -80,6 +80,10 @@ class TestServlet < WEBrick::HTTPServlet::AbstractServlet
|
|
80
80
|
res.status = 404
|
81
81
|
elsif req.path.match(/error$/)
|
82
82
|
res.status = 500
|
83
|
+
elsif req.path.match(/get_cookies$/)
|
84
|
+
res['Content-Type'] = "text/plain"
|
85
|
+
res.body = req['Cookie']
|
86
|
+
return
|
83
87
|
end
|
84
88
|
respond_with("GET#{req.query_string}",req,res)
|
85
89
|
end
|
@@ -90,7 +94,18 @@ class TestServlet < WEBrick::HTTPServlet::AbstractServlet
|
|
90
94
|
end
|
91
95
|
|
92
96
|
def do_POST(req,res)
|
93
|
-
if req.
|
97
|
+
if req.path.match(/set_cookies$/)
|
98
|
+
JSON.parse(req.body || '[]', symbolize_names: true).each do |hash|
|
99
|
+
cookie = WEBrick::Cookie.new(hash.fetch(:name), hash.fetch(:value))
|
100
|
+
cookie.domain = hash[:domain] if hash.key?(:domain)
|
101
|
+
cookie.expires = hash[:expires] if hash.key?(:expires)
|
102
|
+
cookie.path = hash[:path] if hash.key?(:path)
|
103
|
+
cookie.secure = hash[:secure] if hash.key?(:secure)
|
104
|
+
cookie.max_age = hash[:max_age] if hash.key?(:max_age)
|
105
|
+
res.cookies.push(cookie)
|
106
|
+
end
|
107
|
+
respond_with('OK', req, res)
|
108
|
+
elsif req.query['filename'].nil?
|
94
109
|
if req.body
|
95
110
|
params = {}
|
96
111
|
req.body.split('&').map{|s| k,v=s.split('='); params[k] = v }
|
data/tests/tc_curl_easy.rb
CHANGED
@@ -110,7 +110,7 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
110
110
|
end
|
111
111
|
|
112
112
|
def test_class_perform_02
|
113
|
-
data =
|
113
|
+
data = String.new
|
114
114
|
assert_instance_of Curl::Easy, c = Curl::Easy.perform($TEST_URL) { |curl| curl.on_body { |d| data << d; d.length } }
|
115
115
|
|
116
116
|
assert_nil c.body_str
|
@@ -211,7 +211,7 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
211
211
|
end
|
212
212
|
|
213
213
|
def test_get_02
|
214
|
-
data =
|
214
|
+
data = String.new
|
215
215
|
c = Curl::Easy.new($TEST_URL) do |curl|
|
216
216
|
curl.on_body { |d| data << d; d.length }
|
217
217
|
end
|
@@ -765,20 +765,27 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
765
765
|
assert_equal 'GET', curl.body_str
|
766
766
|
end
|
767
767
|
|
768
|
-
def
|
768
|
+
def test_remote
|
769
769
|
curl = Curl::Easy.new(TestServlet.url)
|
770
770
|
curl.http_post([Curl::PostField.content('document_id', 5)])
|
771
771
|
assert_equal "POST\ndocument_id=5", curl.unescape(curl.body_str)
|
772
|
+
|
773
|
+
curl.http_put([Curl::PostField.content('document_id', 5)])
|
774
|
+
assert_equal "PUT\ndocument_id=5", curl.unescape(curl.body_str)
|
775
|
+
|
776
|
+
curl.http_patch([Curl::PostField.content('document_id', 5)])
|
777
|
+
assert_equal "PATCH\ndocument_id=5", curl.unescape(curl.body_str)
|
772
778
|
end
|
773
779
|
|
774
|
-
def
|
780
|
+
def test_remote_is_easy_handle
|
775
781
|
# see: http://pastie.org/560852 and
|
776
782
|
# http://groups.google.com/group/curb---ruby-libcurl-bindings/browse_thread/thread/216bb2d9b037f347?hl=en
|
777
|
-
[:post, :get, :head, :delete].each do |method|
|
783
|
+
[:post, :patch, :put, :get, :head, :delete].each do |method|
|
778
784
|
retries = 0
|
779
785
|
begin
|
780
786
|
count = 0
|
781
|
-
|
787
|
+
m = "http_#{method}".to_sym
|
788
|
+
Curl::Easy.send(m, TestServlet.url) do|c|
|
782
789
|
count += 1
|
783
790
|
assert_equal Curl::Easy, c.class
|
784
791
|
end
|
@@ -787,12 +794,14 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
787
794
|
retries+=1
|
788
795
|
retry if retries < 3
|
789
796
|
raise e
|
797
|
+
rescue ArgumentError => e
|
798
|
+
assert false, "#{m} - #{e.message}"
|
790
799
|
end
|
791
800
|
end
|
792
801
|
end
|
793
802
|
|
794
803
|
# see: https://github.com/rvanlieshout/curb/commit/8bcdefddc0162484681ebd1a92d52a642666a445
|
795
|
-
def
|
804
|
+
def test_multipart_array_remote
|
796
805
|
curl = Curl::Easy.new(TestServlet.url)
|
797
806
|
curl.multipart_form_post = true
|
798
807
|
fields = [
|
@@ -802,9 +811,23 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
802
811
|
curl.http_post(fields)
|
803
812
|
assert_match(/HTTP POST file upload/, curl.body_str)
|
804
813
|
assert_match(/Content-Disposition: form-data/, curl.body_str)
|
814
|
+
|
815
|
+
curl = Curl::Easy.new(TestServlet.url)
|
816
|
+
curl.multipart_form_post = true
|
817
|
+
fields = [
|
818
|
+
Curl::PostField.file('foo', File.expand_path(File.join(File.dirname(__FILE__),'..','README.markdown'))),
|
819
|
+
Curl::PostField.file('bar', File.expand_path(File.join(File.dirname(__FILE__),'..','README.markdown')))
|
820
|
+
]
|
821
|
+
curl.http_put(fields)
|
822
|
+
assert_match(/HTTP POST file upload/, curl.body_str)
|
823
|
+
assert_match(/Content-Disposition: form-data/, curl.body_str)
|
824
|
+
|
825
|
+
curl.http_patch(fields)
|
826
|
+
assert_match(/HTTP POST file upload/, curl.body_str)
|
827
|
+
assert_match(/Content-Disposition: form-data/, curl.body_str)
|
805
828
|
end
|
806
829
|
|
807
|
-
def
|
830
|
+
def test_with_body_remote
|
808
831
|
curl = Curl::Easy.new(TestServlet.url)
|
809
832
|
curl.post_body = 'foo=bar&encoded%20string=val'
|
810
833
|
|
@@ -812,23 +835,42 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
812
835
|
|
813
836
|
assert_equal "POST\nfoo=bar&encoded%20string=val", curl.body_str
|
814
837
|
assert_equal 'foo=bar&encoded%20string=val', curl.post_body
|
838
|
+
|
839
|
+
curl = Curl::Easy.new(TestServlet.url)
|
840
|
+
curl.put_data = 'foo=bar&encoded%20string=val'
|
841
|
+
|
842
|
+
curl.perform
|
843
|
+
|
844
|
+
assert_equal "PUT\nfoo=bar&encoded%20string=val", curl.body_str
|
815
845
|
end
|
816
846
|
|
817
|
-
def
|
847
|
+
def test_form_body_remote
|
818
848
|
curl = Curl::Easy.new(TestServlet.url)
|
819
849
|
curl.http_post('foo=bar', 'encoded%20string=val')
|
820
850
|
|
821
851
|
assert_equal "POST\nfoo=bar&encoded%20string=val", curl.body_str
|
822
852
|
assert_equal 'foo=bar&encoded%20string=val', curl.post_body
|
823
|
-
end
|
824
853
|
|
825
|
-
def test_post_multipart_file_remote
|
826
854
|
curl = Curl::Easy.new(TestServlet.url)
|
827
|
-
curl.
|
828
|
-
|
829
|
-
curl.
|
830
|
-
|
831
|
-
|
855
|
+
curl.http_put('foo=bar', 'encoded%20string=val')
|
856
|
+
|
857
|
+
assert_equal "PUT\nfoo=bar&encoded%20string=val", curl.body_str
|
858
|
+
|
859
|
+
curl = Curl::Easy.new(TestServlet.url)
|
860
|
+
curl.http_patch('foo=bar', 'encoded%20string=val')
|
861
|
+
|
862
|
+
assert_equal "PATCH\nfoo=bar&encoded%20string=val", curl.body_str
|
863
|
+
end
|
864
|
+
|
865
|
+
def test_multipart_file_remote
|
866
|
+
[:put, :post, :patch].each {|method|
|
867
|
+
curl = Curl::Easy.new(TestServlet.url)
|
868
|
+
curl.multipart_form_post = true
|
869
|
+
pf = Curl::PostField.file('readme', File.expand_path(File.join(File.dirname(__FILE__),'..','README.markdown')))
|
870
|
+
curl.send("http_#{method}", pf)
|
871
|
+
assert_match(/HTTP POST file upload/, curl.body_str)
|
872
|
+
assert_match(/Content-Disposition: form-data/, curl.body_str)
|
873
|
+
}
|
832
874
|
end
|
833
875
|
|
834
876
|
def test_delete_remote
|
@@ -1065,16 +1107,22 @@ class TestCurbCurlEasy < Test::Unit::TestCase
|
|
1065
1107
|
assert_equal "PUT\nhello", curl.body_str
|
1066
1108
|
curl.http('COPY')
|
1067
1109
|
assert_equal 'COPY', curl.body_str
|
1110
|
+
|
1111
|
+
curl.http_patch
|
1112
|
+
assert_equal "PATCH\n", curl.body_str
|
1113
|
+
|
1114
|
+
curl.http_put
|
1115
|
+
assert_equal "PUT\n", curl.body_str
|
1068
1116
|
end
|
1069
1117
|
|
1070
1118
|
def test_easy_http_verbs_must_respond_to_str
|
1071
|
-
# issue http://github.com/taf2/curb/issues
|
1119
|
+
# issue http://github.com/taf2/curb/issues/45
|
1072
1120
|
assert_nothing_raised do
|
1073
|
-
c = Curl::Easy.new ; c.url =
|
1121
|
+
c = Curl::Easy.new ; c.url = TestServlet.url ; c.http(:get)
|
1074
1122
|
end
|
1075
1123
|
|
1076
1124
|
assert_raise RuntimeError do
|
1077
|
-
c = Curl::Easy.new ; c.url =
|
1125
|
+
c = Curl::Easy.new ; c.url = TestServlet.url ; c.http(FooNoToS.new)
|
1078
1126
|
end
|
1079
1127
|
|
1080
1128
|
end
|
@@ -0,0 +1,277 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
class TestCurbCurlEasyCookielist < Test::Unit::TestCase
|
5
|
+
def test_setopt_cookielist
|
6
|
+
easy = Curl::Easy.new
|
7
|
+
# DateTime handles time zone correctly
|
8
|
+
expires = (Date.today + 2).to_datetime
|
9
|
+
easy.setopt(Curl::CURLOPT_COOKIELIST, "Set-Cookie: c1=v1; domain=localhost; expires=#{expires.httpdate};")
|
10
|
+
easy.setopt(Curl::CURLOPT_COOKIELIST, 'Set-Cookie: c2=v2; domain=localhost')
|
11
|
+
easy.setopt(Curl::CURLOPT_COOKIELIST, "Set-Cookie: c3=v3; expires=#{expires.httpdate};")
|
12
|
+
easy.setopt(Curl::CURLOPT_COOKIELIST, 'Set-Cookie: c4=v4;')
|
13
|
+
easy.setopt(Curl::CURLOPT_COOKIELIST, "Set-Cookie: c5=v5; domain=127.0.0.1; expires=#{expires.httpdate};")
|
14
|
+
easy.setopt(Curl::CURLOPT_COOKIELIST, 'Set-Cookie: c6=v6; domain=127.0.0.1;;')
|
15
|
+
|
16
|
+
# Since 7.43.0 cookies that were imported in the Set-Cookie format without a domain name are not exported by this option.
|
17
|
+
# So, before 7.43.0, c3 and c4 will be exported too; but that version is far too old for current curb version, so it's not handled here.
|
18
|
+
if Curl::CURL_VERSION.to_f > 8
|
19
|
+
expected_cookielist = [
|
20
|
+
".127.0.0.1\tTRUE\t/\tFALSE\t#{expires.to_time.to_i}\tc5\tv5",
|
21
|
+
".127.0.0.1\tTRUE\t/\tFALSE\t0\tc6\tv6",
|
22
|
+
".localhost\tTRUE\t/\tFALSE\t#{expires.to_time.to_i}\tc1\tv1",
|
23
|
+
".localhost\tTRUE\t/\tFALSE\t0\tc2\tv2",
|
24
|
+
]
|
25
|
+
else
|
26
|
+
expected_cookielist = [
|
27
|
+
"127.0.0.1\tFALSE\t/\tFALSE\t#{expires.to_time.to_i}\tc5\tv5",
|
28
|
+
"127.0.0.1\tFALSE\t/\tFALSE\t0\tc6\tv6",
|
29
|
+
".localhost\tTRUE\t/\tFALSE\t#{expires.to_time.to_i}\tc1\tv1",
|
30
|
+
".localhost\tTRUE\t/\tFALSE\t0\tc2\tv2",
|
31
|
+
]
|
32
|
+
end
|
33
|
+
assert_equal expected_cookielist, easy.cookielist
|
34
|
+
|
35
|
+
easy.url = "#{TestServlet.url}/get_cookies"
|
36
|
+
easy.perform
|
37
|
+
assert_equal 'c6=v6; c5=v5; c4=v4; c3=v3', easy.body_str
|
38
|
+
easy.url = "http://localhost:#{TestServlet.port}#{TestServlet.path}/get_cookies"
|
39
|
+
easy.perform
|
40
|
+
assert_equal 'c2=v2; c1=v1', easy.body_str
|
41
|
+
end
|
42
|
+
|
43
|
+
# libcurl documentation says: "This option also enables the cookie engine", but it's not tracked on the curb level
|
44
|
+
def test_setopt_cookielist_enables_cookie_engine
|
45
|
+
easy = Curl::Easy.new
|
46
|
+
expires = (Date.today + 2).to_datetime
|
47
|
+
easy.url = "http://localhost:#{TestServlet.port}#{TestServlet.path}/set_cookies"
|
48
|
+
easy.setopt(Curl::CURLOPT_COOKIELIST, "Set-Cookie: c1=v1; domain=localhost; expires=#{expires.httpdate};")
|
49
|
+
easy.post_body = JSON.generate([{ name: 'c2', value: 'v2', domain: 'localhost', expires: expires.httpdate, path: '/' }])
|
50
|
+
easy.perform
|
51
|
+
easy.url = "http://localhost:#{TestServlet.port}#{TestServlet.path}/get_cookies"
|
52
|
+
easy.post_body = nil
|
53
|
+
easy.perform
|
54
|
+
|
55
|
+
assert !easy.enable_cookies?
|
56
|
+
assert_equal [".localhost\tTRUE\t/\tFALSE\t#{expires.to_time.to_i}\tc1\tv1", ".localhost\tTRUE\t/\tFALSE\t#{expires.to_time.to_i}\tc2\tv2"], easy.cookielist
|
57
|
+
assert_equal 'c2=v2; c1=v1', easy.body_str
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_setopt_cookielist_invalid_format
|
61
|
+
easy = Curl::Easy.new
|
62
|
+
easy.url = "http://localhost:#{TestServlet.port}#{TestServlet.path}/get_cookies"
|
63
|
+
easy.setopt(Curl::CURLOPT_COOKIELIST, 'Not a cookie')
|
64
|
+
assert_nil easy.cookielist
|
65
|
+
easy.perform
|
66
|
+
assert_equal '', easy.body_str
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_setopt_cookielist_netscape_format
|
70
|
+
easy = Curl::Easy.new
|
71
|
+
expires = (Date.today + 2).to_datetime
|
72
|
+
easy.url = "http://localhost:#{TestServlet.port}#{TestServlet.path}/get_cookies"
|
73
|
+
# Note domain changes for include subdomains
|
74
|
+
[
|
75
|
+
['localhost', 'FALSE', '/', 'TRUE', 0, 'session_http_only', '42'].join("\t"),
|
76
|
+
['.localhost', 'TRUE', '/', 'FALSE', 0, 'session', '43'].join("\t"),
|
77
|
+
['localhost', 'TRUE', '/', 'FALSE', expires.to_time.to_i, 'permanent', '44'].join("\t"),
|
78
|
+
['.localhost', 'FALSE', '/', 'TRUE', expires.to_time.to_i, 'permanent_http_only', '45'].join("\t"),
|
79
|
+
].each { |cookie| easy.setopt(Curl::CURLOPT_COOKIELIST, cookie) }
|
80
|
+
|
81
|
+
expected_cookielist = [
|
82
|
+
['localhost', 'FALSE', '/', 'TRUE', 0, 'session_http_only', '42'].join("\t"),
|
83
|
+
['.localhost', 'TRUE', '/', 'FALSE', 0, 'session', '43'].join("\t"),
|
84
|
+
['.localhost', 'TRUE', '/', 'FALSE', expires.to_time.to_i, 'permanent', '44'].join("\t"),
|
85
|
+
['localhost', 'FALSE', '/', 'TRUE', expires.to_time.to_i, 'permanent_http_only', '45'].join("\t"),
|
86
|
+
]
|
87
|
+
assert_equal expected_cookielist, easy.cookielist
|
88
|
+
easy.perform
|
89
|
+
assert_equal 'permanent_http_only=45; session_http_only=42; permanent=44; session=43', easy.body_str
|
90
|
+
end
|
91
|
+
|
92
|
+
# Multiple cookies and comments are not supported
|
93
|
+
def test_setopt_cookielist_netscape_format_mutliline
|
94
|
+
easy = Curl::Easy.new
|
95
|
+
easy.url = "http://localhost:#{TestServlet.port}#{TestServlet.path}/get_cookies"
|
96
|
+
easy.setopt(
|
97
|
+
Curl::CURLOPT_COOKIELIST,
|
98
|
+
[
|
99
|
+
'# Netscape HTTP Cookie File',
|
100
|
+
['.localhost', 'TRUE', '/', 'FALSE', 0, 'session', '42'].join("\t"),
|
101
|
+
'',
|
102
|
+
].join("\n"),
|
103
|
+
)
|
104
|
+
assert_nil easy.cookielist
|
105
|
+
easy.perform
|
106
|
+
assert_equal '', easy.body_str
|
107
|
+
|
108
|
+
easy.setopt(
|
109
|
+
Curl::CURLOPT_COOKIELIST,
|
110
|
+
[
|
111
|
+
['.localhost', 'TRUE', '/', 'FALSE', 0, 'session', '42'].join("\t"),
|
112
|
+
['.localhost', 'TRUE', '/', 'FALSE', 0, 'session2', '84'].join("\t"),
|
113
|
+
'',
|
114
|
+
].join("\n"),
|
115
|
+
)
|
116
|
+
# Only first cookie is set
|
117
|
+
assert_equal [".localhost\tTRUE\t/\tFALSE\t0\tsession\t42"], easy.cookielist
|
118
|
+
easy.perform
|
119
|
+
assert_equal 'session=42', easy.body_str
|
120
|
+
end
|
121
|
+
|
122
|
+
# ALL erases all cookies held in memory
|
123
|
+
# ALL was added in 7.14.1
|
124
|
+
def test_setopt_cookielist_command_all
|
125
|
+
expires = (Date.today + 2).to_datetime
|
126
|
+
with_permanent_and_session_cookies(expires) do |easy|
|
127
|
+
easy.setopt(Curl::CURLOPT_COOKIELIST, 'ALL')
|
128
|
+
assert_nil easy.cookielist
|
129
|
+
easy.perform
|
130
|
+
assert_equal '', easy.body_str
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# SESS erases all session cookies held in memory
|
135
|
+
# SESS was added in 7.15.4
|
136
|
+
def test_setopt_cookielist_command_sess
|
137
|
+
expires = (Date.today + 2).to_datetime
|
138
|
+
with_permanent_and_session_cookies(expires) do |easy|
|
139
|
+
easy.setopt(Curl::CURLOPT_COOKIELIST, 'SESS')
|
140
|
+
assert_equal [".localhost\tTRUE\t/\tFALSE\t#{expires.to_time.to_i}\tpermanent\t42"], easy.cookielist
|
141
|
+
easy.perform
|
142
|
+
assert_equal 'permanent=42', easy.body_str
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# FLUSH writes all known cookies to the file specified by CURLOPT_COOKIEJAR
|
147
|
+
# FLUSH was added in 7.17.1
|
148
|
+
def test_setopt_cookielist_command_flush
|
149
|
+
expires = (Date.today + 2).to_datetime
|
150
|
+
with_permanent_and_session_cookies(expires) do |easy|
|
151
|
+
cookiejar = File.join(Dir.tmpdir, 'curl_test_cookiejar')
|
152
|
+
assert !File.exist?(cookiejar)
|
153
|
+
begin
|
154
|
+
easy.cookiejar = cookiejar
|
155
|
+
# trick to actually set CURLOPT_COOKIEJAR
|
156
|
+
easy.enable_cookies = true
|
157
|
+
easy.perform
|
158
|
+
assert !File.exist?(cookiejar)
|
159
|
+
easy.setopt(Curl::CURLOPT_COOKIELIST, 'FLUSH')
|
160
|
+
expected_cookiejar = <<~COOKIEJAR
|
161
|
+
# Netscape HTTP Cookie File
|
162
|
+
# https://curl.se/docs/http-cookies.html
|
163
|
+
# This file was generated by libcurl! Edit at your own risk.
|
164
|
+
|
165
|
+
.localhost TRUE / FALSE 0 session 420
|
166
|
+
.localhost TRUE / FALSE #{expires.to_time.to_i} permanent 42
|
167
|
+
COOKIEJAR
|
168
|
+
assert_equal expected_cookiejar, File.read(cookiejar)
|
169
|
+
ensure
|
170
|
+
# Otherwise it'll create this file again
|
171
|
+
easy.close
|
172
|
+
File.unlink(cookiejar) if File.exist?(cookiejar)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# RELOAD loads all cookies from the files specified by CURLOPT_COOKIEFILE
|
178
|
+
# RELOAD was added in 7.39.0
|
179
|
+
def test_setopt_cookielist_command_reload
|
180
|
+
expires = (Date.today + 2).to_datetime
|
181
|
+
expires_file = (Date.today + 4).to_datetime
|
182
|
+
with_permanent_and_session_cookies(expires) do |easy|
|
183
|
+
cookiefile = File.join(Dir.tmpdir, 'curl_test_cookiefile')
|
184
|
+
assert !File.exist?(cookiefile)
|
185
|
+
begin
|
186
|
+
cookielist = [
|
187
|
+
# Won't be updated, added instead
|
188
|
+
".localhost\tTRUE\t/\tFALSE\t#{expires_file.to_time.to_i}\tpermanent\t84",
|
189
|
+
".localhost\tTRUE\t/\tFALSE\t#{expires_file.to_time.to_i}\tpermanent_file\t84",
|
190
|
+
# Won't be updated, added instead
|
191
|
+
".localhost\tTRUE\t/\tFALSE\t0\tsession\t840",
|
192
|
+
".localhost\tTRUE\t/\tFALSE\t0\tsession_file\t840",
|
193
|
+
'',
|
194
|
+
]
|
195
|
+
File.write(cookiefile, cookielist.join("\n"))
|
196
|
+
easy.cookiefile = cookiefile
|
197
|
+
# trick to actually set CURLOPT_COOKIEFILE
|
198
|
+
easy.enable_cookies = true
|
199
|
+
easy.perform
|
200
|
+
easy.setopt(Curl::CURLOPT_COOKIELIST, 'RELOAD')
|
201
|
+
expected_cookielist = [
|
202
|
+
".localhost\tTRUE\t/\tFALSE\t#{expires.to_time.to_i}\tpermanent\t42",
|
203
|
+
".localhost\tTRUE\t/\tFALSE\t0\tsession\t420",
|
204
|
+
".localhost\tTRUE\t/\tFALSE\t#{expires_file.to_time.to_i}\tpermanent\t84",
|
205
|
+
".localhost\tTRUE\t/\tFALSE\t#{expires_file.to_time.to_i}\tpermanent_file\t84",
|
206
|
+
".localhost\tTRUE\t/\tFALSE\t0\tsession\t840",
|
207
|
+
".localhost\tTRUE\t/\tFALSE\t0\tsession_file\t840",
|
208
|
+
]
|
209
|
+
assert_equal expected_cookielist, easy.cookielist
|
210
|
+
easy.perform
|
211
|
+
# Be careful, duplicates are not removed
|
212
|
+
assert_equal 'permanent_file=84; session_file=840; permanent=84; session=840; permanent=42; session=420', easy.body_str
|
213
|
+
ensure
|
214
|
+
File.unlink(cookiefile) if File.exist?(cookiefile)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def test_commands_do_not_enable_cookie_engine
|
220
|
+
%w[ALL SESS FLUSH RELOAD].each do |command|
|
221
|
+
easy = Curl::Easy.new
|
222
|
+
expires = (Date.today + 2).to_datetime
|
223
|
+
easy.url = "http://localhost:#{TestServlet.port}#{TestServlet.path}/set_cookies"
|
224
|
+
easy.setopt(Curl::CURLOPT_COOKIELIST, command)
|
225
|
+
easy.post_body = JSON.generate([{ name: 'c2', value: 'v2', domain: 'localhost', expires: expires.httpdate, path: '/' }])
|
226
|
+
easy.perform
|
227
|
+
easy.url = "http://localhost:#{TestServlet.port}#{TestServlet.path}/get_cookies"
|
228
|
+
easy.post_body = nil
|
229
|
+
easy.perform
|
230
|
+
|
231
|
+
assert !easy.enable_cookies?
|
232
|
+
assert_nil easy.cookielist
|
233
|
+
assert_equal '', easy.body_str
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
|
238
|
+
def test_strings_without_cookie_enable_cookie_engine
|
239
|
+
[
|
240
|
+
'',
|
241
|
+
'# Netscape HTTP Cookie File',
|
242
|
+
'no_a_cookie',
|
243
|
+
].each do |command|
|
244
|
+
easy = Curl::Easy.new
|
245
|
+
expires = (Date.today + 2).to_datetime
|
246
|
+
easy.url = "http://localhost:#{TestServlet.port}#{TestServlet.path}/set_cookies"
|
247
|
+
easy.setopt(Curl::CURLOPT_COOKIELIST, command)
|
248
|
+
easy.post_body = JSON.generate([{ name: 'c2', value: 'v2', domain: 'localhost', expires: expires.httpdate, path: '/' }])
|
249
|
+
easy.perform
|
250
|
+
easy.url = "http://localhost:#{TestServlet.port}#{TestServlet.path}/get_cookies"
|
251
|
+
easy.post_body = nil
|
252
|
+
easy.perform
|
253
|
+
|
254
|
+
assert !easy.enable_cookies?
|
255
|
+
assert_equal [".localhost\tTRUE\t/\tFALSE\t#{expires.to_time.to_i}\tc2\tv2"], easy.cookielist
|
256
|
+
assert_equal 'c2=v2', easy.body_str
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def with_permanent_and_session_cookies(expires)
|
261
|
+
easy = Curl::Easy.new
|
262
|
+
easy.url = "http://localhost:#{TestServlet.port}#{TestServlet.path}/get_cookies"
|
263
|
+
easy.setopt(Curl::CURLOPT_COOKIELIST, "Set-Cookie: permanent=42; domain=localhost; expires=#{expires.httpdate};")
|
264
|
+
easy.setopt(Curl::CURLOPT_COOKIELIST, 'Set-Cookie: session=420; domain=localhost;')
|
265
|
+
assert_equal [".localhost\tTRUE\t/\tFALSE\t#{expires.to_time.to_i}\tpermanent\t42", ".localhost\tTRUE\t/\tFALSE\t0\tsession\t420"], easy.cookielist
|
266
|
+
easy.perform
|
267
|
+
assert_equal 'permanent=42; session=420', easy.body_str
|
268
|
+
|
269
|
+
yield easy
|
270
|
+
end
|
271
|
+
|
272
|
+
include TestServerMethods
|
273
|
+
|
274
|
+
def setup
|
275
|
+
server_setup
|
276
|
+
end
|
277
|
+
end
|
data/tests/tc_curl_multi.rb
CHANGED
@@ -13,7 +13,7 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
13
13
|
# this test fails with libcurl 7.22.0. I didn't investigate, but it may be related
|
14
14
|
# to CURLOPT_MAXCONNECTS bug fixed in 7.30.0:
|
15
15
|
# https://github.com/curl/curl/commit/e87e76e2dc108efb1cae87df496416f49c55fca0
|
16
|
-
omit("Skip, libcurl too old (< 7.22.0)") if Curl::CURL_VERSION.split('.')[1].to_i <= 22
|
16
|
+
omit("Skip, libcurl too old (< 7.22.0)") if Curl::CURL_VERSION.to_f < 8 && Curl::CURL_VERSION.split('.')[1].to_i <= 22
|
17
17
|
|
18
18
|
@server.shutdown if @server
|
19
19
|
@test_thread.kill if @test_thread
|
@@ -114,13 +114,13 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
114
114
|
end
|
115
115
|
|
116
116
|
def test_new_multi_01
|
117
|
-
d1 =
|
117
|
+
d1 = String.new
|
118
118
|
c1 = Curl::Easy.new($TEST_URL) do |curl|
|
119
119
|
curl.headers["User-Agent"] = "myapp-0.0"
|
120
120
|
curl.on_body {|d| d1 << d; d.length }
|
121
121
|
end
|
122
122
|
|
123
|
-
d2 =
|
123
|
+
d2 = String.new
|
124
124
|
c2 = Curl::Easy.new($TEST_URL) do |curl|
|
125
125
|
curl.headers["User-Agent"] = "myapp-0.0"
|
126
126
|
curl.on_body {|d| d2 << d; d.length }
|
@@ -186,7 +186,7 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
186
186
|
rescue Curl::Err::AbortedByCallbackError => e
|
187
187
|
did_raise = true
|
188
188
|
in_file = e.backtrace.detect {|err| err.match?(File.basename(__FILE__)) }
|
189
|
-
in_file_stack = e.backtrace.select {|err| err.match?(File.basename(__FILE__)) }
|
189
|
+
#in_file_stack = e.backtrace.select {|err| err.match?(File.basename(__FILE__)) }
|
190
190
|
assert_match(__FILE__, in_file)
|
191
191
|
in_file.gsub!(__FILE__)
|
192
192
|
parts = in_file.split(':')
|
@@ -206,7 +206,7 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
206
206
|
m = Curl::Multi.new
|
207
207
|
responses = []
|
208
208
|
n.times do|i|
|
209
|
-
responses[i] =
|
209
|
+
responses[i] = String.new
|
210
210
|
c = Curl::Easy.new($TEST_URL) do|curl|
|
211
211
|
curl.on_body{|data| responses[i] << data; data.size }
|
212
212
|
end
|
@@ -230,7 +230,7 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
230
230
|
5.times do|it|
|
231
231
|
responses = []
|
232
232
|
n.times do|i|
|
233
|
-
responses[i] =
|
233
|
+
responses[i] = String.new
|
234
234
|
c = Curl::Easy.new($TEST_URL) do|curl|
|
235
235
|
curl.on_body{|data| responses[i] << data; data.size }
|
236
236
|
end
|
@@ -376,7 +376,7 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
376
376
|
attr_reader :buf
|
377
377
|
|
378
378
|
def t_method
|
379
|
-
@buf =
|
379
|
+
@buf = String.new
|
380
380
|
@m = Curl::Multi.new
|
381
381
|
10.times do|i|
|
382
382
|
c = Curl::Easy.new($TEST_URL)
|
@@ -422,8 +422,8 @@ class TestCurbCurlMulti < Test::Unit::TestCase
|
|
422
422
|
m = Curl::Multi.new
|
423
423
|
# add a few easy handles
|
424
424
|
requests.each do |url|
|
425
|
-
responses[url] =
|
426
|
-
responses["#{url}-header"] =
|
425
|
+
responses[url] = String.new
|
426
|
+
responses["#{url}-header"] = String.new
|
427
427
|
c = Curl::Easy.new(url) do|curl|
|
428
428
|
curl.follow_location = true
|
429
429
|
curl.on_header{|data| responses["#{url}-header"] << data; data.size }
|
metadata
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: curb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ross Bamford
|
8
8
|
- Todd A. Fisher
|
9
|
-
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2025-02-09 00:00:00.000000000 Z
|
13
12
|
dependencies: []
|
14
13
|
description: Curb (probably CUrl-RuBy or something) provides Ruby-language bindings
|
15
14
|
for the libcurl(3), a fully-featured client-side URL transfer library. cURL and
|
@@ -67,6 +66,7 @@ files:
|
|
67
66
|
- tests/tc_curl.rb
|
68
67
|
- tests/tc_curl_download.rb
|
69
68
|
- tests/tc_curl_easy.rb
|
69
|
+
- tests/tc_curl_easy_cookielist.rb
|
70
70
|
- tests/tc_curl_easy_resolve.rb
|
71
71
|
- tests/tc_curl_easy_setopt.rb
|
72
72
|
- tests/tc_curl_maxfilesize.rb
|
@@ -78,9 +78,8 @@ files:
|
|
78
78
|
- tests/unittests.rb
|
79
79
|
homepage: https://github.com/taf2/curb
|
80
80
|
licenses:
|
81
|
-
-
|
81
|
+
- Ruby
|
82
82
|
metadata: {}
|
83
|
-
post_install_message:
|
84
83
|
rdoc_options:
|
85
84
|
- "--main"
|
86
85
|
- README.markdown
|
@@ -98,8 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
98
97
|
- !ruby/object:Gem::Version
|
99
98
|
version: '0'
|
100
99
|
requirements: []
|
101
|
-
rubygems_version: 3.
|
102
|
-
signing_key:
|
100
|
+
rubygems_version: 3.6.2
|
103
101
|
specification_version: 4
|
104
102
|
summary: Ruby libcurl bindings
|
105
103
|
test_files:
|
@@ -125,6 +123,7 @@ test_files:
|
|
125
123
|
- tests/tc_curl.rb
|
126
124
|
- tests/tc_curl_download.rb
|
127
125
|
- tests/tc_curl_easy.rb
|
126
|
+
- tests/tc_curl_easy_cookielist.rb
|
128
127
|
- tests/tc_curl_easy_resolve.rb
|
129
128
|
- tests/tc_curl_easy_setopt.rb
|
130
129
|
- tests/tc_curl_maxfilesize.rb
|