curb 0.9.7 → 1.0.1

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.
data/ext/curb_easy.c CHANGED
@@ -25,6 +25,12 @@ 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
 
@@ -223,6 +229,10 @@ 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
  }
@@ -243,6 +253,7 @@ static void ruby_curl_easy_free(ruby_curl_easy *rbce) {
243
253
  curl_easy_setopt(rbce->curl, CURLOPT_PROGRESSFUNCTION, NULL);
244
254
  curl_easy_setopt(rbce->curl, CURLOPT_NOPROGRESS, 1);
245
255
  curl_easy_cleanup(rbce->curl);
256
+ rbce->curl = NULL;
246
257
  }
247
258
  }
248
259
 
@@ -257,7 +268,10 @@ void curl_easy_free(ruby_curl_easy *rbce) {
257
268
  static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
258
269
  rbce->opts = rb_hash_new();
259
270
 
271
+ memset(rbce->err_buf, 0, sizeof(rbce->err_buf));
272
+
260
273
  rbce->curl_headers = NULL;
274
+ rbce->curl_proxy_headers = NULL;
261
275
  rbce->curl_ftp_commands = NULL;
262
276
  rbce->curl_resolve = NULL;
263
277
 
@@ -277,6 +291,8 @@ static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
277
291
  rbce->ftp_response_timeout = 0;
278
292
  rbce->low_speed_limit = 0;
279
293
  rbce->low_speed_time = 0;
294
+ rbce->max_send_speed_large = 0;
295
+ rbce->max_recv_speed_large = 0;
280
296
  rbce->ssl_version = -1;
281
297
  rbce->use_ssl = -1;
282
298
  rbce->ftp_filemethod = -1;
@@ -313,9 +329,9 @@ static VALUE ruby_curl_easy_allocate(VALUE klass) {
313
329
 
314
330
  /*
315
331
  * call-seq:
316
- * Curl::Easy.new => #<Curl::Easy...>
317
- * Curl::Easy.new(url = nil) => #<Curl::Easy...>
318
- * 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...>
319
335
  *
320
336
  * Initialize a new Curl::Easy instance, optionally supplying the URL.
321
337
  * The block form allows further configuration to be supplied before
@@ -341,9 +357,11 @@ static VALUE ruby_curl_easy_initialize(int argc, VALUE *argv, VALUE self) {
341
357
 
342
358
  ruby_curl_easy_zero(rbce);
343
359
 
360
+ curl_easy_setopt(rbce->curl, CURLOPT_ERRORBUFFER, &rbce->err_buf);
361
+
344
362
  rb_easy_set("url", url);
345
363
 
346
- /* set the new_curl pointer to the curl handle */
364
+ /* set the pointer to the curl handle */
347
365
  ecode = curl_easy_setopt(rbce->curl, CURLOPT_PRIVATE, (void*)self);
348
366
  if (ecode != CURLE_OK) {
349
367
  raise_curl_easy_error_exception(ecode);
@@ -358,8 +376,8 @@ static VALUE ruby_curl_easy_initialize(int argc, VALUE *argv, VALUE self) {
358
376
 
359
377
  /*
360
378
  * call-seq:
361
- * easy.clone => #&lt;easy clone&gt;
362
- * easy.dup => #&lt;easy clone&gt;
379
+ * easy.clone => <easy clone>
380
+ * easy.dup => <easy clone>
363
381
  *
364
382
  * Clone this Curl::Easy instance, creating a new instance.
365
383
  * This method duplicates the underlying CURL* handle.
@@ -373,9 +391,12 @@ static VALUE ruby_curl_easy_clone(VALUE self) {
373
391
  memcpy(newrbce, rbce, sizeof(ruby_curl_easy));
374
392
  newrbce->curl = curl_easy_duphandle(rbce->curl);
375
393
  newrbce->curl_headers = NULL;
394
+ newrbce->curl_proxy_headers = NULL;
376
395
  newrbce->curl_ftp_commands = NULL;
377
396
  newrbce->curl_resolve = NULL;
378
397
 
398
+ curl_easy_setopt(rbce->curl, CURLOPT_ERRORBUFFER, &rbce->err_buf);
399
+
379
400
  return Data_Wrap_Struct(cCurlEasy, curl_easy_mark, curl_easy_free, newrbce);
380
401
  }
381
402
 
@@ -445,7 +466,9 @@ static VALUE ruby_curl_easy_reset(VALUE self) {
445
466
  curl_easy_reset(rbce->curl);
446
467
  ruby_curl_easy_zero(rbce);
447
468
 
448
- /* 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 */
449
472
  ecode = curl_easy_setopt(rbce->curl, CURLOPT_PRIVATE, (void*)self);
450
473
  if (ecode != CURLE_OK) {
451
474
  raise_curl_easy_error_exception(ecode);
@@ -457,6 +480,12 @@ static VALUE ruby_curl_easy_reset(VALUE self) {
457
480
  rbce->curl_headers = NULL;
458
481
  }
459
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
+
460
489
  return opts_dup;
461
490
  }
462
491
 
@@ -509,6 +538,10 @@ static VALUE ruby_curl_easy_headers_set(VALUE self, VALUE headers) {
509
538
  CURB_OBJECT_HSETTER(ruby_curl_easy, headers);
510
539
  }
511
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
+
512
545
  /*
513
546
  * call-seq:
514
547
  * easy.headers => Hash, Array or Str
@@ -524,6 +557,41 @@ static VALUE ruby_curl_easy_headers_get(VALUE self) {
524
557
  return headers;
525
558
  }
526
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
+
527
595
  /*
528
596
  * call-seq:
529
597
  * easy.interface => string
@@ -889,7 +957,7 @@ static VALUE ruby_curl_easy_ftp_commands_set(VALUE self, VALUE ftp_commands) {
889
957
  }
890
958
 
891
959
  /*
892
- * call-seq
960
+ * call-seq:
893
961
  * easy.ftp_commands => array or nil
894
962
  */
895
963
  static VALUE ruby_curl_easy_ftp_commands_get(VALUE self) {
@@ -908,7 +976,7 @@ static VALUE ruby_curl_easy_resolve_set(VALUE self, VALUE resolve) {
908
976
  }
909
977
 
910
978
  /*
911
- * call-seq
979
+ * call-seq:
912
980
  * easy.resolve => array or nil
913
981
  */
914
982
  static VALUE ruby_curl_easy_resolve_get(VALUE self) {
@@ -1139,7 +1207,7 @@ static VALUE ruby_curl_easy_max_redirects_get(VALUE self) {
1139
1207
 
1140
1208
  /*
1141
1209
  * call-seq:
1142
- * easy.timeout = fixnum or nil => fixnum or nil
1210
+ * easy.timeout = float, fixnum or nil => numeric
1143
1211
  *
1144
1212
  * Set the maximum time in seconds that you allow the libcurl transfer
1145
1213
  * operation to take. Normally, name lookups can take a considerable time
@@ -1148,20 +1216,39 @@ static VALUE ruby_curl_easy_max_redirects_get(VALUE self) {
1148
1216
  *
1149
1217
  * Set to nil (or zero) to disable timeout (it will then only timeout
1150
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
+ *
1151
1224
  */
1152
- static VALUE ruby_curl_easy_timeout_set(VALUE self, VALUE timeout) {
1153
- 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);
1154
1236
  }
1155
1237
 
1156
1238
  /*
1157
1239
  * call-seq:
1158
- * easy.timeout => fixnum or nil
1240
+ * easy.timeout => numeric
1159
1241
  *
1160
1242
  * Obtain the maximum time in seconds that you allow the libcurl transfer
1161
1243
  * operation to take.
1244
+ *
1245
+ * Uses timeout_ms internally instead of timeout.
1246
+ *
1162
1247
  */
1163
- static VALUE ruby_curl_easy_timeout_get(VALUE self, VALUE timeout) {
1164
- 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);
1165
1252
  }
1166
1253
 
1167
1254
  /*
@@ -1177,7 +1264,16 @@ static VALUE ruby_curl_easy_timeout_get(VALUE self, VALUE timeout) {
1177
1264
  * on the system's internal timeouts).
1178
1265
  */
1179
1266
  static VALUE ruby_curl_easy_timeout_ms_set(VALUE self, VALUE timeout_ms) {
1180
- 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);
1181
1277
  }
1182
1278
 
1183
1279
  /*
@@ -1187,8 +1283,10 @@ static VALUE ruby_curl_easy_timeout_ms_set(VALUE self, VALUE timeout_ms) {
1187
1283
  * Obtain the maximum time in milliseconds that you allow the libcurl transfer
1188
1284
  * operation to take.
1189
1285
  */
1190
- static VALUE ruby_curl_easy_timeout_ms_get(VALUE self, VALUE timeout_ms) {
1191
- 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);
1192
1290
  }
1193
1291
 
1194
1292
  /*
@@ -1338,6 +1436,46 @@ static VALUE ruby_curl_easy_low_speed_time_get(VALUE self, VALUE low_speed_time)
1338
1436
  CURB_IMMED_GETTER(ruby_curl_easy, low_speed_time, 0);
1339
1437
  }
1340
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
+
1341
1479
  /*
1342
1480
  * call-seq:
1343
1481
  * easy.username = string => string
@@ -1408,6 +1546,7 @@ static VALUE ruby_curl_easy_password_get(VALUE self, VALUE password) {
1408
1546
  * Curl::CURL_SSLVERSION_TLSv1_0
1409
1547
  * Curl::CURL_SSLVERSION_TLSv1_1
1410
1548
  * Curl::CURL_SSLVERSION_TLSv1_2
1549
+ * Curl::CURL_SSLVERSION_TLSv1_3
1411
1550
  */
1412
1551
  static VALUE ruby_curl_easy_ssl_version_set(VALUE self, VALUE ssl_version) {
1413
1552
  CURB_IMMED_SETTER(ruby_curl_easy, ssl_version, -1);
@@ -1456,7 +1595,7 @@ static VALUE ruby_curl_easy_ftp_filemethod_set(VALUE self, VALUE ftp_filemethod)
1456
1595
  }
1457
1596
 
1458
1597
  /*
1459
- * call-seq
1598
+ * call-seq:
1460
1599
  * easy.ftp_filemethod => fixnum
1461
1600
  *
1462
1601
  * Get the configuration for how libcurl will reach files on the server.
@@ -1828,7 +1967,7 @@ static VALUE ruby_curl_easy_resolve_mode_set(VALUE self, VALUE resolve_mode) {
1828
1967
 
1829
1968
  /*
1830
1969
  * call-seq:
1831
- * easy.on_body { |body_data| ... } => &lt;old handler&gt;
1970
+ * easy.on_body { |body_data| ... } => <old handler>
1832
1971
  *
1833
1972
  * Assign or remove the +on_body+ handler for this Curl::Easy instance.
1834
1973
  * To remove a previously-supplied handler, call this method with no
@@ -1847,7 +1986,7 @@ static VALUE ruby_curl_easy_on_body_set(int argc, VALUE *argv, VALUE self) {
1847
1986
 
1848
1987
  /*
1849
1988
  * call-seq:
1850
- * easy.on_success { |easy| ... } => &lt;old handler&gt;
1989
+ * easy.on_success { |easy| ... } => <old handler>
1851
1990
  *
1852
1991
  * Assign or remove the +on_success+ handler for this Curl::Easy instance.
1853
1992
  * To remove a previously-supplied handler, call this method with no
@@ -1862,7 +2001,7 @@ static VALUE ruby_curl_easy_on_success_set(int argc, VALUE *argv, VALUE self) {
1862
2001
 
1863
2002
  /*
1864
2003
  * call-seq:
1865
- * easy.on_failure {|easy,code| ... } => &lt;old handler&gt;
2004
+ * easy.on_failure {|easy,code| ... } => <old handler>
1866
2005
  *
1867
2006
  * Assign or remove the +on_failure+ handler for this Curl::Easy instance.
1868
2007
  * To remove a previously-supplied handler, call this method with no
@@ -1877,7 +2016,7 @@ static VALUE ruby_curl_easy_on_failure_set(int argc, VALUE *argv, VALUE self) {
1877
2016
 
1878
2017
  /*
1879
2018
  * call-seq:
1880
- * easy.on_missing {|easy,code| ... } => &lt;old handler;&gt;
2019
+ * easy.on_missing {|easy,code| ... } => <old handler;>
1881
2020
  *
1882
2021
  * Assign or remove the on_missing handler for this Curl::Easy instance.
1883
2022
  * To remove a previously-supplied handler, call this method with no attached
@@ -1892,7 +2031,7 @@ static VALUE ruby_curl_easy_on_missing_set(int argc, VALUE *argv, VALUE self) {
1892
2031
 
1893
2032
  /*
1894
2033
  * call-seq:
1895
- * easy.on_redirect {|easy,code| ... } => &lt;old handler;&gt;
2034
+ * easy.on_redirect {|easy,code| ... } => <old handler;>
1896
2035
  *
1897
2036
  * Assign or remove the on_redirect handler for this Curl::Easy instance.
1898
2037
  * To remove a previously-supplied handler, call this method with no attached
@@ -1907,7 +2046,7 @@ static VALUE ruby_curl_easy_on_redirect_set(int argc, VALUE *argv, VALUE self) {
1907
2046
 
1908
2047
  /*
1909
2048
  * call-seq:
1910
- * easy.on_complete {|easy| ... } => &lt;old handler&gt;
2049
+ * easy.on_complete {|easy| ... } => <old handler>
1911
2050
  *
1912
2051
  * Assign or remove the +on_complete+ handler for this Curl::Easy instance.
1913
2052
  * To remove a previously-supplied handler, call this method with no
@@ -1921,7 +2060,7 @@ static VALUE ruby_curl_easy_on_complete_set(int argc, VALUE *argv, VALUE self) {
1921
2060
 
1922
2061
  /*
1923
2062
  * call-seq:
1924
- * easy.on_header { |header_data| ... } => &lt;old handler&gt;
2063
+ * easy.on_header { |header_data| ... } => <old handler>
1925
2064
  *
1926
2065
  * Assign or remove the +on_header+ handler for this Curl::Easy instance.
1927
2066
  * To remove a previously-supplied handler, call this method with no
@@ -1937,7 +2076,7 @@ static VALUE ruby_curl_easy_on_header_set(int argc, VALUE *argv, VALUE self) {
1937
2076
 
1938
2077
  /*
1939
2078
  * call-seq:
1940
- * 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>
1941
2080
  *
1942
2081
  * Assign or remove the +on_progress+ handler for this Curl::Easy instance.
1943
2082
  * To remove a previously-supplied handler, call this method with no
@@ -1958,7 +2097,7 @@ static VALUE ruby_curl_easy_on_progress_set(int argc, VALUE *argv, VALUE self) {
1958
2097
 
1959
2098
  /*
1960
2099
  * call-seq:
1961
- * easy.on_debug { |type, data| ... } => &lt;old handler&gt;
2100
+ * easy.on_debug { |type, data| ... } => <old handler>
1962
2101
  *
1963
2102
  * Assign or remove the +on_debug+ handler for this Curl::Easy instance.
1964
2103
  * To remove a previously-supplied handler, call this method with no
@@ -1997,11 +2136,14 @@ static VALUE cb_each_http_header(VALUE header, VALUE wrap) {
1997
2136
 
1998
2137
  name = rb_obj_as_string(rb_ary_entry(header, 0));
1999
2138
  value = rb_obj_as_string(rb_ary_entry(header, 1));
2000
-
2001
- // This is a bit inefficient, but we don't want to be modifying
2002
- // the actual values in the original hash.
2003
- header_str = rb_str_plus(name, rb_str_new2(": "));
2004
- 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
+ }
2005
2147
  } else {
2006
2148
  header_str = rb_obj_as_string(header);
2007
2149
  }
@@ -2012,6 +2154,38 @@ static VALUE cb_each_http_header(VALUE header, VALUE wrap) {
2012
2154
  return header_str;
2013
2155
  }
2014
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
+
2015
2189
  /***********************************************
2016
2190
  * This is an rb_iterate callback used to set up ftp commands.
2017
2191
  */
@@ -2051,6 +2225,7 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2051
2225
  CURL *curl;
2052
2226
  VALUE url, _url = rb_easy_get("url");
2053
2227
  struct curl_slist **hdrs = &(rbce->curl_headers);
2228
+ struct curl_slist **phdrs = &(rbce->curl_proxy_headers);
2054
2229
  struct curl_slist **cmds = &(rbce->curl_ftp_commands);
2055
2230
  struct curl_slist **rslv = &(rbce->curl_resolve);
2056
2231
 
@@ -2061,7 +2236,6 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2061
2236
  }
2062
2237
 
2063
2238
  url = rb_check_string_type(_url);
2064
-
2065
2239
  curl_easy_setopt(curl, CURLOPT_URL, StringValuePtr(url));
2066
2240
 
2067
2241
  // network stuff and auth
@@ -2174,15 +2348,14 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2174
2348
 
2175
2349
  curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, rbce->unrestricted_auth);
2176
2350
 
2177
- /* timeouts override each other we cannot blindly set timeout and timeout_ms */
2178
- if (rbce->timeout && rbce->timeout > 0) {
2179
- curl_easy_setopt(curl, CURLOPT_TIMEOUT, rbce->timeout);
2180
- }
2181
- if (rbce->timeout_ms && rbce->timeout_ms > 0) {
2182
- curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, rbce->timeout_ms);
2183
- }
2351
+ #if HAVE_CURLOPT_TIMEOUT_MS
2352
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, rbce->timeout_ms);
2353
+ #endif
2354
+
2184
2355
  curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, rbce->connect_timeout);
