curb 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e28b1d05b46460867acfadc87bbe422d58d8f2cdb93397fa6ded906b01cd7dab
4
- data.tar.gz: 33e7037c1c2b23a793ec96c73bc80c008939c880702f884d2b258d4afd18c368
3
+ metadata.gz: 805549f6e3fe9190d76c507fa67bf6f6e2a6bbe65bde611cb3aeee6c78cad51f
4
+ data.tar.gz: 7f6cab9c618e4535e7ff46b48d11bb5f307158d850b95bd560432dfeb9ea3144
5
5
  SHA512:
6
- metadata.gz: 6eb448726a4fdce1e0832e36a45b2c8e0a078fbc2c04b3fe34f87fc38397f5c4ecc4fe621aed5637750c5c7a7a9d05b30c03f9f7db153c23d3694eb6adb464e8
7
- data.tar.gz: 624e3f39af79eff874784a662776debc2b405378c6cdc963a50dee525d36aa225cc5f40558f9b35b396c222416006f66c29fc6193e651b5aa84093fdd52cb2ba
6
+ metadata.gz: 98f411070f4c3ef1646de29052a2c9a37e7aa2c9009102a7f4e483d1d1188d8bbdae50647e6dd4c8c7439120619e22df5f8c90bba6e1a088e2762cc29109c63d
7
+ data.tar.gz: bb8ad84d03972d1e42ce61622060019edc4d6286bab9ab459d2fe9e8ccd3cdc04490a8ba58715fd4efa30e3babf82ac96afce3f5b80fddd1d0bd4d47b197a5f1
data/README.markdown CHANGED
@@ -37,18 +37,6 @@ POST request
37
37
  puts res.body
38
38
  ```
39
39
 
40
- PATCH request
41
- ```
42
- res = Curl.patch("https://your-server.com/endpoint", {post: "this"}.to_json) {|http|
43
- http.headers["Content-Type"] = "application/json"
44
- }
45
- puts res.code
46
- puts res.head
47
- puts res.body
48
- ```
49
-
50
-
51
-
52
40
  ## You will need
53
41
 
54
42
  * A working Ruby installation (`2.0.0+` will work but `2.1+` preferred) (it's possible it still works with 1.8.7 but you'd have to tell me if not...)
@@ -64,7 +52,12 @@ tested and reported to work across a variety of platforms / rubies)
64
52
 
65
53
  | Gem Version | Release Date | libcurl versions |
66
54
  | ----------- | ----------- | ---------------- |
67
- | 1.0.0 | Jan 2022 | 7.58 - 7.81 |
55
+ | 1.0.5 | Jan 2023 | 7.58 - 7.87 |
56
+ | 1.0.4 | Jan 2023 | 7.58 - 7.87 |
57
+ | 1.0.3* | Dec 2022 | 7.58 - 7.87 |
58
+ | 1.0.2* | Dec 2022 | 7.58 - 7.87 |
59
+ | 1.0.1 | Apr 2022 | 7.58 - 7.87 |
60
+ | 1.0.0 | Jan 2022 | 7.58 - 7.87 |
68
61
  | 0.9.8 | Jan 2019 | 7.58 - 7.81 |
69
62
  | 0.9.7 | Nov 2018 | 7.56 - 7.60 |
70
63
  | 0.9.6 | May 2018 | 7.51 - 7.59 |
@@ -72,6 +65,8 @@ tested and reported to work across a variety of platforms / rubies)
72
65
  | 0.9.4 | Aug 2017 | 7.41 - 7.58 |
73
66
  | 0.9.3 | Apr 2016 | 7.26 - 7.58 |
74
67
 
68
+ ```*avoid using these version are known to have issues with segmentation faults```
69
+
75
70
  ## Installation...
76
71
 
77
72
  ... will usually be as simple as:
@@ -137,22 +132,22 @@ require 'curb'
137
132
 
138
133
  ```ruby
139
134
  http = Curl.get("http://www.google.com/")
140
- puts http.body_str
135
+ puts http.body
141
136
 
142
137
  http = Curl.post("http://www.google.com/", {:foo => "bar"})
143
- puts http.body_str
138
+ puts http.body
144
139
 
145
140
  http = Curl.get("http://www.google.com/") do |http|
146
141
  http.headers['Cookie'] = 'foo=1;bar=2'
147
142
  end
