curb 0.9.11 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/ext/curb_easy.c CHANGED
@@ -34,7 +34,7 @@ static FILE * rb_io_stdio_file(rb_io_t *fptr) {
34
34
 
35
35
  /* ================== CURL HANDLER FUNCS ==============*/
36
36
 
37
- static VALUE callback_exception(VALUE unused) {
37
+ static VALUE callback_exception(VALUE unused, VALUE exception) {
38
38
  return Qfalse;
39
39
  }
40
40
 
@@ -268,6 +268,8 @@ void curl_easy_free(ruby_curl_easy *rbce) {
268
268
  static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
269
269
  rbce->opts = rb_hash_new();
270
270
 
271
+ memset(rbce->err_buf, 0, sizeof(rbce->err_buf));
272
+
271
273
  rbce->curl_headers = NULL;
272
274
  rbce->curl_proxy_headers = NULL;
273
275
  rbce->curl_ftp_commands = NULL;
@@ -327,9 +329,9 @@ static VALUE ruby_curl_easy_allocate(VALUE klass) {
327
329
 
328
330
  /*
329
331
  * call-seq:
330
- * Curl::Easy.new => #<Curl::Easy...>
331
- * Curl::Easy.new(url = nil) => #<Curl::Easy...>
332
- * 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...>
333
335
  *
334
336
  * Initialize a new Curl::Easy instance, optionally supplying the URL.
335
337
  * The block form allows further configuration to be supplied before
@@ -355,8 +357,9 @@ static VALUE ruby_curl_easy_initialize(int argc, VALUE *argv, VALUE self) {
355
357
 
356
358
  ruby_curl_easy_zero(rbce);
357
359
 
358
- rb_easy_set("url", url);
360
+ curl_easy_setopt(rbce->curl, CURLOPT_ERRORBUFFER, &rbce->err_buf);
359
361
 
362
+ rb_easy_set("url", url);
360
363
 
361
364
  /* set the pointer to the curl handle */
362
365
  ecode = curl_easy_setopt(rbce->curl, CURLOPT_PRIVATE, (void*)self);
@@ -373,8 +376,8 @@ static VALUE ruby_curl_easy_initialize(int argc, VALUE *argv, VALUE self) {
373
376
 
374
377
  /*
375
378
  * call-seq:
376
- * easy.clone => #&lt;easy clone&gt;
377
- * easy.dup => #&lt;easy clone&gt;
379
+ * easy.clone => <easy clone>
380
+ * easy.dup => <easy clone>
378
381
  *
379
382
  * Clone this Curl::Easy instance, creating a new instance.
380
383
  * This method duplicates the underlying CURL* handle.
@@ -392,6 +395,8 @@ static VALUE ruby_curl_easy_clone(VALUE self) {
392
395
  newrbce->curl_ftp_commands = NULL;
393
396
  newrbce->curl_resolve = NULL;
394
397
 
398
+ curl_easy_setopt(rbce->curl, CURLOPT_ERRORBUFFER, &rbce->err_buf);
399
+
395
400
  return Data_Wrap_Struct(cCurlEasy, curl_easy_mark, curl_easy_free, newrbce);
396
401
  }
397
402
 
@@ -461,7 +466,9 @@ static VALUE ruby_curl_easy_reset(VALUE self) {
461
466
  curl_easy_reset(rbce->curl);
462
467
  ruby_curl_easy_zero(rbce);
463
468
 
464
- /* 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 */
465
472
  ecode = curl_easy_setopt(rbce->curl, CURLOPT_PRIVATE, (void*)self);
466
473
  if (ecode != CURLE_OK) {
467
474
  raise_curl_easy_error_exception(ecode);
@@ -950,7 +957,7 @@ static VALUE ruby_curl_easy_ftp_commands_set(VALUE self, VALUE ftp_commands) {
950
957
  }
951
958
 
952
959
  /*
953
- * call-seq
960
+ * call-seq:
954
961
  * easy.ftp_commands => array or nil
955
962
  */
956
963
  static VALUE ruby_curl_easy_ftp_commands_get(VALUE self) {
@@ -969,7 +976,7 @@ static VALUE ruby_curl_easy_resolve_set(VALUE self, VALUE resolve) {
969
976
  }
970
977
 
971
978
  /*
972
- * call-seq
979
+ * call-seq:
973
980
  * easy.resolve => array or nil
974
981
  */
975
982
  static VALUE ruby_curl_easy_resolve_get(VALUE self) {
@@ -1304,7 +1311,7 @@ static VALUE ruby_curl_easy_connect_timeout_set(VALUE self, VALUE connect_timeou
1304
1311
  * Obtain the maximum time in seconds that you allow the connection to the
1305
1312
  * server to take.
1306
1313
  */
1307
- static VALUE ruby_curl_easy_connect_timeout_get(VALUE self, VALUE connect_timeout) {
1314
+ static VALUE ruby_curl_easy_connect_timeout_get(VALUE self) {
1308
1315
  CURB_IMMED_GETTER(ruby_curl_easy, connect_timeout, 0);
1309
1316
  }
1310
1317
 
@@ -1330,7 +1337,7 @@ static VALUE ruby_curl_easy_connect_timeout_ms_set(VALUE self, VALUE connect_tim
1330
1337
  * Obtain the maximum time in milliseconds that you allow the connection to the
1331
1338
  * server to take.
1332
1339
  */
1333
- static VALUE ruby_curl_easy_connect_timeout_ms_get(VALUE self, VALUE connect_timeout_ms) {
1340
+ static VALUE ruby_curl_easy_connect_timeout_ms_get(VALUE self) {
1334
1341
  CURB_IMMED_GETTER(ruby_curl_easy, connect_timeout_ms, 0);
1335
1342
  }
1336
1343
 
@@ -1353,7 +1360,7 @@ static VALUE ruby_curl_easy_dns_cache_timeout_set(VALUE self, VALUE dns_cache_ti
1353
1360
  *
1354
1361
  * Obtain the dns cache timeout in seconds.
1355
1362
  */
1356
- static VALUE ruby_curl_easy_dns_cache_timeout_get(VALUE self, VALUE dns_cache_timeout) {
1363
+ static VALUE ruby_curl_easy_dns_cache_timeout_get(VALUE self) {
1357
1364
  CURB_IMMED_GETTER(ruby_curl_easy, dns_cache_timeout, -1);
1358
1365
  }
1359
1366
 
@@ -1380,7 +1387,7 @@ static VALUE ruby_curl_easy_ftp_response_timeout_set(VALUE self, VALUE ftp_respo
1380
1387
  *
1381
1388
  * Obtain the maximum time that libcurl will wait for FTP command responses.
1382
1389
  */
1383
- static VALUE ruby_curl_easy_ftp_response_timeout_get(VALUE self, VALUE ftp_response_timeout) {
1390
+ static VALUE ruby_curl_easy_ftp_response_timeout_get(VALUE self) {
1384
1391
  CURB_IMMED_GETTER(ruby_curl_easy, ftp_response_timeout, 0);
1385
1392
  }
1386
1393
 
@@ -1403,7 +1410,7 @@ static VALUE ruby_curl_easy_low_speed_limit_set(VALUE self, VALUE low_speed_limi
1403
1410
  * Obtain the minimum transfer speed over +low_speed+time+ below which the
1404
1411
  * transfer will be aborted.
1405
1412
  */
1406
- static VALUE ruby_curl_easy_low_speed_limit_get(VALUE self, VALUE low_speed_limit) {
1413
+ static VALUE ruby_curl_easy_low_speed_limit_get(VALUE self) {
1407
1414
  CURB_IMMED_GETTER(ruby_curl_easy, low_speed_limit, 0);
1408
1415
  }
1409
1416
 
@@ -1425,7 +1432,7 @@ static VALUE ruby_curl_easy_low_speed_time_set(VALUE self, VALUE low_speed_time)
1425
1432
  * Obtain the time that the transfer should be below +low_speed_limit+ for
1426
1433
  * the library to abort it.
1427
1434
  */
1428
- static VALUE ruby_curl_easy_low_speed_time_get(VALUE self, VALUE low_speed_time) {
1435
+ static VALUE ruby_curl_easy_low_speed_time_get(VALUE self) {
1429
1436
  CURB_IMMED_GETTER(ruby_curl_easy, low_speed_time, 0);
1430
1437
  }
1431
1438
 
@@ -1445,7 +1452,7 @@ static VALUE ruby_curl_easy_max_send_speed_large_set(VALUE self, VALUE max_send_
1445
1452
  *
1446
1453
  * Get the maximal sending transfer speed (in bytes per second)
1447
1454
  */
1448
- static VALUE ruby_curl_easy_max_send_speed_large_get(VALUE self, VALUE max_send_speed_large) {
1455
+ static VALUE ruby_curl_easy_max_send_speed_large_get(VALUE self) {
1449
1456
  CURB_IMMED_GETTER(ruby_curl_easy, max_send_speed_large, 0);
1450
1457
  }
1451
1458
 
@@ -1465,7 +1472,7 @@ static VALUE ruby_curl_easy_max_recv_speed_large_set(VALUE self, VALUE max_recv_
1465
1472
  *
1466
1473
  * Get the maximal receiving transfer speed (in bytes per second)
1467
1474
  */
1468
- static VALUE ruby_curl_easy_max_recv_speed_large_get(VALUE self, VALUE max_recv_speed_large) {
1475
+ static VALUE ruby_curl_easy_max_recv_speed_large_get(VALUE self) {
1469
1476
  CURB_IMMED_GETTER(ruby_curl_easy, max_recv_speed_large, 0);
1470
1477
  }
1471
1478
 
@@ -1489,7 +1496,7 @@ static VALUE ruby_curl_easy_username_set(VALUE self, VALUE username) {
1489
1496
  *
1490
1497
  * Get the current username
1491
1498
  */
1492
- static VALUE ruby_curl_easy_username_get(VALUE self, VALUE username) {
1499
+ static VALUE ruby_curl_easy_username_get(VALUE self) {
1493
1500
  #if HAVE_CURLOPT_USERNAME
1494
1501
  CURB_OBJECT_HGETTER(ruby_curl_easy, username);
1495
1502
  #else
@@ -1517,7 +1524,7 @@ static VALUE ruby_curl_easy_password_set(VALUE self, VALUE password) {
1517
1524
  *
1518
1525
  * Get the current password
1519
1526
  */
1520
- static VALUE ruby_curl_easy_password_get(VALUE self, VALUE password) {
1527
+ static VALUE ruby_curl_easy_password_get(VALUE self) {
1521
1528
  #if HAVE_CURLOPT_PASSWORD
1522
1529
  CURB_OBJECT_HGETTER(ruby_curl_easy, password);
1523
1530
  #else
@@ -1539,6 +1546,7 @@ static VALUE ruby_curl_easy_password_get(VALUE self, VALUE password) {
1539
1546
  * Curl::CURL_SSLVERSION_TLSv1_0
1540
1547
  * Curl::CURL_SSLVERSION_TLSv1_1
1541
1548
  * Curl::CURL_SSLVERSION_TLSv1_2
1549
+ * Curl::CURL_SSLVERSION_TLSv1_3
1542
1550
  */
1543
1551
  static VALUE ruby_curl_easy_ssl_version_set(VALUE self, VALUE ssl_version) {
1544
1552
  CURB_IMMED_SETTER(ruby_curl_easy, ssl_version, -1);
@@ -1550,7 +1558,7 @@ static VALUE ruby_curl_easy_ssl_version_set(VALUE self, VALUE ssl_version) {
1550
1558
  *
1551
1559
  * Get the version of SSL/TLS that libcurl will attempt to use.
1552
1560
  */
1553
- static VALUE ruby_curl_easy_ssl_version_get(VALUE self, VALUE ssl_version) {
1561
+ static VALUE ruby_curl_easy_ssl_version_get(VALUE self) {
1554
1562
  CURB_IMMED_GETTER(ruby_curl_easy, ssl_version, -1);
1555
1563
  }
1556
1564
 
@@ -1571,7 +1579,7 @@ static VALUE ruby_curl_easy_use_ssl_set(VALUE self, VALUE use_ssl) {
1571
1579
  *
1572
1580
  * Get the desired level for using SSL on FTP connections.
1573
1581
  */
1574
- static VALUE ruby_curl_easy_use_ssl_get(VALUE self, VALUE use_ssl) {
1582
+ static VALUE ruby_curl_easy_use_ssl_get(VALUE self) {
1575
1583
  CURB_IMMED_GETTER(ruby_curl_easy, use_ssl, -1);
1576
1584
  }
1577
1585
 
@@ -1587,12 +1595,12 @@ static VALUE ruby_curl_easy_ftp_filemethod_set(VALUE self, VALUE ftp_filemethod)
1587
1595
  }
1588
1596
 
1589
1597
  /*
1590
- * call-seq
1598
+ * call-seq:
1591
1599
  * easy.ftp_filemethod => fixnum
1592
1600
  *
1593
1601
  * Get the configuration for how libcurl will reach files on the server.
1594
1602
  */
1595
- static VALUE ruby_curl_easy_ftp_filemethod_get(VALUE self, VALUE ftp_filemethod) {
1603
+ static VALUE ruby_curl_easy_ftp_filemethod_get(VALUE self) {
1596
1604
  CURB_IMMED_GETTER(ruby_curl_easy, ftp_filemethod, -1);
1597
1605
  }
1598
1606
 
@@ -1959,7 +1967,7 @@ static VALUE ruby_curl_easy_resolve_mode_set(VALUE self, VALUE resolve_mode) {
1959
1967
 
1960
1968
  /*
1961
1969
  * call-seq:
1962
- * easy.on_body { |body_data| ... } => &lt;old handler&gt;
1970
+ * easy.on_body { |body_data| ... } => <old handler>
1963
1971
  *
1964
1972
  * Assign or remove the +on_body+ handler for this Curl::Easy instance.
1965
1973
  * To remove a previously-supplied handler, call this method with no
@@ -1978,7 +1986,7 @@ static VALUE ruby_curl_easy_on_body_set(int argc, VALUE *argv, VALUE self) {
1978
1986
 
1979
1987
  /*
1980
1988
  * call-seq:
1981
- * easy.on_success { |easy| ... } => &lt;old handler&gt;
1989
+ * easy.on_success { |easy| ... } => <old handler>
1982
1990
  *
1983
1991
  * Assign or remove the +on_success+ handler for this Curl::Easy instance.
1984
1992
  * To remove a previously-supplied handler, call this method with no
@@ -1993,7 +2001,7 @@ static VALUE ruby_curl_easy_on_success_set(int argc, VALUE *argv, VALUE self) {
1993
2001
 
1994
2002
  /*
1995
2003
  * call-seq:
1996
- * easy.on_failure {|easy,code| ... } => &lt;old handler&gt;
2004
+ * easy.on_failure {|easy,code| ... } => <old handler>
1997
2005
  *
1998
2006
  * Assign or remove the +on_failure+ handler for this Curl::Easy instance.
1999
2007
  * To remove a previously-supplied handler, call this method with no
@@ -2008,7 +2016,7 @@ static VALUE ruby_curl_easy_on_failure_set(int argc, VALUE *argv, VALUE self) {
2008
2016
 
2009
2017
  /*
2010
2018
  * call-seq:
2011
- * easy.on_missing {|easy,code| ... } => &lt;old handler;&gt;
2019
+ * easy.on_missing {|easy,code| ... } => <old handler;>
2012
2020
  *
2013
2021
  * Assign or remove the on_missing handler for this Curl::Easy instance.
2014
2022
  * To remove a previously-supplied handler, call this method with no attached
@@ -2023,7 +2031,7 @@ static VALUE ruby_curl_easy_on_missing_set(int argc, VALUE *argv, VALUE self) {
2023
2031
 
2024
2032
  /*
2025
2033
  * call-seq:
2026
- * easy.on_redirect {|easy,code| ... } => &lt;old handler;&gt;
2034
+ * easy.on_redirect {|easy,code| ... } => <old handler;>
2027
2035
  *
2028
2036
  * Assign or remove the on_redirect handler for this Curl::Easy instance.
2029
2037
  * To remove a previously-supplied handler, call this method with no attached
@@ -2038,7 +2046,7 @@ static VALUE ruby_curl_easy_on_redirect_set(int argc, VALUE *argv, VALUE self) {
2038
2046
 
2039
2047
  /*
2040
2048
  * call-seq:
2041
- * easy.on_complete {|easy| ... } => &lt;old handler&gt;
2049
+ * easy.on_complete {|easy| ... } => <old handler>
2042
2050
  *
2043
2051
  * Assign or remove the +on_complete+ handler for this Curl::Easy instance.
2044
2052
  * To remove a previously-supplied handler, call this method with no
@@ -2052,7 +2060,7 @@ static VALUE ruby_curl_easy_on_complete_set(int argc, VALUE *argv, VALUE self) {
2052
2060
 
2053
2061
  /*
2054
2062
  * call-seq:
2055
- * easy.on_header { |header_data| ... } => &lt;old handler&gt;
2063
+ * easy.on_header { |header_data| ... } => <old handler>
2056
2064
  *
2057
2065
  * Assign or remove the +on_header+ handler for this Curl::Easy instance.
2058
2066
  * To remove a previously-supplied handler, call this method with no
@@ -2068,7 +2076,7 @@ static VALUE ruby_curl_easy_on_header_set(int argc, VALUE *argv, VALUE self) {
2068
2076
 
2069
2077
  /*
2070
2078
  * call-seq:
2071
- * 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>
2072
2080
  *
2073
2081
  * Assign or remove the +on_progress+ handler for this Curl::Easy instance.
2074
2082
  * To remove a previously-supplied handler, call this method with no
@@ -2089,7 +2097,7 @@ static VALUE ruby_curl_easy_on_progress_set(int argc, VALUE *argv, VALUE self) {
2089
2097
 
2090
2098
  /*
2091
2099
  * call-seq:
2092
- * easy.on_debug { |type, data| ... } => &lt;old handler&gt;
2100
+ * easy.on_debug { |type, data| ... } => <old handler>
2093
2101
  *
2094
2102
  * Assign or remove the +on_debug+ handler for this Curl::Easy instance.
2095
2103
  * To remove a previously-supplied handler, call this method with no
@@ -2114,7 +2122,7 @@ static VALUE ruby_curl_easy_on_debug_set(int argc, VALUE *argv, VALUE self) {
2114
2122
  /***********************************************
2115
2123
  * This is an rb_iterate callback used to set up http headers.
2116
2124
  */
2117
- static VALUE cb_each_http_header(VALUE header, VALUE wrap) {
2125
+ static VALUE cb_each_http_header(VALUE header, VALUE wrap, int _c, const VALUE *_ptr, VALUE unused) {
2118
2126
  struct curl_slist **list;
2119
2127
  VALUE header_str = Qnil;
2120
2128
 
@@ -2128,11 +2136,14 @@ static VALUE cb_each_http_header(VALUE header, VALUE wrap) {
2128
2136
 
2129
2137
  name = rb_obj_as_string(rb_ary_entry(header, 0));
2130
2138
  value = rb_obj_as_string(rb_ary_entry(header, 1));
2131
-
2132
- // This is a bit inefficient, but we don't want to be modifying
2133
- // the actual values in the original hash.
2134
- header_str = rb_str_plus(name, rb_str_new2(": "));
2135
- 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
+ }
2136
2147
  } else {
2137
2148
  header_str = rb_obj_as_string(header);
2138
2149
  }
@@ -2146,7 +2157,7 @@ static VALUE cb_each_http_header(VALUE header, VALUE wrap) {
2146
2157
  /***********************************************
2147
2158
  * This is an rb_iterate callback used to set up http proxy headers.
2148
2159
  */
2149
- static VALUE cb_each_http_proxy_header(VALUE proxy_header, VALUE wrap) {
2160
+ static VALUE cb_each_http_proxy_header(VALUE proxy_header, VALUE wrap, int _c, const VALUE *_ptr, VALUE unused) {
2150
2161
  struct curl_slist **list;
2151
2162
  VALUE proxy_header_str = Qnil;
2152
2163
 
@@ -2178,7 +2189,7 @@ static VALUE cb_each_http_proxy_header(VALUE proxy_header, VALUE wrap) {
2178
2189
  /***********************************************
2179
2190
  * This is an rb_iterate callback used to set up ftp commands.
2180
2191
  */
2181
- static VALUE cb_each_ftp_command(VALUE ftp_command, VALUE wrap) {
2192
+ static VALUE cb_each_ftp_command(VALUE ftp_command, VALUE wrap, int _c, const VALUE *_ptr, VALUE unused) {
2182
2193
  struct curl_slist **list;
2183
2194
  VALUE ftp_command_string;
2184
2195
  Data_Get_Struct(wrap, struct curl_slist *, list);
@@ -2192,7 +2203,7 @@ static VALUE cb_each_ftp_command(VALUE ftp_command, VALUE wrap) {
2192
2203
  /***********************************************
2193
2204
  * This is an rb_iterate callback used to set up the resolve list.
2194
2205
  */
2195
- static VALUE cb_each_resolve(VALUE resolve, VALUE wrap) {
2206
+ static VALUE cb_each_resolve(VALUE resolve, VALUE wrap, int _c, const VALUE *_ptr, VALUE unused) {
2196
2207
  struct curl_slist **list;
2197
2208
  VALUE resolve_string;
2198
2209
  Data_Get_Struct(wrap, struct curl_slist *, list);
@@ -2591,7 +2602,7 @@ VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
2591
2602
  }
2592
2603
 
2593
2604
  // set values on cleanup to nil
2594
- rb_easy_del("multi");
2605
+ //rb_easy_del("multi");
2595
2606
 
2596
2607
  return Qnil;
2597
2608
  }
@@ -2607,6 +2618,8 @@ static VALUE ruby_curl_easy_perform_verb_str(VALUE self, const char *verb) {
2607
2618
  Data_Get_Struct(self, ruby_curl_easy, rbce);
2608
2619
  curl = rbce->curl;
2609
2620
 
2621
+ memset(rbce->err_buf, 0, sizeof(rbce->err_buf));
2622
+
2610
2623
  curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, verb);
2611
2624
 
2612
2625
  retval = rb_funcall(self, rb_intern("perform"), 0);
@@ -2672,6 +2685,8 @@ static VALUE ruby_curl_easy_perform_post(int argc, VALUE *argv, VALUE self) {
2672
2685
  Data_Get_Struct(self, ruby_curl_easy, rbce);
2673
2686
  curl = rbce->curl;
2674
2687
 
2688
+ memset(rbce->err_buf, 0, sizeof(rbce->err_buf));
2689
+
2675
2690
  curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, NULL);
2676
2691
 
2677
2692
  if (rbce->multipart_form_post) {
@@ -2743,6 +2758,8 @@ static VALUE ruby_curl_easy_perform_put(VALUE self, VALUE data) {
2743
2758
  Data_Get_Struct(self, ruby_curl_easy, rbce);
2744
2759
  curl = rbce->curl;
2745
2760
 
2761
+ memset(rbce->err_buf, 0, sizeof(rbce->err_buf));
2762
+
2746
2763
  curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, NULL);
2747
2764
  ruby_curl_easy_put_data_set(self, data);
2748
2765
 
@@ -2761,6 +2778,10 @@ static VALUE ruby_curl_easy_perform_put(VALUE self, VALUE data) {
2761
2778
  * your own body handler, this string will be empty.
2762
2779
  */
2763
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
+ */
2764
2785
  CURB_OBJECT_HGETTER(ruby_curl_easy, body_data);
2765
2786
  }
2766
2787
 
@@ -2955,7 +2976,7 @@ static VALUE ruby_curl_easy_connect_time_get(VALUE self) {
2955
2976
  * Retrieve the time, in seconds, it took from the start until the SSL/SSH
2956
2977
  * connect/handshake to the remote host was completed. This time is most often
2957
2978
  * very near to the pre transfer time, except for cases such as HTTP
2958
- * 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
2959
2980
  * for the pipeline and more.
2960
2981
  */
2961
2982
  #if defined(HAVE_CURLINFO_APPCONNECT_TIME)
@@ -3448,6 +3469,21 @@ static VALUE ruby_curl_easy_last_result(VALUE self) {
3448
3469
  return LONG2NUM(rbce->last_result);
3449
3470
  }
3450
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
+
3451
3487
  /*
3452
3488
  * call-seq:
3453
3489
  * easy.setopt Fixnum, value => value
@@ -3546,6 +3582,9 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3546
3582
  case CURLOPT_TCP_NODELAY: {
3547
3583
  curl_easy_setopt(rbce->curl, CURLOPT_TCP_NODELAY, NUM2LONG(val));
3548
3584
  } break;
3585
+ case CURLOPT_RANGE: {
3586
+ curl_easy_setopt(rbce->curl, CURLOPT_RANGE, StringValueCStr(val));
3587
+ } break;
3549
3588
  case CURLOPT_RESUME_FROM: {
3550
3589
  curl_easy_setopt(rbce->curl, CURLOPT_RESUME_FROM, NUM2LONG(val));
3551
3590
  } break;
@@ -3606,6 +3645,20 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3606
3645
  GetOpenFile(val, open_f_ptr);
3607
3646
  curl_easy_setopt(rbce->curl, CURLOPT_STDERR, rb_io_stdio_file(open_f_ptr));
3608
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;
3661
+ #endif
3609
3662
  default:
3610
3663
  rb_raise(rb_eTypeError, "Curb unsupported option");
3611
3664
  }
@@ -3722,6 +3775,7 @@ static VALUE ruby_curl_easy_error_message(VALUE klass, VALUE code) {
3722
3775
  }
3723
3776
 
3724
3777
  /* =================== INIT LIB =====================*/
3778
+ // TODO: https://bugs.ruby-lang.org/issues/18007
3725
3779
  void init_curb_easy() {
3726
3780
  idCall = rb_intern("call");
3727
3781
  idJoin = rb_intern("join");
@@ -3867,6 +3921,7 @@ void init_curb_easy() {
3867
3921
 
3868
3922
  rb_define_method(cCurlEasy, "last_effective_url", ruby_curl_easy_last_effective_url_get, 0);
3869
3923
  rb_define_method(cCurlEasy, "response_code", ruby_curl_easy_response_code_get, 0);
3924
+ rb_define_method(cCurlEasy, "code", ruby_curl_easy_response_code_get, 0);
3870
3925
  #if defined(HAVE_CURLINFO_PRIMARY_IP)
3871
3926
  rb_define_method(cCurlEasy, "primary_ip", ruby_curl_easy_primary_ip_get, 0);
3872
3927
  #endif
@@ -3913,6 +3968,7 @@ void init_curb_easy() {
3913
3968
  rb_define_method(cCurlEasy, "multi", ruby_curl_easy_multi_get, 0);
3914
3969
  rb_define_method(cCurlEasy, "multi=", ruby_curl_easy_multi_set, 1);
3915
3970
  rb_define_method(cCurlEasy, "last_result", ruby_curl_easy_last_result, 0);
3971
+ rb_define_method(cCurlEasy, "last_error", ruby_curl_easy_last_error, 0);
3916
3972
 
3917
3973
  rb_define_method(cCurlEasy, "setopt", ruby_curl_easy_set_opt, 2);
3918
3974
  rb_define_method(cCurlEasy, "getinfo", ruby_curl_easy_get_opt, 1);
data/ext/curb_easy.h CHANGED
@@ -36,6 +36,9 @@ typedef struct {
36
36
  /* The handler */
37
37
  CURL *curl;
38
38
 
39
+ /* Buffer for error details from CURLOPT_ERRORBUFFER */
40
+ char err_buf[CURL_ERROR_SIZE];
41
+
39
42
  VALUE opts; /* rather then allocate everything we might need to store, allocate a Hash and only store objects we actually use... */
40
43
  VALUE multi; /* keep a multi handle alive for each easy handle not being used by a multi handle. This improves easy performance when not within a multi context */
41
44
 
data/ext/curb_macros.h CHANGED
@@ -156,4 +156,16 @@
156
156
  #define CURB_DEFINE(name) \
157
157
  rb_define_const(mCurl, #name, LONG2NUM(name))
158
158
 
159
+ /* copy and raise exception */
160
+ #define CURB_CHECK_RB_CALLBACK_RAISE(did_raise) \
161
+ VALUE exception = rb_hash_aref(did_raise, rb_easy_hkey("error")); \
162
+ if (FIX2INT(rb_hash_size(did_raise)) > 0 && exception != Qnil) { \
163
+ rb_hash_clear(did_raise); \
164
+ VALUE message = rb_funcall(exception, rb_intern("message"), 0); \
165
+ VALUE aborted_exception = rb_exc_new_str(eCurlErrAbortedByCallback, message); \
166
+ VALUE backtrace = rb_funcall(exception, rb_intern("backtrace"), 0); \
167
+ rb_funcall(aborted_exception, rb_intern("set_backtrace"), 1, backtrace); \
168
+ rb_exc_raise(aborted_exception); \
169
+ }
170
+
159
171
  #endif
data/ext/curb_multi.c CHANGED
@@ -43,8 +43,25 @@ static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy);
43
43
  static void rb_curl_multi_read_info(VALUE self, CURLM *mptr);
44
44
  static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running);
45
45
 
46
- static VALUE callback_exception(VALUE unused) {
47
- return Qfalse;
46
+ static VALUE callback_exception(VALUE did_raise, VALUE exception) {
47
+ // TODO: we could have an option to enable exception reporting
48
+ /* VALUE ret = rb_funcall(exception, rb_intern("message"), 0);
49
+ VALUE trace = rb_funcall(exception, rb_intern("backtrace"), 0);
50
+ if (RB_TYPE_P(trace, T_ARRAY) && RARRAY_LEN(trace) > 0) {
51
+ printf("we got an exception: %s:%d\n", StringValueCStr(ret), RARRAY_LEN(trace));
52
+ VALUE sep = rb_str_new_cstr("\n");
53
+ VALUE trace_lines = rb_ary_join(trace, sep);
54
+ if (RB_TYPE_P(trace_lines, T_STRING)) {
55
+ printf("%s\n", StringValueCStr(trace_lines));
56
+ } else {
57
+ printf("trace is not a string??\n");
58
+ }
59
+ } else {
60
+ printf("we got an exception: %s\nno stack available\n", StringValueCStr(ret));
61
+ }
62
+ */
63
+ rb_hash_aset(did_raise, rb_easy_hkey("error"), exception);
64
+ return exception;
48
65
  }
49
66
 
50
67
  void curl_multi_free(ruby_curl_multi *rbcm) {
@@ -64,7 +81,7 @@ static void ruby_curl_multi_init(ruby_curl_multi *rbcm) {
64
81
 
65
82
  /*
66
83
  * call-seq:
67
- * Curl::Multi.new => #&lt;Curl::Easy...&gt;
84
+ * Curl::Multi.new => #<Curl::Easy...>
68
85
  *
69
86
  * Create a new Curl::Multi instance
70
87
  */
@@ -132,7 +149,7 @@ VALUE ruby_curl_multi_get_autoclose(VALUE klass) {
132
149
 
133
150
  /*
134
151
  * call-seq:
135
- * multi.requests => [#&lt;Curl::Easy...&gt;, ...]
152
+ * multi.requests => [#<Curl::Easy...>, ...]
136
153
  *
137
154
  * Returns an array containing all the active requests on this Curl::Multi object.
138
155
  */
@@ -274,7 +291,7 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
274
291
  long response_code = -1;
275
292
  VALUE easy;
276
293
  ruby_curl_easy *rbce = NULL;
277
- VALUE callargs, val = Qtrue;
294
+ VALUE callargs;
278
295
 
279
296
  CURLcode ecode = curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, (char**)&easy);
280
297
 
@@ -295,55 +312,62 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
295
312
  raise_curl_easy_error_exception(ecode);
296
313
  }
297
314
 
315
+ VALUE did_raise = rb_hash_new();
316
+
298
317
  if (!rb_easy_nil("complete_proc")) {
299
318
  callargs = rb_ary_new3(2, rb_easy_get("complete_proc"), easy);
300
319
  rbce->callback_active = 1;
301
- val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
320
+ rb_rescue(call_status_handler1, callargs, callback_exception, did_raise);
302
321
  rbce->callback_active = 0;
303
- //rb_funcall( rb_easy_get("complete_proc"), idCall, 1, easy );
322
+ CURB_CHECK_RB_CALLBACK_RAISE(did_raise);
304
323
  }
305
324
 
325
+ #ifdef HAVE_CURLINFO_RESPONSE_CODE
306
326
  curl_easy_getinfo(rbce->curl, CURLINFO_RESPONSE_CODE, &response_code);
327
+ #else
328
+ // old libcurl
329
+ curl_easy_getinfo(rbce->curl, CURLINFO_HTTP_CODE, &response_code);
330
+ #endif
331
+ long redirect_count;
332
+ curl_easy_getinfo(rbce->curl, CURLINFO_REDIRECT_COUNT, &redirect_count);
307
333
 
308
334
  if (result != 0) {
309
335
  if (!rb_easy_nil("failure_proc")) {
310
336
  callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
311
337
  rbce->callback_active = 1;
312
- val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
338
+ rb_rescue(call_status_handler2, callargs, callback_exception, did_raise);
313
339
  rbce->callback_active = 0;
314
- //rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
340
+ CURB_CHECK_RB_CALLBACK_RAISE(did_raise);
315
341
  }
316
- }
317
- else if (!rb_easy_nil("success_proc") &&
342
+ } else if (!rb_easy_nil("success_proc") &&
318
343
  ((response_code >= 200 && response_code < 300) || response_code == 0)) {
319
344
  /* NOTE: we allow response_code == 0, in the case of non http requests e.g. reading from disk */
320
345
  callargs = rb_ary_new3(2, rb_easy_get("success_proc"), easy);
321
346
  rbce->callback_active = 1;
322
- val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
347
+ rb_rescue(call_status_handler1, callargs, callback_exception, did_raise);
323
348
  rbce->callback_active = 0;
324
- //rb_funcall( rb_easy_get("success_proc"), idCall, 1, easy );
325
- }
326
- else if (!rb_easy_nil("redirect_proc") &&
327
- (response_code >= 300 && response_code < 400)) {
349
+ CURB_CHECK_RB_CALLBACK_RAISE(did_raise);
350
+
351
+ } else if (!rb_easy_nil("redirect_proc") && ((response_code >= 300 && response_code < 400) || redirect_count > 0) ) {
328
352
  rbce->callback_active = 1;
329
353
  callargs = rb_ary_new3(3, rb_easy_get("redirect_proc"), easy, rb_curl_easy_error(result));
330
354
  rbce->callback_active = 0;
331
- val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
332
- }
333
- else if (!rb_easy_nil("missing_proc") &&
355
+ rb_rescue(call_status_handler2, callargs, callback_exception, did_raise);
356
+ CURB_CHECK_RB_CALLBACK_RAISE(did_raise);
357
+ } else if (!rb_easy_nil("missing_proc") &&
334
358
  (response_code >= 400 && response_code < 500)) {
335
359
  rbce->callback_active = 1;
336
360
  callargs = rb_ary_new3(3, rb_easy_get("missing_proc"), easy, rb_curl_easy_error(result));
337
361
  rbce->callback_active = 0;
338
- val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
339
- }
340
- else if (!rb_easy_nil("failure_proc") &&
362
+ rb_rescue(call_status_handler2, callargs, callback_exception, did_raise);
363
+ CURB_CHECK_RB_CALLBACK_RAISE(did_raise);
364
+ } else if (!rb_easy_nil("failure_proc") &&
341
365
  (response_code >= 500 && response_code <= 999)) {
342
366
  callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
343
367
  rbce->callback_active = 1;
344
- val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
368
+ rb_rescue(call_status_handler2, callargs, callback_exception, did_raise);
345
369
  rbce->callback_active = 0;
346
- //rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
370
+ CURB_CHECK_RB_CALLBACK_RAISE(did_raise);
347
371
  }
348
372
 
349
373
  }
@@ -627,6 +651,8 @@ void init_curb_multi() {
627
651
  idCall = rb_intern("call");
628
652
  cCurlMulti = rb_define_class_under(mCurl, "Multi", rb_cObject);
629
653
 
654
+ rb_undef_alloc_func(cCurlMulti);
655
+
630
656
  /* Class methods */
631
657
  rb_define_singleton_method(cCurlMulti, "new", ruby_curl_multi_new, 0);
632
658
  rb_define_singleton_method(cCurlMulti, "default_timeout=", ruby_curl_multi_set_default_timeout, 1);