2356
+ #if HAVE_CURLOPT_CONNECTTIMEOUT_MS
2185
2357
  curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, rbce->connect_timeout_ms);
2358
+ #endif
2186
2359
  curl_easy_setopt(curl, CURLOPT_DNS_CACHE_TIMEOUT, rbce->dns_cache_timeout);
2187
2360
 
2188
2361
  curl_easy_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, rbce->ignore_content_length);
@@ -2201,6 +2374,9 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2201
2374
  curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, rbce->low_speed_limit);
2202
2375
  curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, rbce->low_speed_time);
2203
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
+
2204
2380
  // Set up localport / proxy port
2205
2381
  // FIXME these won't get returned to default if they're unset Ruby
2206
2382
  if (rbce->proxy_port > 0) {
@@ -2334,6 +2510,25 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2334
2510
  }
2335
2511
  }
2336
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
+
2337
2532
  /* Setup FTP commands if necessary */
2338
2533
  if (!rb_easy_nil("ftp_commands")) {
2339
2534
  if (rb_easy_type_check("ftp_commands", T_ARRAY)) {
@@ -2366,7 +2561,7 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2366
2561
  *
2367
2562
  * Clean up a connection
2368
2563
  *
2369
- * Always returns Qtrue.
2564
+ * Always returns Qnil.
2370
2565
  */
2371
2566
  VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
2372
2567
 
@@ -2380,6 +2575,11 @@ VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
2380
2575
  rbce->curl_headers = NULL;
2381
2576
  }
2382
2577
 
2578
+ if (rbce->curl_proxy_headers) {
2579
+ curl_slist_free_all(rbce->curl_proxy_headers);
2580
+ rbce->curl_proxy_headers = NULL;
2581
+ }
2582
+
2383
2583
  ftp_commands = rbce->curl_ftp_commands;
2384
2584
  if (ftp_commands) {
2385
2585
  curl_slist_free_all(ftp_commands);
@@ -2401,6 +2601,9 @@ VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
2401
2601
  curl_easy_setopt(curl, CURLOPT_INFILESIZE, 0);
2402
2602
  }
2403
2603
 
2604
+ // set values on cleanup to nil
2605
+ rb_easy_del("multi");
2606
+
2404
2607
  return Qnil;
2405
2608
  }
