curb 0.9.3 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/ext/curb_easy.c CHANGED
@@ -25,12 +25,18 @@ static VALUE rbstrAmp;
25
25
 
26
26
  VALUE cCurlEasy;
27
27
 
28
+ // for Ruby 1.8
29
+ #ifndef HAVE_RB_IO_STDIO_FILE
30
+ static FILE * rb_io_stdio_file(rb_io_t *fptr) {
31
+ return fptr->f;
32
+ }
33
+ #endif
28
34
 
29
35
  /* ================== CURL HANDLER FUNCS ==============*/
30
36
 
31
37
  static VALUE callback_exception(VALUE unused) {
32
38
  return Qfalse;
33
- }
39
+ }
34
40
 
35
41
  /* These handle both body and header data */
36
42
  static size_t default_data_handler(char *stream,
@@ -223,12 +229,31 @@ static void ruby_curl_easy_free(ruby_curl_easy *rbce) {
223
229
  curl_slist_free_all(rbce->curl_headers);
224
230
  }
225
231
 
232
+ if (rbce->curl_proxy_headers) {
233
+ curl_slist_free_all(rbce->curl_proxy_headers);
234
+ }
235
+
226
236
  if (rbce->curl_ftp_commands) {
227
237
  curl_slist_free_all(rbce->curl_ftp_commands);
228
238
  }
229
239
 
240
+ if (rbce->curl_resolve) {
241
+ curl_slist_free_all(rbce->curl_resolve);
242
+ }
243
+
230
244
  if (rbce->curl) {
245
+ /* disable any progress or debug events */
246
+ curl_easy_setopt(rbce->curl, CURLOPT_WRITEFUNCTION, NULL);
247
+ curl_easy_setopt(rbce->curl, CURLOPT_WRITEDATA, NULL);
248
+ curl_easy_setopt(rbce->curl, CURLOPT_HEADERFUNCTION, NULL);
249
+ curl_easy_setopt(rbce->curl, CURLOPT_HEADERDATA, NULL);
250
+ curl_easy_setopt(rbce->curl, CURLOPT_DEBUGFUNCTION, NULL);
251
+ curl_easy_setopt(rbce->curl, CURLOPT_DEBUGDATA, NULL);
252
+ curl_easy_setopt(rbce->curl, CURLOPT_VERBOSE, 0);
253
+ curl_easy_setopt(rbce->curl, CURLOPT_PROGRESSFUNCTION, NULL);
254
+ curl_easy_setopt(rbce->curl, CURLOPT_NOPROGRESS, 1);
231
255
  curl_easy_cleanup(rbce->curl);
256
+ rbce->curl = NULL;
232
257
  }
233
258
  }
234
259
 
@@ -243,8 +268,12 @@ void curl_easy_free(ruby_curl_easy *rbce) {
243
268
  static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
244
269
  rbce->opts = rb_hash_new();
245
270
 
271
+ memset(rbce->err_buf, 0, sizeof(rbce->err_buf));
272
+
246
273
  rbce->curl_headers = NULL;
274
+ rbce->curl_proxy_headers = NULL;
247
275
  rbce->curl_ftp_commands = NULL;
276
+ rbce->curl_resolve = NULL;
248
277
 
249
278
  /* various-typed opts */
250
279
  rbce->local_port = 0;
@@ -262,6 +291,8 @@ static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
262
291
  rbce->ftp_response_timeout = 0;
263
292
  rbce->low_speed_limit = 0;
264
293
  rbce->low_speed_time = 0;
294
+ rbce->max_send_speed_large = 0;
295
+ rbce->max_recv_speed_large = 0;
265
296
  rbce->ssl_version = -1;
266
297
  rbce->use_ssl = -1;
267
298
  rbce->ftp_filemethod = -1;
@@ -283,25 +314,37 @@ static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
283
314
  rbce->callback_active = 0;
284
315
  }
285
316
 
317
+ /*
318
+ * Allocate space for a Curl::Easy instance.
319
+ */
320
+ static VALUE ruby_curl_easy_allocate(VALUE klass) {
321
+ ruby_curl_easy *rbce;
322
+ rbce = ALLOC(ruby_curl_easy);
323
+ rbce->curl = NULL;
324
+ rbce->opts = Qnil;
325
+ rbce->multi = Qnil;
326
+ ruby_curl_easy_zero(rbce);
327
+ return Data_Wrap_Struct(klass, curl_easy_mark, curl_easy_free, rbce);
328
+ }
329
+
286
330
  /*
287
331
  * call-seq:
288
- * Curl::Easy.new => #<Curl::Easy...>
289
- * Curl::Easy.new(url = nil) => #<Curl::Easy...>
290
- * Curl::Easy.new(url = nil) { |self| ... } => #<Curl::Easy...>
332
+ * Curl::Easy.new => #<Curl::Easy...>
333
+ * Curl::Easy.new(url = nil) => #<Curl::Easy...>
334
+ * Curl::Easy.new(url = nil) { |self| ... } => #<Curl::Easy...>
291
335
  *
292
- * Create a new Curl::Easy instance, optionally supplying the URL.
336
+ * Initialize a new Curl::Easy instance, optionally supplying the URL.
293
337
  * The block form allows further configuration to be supplied before
294
338
  * the instance is returned.
295
339
  */