148
- puts http.body_str
143
+ puts http.body
149
144
  ```
150
145
 
151
146
  ### Simple fetch via HTTP:
152
147
 
153
148
  ```ruby
154
149
  c = Curl::Easy.perform("http://www.google.co.uk")
155
- puts c.body_str
150
+ puts c.body
156
151
  ```
157
152
 
158
153
  Same thing, more manual:
@@ -160,13 +155,13 @@ Same thing, more manual:
160
155
  ```ruby
161
156
  c = Curl::Easy.new("http://www.google.co.uk")
162
157
  c.perform
163
- puts c.body_str
158
+ puts c.body
164
159
  ```
165
160
 
166
161
  ### Additional config:
167
162
 
168
163
  ```ruby
169
- Curl::Easy.perform("http://www.google.co.uk") do |curl|
164
+ http = Curl::Easy.perform("http://www.google.co.uk") do |curl|
170
165
  curl.headers["User-Agent"] = "myapp-0.0"
171
166
  curl.verbose = true
172
167
  end
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.5"
32
- #define CURB_VER_NUM 1005
31
+ #define CURB_VERSION "1.0.7"
32
+ #define CURB_VER_NUM 1007
33
33
  #define CURB_VER_MAJ 1
34
34
  #define CURB_VER_MIN 0
35
- #define CURB_VER_MIC 5
35
+ #define CURB_VER_MIC 7
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
 