2406
2609
 
@@ -2415,6 +2618,8 @@ static VALUE ruby_curl_easy_perform_verb_str(VALUE self, const char *verb) {
2415
2618
  Data_Get_Struct(self, ruby_curl_easy, rbce);
2416
2619
  curl = rbce->curl;
2417
2620
 
2621
+ memset(rbce->err_buf, 0, sizeof(rbce->err_buf));
2622
+
2418
2623
  curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, verb);
2419
2624
 
2420
2625
  retval = rb_funcall(self, rb_intern("perform"), 0);
@@ -2480,6 +2685,8 @@ static VALUE ruby_curl_easy_perform_post(int argc, VALUE *argv, VALUE self) {
2480
2685
  Data_Get_Struct(self, ruby_curl_easy, rbce);
2481
2686
  curl = rbce->curl;
2482
2687
 
2688
+ memset(rbce->err_buf, 0, sizeof(rbce->err_buf));
2689
+
2483
2690
  curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, NULL);
2484
2691
 
2485
2692
  if (rbce->multipart_form_post) {
@@ -2551,6 +2758,8 @@ static VALUE ruby_curl_easy_perform_put(VALUE self, VALUE data) {
2551
2758
  Data_Get_Struct(self, ruby_curl_easy, rbce);
2552
2759
  curl = rbce->curl;
2553
2760
 
2761
+ memset(rbce->err_buf, 0, sizeof(rbce->err_buf));
2762
+
2554
2763
  curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, NULL);
2555
2764
  ruby_curl_easy_put_data_set(self, data);
2556
2765
 
@@ -2569,6 +2778,10 @@ static VALUE ruby_curl_easy_perform_put(VALUE self, VALUE data) {
2569
2778
  * your own body handler, this string will be empty.
2570
2779
  */
2571
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
+ */
2572
2785
  CURB_OBJECT_HGETTER(ruby_curl_easy, body_data);
2573
2786
  }
2574
2787
 
@@ -2763,7 +2976,7 @@ static VALUE ruby_curl_easy_connect_time_get(VALUE self) {
2763
2976
  * Retrieve the time, in seconds, it took from the start until the SSL/SSH
2764
2977
  * connect/handshake to the remote host was completed. This time is most often
2765
2978
  * very near to the pre transfer time, except for cases such as HTTP
2766
- * 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
2767
2980
  * for the pipeline and more.
2768
2981
  */
2769
2982
  #if defined(HAVE_CURLINFO_APPCONNECT_TIME)
@@ -3256,6 +3469,21 @@ static VALUE ruby_curl_easy_last_result(VALUE self) {
3256
3469
  return LONG2NUM(rbce->last_result);
3257
3470
  }
3258
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
+
3259
3487
  /*
3260
3488
  * call-seq:
3261
3489
  * easy.setopt Fixnum, value => value
@@ -3265,6 +3493,7 @@ static VALUE ruby_curl_easy_last_result(VALUE self) {
3265
3493
  static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3266
3494
  ruby_curl_easy *rbce;
3267
3495
  long option = NUM2LONG(opt);
3496
+ rb_io_t *open_f_ptr;
3268
3497
 
3269
3498
  Data_Get_Struct(self, ruby_curl_easy, rbce);
3270
3499
 
@@ -3353,6 +3582,9 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3353
3582
  case CURLOPT_TCP_NODELAY: {
3354
3583
  curl_easy_setopt(rbce->curl, CURLOPT_TCP_NODELAY, NUM2LONG(val));
3355
3584
  } break;
3585
+ case CURLOPT_RANGE: {
3586
+ curl_easy_setopt(rbce->curl, CURLOPT_RANGE, StringValueCStr(val));
3587
+ } break;
3356
3588
  case CURLOPT_RESUME_FROM: {
3357
3589
  curl_easy_setopt(rbce->curl, CURLOPT_RESUME_FROM, NUM2LONG(val));
3358
3590
  } break;
@@ -3384,6 +3616,43 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3384
3616
  case CURLOPT_MAX_RECV_SPEED_LARGE: {
3385
3617
  curl_easy_setopt(rbce->curl, CURLOPT_MAX_RECV_SPEED_LARGE, (curl_off_t) NUM2LL(val));
3386
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;
3387
3656
  #endif
3388
3657
  default:
3389
3658
  rb_raise(rb_eTypeError, "Curb unsupported option");
@@ -3520,6 +3789,10 @@ void init_curb_easy() {
3520
3789
  /* Attributes for config next perform */
3521
3790
  rb_define_method(cCurlEasy, "url", ruby_curl_easy_url_get, 0);
3522
3791
  rb_define_method(cCurlEasy, "proxy_url", ruby_curl_easy_proxy_url_get, 0);
3792
+
3793
+ rb_define_method(cCurlEasy, "proxy_headers=", ruby_curl_easy_proxy_headers_set, 1);
3794
+ rb_define_method(cCurlEasy, "proxy_headers", ruby_curl_easy_proxy_headers_get, 0);
3795
+
3523
3796
  rb_define_method(cCurlEasy, "headers=", ruby_curl_easy_headers_set, 1);
3524
3797
  rb_define_method(cCurlEasy, "headers", ruby_curl_easy_headers_get, 0);
3525
3798
  rb_define_method(cCurlEasy, "interface", ruby_curl_easy_interface_get, 0);
@@ -3579,6 +3852,10 @@ void init_curb_easy() {
3579
3852
  rb_define_method(cCurlEasy, "low_speed_limit", ruby_curl_easy_low_speed_limit_get, 0);
3580
3853
  rb_define_method(cCurlEasy, "low_speed_time=", ruby_curl_easy_low_speed_time_set, 1);
3581
3854
  rb_define_method(cCurlEasy, "low_speed_time", ruby_curl_easy_low_speed_time_get, 0);
3855
+ rb_define_method(cCurlEasy, "max_send_speed_large=", ruby_curl_easy_max_send_speed_large_set, 1);
3856
+ rb_define_method(cCurlEasy, "max_send_speed_large", ruby_curl_easy_max_send_speed_large_get, 0);
3857
+ rb_define_method(cCurlEasy, "max_recv_speed_large=", ruby_curl_easy_max_recv_speed_large_set, 1);
3858
+ rb_define_method(cCurlEasy, "max_recv_speed_large", ruby_curl_easy_max_recv_speed_large_get, 0);
3582
3859
  rb_define_method(cCurlEasy, "ssl_version=", ruby_curl_easy_ssl_version_set, 1);
3583
3860
  rb_define_method(cCurlEasy, "ssl_version", ruby_curl_easy_ssl_version_get, 0);
3584
3861
  rb_define_method(cCurlEasy, "use_ssl=", ruby_curl_easy_use_ssl_set, 1);
@@ -3684,6 +3961,7 @@ void init_curb_easy() {
3684
3961
  rb_define_method(cCurlEasy, "multi", ruby_curl_easy_multi_get, 0);
3685
3962
  rb_define_method(cCurlEasy, "multi=", ruby_curl_easy_multi_set, 1);
3686
3963
  rb_define_method(cCurlEasy, "last_result", ruby_curl_easy_last_result, 0);
3964
+ rb_define_method(cCurlEasy, "last_error", ruby_curl_easy_last_error, 0);
3687
3965
 
3688
3966
  rb_define_method(cCurlEasy, "setopt", ruby_curl_easy_set_opt, 2);
3689
3967
  rb_define_method(cCurlEasy, "getinfo", ruby_curl_easy_get_opt, 1);