296
- static VALUE ruby_curl_easy_new(int argc, VALUE *argv, VALUE klass) {
340
+ static VALUE ruby_curl_easy_initialize(int argc, VALUE *argv, VALUE self) {
297
341
  CURLcode ecode;
298
342
  VALUE url, blk;
299
- VALUE new_curl;
300
343
  ruby_curl_easy *rbce;
301
344
 
302
345
  rb_scan_args(argc, argv, "01&", &url, &blk);
303
346
 
304
- rbce = ALLOC(ruby_curl_easy);
347
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
305
348
 
306
349
  /* handler */
307
350
  rbce->curl = curl_easy_init();
@@ -309,32 +352,32 @@ static VALUE ruby_curl_easy_new(int argc, VALUE *argv, VALUE klass) {
309
352
  rb_raise(eCurlErrFailedInit, "Failed to initialize easy handle");
310
353
  }
311
354
 
312
- new_curl = Data_Wrap_Struct(klass, curl_easy_mark, curl_easy_free, rbce);
313
-
314
355
  rbce->multi = Qnil;
315
356
  rbce->opts = Qnil;
316
357
 
317
358
  ruby_curl_easy_zero(rbce);
318
359
 
360
+ curl_easy_setopt(rbce->curl, CURLOPT_ERRORBUFFER, &rbce->err_buf);
361
+
319
362
  rb_easy_set("url", url);
320
363
 
321
- /* set the new_curl pointer to the curl handle */
322
- ecode = curl_easy_setopt(rbce->curl, CURLOPT_PRIVATE, (void*)new_curl);
364
+ /* set the pointer to the curl handle */
365
+ ecode = curl_easy_setopt(rbce->curl, CURLOPT_PRIVATE, (void*)self);
323
366
  if (ecode != CURLE_OK) {
324
367
  raise_curl_easy_error_exception(ecode);
325
368
  }
326
369
 
327
370
  if (blk != Qnil) {
328
- rb_funcall(blk, idCall, 1, new_curl);
371
+ rb_funcall(blk, idCall, 1, self);
329
372
  }
330
373
 
331
- return new_curl;
374
+ return self;
332
375
  }
333
376
 
334
377
  /*
335
378
  * call-seq:
336
- * easy.clone => #&lt;easy clone&gt;
337
- * easy.dup => #&lt;easy clone&gt;
379
+ * easy.clone => <easy clone>
380
+ * easy.dup => <easy clone>
338
381
  *
339
382
  * Clone this Curl::Easy instance, creating a new instance.
340
383
  * This method duplicates the underlying CURL* handle.
@@ -348,7 +391,11 @@ static VALUE ruby_curl_easy_clone(VALUE self) {
348
391
  memcpy(newrbce, rbce, sizeof(ruby_curl_easy));
349
392
  newrbce->curl = curl_easy_duphandle(rbce->curl);
350
393
  newrbce->curl_headers = NULL;
394
+ newrbce->curl_proxy_headers = NULL;
351
395
  newrbce->curl_ftp_commands = NULL;
396
+ newrbce->curl_resolve = NULL;
397
+
398
+ curl_easy_setopt(rbce->curl, CURLOPT_ERRORBUFFER, &rbce->err_buf);
352
399
 
353
400
  return Data_Wrap_Struct(cCurlEasy, curl_easy_mark, curl_easy_free, newrbce);
354
401
  }
@@ -358,7 +405,7 @@ static VALUE ruby_curl_easy_clone(VALUE self) {
358
405
  * easy.close => nil
359
406
  *
360
407
  * Close the Curl::Easy instance. Any open connections are closed
361
- * The easy handle is reinitialized. If a previous multi handle was
408
+ * The easy handle is reinitialized. If a previous multi handle was
362
409
  * open it is set to nil and will be cleared after a GC.
363
410
  */
364
411
  static VALUE ruby_curl_easy_close(VALUE self) {
@@ -419,7 +466,9 @@ static VALUE ruby_curl_easy_reset(VALUE self) {
419
466
  curl_easy_reset(rbce->curl);
420
467
  ruby_curl_easy_zero(rbce);
421
468
 
422
- /* rest clobbers the private setting, so reset it to self */
469
+ curl_easy_setopt(rbce->curl, CURLOPT_ERRORBUFFER, &rbce->err_buf);
470
+
471
+ /* reset clobbers the private setting, so reset it to self */
423
472
  ecode = curl_easy_setopt(rbce->curl, CURLOPT_PRIVATE, (void*)self);
424
473
  if (ecode != CURLE_OK) {
425
474
  raise_curl_easy_error_exception(ecode);
@@ -431,6 +480,12 @@ static VALUE ruby_curl_easy_reset(VALUE self) {
431
480
  rbce->curl_headers = NULL;
432
481
  }
433
482
 
483
+ /* Free everything up */
484
+ if (rbce->curl_proxy_headers) {
485
+ curl_slist_free_all(rbce->curl_proxy_headers);
486
+ rbce->curl_proxy_headers = NULL;
487
+ }
488
+
434
489
  return opts_dup;
435
490
  }
436
491
 
@@ -483,6 +538,10 @@ static VALUE ruby_curl_easy_headers_set(VALUE self, VALUE headers) {
483
538
  CURB_OBJECT_HSETTER(ruby_curl_easy, headers);
484
539
  }
485
540
 
541
+ static VALUE ruby_curl_easy_proxy_headers_set(VALUE self, VALUE proxy_headers) {
542
+ CURB_OBJECT_HSETTER(ruby_curl_easy, proxy_headers);
543
+ }
544
+
486
545
  /*
487
546
  * call-seq:
488
547
  * easy.headers => Hash, Array or Str
@@ -493,11 +552,46 @@ static VALUE ruby_curl_easy_headers_get(VALUE self) {
493
552
  ruby_curl_easy *rbce;
494
553
  VALUE headers;
495
554
  Data_Get_Struct(self, ruby_curl_easy, rbce);
496
- headers = rb_easy_get("headers");//rb_hash_aref(rbce->opts, rb_intern("headers"));
555
+ headers = rb_easy_get("headers");//rb_hash_aref(rbce->opts, rb_intern("headers"));
497
556
  if (headers == Qnil) { headers = rb_easy_set("headers", rb_hash_new()); }
498
557
  return headers;
499
558
  }
500
559
 
560
+ /*
561
+ * call-seq:
562
+ * easy.proxy_headers = "Header: val" => "Header: val"
563
+ * easy.proxy_headers = {"Header" => "val" ..., "Header" => "val"} => {"Header: val", ...}
564
+ * easy.proxy_headers = ["Header: val" ..., "Header: val"] => ["Header: val", ...]
565
+ *
566
+ *
567
+ * For example to set a standard or custom header:
568
+ *
569
+ * easy.proxy_headers["MyHeader"] = "myval"
570
+ *
571
+ * To remove a standard header (this is useful when removing libcurls default
572
+ * 'Expect: 100-Continue' header when using HTTP form posts):
573
+ *
574
+ * easy.proxy_headers["Expect"] = ''
575
+ *
576
+ * Anything passed to libcurl as a header will be converted to a string during
577
+ * the perform step.
578
+ */
579
+
580
+ /*
581
+ * call-seq:
582
+ * easy.proxy_headers => Hash, Array or Str
583
+ *
584
+ * Obtain the custom HTTP proxy_headers for following requests.
585
+ */
586
+ static VALUE ruby_curl_easy_proxy_headers_get(VALUE self) {
587
+ ruby_curl_easy *rbce;
588
+ VALUE proxy_headers;
589
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
590
+ proxy_headers = rb_easy_get("proxy_headers");//rb_hash_aref(rbce->opts, rb_intern("proxy_headers"));
591
+ if (proxy_headers == Qnil) { proxy_headers = rb_easy_set("proxy_headers", rb_hash_new()); }
592
+ return proxy_headers;
593
+ }
594
+
501
595
  /*
502
596
  * call-seq:
503
597
  * easy.interface => string
@@ -705,29 +799,29 @@ static VALUE ruby_curl_easy_useragent_get(VALUE self) {
705
799
  /*
706
800
  * call-seq:
707
801
  * easy.post_body = "some=form%20data&to=send" => string or nil
708
- *
802
+ *
709
803
  * Sets the POST body of this Curl::Easy instance. This is expected to be
710
804
  * URL encoded; no additional processing or encoding is done on the string.
711
805
  * The content-type header will be set to application/x-www-form-urlencoded.
712
- *
806
+ *
713
807
  * This is handy if you want to perform a POST against a Curl::Multi instance.
714
808
  */
715
809
  static VALUE ruby_curl_easy_post_body_set(VALUE self, VALUE post_body) {
716
810
  ruby_curl_easy *rbce;
717
811
  CURL *curl;
718
-
812
+
719
813
  char *data;
720
814
  long len;
721
815
 
722
816
  Data_Get_Struct(self, ruby_curl_easy, rbce);
723
-
817
+
724
818
  curl = rbce->curl;
725
-
819
+
726
820
  if ( post_body == Qnil ) {
727
821
  rb_easy_del("postdata_buffer");
728
822
  curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
729
-
730
- } else {
823
+
824
+ } else {
731
825
  if (rb_type(post_body) == T_STRING) {
732
826
  data = StringValuePtr(post_body);
733
827
  len = RSTRING_LEN(post_body);
@@ -740,19 +834,19 @@ static VALUE ruby_curl_easy_post_body_set(VALUE self, VALUE post_body) {
740
834
  else {
741
835
  rb_raise(rb_eRuntimeError, "post data must respond_to .to_s");
742
836
  }
743
-
744
- // Store the string, since it has to hang around for the duration of the
837
+
838
+ // Store the string, since it has to hang around for the duration of the
745
839
  // request. See CURLOPT_POSTFIELDS in the libcurl docs.
746
840
  //rbce->postdata_buffer = post_body;
747
841
  rb_easy_set("postdata_buffer", post_body);
748
-
842
+
749
843
  curl_easy_setopt(curl, CURLOPT_POST, 1);
750
844
  curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
751
845
  curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len);
752
-
846
+
753
847
  return post_body;
754
848
  }
755
-
849
+
756
850
  return Qnil;
757
851
  }
758
852
 
@@ -769,7 +863,7 @@ static VALUE ruby_curl_easy_post_body_get(VALUE self) {
769
863
  /*
770
864
  * call-seq:
771
865
  * easy.put_data = data => ""
772
- *
866
+ *
773
867
  * Points this Curl::Easy instance to data to be uploaded via PUT. This
774
868
  * sets the request to a PUT type request - useful if you want to PUT via
775
869
  * a multi handle.
@@ -801,7 +895,7 @@ static VALUE ruby_curl_easy_put_data_set(VALUE self, VALUE data) {
801
895
  curl_easy_setopt(curl, CURLOPT_SEEKDATA, rbce);
802
896
  #endif
803
897
 
804
- /*
898
+ /*
805
899
  * we need to set specific headers for the PUT to work... so
806
900
  * convert the internal headers structure to a HASH if one is set
807
901
  */
@@ -815,7 +909,7 @@ static VALUE ruby_curl_easy_put_data_set(VALUE self, VALUE data) {
815
909
  if (NIL_P(data)) { return data; }
816
910
 
817
911
  headers = rb_easy_get("headers");
818
- if( headers == Qnil ) {
912
+ if( headers == Qnil ) {
819
913
  headers = rb_hash_new();
820
914
  }
821
915
 
@@ -863,13 +957,32 @@ static VALUE ruby_curl_easy_ftp_commands_set(VALUE self, VALUE ftp_commands) {
863
957
  }
864
958
 
865
959
  /*
866
- * call-seq
960
+ * call-seq:
867
961
  * easy.ftp_commands => array or nil
868
962
  */
869
963
  static VALUE ruby_curl_easy_ftp_commands_get(VALUE self) {
870
964
  CURB_OBJECT_HGETTER(ruby_curl_easy, ftp_commands);
871
965
  }
872
966
 
967
+ /*
968
+ * call-seq:
969
+ * easy.resolve = [ "example.com:80:127.0.0.1" ] => [ "example.com:80:127.0.0.1" ]
970
+ *
971
+ * Set the resolve list to statically resolve hostnames to IP addresses,
972
+ * bypassing DNS for matching hostname/port combinations.
973
+ */
974
+ static VALUE ruby_curl_easy_resolve_set(VALUE self, VALUE resolve) {
975
+ CURB_OBJECT_HSETTER(ruby_curl_easy, resolve);
976
+ }
977
+
978
+ /*
979
+ * call-seq:
980
+ * easy.resolve => array or nil
981
+ */
982
+ static VALUE ruby_curl_easy_resolve_get(VALUE self) {
983
+ CURB_OBJECT_HGETTER(ruby_curl_easy, resolve);
984
+ }
985
+
873
986
  /* ================== IMMED ATTRS ==================*/
874
987
 
875
988
  /*
@@ -980,7 +1093,7 @@ static VALUE ruby_curl_easy_proxy_type_get(VALUE self) {
980
1093
  (!strncmp("ntlm",node,4)) ? CURLAUTH_NTLM : \
981
1094
  (!strncmp("anysafe",node,7)) ? CURLAUTH_ANYSAFE : \
982
1095
  (!strncmp("any",node,3)) ? CURLAUTH_ANY : 0
983
- #else
1096
+ #else
984
1097
  #define CURL_HTTPAUTH_STR_TO_NUM(node) \
985
1098
  (!strncmp("basic",node,5)) ? CURLAUTH_BASIC : \
986
1099
  (!strncmp("digest",node,6)) ? CURLAUTH_DIGEST : \
@@ -1012,7 +1125,7 @@ static VALUE ruby_curl_easy_http_auth_types_set(int argc, VALUE *argv, VALUE sel
1012
1125
 
1013
1126
  if (len == 1 && (rb_ary_entry(args_ary,0) == Qnil || TYPE(rb_ary_entry(args_ary,0)) == T_FIXNUM ||
1014
1127
  TYPE(rb_ary_entry(args_ary,0)) == T_BIGNUM)) {
1015
- if (rb_ary_entry(args_ary,0) == Qnil) {
1128
+ if (rb_ary_entry(args_ary,0) == Qnil) {
1016
1129
  rbce->http_auth_types = 0;
1017
1130
  }
1018
1131
  else {
@@ -1094,7 +1207,7 @@ static VALUE ruby_curl_easy_max_redirects_get(VALUE self) {
1094
1207
 
1095
1208
  /*
1096
1209
  * call-seq:
1097
- * easy.timeout = fixnum or nil => fixnum or nil
1210
+ * easy.timeout = float, fixnum or nil => numeric
1098
1211
  *
1099
1212
  * Set the maximum time in seconds that you allow the libcurl transfer
1100
1213
  * operation to take. Normally, name lookups can take a considerable time
@@ -1103,20 +1216,39 @@ static VALUE ruby_curl_easy_max_redirects_get(VALUE self) {
1103
1216
  *
1104
1217
  * Set to nil (or zero) to disable timeout (it will then only timeout
1105
1218
  * on the system's internal timeouts).
1219
+ *
1220
+ * Uses timeout_ms internally instead of timeout because it allows for
1221
+ * better precision and libcurl will use the last set value when both
1222
+ * timeout and timeout_ms are set.
1223
+ *
1106
1224
  */
1107
- static VALUE ruby_curl_easy_timeout_set(VALUE self, VALUE timeout) {
1108
- CURB_IMMED_SETTER(ruby_curl_easy, timeout, 0);
1225
+ static VALUE ruby_curl_easy_timeout_set(VALUE self, VALUE timeout_s) {
1226
+ ruby_curl_easy *rbce;
1227
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1228
+
1229
+ if (Qnil == timeout_s || NUM2DBL(timeout_s) <= 0.0) {
1230
+ rbce->timeout_ms = 0;
1231
+ } else {
1232
+ rbce->timeout_ms = (unsigned long)(NUM2DBL(timeout_s) * 1000);
1233
+ }
1234
+
1235
+ return DBL2NUM(rbce->timeout_ms / 1000.0);
1109
1236
  }
1110
1237
 
1111
1238
  /*
1112
1239
  * call-seq:
1113
- * easy.timeout => fixnum or nil
1240
+ * easy.timeout => numeric
1114
1241
  *
1115
1242
  * Obtain the maximum time in seconds that you allow the libcurl transfer
1116
1243
  * operation to take.
1244
+ *
1245
+ * Uses timeout_ms internally instead of timeout.
1246
+ *
1117
1247
  */
1118
- static VALUE ruby_curl_easy_timeout_get(VALUE self, VALUE timeout) {
1119
- CURB_IMMED_GETTER(ruby_curl_easy, timeout, 0);
1248
+ static VALUE ruby_curl_easy_timeout_get(VALUE self) {
1249
+ ruby_curl_easy *rbce;
1250
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1251
+ return DBL2NUM(rbce->timeout_ms / 1000.0);
1120
1252
  }
1121
1253
 
1122
1254
  /*
@@ -1132,7 +1264,16 @@ static VALUE ruby_curl_easy_timeout_get(VALUE self, VALUE timeout) {
1132
1264
  * on the system's internal timeouts).
1133
1265
  */
1134
1266
  static VALUE ruby_curl_easy_timeout_ms_set(VALUE self, VALUE timeout_ms) {
1135
- CURB_IMMED_SETTER(ruby_curl_easy, timeout_ms, 0);
1267
+ ruby_curl_easy *rbce;
1268
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1269
+
1270
+ if (Qnil == timeout_ms || NUM2DBL(timeout_ms) <= 0.0) {
1271
+ rbce->timeout_ms = 0;
1272
+ } else {
1273
+ rbce->timeout_ms = NUM2ULONG(timeout_ms);
1274
+ }
1275
+
1276
+ return ULONG2NUM(rbce->timeout_ms);
1136
1277
  }
1137
1278
 
1138
1279
  /*
@@ -1142,8 +1283,10 @@ static VALUE ruby_curl_easy_timeout_ms_set(VALUE self, VALUE timeout_ms) {
1142
1283
  * Obtain the maximum time in milliseconds that you allow the libcurl transfer
1143
1284
  * operation to take.
1144
1285
  */
1145
- static VALUE ruby_curl_easy_timeout_ms_get(VALUE self, VALUE timeout_ms) {
1146
- CURB_IMMED_GETTER(ruby_curl_easy, timeout_ms, 0);
1286
+ static VALUE ruby_curl_easy_timeout_ms_get(VALUE self) {
1287
+ ruby_curl_easy *rbce;
1288
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1289
+ return LONG2NUM(rbce->timeout_ms);
1147
1290
  }
1148
1291
 
1149
1292
  /*
@@ -1293,6 +1436,46 @@ static VALUE ruby_curl_easy_low_speed_time_get(VALUE self, VALUE low_speed_time)
1293
1436
  CURB_IMMED_GETTER(ruby_curl_easy, low_speed_time, 0);
1294
1437
  }
1295
1438
 
1439
+ /*
1440
+ * call-seq:
1441
+ * easy.max_send_speed_large = fixnum or nil => fixnum or nil
1442
+ *
1443
+ * Set the maximal sending transfer speed (in bytes per second)
1444
+ */
1445
+ static VALUE ruby_curl_easy_max_send_speed_large_set(VALUE self, VALUE max_send_speed_large) {
1446
+ CURB_IMMED_SETTER(ruby_curl_easy, max_send_speed_large, 0);
1447
+ }
1448
+
1449
+ /*
1450
+ * call-seq:
1451
+ * easy.max_send_speed_large = fixnum or nil => fixnum or nil
1452
+ *
1453
+ * Get the maximal sending transfer speed (in bytes per second)
1454
+ */
1455
+ static VALUE ruby_curl_easy_max_send_speed_large_get(VALUE self, VALUE max_send_speed_large) {
1456
+ CURB_IMMED_GETTER(ruby_curl_easy, max_send_speed_large, 0);
1457
+ }
1458
+
1459
+ /*
1460
+ * call-seq:
1461
+ * easy.max_recv_speed_large = fixnum or nil => fixnum or nil
1462
+ *
1463
+ * Set the maximal receiving transfer speed (in bytes per second)
1464
+ */
1465
+ static VALUE ruby_curl_easy_max_recv_speed_large_set(VALUE self, VALUE max_recv_speed_large) {
1466
+ CURB_IMMED_SETTER(ruby_curl_easy, max_recv_speed_large, 0);
1467
+ }
1468
+
1469
+ /*
1470
+ * call-seq:
1471
+ * easy.max_recv_speed_large = fixnum or nil => fixnum or nil
1472
+ *
1473
+ * Get the maximal receiving transfer speed (in bytes per second)
1474
+ */
1475
+ static VALUE ruby_curl_easy_max_recv_speed_large_get(VALUE self, VALUE max_recv_speed_large) {
1476
+ CURB_IMMED_GETTER(ruby_curl_easy, max_recv_speed_large, 0);
1477
+ }
1478
+
1296
1479
  /*
1297
1480
  * call-seq:
1298
1481
  * easy.username = string => string
@@ -1310,7 +1493,7 @@ static VALUE ruby_curl_easy_username_set(VALUE self, VALUE username) {
1310
1493
  /*
1311
1494
  * call-seq:
1312
1495
  * easy.username => string
1313
- *
1496
+ *
1314
1497
  * Get the current username
1315
1498
  */
1316
1499
  static VALUE ruby_curl_easy_username_get(VALUE self, VALUE username) {
@@ -1338,7 +1521,7 @@ static VALUE ruby_curl_easy_password_set(VALUE self, VALUE password) {
1338
1521
  /*
1339
1522
  * call-seq:
1340
1523
  * easy.password => string
1341
- *
1524
+ *
1342
1525
  * Get the current password
1343
1526
  */
1344
1527
  static VALUE ruby_curl_easy_password_get(VALUE self, VALUE password) {
@@ -1363,6 +1546,7 @@ static VALUE ruby_curl_easy_password_get(VALUE self, VALUE password) {
1363
1546
  * Curl::CURL_SSLVERSION_TLSv1_0
1364
1547
  * Curl::CURL_SSLVERSION_TLSv1_1
1365
1548
  * Curl::CURL_SSLVERSION_TLSv1_2
1549
+ * Curl::CURL_SSLVERSION_TLSv1_3
1366
1550
  */
1367
1551
  static VALUE ruby_curl_easy_ssl_version_set(VALUE self, VALUE ssl_version) {
1368
1552
  CURB_IMMED_SETTER(ruby_curl_easy, ssl_version, -1);
@@ -1381,7 +1565,7 @@ static VALUE ruby_curl_easy_ssl_version_get(VALUE self, VALUE ssl_version) {
1381
1565
  /*
1382
1566
  * call-seq:
1383
1567
  * easy.use_ssl = value => fixnum or nil
1384
- *
1568
+ *
1385
1569
  * Ensure libcurl uses SSL for FTP connections. Valid options are Curl::CURL_USESSL_NONE,
1386
1570
  * Curl::CURL_USESSL_TRY, Curl::CURL_USESSL_CONTROL, and Curl::CURL_USESSL_ALL.
1387
1571
  */
@@ -1411,7 +1595,7 @@ static VALUE ruby_curl_easy_ftp_filemethod_set(VALUE self, VALUE ftp_filemethod)
1411
1595
  }
1412
1596
 
1413
1597
  /*
1414
- * call-seq
1598
+ * call-seq:
1415
1599
  * easy.ftp_filemethod => fixnum
1416
1600
  *
1417
1601
  * Get the configuration for how libcurl will reach files on the server.
@@ -1783,7 +1967,7 @@ static VALUE ruby_curl_easy_resolve_mode_set(VALUE self, VALUE resolve_mode) {
1783
1967
 
1784
1968
  /*
1785
1969
  * call-seq:
1786
- * easy.on_body { |body_data| ... } => &lt;old handler&gt;
1970
+ * easy.on_body { |body_data| ... } => <old handler>
1787
1971
  *
1788
1972
  * Assign or remove the +on_body+ handler for this Curl::Easy instance.
1789
1973
  * To remove a previously-supplied handler, call this method with no
@@ -1802,7 +1986,7 @@ static VALUE ruby_curl_easy_on_body_set(int argc, VALUE *argv, VALUE self) {
1802
1986
 
1803
1987
  /*
1804
1988
  * call-seq:
1805
- * easy.on_success { |easy| ... } => &lt;old handler&gt;
1989
+ * easy.on_success { |easy| ... } => <old handler>
1806
1990
  *
1807
1991
  * Assign or remove the +on_success+ handler for this Curl::Easy instance.
1808
1992
  * To remove a previously-supplied handler, call this method with no
@@ -1817,7 +2001,7 @@ static VALUE ruby_curl_easy_on_success_set(int argc, VALUE *argv, VALUE self) {
1817
2001
 
1818
2002
  /*
1819
2003
  * call-seq:
1820
- * easy.on_failure {|easy,code| ... } => &lt;old handler&gt;
2004
+ * easy.on_failure {|easy,code| ... } => <old handler>
1821
2005
  *
1822
2006
  * Assign or remove the +on_failure+ handler for this Curl::Easy instance.
1823
2007
  * To remove a previously-supplied handler, call this method with no
@@ -1832,13 +2016,13 @@ static VALUE ruby_curl_easy_on_failure_set(int argc, VALUE *argv, VALUE self) {
1832
2016
 
1833
2017
  /*
1834
2018
  * call-seq:
1835
- * easy.on_missing {|easy,code| ... } => &lt;old handler;&gt;
2019
+ * easy.on_missing {|easy,code| ... } => <old handler;>
1836
2020
  *
1837
2021
  * Assign or remove the on_missing handler for this Curl::Easy instance.
1838
2022
  * To remove a previously-supplied handler, call this method with no attached
1839
2023
  * block.
1840
2024
  *
1841
- * The +on_missing+ handler is called when request is finished with a
2025
+ * The +on_missing+ handler is called when request is finished with a
1842
2026
  * status of 40x
1843
2027
  */
1844
2028
  static VALUE ruby_curl_easy_on_missing_set(int argc, VALUE *argv, VALUE self) {
@@ -1847,13 +2031,13 @@ static VALUE ruby_curl_easy_on_missing_set(int argc, VALUE *argv, VALUE self) {
1847
2031
 
1848
2032
  /*
1849
2033
  * call-seq:
1850
- * easy.on_redirect {|easy,code| ... } => &lt;old handler;&gt;
2034
+ * easy.on_redirect {|easy,code| ... } => <old handler;>
1851
2035
  *
1852
2036
  * Assign or remove the on_redirect handler for this Curl::Easy instance.
1853
2037
  * To remove a previously-supplied handler, call this method with no attached
1854
2038
  * block.
1855
2039
  *
1856
- * The +on_redirect+ handler is called when request is finished with a
2040
+ * The +on_redirect+ handler is called when request is finished with a
1857
2041
  * status of 30x
1858
2042
  */
1859
2043
  static VALUE ruby_curl_easy_on_redirect_set(int argc, VALUE *argv, VALUE self) {
@@ -1862,7 +2046,7 @@ static VALUE ruby_curl_easy_on_redirect_set(int argc, VALUE *argv, VALUE self) {
1862
2046
 
1863
2047
  /*
1864
2048
  * call-seq:
1865
- * easy.on_complete {|easy| ... } => &lt;old handler&gt;
2049
+ * easy.on_complete {|easy| ... } => <old handler>
1866
2050
  *
1867
2051
  * Assign or remove the +on_complete+ handler for this Curl::Easy instance.
1868
2052
  * To remove a previously-supplied handler, call this method with no
@@ -1876,7 +2060,7 @@ static VALUE ruby_curl_easy_on_complete_set(int argc, VALUE *argv, VALUE self) {
1876
2060
 
1877
2061
  /*
1878
2062
  * call-seq:
1879
- * easy.on_header { |header_data| ... } => &lt;old handler&gt;
2063
+ * easy.on_header { |header_data| ... } => <old handler>
1880
2064
  *
1881
2065
  * Assign or remove the +on_header+ handler for this Curl::Easy instance.
1882
2066
  * To remove a previously-supplied handler, call this method with no
@@ -1892,7 +2076,7 @@ static VALUE ruby_curl_easy_on_header_set(int argc, VALUE *argv, VALUE self) {
1892
2076
 
1893
2077
  /*
1894
2078
  * call-seq:
1895
- * easy.on_progress { |dl_total, dl_now, ul_total, ul_now| ... } => &lt;old handler&gt;
2079
+ * easy.on_progress { |dl_total, dl_now, ul_total, ul_now| ... } => <old handler>
1896
2080
  *
1897
2081
  * Assign or remove the +on_progress+ handler for this Curl::Easy instance.
1898
2082
  * To remove a previously-supplied handler, call this method with no
@@ -1913,7 +2097,7 @@ static VALUE ruby_curl_easy_on_progress_set(int argc, VALUE *argv, VALUE self) {
1913
2097
 
1914
2098
  /*
1915
2099
  * call-seq:
1916
- * easy.on_debug { |type, data| ... } => &lt;old handler&gt;
2100
+ * easy.on_debug { |type, data| ... } => <old handler>
1917
2101
  *
1918
2102
  * Assign or remove the +on_debug+ handler for this Curl::Easy instance.
1919
2103
  * To remove a previously-supplied handler, call this method with no
@@ -1952,11 +2136,14 @@ static VALUE cb_each_http_header(VALUE header, VALUE wrap) {
1952
2136
 
1953
2137
  name = rb_obj_as_string(rb_ary_entry(header, 0));
1954
2138
  value = rb_obj_as_string(rb_ary_entry(header, 1));
1955
-
1956
- // This is a bit inefficient, but we don't want to be modifying
1957
- // the actual values in the original hash.
1958
- header_str = rb_str_plus(name, rb_str_new2(": "));
1959
- header_str = rb_str_plus(header_str, value);
2139
+ if (rb_str_strlen(value) == 0) { // removing the header e.g. Accept: with nothing trailing should remove it see: https://curl.se/libcurl/c/CURLOPT_HTTPHEADER.html
2140
+ header_str = rb_str_plus(name, rb_str_new2(":"));
2141
+ } else {
2142
+ // This is a bit inefficient, but we don't want to be modifying
2143
+ // the actual values in the original hash.
2144
+ header_str = rb_str_plus(name, rb_str_new2(": "));
2145
+ header_str = rb_str_plus(header_str, value);
2146
+ }
1960
2147
  } else {
1961
2148
  header_str = rb_obj_as_string(header);
1962
2149
  }
@@ -1967,6 +2154,38 @@ static VALUE cb_each_http_header(VALUE header, VALUE wrap) {
1967
2154
  return header_str;
1968
2155
  }
1969
2156
 
2157
+ /***********************************************
2158
+ * This is an rb_iterate callback used to set up http proxy headers.
2159
+ */
2160
+ static VALUE cb_each_http_proxy_header(VALUE proxy_header, VALUE wrap) {
2161
+ struct curl_slist **list;
2162
+ VALUE proxy_header_str = Qnil;
2163
+
2164
+ Data_Get_Struct(wrap, struct curl_slist *, list);
2165
+
2166
+ //rb_p(proxy_header);
2167
+
2168
+ if (rb_type(proxy_header) == T_ARRAY) {
2169
+ // we're processing a hash, proxy header is [name, val]
2170
+ VALUE name, value;
2171
+
2172
+ name = rb_obj_as_string(rb_ary_entry(proxy_header, 0));
2173
+ value = rb_obj_as_string(rb_ary_entry(proxy_header, 1));
2174
+
2175
+ // This is a bit inefficient, but we don't want to be modifying
2176
+ // the actual values in the original hash.
2177
+ proxy_header_str = rb_str_plus(name, rb_str_new2(": "));
2178
+ proxy_header_str = rb_str_plus(proxy_header_str, value);
2179
+ } else {
2180
+ proxy_header_str = rb_obj_as_string(proxy_header);
2181
+ }
2182
+
2183
+ //rb_p(header_str);
2184
+
2185
+ *list = curl_slist_append(*list, StringValuePtr(proxy_header_str));
2186
+ return proxy_header_str;
2187
+ }
2188
+
1970
2189
  /***********************************************
1971
2190
  * This is an rb_iterate callback used to set up ftp commands.
1972
2191
  */
@@ -1981,6 +2200,20 @@ static VALUE cb_each_ftp_command(VALUE ftp_command, VALUE wrap) {
1981
2200
  return ftp_command_string;
1982
2201
  }
1983
2202
 
2203
+ /***********************************************
2204
+ * This is an rb_iterate callback used to set up the resolve list.
2205
+ */
2206
+ static VALUE cb_each_resolve(VALUE resolve, VALUE wrap) {
2207
+ struct curl_slist **list;
2208
+ VALUE resolve_string;
2209
+ Data_Get_Struct(wrap, struct curl_slist *, list);
2210
+
2211
+ resolve_string = rb_obj_as_string(resolve);
2212
+ *list = curl_slist_append(*list, StringValuePtr(resolve));
2213
+
2214
+ return resolve_string;
2215
+ }
2216
+
1984
2217
  /***********************************************
1985
2218
  *
1986
2219
  * Setup a connection
@@ -1992,7 +2225,9 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
1992
2225
  CURL *curl;
1993
2226
  VALUE url, _url = rb_easy_get("url");
1994
2227
  struct curl_slist **hdrs = &(rbce->curl_headers);
2228
+ struct curl_slist **phdrs = &(rbce->curl_proxy_headers);
1995
2229
  struct curl_slist **cmds = &(rbce->curl_ftp_commands);
2230
+ struct curl_slist **rslv = &(rbce->curl_resolve);
1996
2231
 
1997
2232
  curl = rbce->curl;
1998
2233
 
@@ -2001,7 +2236,6 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2001
2236
  }
2002
2237
 
2003
2238
  url = rb_check_string_type(_url);
2004
-
2005
2239
  curl_easy_setopt(curl, CURLOPT_URL, StringValuePtr(url));
2006
2240
 
2007
2241
  // network stuff and auth
@@ -2114,15 +2348,14 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2114
2348
 
2115
2349
  curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, rbce->unrestricted_auth);
2116
2350
 
2117
- /* timeouts override each other we cannot blindly set timeout and timeout_ms */
2118
- if (rbce->timeout && rbce->timeout > 0) {
2119
- curl_easy_setopt(curl, CURLOPT_TIMEOUT, rbce->timeout);
2120
- }
2121
- if (rbce->timeout_ms && rbce->timeout_ms > 0) {
2122
- curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, rbce->timeout_ms);
2123
- }
2351
+ #if HAVE_CURLOPT_TIMEOUT_MS
2352
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, rbce->timeout_ms);
2353
+ #endif
2354
+
2124
2355
  curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, rbce->connect_timeout);
2356
+ #if HAVE_CURLOPT_CONNECTTIMEOUT_MS
2125
2357
  curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, rbce->connect_timeout_ms);
2358
+ #endif
2126
2359
  curl_easy_setopt(curl, CURLOPT_DNS_CACHE_TIMEOUT, rbce->dns_cache_timeout);
2127
2360
 
2128
2361
  curl_easy_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, rbce->ignore_content_length);
@@ -2141,6 +2374,9 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2141
2374
  curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, rbce->low_speed_limit);
2142
2375
  curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, rbce->low_speed_time);
2143
2376
 
2377
+ curl_easy_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE, rbce->max_recv_speed_large);
2378
+ curl_easy_setopt(curl, CURLOPT_MAX_SEND_SPEED_LARGE, rbce->max_send_speed_large);
2379
+
2144
2380
  // Set up localport / proxy port
2145
2381
  // FIXME these won't get returned to default if they're unset Ruby
2146
2382
  if (rbce->proxy_port > 0) {
@@ -2247,7 +2483,7 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2247
2483
  rb_warn("libcurl is not configured with SSL support");
2248
2484
  }
2249
2485
  #endif
2250
-
2486
+
2251
2487
  if (rbce->ftp_filemethod > 0) {
2252
2488
  curl_easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, rbce->ftp_filemethod);
2253
2489
  }
@@ -2274,6 +2510,25 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2274
2510
  }
2275
2511
  }
2276
2512
 
2513
+ #if HAVE_CURLOPT_PROXYHEADER
2514
+ /* Setup HTTP proxy headers if necessary */
2515
+ curl_easy_setopt(curl, CURLOPT_PROXYHEADER, NULL); // XXX: maybe we shouldn't be clearing this?
2516
+
2517
+ if (!rb_easy_nil("proxy_headers")) {
2518
+ if (rb_easy_type_check("proxy_headers", T_ARRAY) || rb_easy_type_check("proxy_headers", T_HASH)) {
2519
+ VALUE wrap = Data_Wrap_Struct(rb_cObject, 0, 0, phdrs);
2520
+ rb_iterate(rb_each, rb_easy_get("proxy_headers"), cb_each_http_proxy_header, wrap);
2521
+ } else {
2522
+ VALUE proxy_headers_str = rb_obj_as_string(rb_easy_get("proxy_headers"));
2523
+ *phdrs = curl_slist_append(*hdrs, StringValuePtr(proxy_headers_str));
2524
+ }
2525
+
2526
+ if (*phdrs) {
2527
+ curl_easy_setopt(curl, CURLOPT_PROXYHEADER, *phdrs);
2528
+ }
2529
+ }
2530
+ #endif
2531
+
2277
2532
  /* Setup FTP commands if necessary */
2278
2533
  if (!rb_easy_nil("ftp_commands")) {
2279
2534
  if (rb_easy_type_check("ftp_commands", T_ARRAY)) {
@@ -2286,18 +2541,33 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2286
2541
  }
2287
2542
  }
2288
2543
 
2544
+ #if HAVE_CURLOPT_RESOLVE
2545
+ /* Setup resolve list if necessary */
2546
+ if (!rb_easy_nil("resolve")) {
2547
+ if (rb_easy_type_check("resolve", T_ARRAY)) {
2548
+ VALUE wrap = Data_Wrap_Struct(rb_cObject, 0, 0, rslv);
2549
+ rb_iterate(rb_each, rb_easy_get("resolve"), cb_each_resolve, wrap);
2550
+ }
2551
+
2552
+ if (*rslv) {
2553
+ curl_easy_setopt(curl, CURLOPT_RESOLVE, *rslv);
2554
+ }
2555
+ }
2556
+ #endif
2557
+
2289
2558
  return Qnil;
2290
2559
  }
2291
2560
  /***********************************************
2292
2561
  *
2293
2562
  * Clean up a connection
2294
2563
  *
2295
- * Always returns Qtrue.
2564
+ * Always returns Qnil.
2296
2565
  */
2297
2566
  VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
2298
2567
 
2299
2568
  CURL *curl = rbce->curl;
2300
2569
  struct curl_slist *ftp_commands;
2570
+ struct curl_slist *resolve;
2301
2571
 
2302
2572
  /* Free everything up */
2303
2573
  if (rbce->curl_headers) {
@@ -2305,12 +2575,23 @@ VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
2305
2575
  rbce->curl_headers = NULL;
2306
2576
  }
2307
2577
 
2578
+ if (rbce->curl_proxy_headers) {
2579
+ curl_slist_free_all(rbce->curl_proxy_headers);
2580
+ rbce->curl_proxy_headers = NULL;
2581
+ }
2582
+
2308
2583
  ftp_commands = rbce->curl_ftp_commands;
2309
2584
  if (ftp_commands) {
2310
2585
  curl_slist_free_all(ftp_commands);
2311
2586
  rbce->curl_ftp_commands = NULL;
2312
2587
  }
2313
2588
 
2589
+ resolve = rbce->curl_resolve;
2590
+ if (resolve) {
2591
+ curl_slist_free_all(resolve);
2592
+ rbce->curl_resolve = NULL;
2593
+ }
2594
+
2314
2595
  /* clean up a PUT request's curl options. */
2315
2596
  if (!rb_easy_nil("upload")) {
2316
2597
  rb_easy_del("upload"); // set the upload object to Qnil to let the GC clean up
@@ -2320,6 +2601,9 @@ VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
2320
2601
  curl_easy_setopt(curl, CURLOPT_INFILESIZE, 0);
2321
2602
  }
2322
2603
 
2604
+ // set values on cleanup to nil
2605
+ rb_easy_del("multi");
2606
+
2323
2607
  return Qnil;
2324
2608
  }
2325
2609
 
@@ -2334,6 +2618,8 @@ static VALUE ruby_curl_easy_perform_verb_str(VALUE self, const char *verb) {
2334
2618
  Data_Get_Struct(self, ruby_curl_easy, rbce);
2335
2619
  curl = rbce->curl;
2336
2620
 
2621
+ memset(rbce->err_buf, 0, sizeof(rbce->err_buf));
2622
+
2337
2623
  curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, verb);
2338
2624
 
2339
2625
  retval = rb_funcall(self, rb_intern("perform"), 0);
@@ -2399,6 +2685,8 @@ static VALUE ruby_curl_easy_perform_post(int argc, VALUE *argv, VALUE self) {
2399
2685
  Data_Get_Struct(self, ruby_curl_easy, rbce);
2400
2686
  curl = rbce->curl;
2401
2687
 
2688
+ memset(rbce->err_buf, 0, sizeof(rbce->err_buf));
2689
+
2402
2690
  curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, NULL);
2403
2691
 
2404
2692
  if (rbce->multipart_form_post) {
@@ -2470,6 +2758,8 @@ static VALUE ruby_curl_easy_perform_put(VALUE self, VALUE data) {
2470
2758
  Data_Get_Struct(self, ruby_curl_easy, rbce);
2471
2759
  curl = rbce->curl;
2472
2760
 
2761
+ memset(rbce->err_buf, 0, sizeof(rbce->err_buf));
2762
+
2473
2763
  curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, NULL);
2474
2764
  ruby_curl_easy_put_data_set(self, data);
2475
2765
 
@@ -2488,6 +2778,10 @@ static VALUE ruby_curl_easy_perform_put(VALUE self, VALUE data) {
2488
2778
  * your own body handler, this string will be empty.
2489
2779
  */
2490
2780
  static VALUE ruby_curl_easy_body_str_get(VALUE self) {
2781
+ /*
2782
+ TODO: can we force_encoding on the return here if we see charset=utf-8 in the content-type header?
2783
+ Content-Type: application/json; charset=utf-8
2784
+ */
2491
2785
  CURB_OBJECT_HGETTER(ruby_curl_easy, body_data);
2492
2786
  }
2493
2787
 
@@ -2564,7 +2858,7 @@ static VALUE ruby_curl_easy_response_code_get(VALUE self) {
2564
2858
  static VALUE ruby_curl_easy_primary_ip_get(VALUE self) {
2565
2859
  ruby_curl_easy *rbce;
2566
2860
  char* ip;
2567
-
2861
+
2568
2862
  Data_Get_Struct(self, ruby_curl_easy, rbce);
2569
2863
  curl_easy_getinfo(rbce->curl, CURLINFO_PRIMARY_IP, &ip);
2570
2864
 
@@ -2682,7 +2976,7 @@ static VALUE ruby_curl_easy_connect_time_get(VALUE self) {
2682
2976
  * Retrieve the time, in seconds, it took from the start until the SSL/SSH
2683
2977
  * connect/handshake to the remote host was completed. This time is most often
2684
2978
  * very near to the pre transfer time, except for cases such as HTTP
2685
- * pippelining where the pretransfer time can be delayed due to waits in line
2979
+ * pipelining where the pretransfer time can be delayed due to waits in line
2686
2980
  * for the pipeline and more.
2687
2981
  */
2688
2982
  #if defined(HAVE_CURLINFO_APPCONNECT_TIME)
@@ -2789,7 +3083,7 @@ static VALUE ruby_curl_easy_redirect_count_get(VALUE self) {
2789
3083
  * call-seq:
2790
3084
  * easy.redirect_url => "http://some.url" or nil
2791
3085
  *
2792
- * Retrieve the URL a redirect would take you to if you
3086
+ * Retrieve the URL a redirect would take you to if you
2793
3087
  * would enable CURLOPT_FOLLOWLOCATION.
2794
3088
  *
2795
3089
  * Requires libcurl 7.18.2 or higher, otherwise -1 is always returned.
@@ -3072,12 +3366,40 @@ static VALUE ruby_curl_easy_num_connects_get(VALUE self) {
3072
3366
  }
3073
3367
 
3074
3368
 
3075
- /* TODO this needs to be implemented.
3369
+ /*
3370
+ * call-seq:
3371
+ * easy.cookielist => array
3372
+ *
3373
+ * Retrieves the cookies curl knows in an array of strings.
3374
+ * Returned strings are in Netscape cookiejar format or in Set-Cookie format.
3375
+ *
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).
3379
+ */
3380
+ static VALUE ruby_curl_easy_cookielist_get(VALUE self) {
3381
+ #ifdef HAVE_CURLINFO_COOKIELIST
3382
+ ruby_curl_easy *rbce;
3383
+ struct curl_slist *cookies;
3384
+ struct curl_slist *cookie;
3385
+ VALUE rb_cookies;
3386
+
3387
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
3388
+ curl_easy_getinfo(rbce->curl, CURLINFO_COOKIELIST, &cookies);
3389
+ if (!cookies)
3390
+ return Qnil;
3391
+ rb_cookies = rb_ary_new();
3392
+ for (cookie = cookies; cookie; cookie = cookie->next)
3393
+ rb_ary_push(rb_cookies, rb_str_new2(cookie->data));
3394
+ curl_slist_free_all(cookies);
3395
+ return rb_cookies;
3076
3396
 
3077
- CURLINFO_COOKIELIST
3397
+ #else
3398
+ rb_warn("Installed libcurl is too old to support cookielist");
3399
+ return INT2FIX(-1);
3400
+ #endif
3401
+ }
3078
3402
 
3079
- Pass a pointer to a 'struct curl_slist *' to receive a linked-list of all cookies cURL knows (expired ones, too). Don't forget to curl_slist_free_all(3) the list after it has been used. If there are no cookies (cookies for the handle have not been enabled or simply none have been received) 'struct curl_slist *' will be set to point to NULL. (Added in 7.14.1)
3080
- */
3081
3403
 
3082
3404
  /* TODO this needs to be implemented. Could probably support CONNECT_ONLY by having this
3083
3405
  * return an open Socket or something.
@@ -3147,6 +3469,21 @@ static VALUE ruby_curl_easy_last_result(VALUE self) {
3147
3469
  return LONG2NUM(rbce->last_result);
3148
3470
  }
3149
3471
 
3472
+ /*
3473
+ * call-seq:
3474
+ * easy.last_error => "Error details" or nil
3475
+ */
3476
+ static VALUE ruby_curl_easy_last_error(VALUE self) {
3477
+ ruby_curl_easy *rbce;
3478
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
3479
+
3480
+ if (rbce->err_buf[0]) { // curl returns NULL or empty string if none
3481
+ return rb_str_new2(rbce->err_buf);
3482
+ } else {
3483
+ return Qnil;
3484
+ }
3485
+ }
3486
+
3150
3487
  /*
3151
3488
  * call-seq:
3152
3489
  * easy.setopt Fixnum, value => value
@@ -3156,6 +3493,7 @@ static VALUE ruby_curl_easy_last_result(VALUE self) {
3156
3493
  static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3157
3494
  ruby_curl_easy *rbce;
3158
3495
  long option = NUM2LONG(opt);
3496
+ rb_io_t *open_f_ptr;
3159
3497
 
3160
3498
  Data_Get_Struct(self, ruby_curl_easy, rbce);
3161
3499
 
@@ -3193,6 +3531,12 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3193
3531
  case CURLOPT_HEADER:
3194
3532
  case CURLOPT_NOPROGRESS:
3195
3533
  case CURLOPT_NOSIGNAL:
3534
+ #if HAVE_CURLOPT_PATH_AS_IS
3535
+ case CURLOPT_PATH_AS_IS:
3536
+ #endif
3537
+ #if HAVE_CURLOPT_PIPEWAIT
3538
+ case CURLOPT_PIPEWAIT:
3539
+ #endif
3196
3540
  case CURLOPT_HTTPGET:
3197
3541
  case CURLOPT_NOBODY: {
3198
3542
  int type = rb_type(val);
@@ -3238,6 +3582,9 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3238
3582
  case CURLOPT_TCP_NODELAY: {
3239
3583
  curl_easy_setopt(rbce->curl, CURLOPT_TCP_NODELAY, NUM2LONG(val));
3240
3584
  } break;
3585
+ case CURLOPT_RANGE: {
3586
+ curl_easy_setopt(rbce->curl, CURLOPT_RANGE, StringValueCStr(val));
3587
+ } break;
3241
3588
  case CURLOPT_RESUME_FROM: {
3242
3589
  curl_easy_setopt(rbce->curl, CURLOPT_RESUME_FROM, NUM2LONG(val));
3243
3590
  } break;
@@ -3259,6 +3606,58 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3259
3606
  case CURLOPT_UNIX_SOCKET_PATH: {
3260
3607
  curl_easy_setopt(rbce->curl, CURLOPT_UNIX_SOCKET_PATH, StringValueCStr(val));
3261
3608
  } break;
3609
+ #endif
3610
+ #if HAVE_CURLOPT_MAX_SEND_SPEED_LARGE
3611
+ case CURLOPT_MAX_SEND_SPEED_LARGE: {
3612
+ curl_easy_setopt(rbce->curl, CURLOPT_MAX_SEND_SPEED_LARGE, (curl_off_t) NUM2LL(val));
3613
+ } break;
3614
+ #endif
3615
+ #if HAVE_CURLOPT_MAX_RECV_SPEED_LARGE
3616
+ case CURLOPT_MAX_RECV_SPEED_LARGE: {
3617
+ curl_easy_setopt(rbce->curl, CURLOPT_MAX_RECV_SPEED_LARGE, (curl_off_t) NUM2LL(val));
3618
+ } break;
3619
+ #endif
3620
+ #if HAVE_CURLOPT_MAXFILESIZE
3621
+ case CURLOPT_MAXFILESIZE:
3622
+ curl_easy_setopt(rbce->curl, CURLOPT_MAXFILESIZE, NUM2LONG(val));
3623
+ break;
3624
+ #endif
3625
+ #if HAVE_CURLOPT_TCP_KEEPALIVE
3626
+ case CURLOPT_TCP_KEEPALIVE:
3627
+ curl_easy_setopt(rbce->curl, CURLOPT_TCP_KEEPALIVE, NUM2LONG(val));
3628
+ break;
3629
+ case CURLOPT_TCP_KEEPIDLE:
3630
+ curl_easy_setopt(rbce->curl, CURLOPT_TCP_KEEPIDLE, NUM2LONG(val));
3631
+ break;
3632
+ case CURLOPT_TCP_KEEPINTVL:
3633
+ curl_easy_setopt(rbce->curl, CURLOPT_TCP_KEEPINTVL, NUM2LONG(val));
3634
+ break;
3635
+ #endif
3636
+ #if HAVE_CURLOPT_HAPROXYPROTOCOL
3637
+ case CURLOPT_HAPROXYPROTOCOL:
3638
+ curl_easy_setopt(rbce->curl, CURLOPT_HAPROXYPROTOCOL, NUM2LONG(val));
3639
+ break;
3640
+ #endif
3641
+ case CURLOPT_STDERR:
3642
+ // libcurl requires raw FILE pointer and this should be IO object in Ruby.
3643
+ // Tempfile or StringIO won't work.
3644
+ Check_Type(val, T_FILE);
3645
+ GetOpenFile(val, open_f_ptr);
3646
+ curl_easy_setopt(rbce->curl, CURLOPT_STDERR, rb_io_stdio_file(open_f_ptr));
3647
+ break;
3648
+ case CURLOPT_PROTOCOLS:
3649
+ case CURLOPT_REDIR_PROTOCOLS:
3650
+ curl_easy_setopt(rbce->curl, option, NUM2LONG(val));
3651
+ break;
3652
+ #if HAVE_CURLOPT_SSL_SESSIONID_CACHE
3653
+ case CURLOPT_SSL_SESSIONID_CACHE:
3654
+ curl_easy_setopt(rbce->curl, CURLOPT_SSL_SESSIONID_CACHE, NUM2LONG(val));
3655
+ break;
3656
+ #endif
3657
+ #if HAVE_CURLOPT_PROXY_SSL_VERIFYHOST
3658
+ case CURLOPT_PROXY_SSL_VERIFYHOST:
3659
+ curl_easy_setopt(rbce->curl, CURLOPT_PROXY_SSL_VERIFYHOST, NUM2LONG(val));
3660
+ break;
3262
3661
  #endif
3263
3662
  default:
3264
3663
  rb_raise(rb_eTypeError, "Curb unsupported option");
@@ -3376,6 +3775,7 @@ static VALUE ruby_curl_easy_error_message(VALUE klass, VALUE code) {
3376
3775
  }
3377
3776
 
3378
3777
  /* =================== INIT LIB =====================*/
3778
+ // TODO: https://bugs.ruby-lang.org/issues/18007
3379
3779
  void init_curb_easy() {
3380
3780
  idCall = rb_intern("call");
3381
3781
  idJoin = rb_intern("join");
@@ -3386,12 +3786,19 @@ void init_curb_easy() {
3386
3786
  cCurlEasy = rb_define_class_under(mCurl, "Easy", rb_cObject);
3387
3787
 
3388
3788
  /* Class methods */
3389
- rb_define_singleton_method(cCurlEasy, "new", ruby_curl_easy_new, -1);
3789
+ rb_define_alloc_func(cCurlEasy, ruby_curl_easy_allocate);
3390
3790
  rb_define_singleton_method(cCurlEasy, "error", ruby_curl_easy_error_message, 1);
3391
3791
 
3792
+ /* Initialize method */
3793
+ rb_define_method(cCurlEasy, "initialize", ruby_curl_easy_initialize, -1);
3794
+
3392
3795
  /* Attributes for config next perform */
3393
3796
  rb_define_method(cCurlEasy, "url", ruby_curl_easy_url_get, 0);
3394
3797
  rb_define_method(cCurlEasy, "proxy_url", ruby_curl_easy_proxy_url_get, 0);
3798
+
3799
+ rb_define_method(cCurlEasy, "proxy_headers=", ruby_curl_easy_proxy_headers_set, 1);
3800
+ rb_define_method(cCurlEasy, "proxy_headers", ruby_curl_easy_proxy_headers_get, 0);
3801
+
3395
3802
  rb_define_method(cCurlEasy, "headers=", ruby_curl_easy_headers_set, 1);
3396
3803
  rb_define_method(cCurlEasy, "headers", ruby_curl_easy_headers_get, 0);
3397
3804
  rb_define_method(cCurlEasy, "interface", ruby_curl_easy_interface_get, 0);
@@ -3418,6 +3825,8 @@ void init_curb_easy() {
3418
3825
  rb_define_method(cCurlEasy, "put_data=", ruby_curl_easy_put_data_set, 1);
3419
3826
  rb_define_method(cCurlEasy, "ftp_commands=", ruby_curl_easy_ftp_commands_set, 1);
3420
3827
  rb_define_method(cCurlEasy, "ftp_commands", ruby_curl_easy_ftp_commands_get, 0);
3828
+ rb_define_method(cCurlEasy, "resolve=", ruby_curl_easy_resolve_set, 1);
3829
+ rb_define_method(cCurlEasy, "resolve", ruby_curl_easy_resolve_get, 0);
3421
3830
 
3422
3831
  rb_define_method(cCurlEasy, "local_port=", ruby_curl_easy_local_port_set, 1);
3423
3832
  rb_define_method(cCurlEasy, "local_port", ruby_curl_easy_local_port_get, 0);
@@ -3449,6 +3858,10 @@ void init_curb_easy() {
3449
3858
  rb_define_method(cCurlEasy, "low_speed_limit", ruby_curl_easy_low_speed_limit_get, 0);
3450
3859
  rb_define_method(cCurlEasy, "low_speed_time=", ruby_curl_easy_low_speed_time_set, 1);
3451
3860
  rb_define_method(cCurlEasy, "low_speed_time", ruby_curl_easy_low_speed_time_get, 0);
3861
+ rb_define_method(cCurlEasy, "max_send_speed_large=", ruby_curl_easy_max_send_speed_large_set, 1);
3862
+ rb_define_method(cCurlEasy, "max_send_speed_large", ruby_curl_easy_max_send_speed_large_get, 0);
3863
+ rb_define_method(cCurlEasy, "max_recv_speed_large=", ruby_curl_easy_max_recv_speed_large_set, 1);
3864
+ rb_define_method(cCurlEasy, "max_recv_speed_large", ruby_curl_easy_max_recv_speed_large_get, 0);
3452
3865
  rb_define_method(cCurlEasy, "ssl_version=", ruby_curl_easy_ssl_version_set, 1);
3453
3866
  rb_define_method(cCurlEasy, "ssl_version", ruby_curl_easy_ssl_version_get, 0);
3454
3867
  rb_define_method(cCurlEasy, "use_ssl=", ruby_curl_easy_use_ssl_set, 1);
@@ -3536,6 +3949,7 @@ void init_curb_easy() {
3536
3949
  rb_define_method(cCurlEasy, "content_type", ruby_curl_easy_content_type_get, 0);
3537
3950
  rb_define_method(cCurlEasy, "os_errno", ruby_curl_easy_os_errno_get, 0);
3538
3951
  rb_define_method(cCurlEasy, "num_connects", ruby_curl_easy_num_connects_get, 0);
3952
+ rb_define_method(cCurlEasy, "cookielist", ruby_curl_easy_cookielist_get, 0);
3539
3953
  rb_define_method(cCurlEasy, "ftp_entry_path", ruby_curl_easy_ftp_entry_path_get, 0);
3540
3954
 
3541
3955
  rb_define_method(cCurlEasy, "close", ruby_curl_easy_close, 0);
@@ -3553,6 +3967,7 @@ void init_curb_easy() {
3553
3967
  rb_define_method(cCurlEasy, "multi", ruby_curl_easy_multi_get, 0);
3554
3968
  rb_define_method(cCurlEasy, "multi=", ruby_curl_easy_multi_set, 1);
3555
3969
  rb_define_method(cCurlEasy, "last_result", ruby_curl_easy_last_result, 0);
3970
+ rb_define_method(cCurlEasy, "last_error", ruby_curl_easy_last_error, 0);
3556
3971
 
3557
3972
  rb_define_method(cCurlEasy, "setopt", ruby_curl_easy_set_opt, 2);
3558
3973
  rb_define_method(cCurlEasy, "getinfo", ruby_curl_easy_get_opt, 1);