@@ -79,7 +80,7 @@ static size_t read_data_handler(void *ptr,
79
80
  remaining = len - rbcu->offset;
80
81
  str_ptr = RSTRING_PTR(str);
81
82
 
82
- if( remaining < read_bytes ) {
83
+ if( remaining <= read_bytes ) {
83
84
  if( remaining > 0 ) {
84
85
  memcpy(ptr, str_ptr+rbcu->offset, remaining);
85
86
  read_bytes = remaining;
@@ -87,14 +88,10 @@ static size_t read_data_handler(void *ptr,
87
88
  }
88
89
  return remaining;
89
90
  }
90
- else if( remaining > read_bytes ) { // read_bytes <= remaining - send what we can fit in the buffer(ptr)
91
+ else { // read_bytes < remaining - send what we can fit in the buffer(ptr)
91
92
  memcpy(ptr, str_ptr+rbcu->offset, read_bytes);
92
93
  rbcu->offset += read_bytes;
93
94
  }
94
- else { // they're equal
95
- memcpy(ptr, str_ptr+rbcu->offset, --read_bytes);
96
- rbcu->offset += read_bytes;
97
- }
98
95
  return read_bytes;
99
96
  }
100
97
  else {
@@ -268,7 +265,7 @@ void curl_easy_free(ruby_curl_easy *rbce) {
268
265
  static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
269
266
  rbce->opts = rb_hash_new();
270
267
 
271
- memset(rbce->err_buf, 0, sizeof(rbce->err_buf));
268
+ memset(rbce->err_buf, 0, CURL_ERROR_SIZE);
272
269
 
273
270
  rbce->curl_headers = NULL;
274
271
  rbce->curl_proxy_headers = NULL;
@@ -374,6 +371,16 @@ static VALUE ruby_curl_easy_initialize(int argc, VALUE *argv, VALUE self) {
374
371
  return self;
375
372
  }
376
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
+
377
384
  /*
378
385
  * call-seq:
379
386
  * easy.clone => <easy clone>
@@ -388,14 +395,22 @@ static VALUE ruby_curl_easy_clone(VALUE self) {
388
395
  Data_Get_Struct(self, ruby_curl_easy, rbce);
389
396
 
390
397
  newrbce = ALLOC(ruby_curl_easy);
398
+ /* shallow copy */
391
399
  memcpy(newrbce, rbce, sizeof(ruby_curl_easy));
400
+
401
+ /* now deep copy */
392
402
  newrbce->curl = curl_easy_duphandle(rbce->curl);
393
- newrbce->curl_headers = NULL;
394
- newrbce->curl_proxy_headers = NULL;
395
- newrbce->curl_ftp_commands = NULL;
396
- 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;
397
407
 
398
- curl_easy_setopt(rbce->curl, CURLOPT_ERRORBUFFER, &rbce->err_buf);
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);
399
414
 
400
415
  return Data_Wrap_Struct(cCurlEasy, curl_easy_mark, curl_easy_free, newrbce);
401
416
  }
@@ -2618,7 +2633,7 @@ static VALUE ruby_curl_easy_perform_verb_str(VALUE self, const char *verb) {
2618
2633
  Data_Get_Struct(self, ruby_curl_easy, rbce);
2619
2634
  curl = rbce->curl;
2620
2635
 
2621
- memset(rbce->err_buf, 0, sizeof(rbce->err_buf));
2636
+ memset(rbce->err_buf, 0, CURL_ERROR_SIZE);
2622
2637
 
2623
2638
  curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, verb);
2624
2639
 
@@ -2685,7 +2700,7 @@ static VALUE ruby_curl_easy_perform_post(int argc, VALUE *argv, VALUE self) {
2685
2700
  Data_Get_Struct(self, ruby_curl_easy, rbce);
2686
2701
  curl = rbce->curl;
2687
2702
 
2688
- memset(rbce->err_buf, 0, sizeof(rbce->err_buf));
2703
+ memset(rbce->err_buf, 0, CURL_ERROR_SIZE);
2689
2704
 
2690
2705
  curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, NULL);
2691
2706
 
@@ -2758,7 +2773,7 @@ static VALUE ruby_curl_easy_perform_put(VALUE self, VALUE data) {
2758
2773
  Data_Get_Struct(self, ruby_curl_easy, rbce);
2759
2774
  curl = rbce->curl;
2760
2775
 
2761
- memset(rbce->err_buf, 0, sizeof(rbce->err_buf));
2776
+ memset(rbce->err_buf, 0, CURL_ERROR_SIZE);
2762
2777
 
2763
2778
  curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, NULL);
2764
2779
  ruby_curl_easy_put_data_set(self, data);
@@ -3368,14 +3383,16 @@ static VALUE ruby_curl_easy_num_connects_get(VALUE self) {
3368
3383
 
3369
3384
  /*
3370
3385
  * call-seq:
3371
- * easy.cookielist => array
3386
+ * easy.cookielist => cookielist
3372
3387
  *
3373
3388
  * Retrieves the cookies curl knows in an array of strings.
3374
3389
  * Returned strings are in Netscape cookiejar format or in Set-Cookie format.
3390
+ * Since 7.43.0 cookies in the Set-Cookie format without a domain name are not exported.
3375
3391
  *
3376
- * See also option CURLINFO_COOKIELIST of curl_easy_getopt(3) to see how libcurl behaves.
3377
- *
3378
- * (requires libcurl 7.14.1 or higher, otherwise -1 is always returned).
3392
+ * @see https://curl.se/libcurl/c/CURLINFO_COOKIELIST.html option <code>CURLINFO_COOKIELIST</code> of
3393
+ * <code>curl_easy_getopt(3)</code> to see how libcurl behaves.
3394
+ * @note requires libcurl 7.14.1 or higher, otherwise +-1+ is always returned
3395
+ * @return [Array<String>, nil, -1] array of strings, or +nil+ if there are no cookies, or +-1+ if the libcurl version is too old
3379
3396
  */
3380
3397
  static VALUE ruby_curl_easy_cookielist_get(VALUE self) {
3381
3398
  #ifdef HAVE_CURLINFO_COOKIELIST
@@ -3486,9 +3503,16 @@ static VALUE ruby_curl_easy_last_error(VALUE self) {
3486
3503
 
3487
3504
  /*
3488
3505
  * call-seq:
3489
- * easy.setopt Fixnum, value => value
3506
+ * easy.setopt(opt, val) => val
3490
3507
  *
3491
3508
  * Initial access to libcurl curl_easy_setopt
3509
+ *
3510
+ * @param [Fixnum] opt The option to set, see +Curl::CURLOPT_*+ constants
3511
+ * @param [Object] val
3512
+ * @return [Object] val
3513
+ * @raise [TypeError] if the option is not supported
3514
+ * @note Some options - like url or cookie - aren't set directly throught +curl_easy_setopt+, but stored in the Ruby object state.
3515
+ * @note When +curl_easy_setopt+ is called, return value is not checked here.
3492
3516
  */
3493
3517
  static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3494
3518
  ruby_curl_easy *rbce;
@@ -3654,6 +3678,11 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3654
3678
  curl_easy_setopt(rbce->curl, CURLOPT_SSL_SESSIONID_CACHE, NUM2LONG(val));
3655
3679
  break;
3656
3680
  #endif
3681
+ #if HAVE_CURLOPT_COOKIELIST
3682
+ case CURLOPT_COOKIELIST: {
3683
+ curl_easy_setopt(rbce->curl, CURLOPT_COOKIELIST, StringValueCStr(val));
3684
+ } break;
3685
+ #endif
3657
3686
  #if HAVE_CURLOPT_PROXY_SSL_VERIFYHOST
3658
3687
  case CURLOPT_PROXY_SSL_VERIFYHOST:
3659
3688
  curl_easy_setopt(rbce->curl, CURLOPT_PROXY_SSL_VERIFYHOST, NUM2LONG(val));
@@ -3668,9 +3697,13 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3668
3697
 
3669
3698
  /*
3670
3699
  * call-seq:
3671
- * easy.getinfo Fixnum => value
3700
+ * easy.getinfo(opt) => nil
3672
3701
  *
3673
3702
  * Iniital access to libcurl curl_easy_getinfo, remember getinfo doesn't return the same values as setopt
3703
+ *
3704
+ * @note This method is not implemented yet.
3705
+ * @param [Fixnum] code Constant +CURLINFO_*+ from libcurl
3706
+ * @return [nil]
3674
3707
  */
3675
3708
  static VALUE ruby_curl_easy_get_opt(VALUE self, VALUE opt) {
3676
3709
  return Qnil;
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
- if (rbcpf->name != Qnil) {
433
- name = rbcpf->name;
434
- if (rb_type(name) == T_STRING) {
435
- name = rbcpf->name;
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
- else {
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
- tmpchrs = curl_escape(StringValuePtr(name), (int)RSTRING_LEN(name));
448
-
449
- if (!tmpchrs) {
450
- rb_raise(eCurlErrInvalidPostField, "Failed to url-encode name `%s'", tmpchrs);
451
- } else {
452
- VALUE tmpcontent = Qnil;
453
- VALUE escd_name = rb_str_new2(tmpchrs);
454
- curl_free(tmpchrs);
455
-
456
- if (rbcpf->content_proc != Qnil) {
457
- tmpcontent = rb_funcall(rbcpf->content_proc, idCall, 1, self);
458
- } else if (rbcpf->content != Qnil) {
459
- tmpcontent = rbcpf->content;
460
- } else if (rbcpf->local_file != Qnil) {
461
- tmpcontent = rbcpf->local_file;
462
- } else if (rbcpf->remote_file != Qnil) {
463
- tmpcontent = rbcpf->remote_file;
464
- } else {
465
- tmpcontent = rb_str_new2("");
466
- }
467
- if (TYPE(tmpcontent) != T_STRING) {
468
- if (rb_respond_to(tmpcontent, rb_intern("to_s"))) {
469
- tmpcontent = rb_funcall(tmpcontent, rb_intern("to_s"), 0);
470
- }
471
- else {
472
- rb_raise(rb_eRuntimeError, "postfield(%s) is not a string and does not respond_to to_s", RSTRING_PTR(escd_name) );
473
- }
474
- }
475
- //fprintf(stderr, "encoding content: %ld - %s\n", RSTRING_LEN(tmpcontent), RSTRING_PTR(tmpcontent) );
476
- tmpchrs = curl_escape(RSTRING_PTR(tmpcontent), (int)RSTRING_LEN(tmpcontent));
477
- if (!tmpchrs) {
478
- rb_raise(eCurlErrInvalidPostField, "Failed to url-encode content `%s'", tmpchrs);
479
- } else {
480
- VALUE escd_content = rb_str_new2(tmpchrs);
481
- curl_free(tmpchrs);
482
-
483
- result = escd_name;
484
- rb_str_cat(result, "=", 1);
485
- rb_str_concat(result, escd_content);
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
@@ -9,7 +9,7 @@ if find_executable('curl-config')
9
9
  else
10
10
  $LIBS << " #{`curl-config --libs`.strip}"
11
11
  end
12
- ca_bundle_path=`curl-config --ca`.strip
12
+ ca_bundle_path=`curl-config --ca`.strip.gsub(/^"([^"]+)"$/,'\1')
13
13
  if !ca_bundle_path.nil? and ca_bundle_path != ''
14
14
  $defs.push( %{-D HAVE_CURL_CONFIG_CA} )
15
15
  $defs.push( %{-D CURL_CONFIG_CA='#{ca_bundle_path.inspect}'} )
@@ -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/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.query['filename'].nil?
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 }
@@ -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
@@ -1068,13 +1068,13 @@ class TestCurbCurlEasy < Test::Unit::TestCase
1068
1068
  end
1069
1069
 
1070
1070
  def test_easy_http_verbs_must_respond_to_str
1071
- # issue http://github.com/taf2/curb/issues#issue/45
1071
+ # issue http://github.com/taf2/curb/issues/45
1072
1072
  assert_nothing_raised do
1073
- c = Curl::Easy.new ; c.url = 'http://example.com' ; c.http(:get)
1073
+ c = Curl::Easy.new ; c.url = TestServlet.url ; c.http(:get)
1074
1074
  end
1075
1075
 
1076
1076
  assert_raise RuntimeError do
1077
- c = Curl::Easy.new ; c.url = 'http://example.com' ; c.http(FooNoToS.new)
1077
+ c = Curl::Easy.new ; c.url = TestServlet.url ; c.http(FooNoToS.new)
1078
1078
  end
1079
1079
 
1080
1080
  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
@@ -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.5
4
+ version: 1.0.7
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: 2023-01-04 00:00:00.000000000 Z
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
@@ -53,7 +52,6 @@ files:
53
52
  - tests/bug_follow_redirect_288.rb
54
53
  - tests/bug_instance_post_differs_from_class_post.rb
55
54
  - tests/bug_issue102.rb
56
- - tests/bug_issue277.rb
57
55
  - tests/bug_multi_segfault.rb
58
56
  - tests/bug_postfields_crash.rb
59
57
  - tests/bug_postfields_crash2.rb
@@ -67,6 +65,7 @@ files:
67
65
  - tests/tc_curl.rb
68
66
  - tests/tc_curl_download.rb
69
67
  - tests/tc_curl_easy.rb
68
+ - tests/tc_curl_easy_cookielist.rb
70
69
  - tests/tc_curl_easy_resolve.rb
71
70
  - tests/tc_curl_easy_setopt.rb
72
71
  - tests/tc_curl_maxfilesize.rb
@@ -78,9 +77,8 @@ files:
78
77
  - tests/unittests.rb
79
78
  homepage: https://github.com/taf2/curb
80
79
  licenses:
81
- - MIT
80
+ - Ruby
82
81
  metadata: {}
83
- post_install_message:
84
82
  rdoc_options:
85
83
  - "--main"
86
84
  - README.markdown
@@ -98,8 +96,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
98
96
  - !ruby/object:Gem::Version
99
97
  version: '0'
100
98
  requirements: []
101
- rubygems_version: 3.2.33
102
- signing_key:
99
+ rubygems_version: 3.6.2
103
100
  specification_version: 4
104
101
  summary: Ruby libcurl bindings
105
102
  test_files:
@@ -111,7 +108,6 @@ test_files:
111
108
  - tests/bug_follow_redirect_288.rb
112
109
  - tests/bug_instance_post_differs_from_class_post.rb
113
110
  - tests/bug_issue102.rb
114
- - tests/bug_issue277.rb
115
111
  - tests/bug_multi_segfault.rb
116
112
  - tests/bug_postfields_crash.rb
117
113
  - tests/bug_postfields_crash2.rb
@@ -125,6 +121,7 @@ test_files:
125
121
  - tests/tc_curl.rb
126
122
  - tests/tc_curl_download.rb
127
123
  - tests/tc_curl_easy.rb
124
+ - tests/tc_curl_easy_cookielist.rb
128
125
  - tests/tc_curl_easy_resolve.rb
129
126
  - tests/tc_curl_easy_setopt.rb
130
127
  - tests/tc_curl_maxfilesize.rb
@@ -1,32 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
-
3
-
4
- require 'curb'
5
-
6
- class BugIssue102 < Test::Unit::TestCase
7
-
8
- def test_gc_closewait
9
- 100.times do
10
- responses = {}
11
- requests = ["http://www.google.co.uk/", "http://www.ruby-lang.org/"]
12
- m = Curl::Multi.new
13
- # add a few easy handles
14
- requests.each do |url|
15
- responses[url] = ""
16
- c = Curl::Easy.new(url) do|curl|
17
- curl.follow_location = true
18
- curl.on_body{|data| responses[url] << data; data.size }
19
- curl.on_success {|easy| #puts "success, add more easy handles"
20
- }
21
- end
22
- m.add(c)
23
- end
24
-
25
- m.perform do
26
- #puts "idling... can do some work here"
27
- end
28
- GC.start
29
- end
30
- end
31
-
32
- end