curb 0.9.2 → 0.9.11

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
 
@@ -244,7 +269,9 @@ static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
244
269
  rbce->opts = rb_hash_new();
245
270
 
246
271
  rbce->curl_headers = NULL;
272
+ rbce->curl_proxy_headers = NULL;
247
273
  rbce->curl_ftp_commands = NULL;
274
+ rbce->curl_resolve = NULL;
248
275
 
249
276
  /* various-typed opts */
250
277
  rbce->local_port = 0;
@@ -262,6 +289,8 @@ static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
262
289
  rbce->ftp_response_timeout = 0;
263
290
  rbce->low_speed_limit = 0;
264
291
  rbce->low_speed_time = 0;
292
+ rbce->max_send_speed_large = 0;
293
+ rbce->max_recv_speed_large = 0;
265
294
  rbce->ssl_version = -1;
266
295
  rbce->use_ssl = -1;
267
296
  rbce->ftp_filemethod = -1;
@@ -283,25 +312,37 @@ static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
283
312
  rbce->callback_active = 0;
284
313
  }
285
314
 
315
+ /*
316
+ * Allocate space for a Curl::Easy instance.
317
+ */
318
+ static VALUE ruby_curl_easy_allocate(VALUE klass) {
319
+ ruby_curl_easy *rbce;
320
+ rbce = ALLOC(ruby_curl_easy);
321
+ rbce->curl = NULL;
322
+ rbce->opts = Qnil;
323
+ rbce->multi = Qnil;
324
+ ruby_curl_easy_zero(rbce);
325
+ return Data_Wrap_Struct(klass, curl_easy_mark, curl_easy_free, rbce);
326
+ }
327
+
286
328
  /*
287
329
  * call-seq:
288
330
  * Curl::Easy.new => #<Curl::Easy...>
289
331
  * Curl::Easy.new(url = nil) => #<Curl::Easy...>
290
332
  * Curl::Easy.new(url = nil) { |self| ... } => #<Curl::Easy...>
291
333
  *
292
- * Create a new Curl::Easy instance, optionally supplying the URL.
334
+ * Initialize a new Curl::Easy instance, optionally supplying the URL.
293
335
  * The block form allows further configuration to be supplied before
294
336
  * the instance is returned.
295
337
  */
296
- static VALUE ruby_curl_easy_new(int argc, VALUE *argv, VALUE klass) {
338
+ static VALUE ruby_curl_easy_initialize(int argc, VALUE *argv, VALUE self) {
297
339
  CURLcode ecode;
298
340
  VALUE url, blk;
299
- VALUE new_curl;
300
341
  ruby_curl_easy *rbce;
301
342
 
302
343
  rb_scan_args(argc, argv, "01&", &url, &blk);
303
344
 
304
- rbce = ALLOC(ruby_curl_easy);
345
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
305
346
 
306
347
  /* handler */
307
348
  rbce->curl = curl_easy_init();
@@ -309,8 +350,6 @@ static VALUE ruby_curl_easy_new(int argc, VALUE *argv, VALUE klass) {
309
350
  rb_raise(eCurlErrFailedInit, "Failed to initialize easy handle");
310
351
  }
311
352
 
312
- new_curl = Data_Wrap_Struct(klass, curl_easy_mark, curl_easy_free, rbce);
313
-
314
353
  rbce->multi = Qnil;
315
354
  rbce->opts = Qnil;
316
355
 
@@ -318,17 +357,18 @@ static VALUE ruby_curl_easy_new(int argc, VALUE *argv, VALUE klass) {
318
357
 
319
358
  rb_easy_set("url", url);
320
359
 
321
- /* set the new_curl pointer to the curl handle */
322
- ecode = curl_easy_setopt(rbce->curl, CURLOPT_PRIVATE, (void*)new_curl);
360
+
361
+ /* set the pointer to the curl handle */
362
+ ecode = curl_easy_setopt(rbce->curl, CURLOPT_PRIVATE, (void*)self);
323
363
  if (ecode != CURLE_OK) {
324
364
  raise_curl_easy_error_exception(ecode);
325
365
  }
326
366
 
327
367
  if (blk != Qnil) {
328
- rb_funcall(blk, idCall, 1, new_curl);
368
+ rb_funcall(blk, idCall, 1, self);
329
369
  }
330
370
 
331
- return new_curl;
371
+ return self;
332
372
  }
333
373
 
334
374
  /*
@@ -348,7 +388,9 @@ static VALUE ruby_curl_easy_clone(VALUE self) {
348
388
  memcpy(newrbce, rbce, sizeof(ruby_curl_easy));
349
389
  newrbce->curl = curl_easy_duphandle(rbce->curl);
350
390
  newrbce->curl_headers = NULL;
391
+ newrbce->curl_proxy_headers = NULL;
351
392
  newrbce->curl_ftp_commands = NULL;
393
+ newrbce->curl_resolve = NULL;
352
394
 
353
395
  return Data_Wrap_Struct(cCurlEasy, curl_easy_mark, curl_easy_free, newrbce);
354
396
  }
@@ -358,7 +400,7 @@ static VALUE ruby_curl_easy_clone(VALUE self) {
358
400
  * easy.close => nil
359
401
  *
360
402
  * Close the Curl::Easy instance. Any open connections are closed
361
- * The easy handle is reinitialized. If a previous multi handle was
403
+ * The easy handle is reinitialized. If a previous multi handle was
362
404
  * open it is set to nil and will be cleared after a GC.
363
405
  */
364
406
  static VALUE ruby_curl_easy_close(VALUE self) {
@@ -431,6 +473,12 @@ static VALUE ruby_curl_easy_reset(VALUE self) {
431
473
  rbce->curl_headers = NULL;
432
474
  }
433
475
 
476
+ /* Free everything up */
477
+ if (rbce->curl_proxy_headers) {
478
+ curl_slist_free_all(rbce->curl_proxy_headers);
479
+ rbce->curl_proxy_headers = NULL;
480
+ }
481
+
434
482
  return opts_dup;
435
483
  }
436
484
 
@@ -483,6 +531,10 @@ static VALUE ruby_curl_easy_headers_set(VALUE self, VALUE headers) {
483
531
  CURB_OBJECT_HSETTER(ruby_curl_easy, headers);
484
532
  }
485
533
 
534
+ static VALUE ruby_curl_easy_proxy_headers_set(VALUE self, VALUE proxy_headers) {
535
+ CURB_OBJECT_HSETTER(ruby_curl_easy, proxy_headers);
536
+ }
537
+
486
538
  /*
487
539
  * call-seq:
488
540
  * easy.headers => Hash, Array or Str
@@ -493,11 +545,46 @@ static VALUE ruby_curl_easy_headers_get(VALUE self) {
493
545
  ruby_curl_easy *rbce;
494
546
  VALUE headers;
495
547
  Data_Get_Struct(self, ruby_curl_easy, rbce);
496
- headers = rb_easy_get("headers");//rb_hash_aref(rbce->opts, rb_intern("headers"));
548
+ headers = rb_easy_get("headers");//rb_hash_aref(rbce->opts, rb_intern("headers"));
497
549
  if (headers == Qnil) { headers = rb_easy_set("headers", rb_hash_new()); }
498
550
  return headers;
499
551
  }
500
552
 
553
+ /*
554
+ * call-seq:
555
+ * easy.proxy_headers = "Header: val" => "Header: val"
556
+ * easy.proxy_headers = {"Header" => "val" ..., "Header" => "val"} => {"Header: val", ...}
557
+ * easy.proxy_headers = ["Header: val" ..., "Header: val"] => ["Header: val", ...]
558
+ *
559
+ *
560
+ * For example to set a standard or custom header:
561
+ *
562
+ * easy.proxy_headers["MyHeader"] = "myval"
563
+ *
564
+ * To remove a standard header (this is useful when removing libcurls default
565
+ * 'Expect: 100-Continue' header when using HTTP form posts):
566
+ *
567
+ * easy.proxy_headers["Expect"] = ''
568
+ *
569
+ * Anything passed to libcurl as a header will be converted to a string during
570
+ * the perform step.
571
+ */
572
+
573
+ /*
574
+ * call-seq:
575
+ * easy.proxy_headers => Hash, Array or Str
576
+ *
577
+ * Obtain the custom HTTP proxy_headers for following requests.
578
+ */
579
+ static VALUE ruby_curl_easy_proxy_headers_get(VALUE self) {
580
+ ruby_curl_easy *rbce;
581
+ VALUE proxy_headers;
582
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
583
+ proxy_headers = rb_easy_get("proxy_headers");//rb_hash_aref(rbce->opts, rb_intern("proxy_headers"));
584
+ if (proxy_headers == Qnil) { proxy_headers = rb_easy_set("proxy_headers", rb_hash_new()); }
585
+ return proxy_headers;
586
+ }
587
+
501
588
  /*
502
589
  * call-seq:
503
590
  * easy.interface => string
@@ -705,29 +792,29 @@ static VALUE ruby_curl_easy_useragent_get(VALUE self) {
705
792
  /*
706
793
  * call-seq:
707
794
  * easy.post_body = "some=form%20data&to=send" => string or nil
708
- *
795
+ *
709
796
  * Sets the POST body of this Curl::Easy instance. This is expected to be
710
797
  * URL encoded; no additional processing or encoding is done on the string.
711
798
  * The content-type header will be set to application/x-www-form-urlencoded.
712
- *
799
+ *
713
800
  * This is handy if you want to perform a POST against a Curl::Multi instance.
714
801
  */
715
802
  static VALUE ruby_curl_easy_post_body_set(VALUE self, VALUE post_body) {
716
803
  ruby_curl_easy *rbce;
717
804
  CURL *curl;
718
-
805
+
719
806
  char *data;
720
807
  long len;
721
808
 
722
809
  Data_Get_Struct(self, ruby_curl_easy, rbce);
723
-
810
+
724
811
  curl = rbce->curl;
725
-
812
+
726
813
  if ( post_body == Qnil ) {
727
814
  rb_easy_del("postdata_buffer");
728
815
  curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
729
-
730
- } else {
816
+
817
+ } else {
731
818
  if (rb_type(post_body) == T_STRING) {
732
819
  data = StringValuePtr(post_body);
733
820
  len = RSTRING_LEN(post_body);
@@ -740,19 +827,19 @@ static VALUE ruby_curl_easy_post_body_set(VALUE self, VALUE post_body) {
740
827
  else {
741
828
  rb_raise(rb_eRuntimeError, "post data must respond_to .to_s");
742
829
  }
743
-
744
- // Store the string, since it has to hang around for the duration of the
830
+
831
+ // Store the string, since it has to hang around for the duration of the
745
832
  // request. See CURLOPT_POSTFIELDS in the libcurl docs.
746
833
  //rbce->postdata_buffer = post_body;
747
834
  rb_easy_set("postdata_buffer", post_body);
748
-
835
+
749
836
  curl_easy_setopt(curl, CURLOPT_POST, 1);
750
837
  curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
751
838
  curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len);
752
-
839
+
753
840
  return post_body;
754
841
  }
755
-
842
+
756
843
  return Qnil;
757
844
  }
758
845
 
@@ -769,7 +856,7 @@ static VALUE ruby_curl_easy_post_body_get(VALUE self) {
769
856
  /*
770
857
  * call-seq:
771
858
  * easy.put_data = data => ""
772
- *
859
+ *
773
860
  * Points this Curl::Easy instance to data to be uploaded via PUT. This
774
861
  * sets the request to a PUT type request - useful if you want to PUT via
775
862
  * a multi handle.
@@ -801,7 +888,7 @@ static VALUE ruby_curl_easy_put_data_set(VALUE self, VALUE data) {
801
888
  curl_easy_setopt(curl, CURLOPT_SEEKDATA, rbce);
802
889
  #endif
803
890
 
804
- /*
891
+ /*
805
892
  * we need to set specific headers for the PUT to work... so
806
893
  * convert the internal headers structure to a HASH if one is set
807
894
  */
@@ -815,7 +902,7 @@ static VALUE ruby_curl_easy_put_data_set(VALUE self, VALUE data) {
815
902
  if (NIL_P(data)) { return data; }
816
903
 
817
904
  headers = rb_easy_get("headers");
818
- if( headers == Qnil ) {
905
+ if( headers == Qnil ) {
819
906
  headers = rb_hash_new();
820
907
  }
821
908
 
@@ -870,6 +957,25 @@ static VALUE ruby_curl_easy_ftp_commands_get(VALUE self) {
870
957
  CURB_OBJECT_HGETTER(ruby_curl_easy, ftp_commands);
871
958
  }
872
959
 
960
+ /*
961
+ * call-seq:
962
+ * easy.resolve = [ "example.com:80:127.0.0.1" ] => [ "example.com:80:127.0.0.1" ]
963
+ *
964
+ * Set the resolve list to statically resolve hostnames to IP addresses,
965
+ * bypassing DNS for matching hostname/port combinations.
966
+ */
967
+ static VALUE ruby_curl_easy_resolve_set(VALUE self, VALUE resolve) {
968
+ CURB_OBJECT_HSETTER(ruby_curl_easy, resolve);
969
+ }
970
+
971
+ /*
972
+ * call-seq
973
+ * easy.resolve => array or nil
974
+ */
975
+ static VALUE ruby_curl_easy_resolve_get(VALUE self) {
976
+ CURB_OBJECT_HGETTER(ruby_curl_easy, resolve);
977
+ }
978
+
873
979
  /* ================== IMMED ATTRS ==================*/
874
980
 
875
981
  /*
@@ -980,7 +1086,7 @@ static VALUE ruby_curl_easy_proxy_type_get(VALUE self) {
980
1086
  (!strncmp("ntlm",node,4)) ? CURLAUTH_NTLM : \
981
1087
  (!strncmp("anysafe",node,7)) ? CURLAUTH_ANYSAFE : \
982
1088
  (!strncmp("any",node,3)) ? CURLAUTH_ANY : 0
983
- #else
1089
+ #else
984
1090
  #define CURL_HTTPAUTH_STR_TO_NUM(node) \
985
1091
  (!strncmp("basic",node,5)) ? CURLAUTH_BASIC : \
986
1092
  (!strncmp("digest",node,6)) ? CURLAUTH_DIGEST : \
@@ -1012,7 +1118,7 @@ static VALUE ruby_curl_easy_http_auth_types_set(int argc, VALUE *argv, VALUE sel
1012
1118
 
1013
1119
  if (len == 1 && (rb_ary_entry(args_ary,0) == Qnil || TYPE(rb_ary_entry(args_ary,0)) == T_FIXNUM ||
1014
1120
  TYPE(rb_ary_entry(args_ary,0)) == T_BIGNUM)) {
1015
- if (rb_ary_entry(args_ary,0) == Qnil) {
1121
+ if (rb_ary_entry(args_ary,0) == Qnil) {
1016
1122
  rbce->http_auth_types = 0;
1017
1123
  }
1018
1124
  else {
@@ -1094,7 +1200,7 @@ static VALUE ruby_curl_easy_max_redirects_get(VALUE self) {
1094
1200
 
1095
1201
  /*
1096
1202
  * call-seq:
1097
- * easy.timeout = fixnum or nil => fixnum or nil
1203
+ * easy.timeout = float, fixnum or nil => numeric
1098
1204
  *
1099
1205
  * Set the maximum time in seconds that you allow the libcurl transfer
1100
1206
  * operation to take. Normally, name lookups can take a considerable time
@@ -1103,20 +1209,39 @@ static VALUE ruby_curl_easy_max_redirects_get(VALUE self) {
1103
1209
  *
1104
1210
  * Set to nil (or zero) to disable timeout (it will then only timeout
1105
1211
  * on the system's internal timeouts).
1212
+ *
1213
+ * Uses timeout_ms internally instead of timeout because it allows for
1214
+ * better precision and libcurl will use the last set value when both
1215
+ * timeout and timeout_ms are set.
1216
+ *
1106
1217
  */
1107
- static VALUE ruby_curl_easy_timeout_set(VALUE self, VALUE timeout) {
1108
- CURB_IMMED_SETTER(ruby_curl_easy, timeout, 0);
1218
+ static VALUE ruby_curl_easy_timeout_set(VALUE self, VALUE timeout_s) {
1219
+ ruby_curl_easy *rbce;
1220
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1221
+
1222
+ if (Qnil == timeout_s || NUM2DBL(timeout_s) <= 0.0) {
1223
+ rbce->timeout_ms = 0;
1224
+ } else {
1225
+ rbce->timeout_ms = (unsigned long)(NUM2DBL(timeout_s) * 1000);
1226
+ }
1227
+
1228
+ return DBL2NUM(rbce->timeout_ms / 1000.0);
1109
1229
  }
1110
1230
 
1111
1231
  /*
1112
1232
  * call-seq:
1113
- * easy.timeout => fixnum or nil
1233
+ * easy.timeout => numeric
1114
1234
  *
1115
1235
  * Obtain the maximum time in seconds that you allow the libcurl transfer
1116
1236
  * operation to take.
1237
+ *
1238
+ * Uses timeout_ms internally instead of timeout.
1239
+ *
1117
1240
  */
1118
- static VALUE ruby_curl_easy_timeout_get(VALUE self, VALUE timeout) {
1119
- CURB_IMMED_GETTER(ruby_curl_easy, timeout, 0);
1241
+ static VALUE ruby_curl_easy_timeout_get(VALUE self) {
1242
+ ruby_curl_easy *rbce;
1243
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1244
+ return DBL2NUM(rbce->timeout_ms / 1000.0);
1120
1245
  }
1121
1246
 
1122
1247
  /*
@@ -1132,7 +1257,16 @@ static VALUE ruby_curl_easy_timeout_get(VALUE self, VALUE timeout) {
1132
1257
  * on the system's internal timeouts).
1133
1258
  */
1134
1259
  static VALUE ruby_curl_easy_timeout_ms_set(VALUE self, VALUE timeout_ms) {
1135
- CURB_IMMED_SETTER(ruby_curl_easy, timeout_ms, 0);
1260
+ ruby_curl_easy *rbce;
1261
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1262
+
1263
+ if (Qnil == timeout_ms || NUM2DBL(timeout_ms) <= 0.0) {
1264
+ rbce->timeout_ms = 0;
1265
+ } else {
1266
+ rbce->timeout_ms = NUM2ULONG(timeout_ms);
1267
+ }
1268
+
1269
+ return ULONG2NUM(rbce->timeout_ms);
1136
1270
  }
1137
1271
 
1138
1272
  /*
@@ -1142,8 +1276,10 @@ static VALUE ruby_curl_easy_timeout_ms_set(VALUE self, VALUE timeout_ms) {
1142
1276
  * Obtain the maximum time in milliseconds that you allow the libcurl transfer
1143
1277
  * operation to take.
1144
1278
  */
1145
- static VALUE ruby_curl_easy_timeout_ms_get(VALUE self, VALUE timeout_ms) {
1146
- CURB_IMMED_GETTER(ruby_curl_easy, timeout_ms, 0);
1279
+ static VALUE ruby_curl_easy_timeout_ms_get(VALUE self) {
1280
+ ruby_curl_easy *rbce;
1281
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1282
+ return LONG2NUM(rbce->timeout_ms);
1147
1283
  }
1148
1284
 
1149
1285
  /*
@@ -1293,6 +1429,46 @@ static VALUE ruby_curl_easy_low_speed_time_get(VALUE self, VALUE low_speed_time)
1293
1429
  CURB_IMMED_GETTER(ruby_curl_easy, low_speed_time, 0);
1294
1430
  }
1295
1431
 
1432
+ /*
1433
+ * call-seq:
1434
+ * easy.max_send_speed_large = fixnum or nil => fixnum or nil
1435
+ *
1436
+ * Set the maximal sending transfer speed (in bytes per second)
1437
+ */
1438
+ static VALUE ruby_curl_easy_max_send_speed_large_set(VALUE self, VALUE max_send_speed_large) {
1439
+ CURB_IMMED_SETTER(ruby_curl_easy, max_send_speed_large, 0);
1440
+ }
1441
+
1442
+ /*
1443
+ * call-seq:
1444
+ * easy.max_send_speed_large = fixnum or nil => fixnum or nil
1445
+ *
1446
+ * Get the maximal sending transfer speed (in bytes per second)
1447
+ */
1448
+ static VALUE ruby_curl_easy_max_send_speed_large_get(VALUE self, VALUE max_send_speed_large) {
1449
+ CURB_IMMED_GETTER(ruby_curl_easy, max_send_speed_large, 0);
1450
+ }
1451
+
1452
+ /*
1453
+ * call-seq:
1454
+ * easy.max_recv_speed_large = fixnum or nil => fixnum or nil
1455
+ *
1456
+ * Set the maximal receiving transfer speed (in bytes per second)
1457
+ */
1458
+ static VALUE ruby_curl_easy_max_recv_speed_large_set(VALUE self, VALUE max_recv_speed_large) {
1459
+ CURB_IMMED_SETTER(ruby_curl_easy, max_recv_speed_large, 0);
1460
+ }
1461
+
1462
+ /*
1463
+ * call-seq:
1464
+ * easy.max_recv_speed_large = fixnum or nil => fixnum or nil
1465
+ *
1466
+ * Get the maximal receiving transfer speed (in bytes per second)
1467
+ */
1468
+ static VALUE ruby_curl_easy_max_recv_speed_large_get(VALUE self, VALUE max_recv_speed_large) {
1469
+ CURB_IMMED_GETTER(ruby_curl_easy, max_recv_speed_large, 0);
1470
+ }
1471
+
1296
1472
  /*
1297
1473
  * call-seq:
1298
1474
  * easy.username = string => string
@@ -1310,7 +1486,7 @@ static VALUE ruby_curl_easy_username_set(VALUE self, VALUE username) {
1310
1486
  /*
1311
1487
  * call-seq:
1312
1488
  * easy.username => string
1313
- *
1489
+ *
1314
1490
  * Get the current username
1315
1491
  */
1316
1492
  static VALUE ruby_curl_easy_username_get(VALUE self, VALUE username) {
@@ -1338,7 +1514,7 @@ static VALUE ruby_curl_easy_password_set(VALUE self, VALUE password) {
1338
1514
  /*
1339
1515
  * call-seq:
1340
1516
  * easy.password => string
1341
- *
1517
+ *
1342
1518
  * Get the current password
1343
1519
  */
1344
1520
  static VALUE ruby_curl_easy_password_get(VALUE self, VALUE password) {
@@ -1381,7 +1557,7 @@ static VALUE ruby_curl_easy_ssl_version_get(VALUE self, VALUE ssl_version) {
1381
1557
  /*
1382
1558
  * call-seq:
1383
1559
  * easy.use_ssl = value => fixnum or nil
1384
- *
1560
+ *
1385
1561
  * Ensure libcurl uses SSL for FTP connections. Valid options are Curl::CURL_USESSL_NONE,
1386
1562
  * Curl::CURL_USESSL_TRY, Curl::CURL_USESSL_CONTROL, and Curl::CURL_USESSL_ALL.
1387
1563
  */
@@ -1838,7 +2014,7 @@ static VALUE ruby_curl_easy_on_failure_set(int argc, VALUE *argv, VALUE self) {
1838
2014
  * To remove a previously-supplied handler, call this method with no attached
1839
2015
  * block.
1840
2016
  *
1841
- * The +on_missing+ handler is called when request is finished with a
2017
+ * The +on_missing+ handler is called when request is finished with a
1842
2018
  * status of 40x
1843
2019
  */
1844
2020
  static VALUE ruby_curl_easy_on_missing_set(int argc, VALUE *argv, VALUE self) {
@@ -1853,7 +2029,7 @@ static VALUE ruby_curl_easy_on_missing_set(int argc, VALUE *argv, VALUE self) {
1853
2029
  * To remove a previously-supplied handler, call this method with no attached
1854
2030
  * block.
1855
2031
  *
1856
- * The +on_redirect+ handler is called when request is finished with a
2032
+ * The +on_redirect+ handler is called when request is finished with a
1857
2033
  * status of 30x
1858
2034
  */
1859
2035
  static VALUE ruby_curl_easy_on_redirect_set(int argc, VALUE *argv, VALUE self) {
@@ -1967,6 +2143,38 @@ static VALUE cb_each_http_header(VALUE header, VALUE wrap) {
1967
2143
  return header_str;
1968
2144
  }
1969
2145
 
2146
+ /***********************************************
2147
+ * This is an rb_iterate callback used to set up http proxy headers.
2148
+ */
2149
+ static VALUE cb_each_http_proxy_header(VALUE proxy_header, VALUE wrap) {
2150
+ struct curl_slist **list;
2151
+ VALUE proxy_header_str = Qnil;
2152
+
2153
+ Data_Get_Struct(wrap, struct curl_slist *, list);
2154
+
2155
+ //rb_p(proxy_header);
2156
+
2157
+ if (rb_type(proxy_header) == T_ARRAY) {
2158
+ // we're processing a hash, proxy header is [name, val]
2159
+ VALUE name, value;
2160
+
2161
+ name = rb_obj_as_string(rb_ary_entry(proxy_header, 0));
2162
+ value = rb_obj_as_string(rb_ary_entry(proxy_header, 1));
2163
+
2164
+ // This is a bit inefficient, but we don't want to be modifying
2165
+ // the actual values in the original hash.
2166
+ proxy_header_str = rb_str_plus(name, rb_str_new2(": "));
2167
+ proxy_header_str = rb_str_plus(proxy_header_str, value);
2168
+ } else {
2169
+ proxy_header_str = rb_obj_as_string(proxy_header);
2170
+ }
2171
+
2172
+ //rb_p(header_str);
2173
+
2174
+ *list = curl_slist_append(*list, StringValuePtr(proxy_header_str));
2175
+ return proxy_header_str;
2176
+ }
2177
+
1970
2178
  /***********************************************
1971
2179
  * This is an rb_iterate callback used to set up ftp commands.
1972
2180
  */
@@ -1981,6 +2189,20 @@ static VALUE cb_each_ftp_command(VALUE ftp_command, VALUE wrap) {
1981
2189
  return ftp_command_string;
1982
2190
  }
1983
2191
 
2192
+ /***********************************************
2193
+ * This is an rb_iterate callback used to set up the resolve list.
2194
+ */
2195
+ static VALUE cb_each_resolve(VALUE resolve, VALUE wrap) {
2196
+ struct curl_slist **list;
2197
+ VALUE resolve_string;
2198
+ Data_Get_Struct(wrap, struct curl_slist *, list);
2199
+
2200
+ resolve_string = rb_obj_as_string(resolve);
2201
+ *list = curl_slist_append(*list, StringValuePtr(resolve));
2202
+
2203
+ return resolve_string;
2204
+ }
2205
+
1984
2206
  /***********************************************
1985
2207
  *
1986
2208
  * Setup a connection
@@ -1992,7 +2214,9 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
1992
2214
  CURL *curl;
1993
2215
  VALUE url, _url = rb_easy_get("url");
1994
2216
  struct curl_slist **hdrs = &(rbce->curl_headers);
2217
+ struct curl_slist **phdrs = &(rbce->curl_proxy_headers);
1995
2218
  struct curl_slist **cmds = &(rbce->curl_ftp_commands);
2219
+ struct curl_slist **rslv = &(rbce->curl_resolve);
1996
2220
 
1997
2221
  curl = rbce->curl;
1998
2222
 
@@ -2001,7 +2225,6 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2001
2225
  }
2002
2226
 
2003
2227
  url = rb_check_string_type(_url);
2004
-
2005
2228
  curl_easy_setopt(curl, CURLOPT_URL, StringValuePtr(url));
2006
2229
 
2007
2230
  // network stuff and auth
@@ -2114,15 +2337,14 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2114
2337
 
2115
2338
  curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, rbce->unrestricted_auth);
2116
2339
 
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
- }
2340
+ #if HAVE_CURLOPT_TIMEOUT_MS
2341
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, rbce->timeout_ms);
2342
+ #endif
2343
+
2124
2344
  curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, rbce->connect_timeout);
2345
+ #if HAVE_CURLOPT_CONNECTTIMEOUT_MS
2125
2346
  curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, rbce->connect_timeout_ms);
2347
+ #endif
2126
2348
  curl_easy_setopt(curl, CURLOPT_DNS_CACHE_TIMEOUT, rbce->dns_cache_timeout);
2127
2349
 
2128
2350
  curl_easy_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, rbce->ignore_content_length);
@@ -2141,6 +2363,9 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2141
2363
  curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, rbce->low_speed_limit);
2142
2364
  curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, rbce->low_speed_time);
2143
2365
 
2366
+ curl_easy_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE, rbce->max_recv_speed_large);
2367
+ curl_easy_setopt(curl, CURLOPT_MAX_SEND_SPEED_LARGE, rbce->max_send_speed_large);
2368
+
2144
2369
  // Set up localport / proxy port
2145
2370
  // FIXME these won't get returned to default if they're unset Ruby
2146
2371
  if (rbce->proxy_port > 0) {
@@ -2247,7 +2472,7 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2247
2472
  rb_warn("libcurl is not configured with SSL support");
2248
2473
  }
2249
2474
  #endif
2250
-
2475
+
2251
2476
  if (rbce->ftp_filemethod > 0) {
2252
2477
  curl_easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, rbce->ftp_filemethod);
2253
2478
  }
@@ -2274,6 +2499,25 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2274
2499
  }
2275
2500
  }
2276
2501
 
2502
+ #if HAVE_CURLOPT_PROXYHEADER
2503
+ /* Setup HTTP proxy headers if necessary */
2504
+ curl_easy_setopt(curl, CURLOPT_PROXYHEADER, NULL); // XXX: maybe we shouldn't be clearing this?
2505
+
2506
+ if (!rb_easy_nil("proxy_headers")) {
2507
+ if (rb_easy_type_check("proxy_headers", T_ARRAY) || rb_easy_type_check("proxy_headers", T_HASH)) {
2508
+ VALUE wrap = Data_Wrap_Struct(rb_cObject, 0, 0, phdrs);
2509
+ rb_iterate(rb_each, rb_easy_get("proxy_headers"), cb_each_http_proxy_header, wrap);
2510
+ } else {
2511
+ VALUE proxy_headers_str = rb_obj_as_string(rb_easy_get("proxy_headers"));
2512
+ *phdrs = curl_slist_append(*hdrs, StringValuePtr(proxy_headers_str));
2513
+ }
2514
+
2515
+ if (*phdrs) {
2516
+ curl_easy_setopt(curl, CURLOPT_PROXYHEADER, *phdrs);
2517
+ }
2518
+ }
2519
+ #endif
2520
+
2277
2521
  /* Setup FTP commands if necessary */
2278
2522
  if (!rb_easy_nil("ftp_commands")) {
2279
2523
  if (rb_easy_type_check("ftp_commands", T_ARRAY)) {
@@ -2286,18 +2530,33 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2286
2530
  }
2287
2531
  }
2288
2532
 
2533
+ #if HAVE_CURLOPT_RESOLVE
2534
+ /* Setup resolve list if necessary */
2535
+ if (!rb_easy_nil("resolve")) {
2536
+ if (rb_easy_type_check("resolve", T_ARRAY)) {
2537
+ VALUE wrap = Data_Wrap_Struct(rb_cObject, 0, 0, rslv);
2538
+ rb_iterate(rb_each, rb_easy_get("resolve"), cb_each_resolve, wrap);
2539
+ }
2540
+
2541
+ if (*rslv) {
2542
+ curl_easy_setopt(curl, CURLOPT_RESOLVE, *rslv);
2543
+ }
2544
+ }
2545
+ #endif
2546
+
2289
2547
  return Qnil;
2290
2548
  }
2291
2549
  /***********************************************
2292
2550
  *
2293
2551
  * Clean up a connection
2294
2552
  *
2295
- * Always returns Qtrue.
2553
+ * Always returns Qnil.
2296
2554
  */
2297
2555
  VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
2298
2556
 
2299
2557
  CURL *curl = rbce->curl;
2300
2558
  struct curl_slist *ftp_commands;
2559
+ struct curl_slist *resolve;
2301
2560
 
2302
2561
  /* Free everything up */
2303
2562
  if (rbce->curl_headers) {
@@ -2305,12 +2564,23 @@ VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
2305
2564
  rbce->curl_headers = NULL;
2306
2565
  }
2307
2566
 
2567
+ if (rbce->curl_proxy_headers) {
2568
+ curl_slist_free_all(rbce->curl_proxy_headers);
2569
+ rbce->curl_proxy_headers = NULL;
2570
+ }
2571
+
2308
2572
  ftp_commands = rbce->curl_ftp_commands;
2309
2573
  if (ftp_commands) {
2310
2574
  curl_slist_free_all(ftp_commands);
2311
2575
  rbce->curl_ftp_commands = NULL;
2312
2576
  }
2313
2577
 
2578
+ resolve = rbce->curl_resolve;
2579
+ if (resolve) {
2580
+ curl_slist_free_all(resolve);
2581
+ rbce->curl_resolve = NULL;
2582
+ }
2583
+
2314
2584
  /* clean up a PUT request's curl options. */
2315
2585
  if (!rb_easy_nil("upload")) {
2316
2586
  rb_easy_del("upload"); // set the upload object to Qnil to let the GC clean up
@@ -2320,6 +2590,9 @@ VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
2320
2590
  curl_easy_setopt(curl, CURLOPT_INFILESIZE, 0);
2321
2591
  }
2322
2592
 
2593
+ // set values on cleanup to nil
2594
+ rb_easy_del("multi");
2595
+
2323
2596
  return Qnil;
2324
2597
  }
2325
2598
 
@@ -2564,7 +2837,7 @@ static VALUE ruby_curl_easy_response_code_get(VALUE self) {
2564
2837
  static VALUE ruby_curl_easy_primary_ip_get(VALUE self) {
2565
2838
  ruby_curl_easy *rbce;
2566
2839
  char* ip;
2567
-
2840
+
2568
2841
  Data_Get_Struct(self, ruby_curl_easy, rbce);
2569
2842
  curl_easy_getinfo(rbce->curl, CURLINFO_PRIMARY_IP, &ip);
2570
2843
 
@@ -2789,7 +3062,7 @@ static VALUE ruby_curl_easy_redirect_count_get(VALUE self) {
2789
3062
  * call-seq:
2790
3063
  * easy.redirect_url => "http://some.url" or nil
2791
3064
  *
2792
- * Retrieve the URL a redirect would take you to if you
3065
+ * Retrieve the URL a redirect would take you to if you
2793
3066
  * would enable CURLOPT_FOLLOWLOCATION.
2794
3067
  *
2795
3068
  * Requires libcurl 7.18.2 or higher, otherwise -1 is always returned.
@@ -3072,12 +3345,40 @@ static VALUE ruby_curl_easy_num_connects_get(VALUE self) {
3072
3345
  }
3073
3346
 
3074
3347
 
3075
- /* TODO this needs to be implemented.
3348
+ /*
3349
+ * call-seq:
3350
+ * easy.cookielist => array
3351
+ *
3352
+ * Retrieves the cookies curl knows in an array of strings.
3353
+ * Returned strings are in Netscape cookiejar format or in Set-Cookie format.
3354
+ *
3355
+ * See also option CURLINFO_COOKIELIST of curl_easy_getopt(3) to see how libcurl behaves.
3356
+ *
3357
+ * (requires libcurl 7.14.1 or higher, otherwise -1 is always returned).
3358
+ */
3359
+ static VALUE ruby_curl_easy_cookielist_get(VALUE self) {
3360
+ #ifdef HAVE_CURLINFO_COOKIELIST
3361
+ ruby_curl_easy *rbce;
3362
+ struct curl_slist *cookies;
3363
+ struct curl_slist *cookie;
3364
+ VALUE rb_cookies;
3365
+
3366
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
3367
+ curl_easy_getinfo(rbce->curl, CURLINFO_COOKIELIST, &cookies);
3368
+ if (!cookies)
3369
+ return Qnil;
3370
+ rb_cookies = rb_ary_new();
3371
+ for (cookie = cookies; cookie; cookie = cookie->next)
3372
+ rb_ary_push(rb_cookies, rb_str_new2(cookie->data));
3373
+ curl_slist_free_all(cookies);
3374
+ return rb_cookies;
3076
3375
 
3077
- CURLINFO_COOKIELIST
3376
+ #else
3377
+ rb_warn("Installed libcurl is too old to support cookielist");
3378
+ return INT2FIX(-1);
3379
+ #endif
3380
+ }
3078
3381
 
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
3382
 
3082
3383
  /* TODO this needs to be implemented. Could probably support CONNECT_ONLY by having this
3083
3384
  * return an open Socket or something.
@@ -3156,6 +3457,7 @@ static VALUE ruby_curl_easy_last_result(VALUE self) {
3156
3457
  static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3157
3458
  ruby_curl_easy *rbce;
3158
3459
  long option = NUM2LONG(opt);
3460
+ rb_io_t *open_f_ptr;
3159
3461
 
3160
3462
  Data_Get_Struct(self, ruby_curl_easy, rbce);
3161
3463
 
@@ -3193,6 +3495,12 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3193
3495
  case CURLOPT_HEADER:
3194
3496
  case CURLOPT_NOPROGRESS:
3195
3497
  case CURLOPT_NOSIGNAL:
3498
+ #if HAVE_CURLOPT_PATH_AS_IS
3499
+ case CURLOPT_PATH_AS_IS:
3500
+ #endif
3501
+ #if HAVE_CURLOPT_PIPEWAIT
3502
+ case CURLOPT_PIPEWAIT:
3503
+ #endif
3196
3504
  case CURLOPT_HTTPGET:
3197
3505
  case CURLOPT_NOBODY: {
3198
3506
  int type = rb_type(val);
@@ -3260,6 +3568,44 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3260
3568
  curl_easy_setopt(rbce->curl, CURLOPT_UNIX_SOCKET_PATH, StringValueCStr(val));
3261
3569
  } break;
3262
3570
  #endif
3571
+ #if HAVE_CURLOPT_MAX_SEND_SPEED_LARGE
3572
+ case CURLOPT_MAX_SEND_SPEED_LARGE: {
3573
+ curl_easy_setopt(rbce->curl, CURLOPT_MAX_SEND_SPEED_LARGE, (curl_off_t) NUM2LL(val));
3574
+ } break;
3575
+ #endif
3576
+ #if HAVE_CURLOPT_MAX_RECV_SPEED_LARGE
3577
+ case CURLOPT_MAX_RECV_SPEED_LARGE: {
3578
+ curl_easy_setopt(rbce->curl, CURLOPT_MAX_RECV_SPEED_LARGE, (curl_off_t) NUM2LL(val));
3579
+ } break;
3580
+ #endif
3581
+ #if HAVE_CURLOPT_MAXFILESIZE
3582
+ case CURLOPT_MAXFILESIZE:
3583
+ curl_easy_setopt(rbce->curl, CURLOPT_MAXFILESIZE, NUM2LONG(val));
3584
+ break;
3585
+ #endif
3586
+ #if HAVE_CURLOPT_TCP_KEEPALIVE
3587
+ case CURLOPT_TCP_KEEPALIVE:
3588
+ curl_easy_setopt(rbce->curl, CURLOPT_TCP_KEEPALIVE, NUM2LONG(val));
3589
+ break;
3590
+ case CURLOPT_TCP_KEEPIDLE:
3591
+ curl_easy_setopt(rbce->curl, CURLOPT_TCP_KEEPIDLE, NUM2LONG(val));
3592
+ break;
3593
+ case CURLOPT_TCP_KEEPINTVL:
3594
+ curl_easy_setopt(rbce->curl, CURLOPT_TCP_KEEPINTVL, NUM2LONG(val));
3595
+ break;
3596
+ #endif
3597
+ #if HAVE_CURLOPT_HAPROXYPROTOCOL
3598
+ case CURLOPT_HAPROXYPROTOCOL:
3599
+ curl_easy_setopt(rbce->curl, CURLOPT_HAPROXYPROTOCOL, NUM2LONG(val));
3600
+ break;
3601
+ #endif
3602
+ case CURLOPT_STDERR:
3603
+ // libcurl requires raw FILE pointer and this should be IO object in Ruby.
3604
+ // Tempfile or StringIO won't work.
3605
+ Check_Type(val, T_FILE);
3606
+ GetOpenFile(val, open_f_ptr);
3607
+ curl_easy_setopt(rbce->curl, CURLOPT_STDERR, rb_io_stdio_file(open_f_ptr));
3608
+ break;
3263
3609
  default:
3264
3610
  rb_raise(rb_eTypeError, "Curb unsupported option");
3265
3611
  }
@@ -3386,12 +3732,19 @@ void init_curb_easy() {
3386
3732
  cCurlEasy = rb_define_class_under(mCurl, "Easy", rb_cObject);
3387
3733
 
3388
3734
  /* Class methods */
3389
- rb_define_singleton_method(cCurlEasy, "new", ruby_curl_easy_new, -1);
3735
+ rb_define_alloc_func(cCurlEasy, ruby_curl_easy_allocate);
3390
3736
  rb_define_singleton_method(cCurlEasy, "error", ruby_curl_easy_error_message, 1);
3391
3737
 
3738
+ /* Initialize method */
3739
+ rb_define_method(cCurlEasy, "initialize", ruby_curl_easy_initialize, -1);
3740
+
3392
3741
  /* Attributes for config next perform */
3393
3742
  rb_define_method(cCurlEasy, "url", ruby_curl_easy_url_get, 0);
3394
3743
  rb_define_method(cCurlEasy, "proxy_url", ruby_curl_easy_proxy_url_get, 0);
3744
+
3745
+ rb_define_method(cCurlEasy, "proxy_headers=", ruby_curl_easy_proxy_headers_set, 1);
3746
+ rb_define_method(cCurlEasy, "proxy_headers", ruby_curl_easy_proxy_headers_get, 0);
3747
+
3395
3748
  rb_define_method(cCurlEasy, "headers=", ruby_curl_easy_headers_set, 1);
3396
3749
  rb_define_method(cCurlEasy, "headers", ruby_curl_easy_headers_get, 0);
3397
3750
  rb_define_method(cCurlEasy, "interface", ruby_curl_easy_interface_get, 0);
@@ -3418,6 +3771,8 @@ void init_curb_easy() {
3418
3771
  rb_define_method(cCurlEasy, "put_data=", ruby_curl_easy_put_data_set, 1);
3419
3772
  rb_define_method(cCurlEasy, "ftp_commands=", ruby_curl_easy_ftp_commands_set, 1);
3420
3773
  rb_define_method(cCurlEasy, "ftp_commands", ruby_curl_easy_ftp_commands_get, 0);
3774
+ rb_define_method(cCurlEasy, "resolve=", ruby_curl_easy_resolve_set, 1);
3775
+ rb_define_method(cCurlEasy, "resolve", ruby_curl_easy_resolve_get, 0);
3421
3776
 
3422
3777
  rb_define_method(cCurlEasy, "local_port=", ruby_curl_easy_local_port_set, 1);
3423
3778
  rb_define_method(cCurlEasy, "local_port", ruby_curl_easy_local_port_get, 0);
@@ -3449,6 +3804,10 @@ void init_curb_easy() {
3449
3804
  rb_define_method(cCurlEasy, "low_speed_limit", ruby_curl_easy_low_speed_limit_get, 0);
3450
3805
  rb_define_method(cCurlEasy, "low_speed_time=", ruby_curl_easy_low_speed_time_set, 1);
3451
3806
  rb_define_method(cCurlEasy, "low_speed_time", ruby_curl_easy_low_speed_time_get, 0);
3807
+ rb_define_method(cCurlEasy, "max_send_speed_large=", ruby_curl_easy_max_send_speed_large_set, 1);
3808
+ rb_define_method(cCurlEasy, "max_send_speed_large", ruby_curl_easy_max_send_speed_large_get, 0);
3809
+ rb_define_method(cCurlEasy, "max_recv_speed_large=", ruby_curl_easy_max_recv_speed_large_set, 1);
3810
+ rb_define_method(cCurlEasy, "max_recv_speed_large", ruby_curl_easy_max_recv_speed_large_get, 0);
3452
3811
  rb_define_method(cCurlEasy, "ssl_version=", ruby_curl_easy_ssl_version_set, 1);
3453
3812
  rb_define_method(cCurlEasy, "ssl_version", ruby_curl_easy_ssl_version_get, 0);
3454
3813
  rb_define_method(cCurlEasy, "use_ssl=", ruby_curl_easy_use_ssl_set, 1);
@@ -3536,6 +3895,7 @@ void init_curb_easy() {
3536
3895
  rb_define_method(cCurlEasy, "content_type", ruby_curl_easy_content_type_get, 0);
3537
3896
  rb_define_method(cCurlEasy, "os_errno", ruby_curl_easy_os_errno_get, 0);
3538
3897
  rb_define_method(cCurlEasy, "num_connects", ruby_curl_easy_num_connects_get, 0);
3898
+ rb_define_method(cCurlEasy, "cookielist", ruby_curl_easy_cookielist_get, 0);
3539
3899
  rb_define_method(cCurlEasy, "ftp_entry_path", ruby_curl_easy_ftp_entry_path_get, 0);
3540
3900
 
3541
3901
  rb_define_method(cCurlEasy, "close", ruby_curl_easy_close, 0);