curb 0.8.4 → 0.9.11

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,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,
@@ -96,6 +102,25 @@ static size_t read_data_handler(void *ptr,
96
102
  }
97
103
  }
98
104
 
105
+ int seek_data_handler(ruby_curl_easy *rbce,
106
+ curl_off_t offset,
107
+ int origin) {
108
+
109
+ VALUE upload = rb_easy_get("upload");
110
+ VALUE stream = ruby_curl_upload_stream_get(upload);
111
+
112
+ if (rb_respond_to(stream, rb_intern("seek"))) {
113
+ rb_funcall(stream, rb_intern("seek"), 2, SEEK_SET, offset);
114
+ } else {
115
+ ruby_curl_upload *rbcu;
116
+ Data_Get_Struct(upload, ruby_curl_upload, rbcu);
117
+ // This OK because curl only uses SEEK_SET as per the documentation
118
+ rbcu->offset = offset;
119
+ }
120
+
121
+ return 0;
122
+ }
123
+
99
124
  static size_t proc_data_handler(char *stream,
100
125
  size_t size,
101
126
  size_t nmemb,
@@ -173,7 +198,7 @@ static int proc_progress_handler(VALUE proc,
173
198
 
174
199
  static VALUE call_debug_handler(VALUE ary) {
175
200
  return rb_funcall(rb_ary_entry(ary, 0), idCall, 2,
176
- rb_ary_entry(ary, 1), // INT2FIX(type),
201
+ rb_ary_entry(ary, 1), // INT2NUM(type),
177
202
  rb_ary_entry(ary, 2)); // rb_str_new(data, data_len)
178
203
  }
179
204
  static int proc_debug_handler(CURL *curl,
@@ -183,13 +208,13 @@ static int proc_debug_handler(CURL *curl,
183
208
  VALUE proc) {
184
209
  VALUE callargs = rb_ary_new2(3);
185
210
  rb_ary_store(callargs, 0, proc);
186
- rb_ary_store(callargs, 1, INT2FIX(type));
211
+ rb_ary_store(callargs, 1, INT2NUM(type));
187
212
  rb_ary_store(callargs, 2, rb_str_new(data, data_len));
188
213
  rb_rescue(call_debug_handler, callargs, callback_exception, Qnil);
189
214
  /* no way to indicate to libcurl that we should break out given an exception in the on_debug handler...
190
215
  * this means exceptions will be swallowed
191
216
  */
192
- //rb_funcall(proc, idCall, 2, INT2FIX(type), rb_str_new(data, data_len));
217
+ //rb_funcall(proc, idCall, 2, INT2NUM(type), rb_str_new(data, data_len));
193
218
  return 0;
194
219
  }
195
220
 
@@ -204,12 +229,31 @@ static void ruby_curl_easy_free(ruby_curl_easy *rbce) {
204
229
  curl_slist_free_all(rbce->curl_headers);
205
230
  }
206
231
 
232
+ if (rbce->curl_proxy_headers) {
233
+ curl_slist_free_all(rbce->curl_proxy_headers);
234
+ }
235
+
207
236
  if (rbce->curl_ftp_commands) {
208
237
  curl_slist_free_all(rbce->curl_ftp_commands);
209
238
  }
210
239
 
240
+ if (rbce->curl_resolve) {
241
+ curl_slist_free_all(rbce->curl_resolve);
242
+ }
243
+
211
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);
212
255
  curl_easy_cleanup(rbce->curl);
256
+ rbce->curl = NULL;
213
257
  }
214
258
  }
215
259
 
@@ -225,7 +269,9 @@ static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
225
269
  rbce->opts = rb_hash_new();
226
270
 
227
271
  rbce->curl_headers = NULL;
272
+ rbce->curl_proxy_headers = NULL;
228
273
  rbce->curl_ftp_commands = NULL;
274
+ rbce->curl_resolve = NULL;
229
275
 
230
276
  /* various-typed opts */
231
277
  rbce->local_port = 0;
@@ -236,11 +282,15 @@ static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
236
282
  rbce->proxy_auth_types = 0;
237
283
  rbce->max_redirs = -1;
238
284
  rbce->timeout = 0;
285
+ rbce->timeout_ms = 0;
239
286
  rbce->connect_timeout = 0;
287
+ rbce->connect_timeout_ms = 0;
240
288
  rbce->dns_cache_timeout = 60;
241
289
  rbce->ftp_response_timeout = 0;
242
290
  rbce->low_speed_limit = 0;
243
291
  rbce->low_speed_time = 0;
292
+ rbce->max_send_speed_large = 0;
293
+ rbce->max_recv_speed_large = 0;
244
294
  rbce->ssl_version = -1;
245
295
  rbce->use_ssl = -1;
246
296
  rbce->ftp_filemethod = -1;
@@ -262,25 +312,37 @@ static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
262
312
  rbce->callback_active = 0;
263
313
  }
264
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
+
265
328
  /*
266
329
  * call-seq:
267
330
  * Curl::Easy.new => #<Curl::Easy...>
268
331
  * Curl::Easy.new(url = nil) => #<Curl::Easy...>
269
332
  * Curl::Easy.new(url = nil) { |self| ... } => #<Curl::Easy...>
270
333
  *
271
- * Create a new Curl::Easy instance, optionally supplying the URL.
334
+ * Initialize a new Curl::Easy instance, optionally supplying the URL.
272
335
  * The block form allows further configuration to be supplied before
273
336
  * the instance is returned.
274
337
  */
275
- 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) {
276
339
  CURLcode ecode;
277
340
  VALUE url, blk;
278
- VALUE new_curl;
279
341
  ruby_curl_easy *rbce;
280
342
 
281
343
  rb_scan_args(argc, argv, "01&", &url, &blk);
282
344
 
283
- rbce = ALLOC(ruby_curl_easy);
345
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
284
346
 
285
347
  /* handler */
286
348
  rbce->curl = curl_easy_init();
@@ -288,8 +350,6 @@ static VALUE ruby_curl_easy_new(int argc, VALUE *argv, VALUE klass) {
288
350
  rb_raise(eCurlErrFailedInit, "Failed to initialize easy handle");
289
351
  }
290
352
 
291
- new_curl = Data_Wrap_Struct(klass, curl_easy_mark, curl_easy_free, rbce);
292
-
293
353
  rbce->multi = Qnil;
294
354
  rbce->opts = Qnil;
295
355
 
@@ -297,17 +357,18 @@ static VALUE ruby_curl_easy_new(int argc, VALUE *argv, VALUE klass) {
297
357
 
298
358
  rb_easy_set("url", url);
299
359
 
300
- /* set the new_curl pointer to the curl handle */
301
- 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);
302
363
  if (ecode != CURLE_OK) {
303
364
  raise_curl_easy_error_exception(ecode);
304
365
  }
305
366
 
306
367
  if (blk != Qnil) {
307
- rb_funcall(blk, idCall, 1, new_curl);
368
+ rb_funcall(blk, idCall, 1, self);
308
369
  }
309
370
 
310
- return new_curl;
371
+ return self;
311
372
  }
312
373
 
313
374
  /*
@@ -327,7 +388,9 @@ static VALUE ruby_curl_easy_clone(VALUE self) {
327
388
  memcpy(newrbce, rbce, sizeof(ruby_curl_easy));
328
389
  newrbce->curl = curl_easy_duphandle(rbce->curl);
329
390
  newrbce->curl_headers = NULL;
391
+ newrbce->curl_proxy_headers = NULL;
330
392
  newrbce->curl_ftp_commands = NULL;
393
+ newrbce->curl_resolve = NULL;
331
394
 
332
395
  return Data_Wrap_Struct(cCurlEasy, curl_easy_mark, curl_easy_free, newrbce);
333
396
  }
@@ -337,7 +400,7 @@ static VALUE ruby_curl_easy_clone(VALUE self) {
337
400
  * easy.close => nil
338
401
  *
339
402
  * Close the Curl::Easy instance. Any open connections are closed
340
- * The easy handle is reinitialized. If a previous multi handle was
403
+ * The easy handle is reinitialized. If a previous multi handle was
341
404
  * open it is set to nil and will be cleared after a GC.
342
405
  */
343
406
  static VALUE ruby_curl_easy_close(VALUE self) {
@@ -410,6 +473,12 @@ static VALUE ruby_curl_easy_reset(VALUE self) {
410
473
  rbce->curl_headers = NULL;
411
474
  }
412
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
+
413
482
  return opts_dup;
414
483
  }
415
484
 
@@ -462,6 +531,10 @@ static VALUE ruby_curl_easy_headers_set(VALUE self, VALUE headers) {
462
531
  CURB_OBJECT_HSETTER(ruby_curl_easy, headers);
463
532
  }
464
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
+
465
538
  /*
466
539
  * call-seq:
467
540
  * easy.headers => Hash, Array or Str
@@ -472,11 +545,46 @@ static VALUE ruby_curl_easy_headers_get(VALUE self) {
472
545
  ruby_curl_easy *rbce;
473
546
  VALUE headers;
474
547
  Data_Get_Struct(self, ruby_curl_easy, rbce);
475
- 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"));
476
549
  if (headers == Qnil) { headers = rb_easy_set("headers", rb_hash_new()); }
477
550
  return headers;
478
551
  }
479
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
+
480
588
  /*
481
589
  * call-seq:
482
590
  * easy.interface => string
@@ -684,29 +792,29 @@ static VALUE ruby_curl_easy_useragent_get(VALUE self) {
684
792
  /*
685
793
  * call-seq:
686
794
  * easy.post_body = "some=form%20data&to=send" => string or nil
687
- *
795
+ *
688
796
  * Sets the POST body of this Curl::Easy instance. This is expected to be
689
797
  * URL encoded; no additional processing or encoding is done on the string.
690
798
  * The content-type header will be set to application/x-www-form-urlencoded.
691
- *
799
+ *
692
800
  * This is handy if you want to perform a POST against a Curl::Multi instance.
693
801
  */
694
802
  static VALUE ruby_curl_easy_post_body_set(VALUE self, VALUE post_body) {
695
803
  ruby_curl_easy *rbce;
696
804
  CURL *curl;
697
-
805
+
698
806
  char *data;
699
807
  long len;
700
808
 
701
809
  Data_Get_Struct(self, ruby_curl_easy, rbce);
702
-
810
+
703
811
  curl = rbce->curl;
704
-
812
+
705
813
  if ( post_body == Qnil ) {
706
814
  rb_easy_del("postdata_buffer");
707
815
  curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
708
-
709
- } else {
816
+
817
+ } else {
710
818
  if (rb_type(post_body) == T_STRING) {
711
819
  data = StringValuePtr(post_body);
712
820
  len = RSTRING_LEN(post_body);
@@ -719,19 +827,19 @@ static VALUE ruby_curl_easy_post_body_set(VALUE self, VALUE post_body) {
719
827
  else {
720
828
  rb_raise(rb_eRuntimeError, "post data must respond_to .to_s");
721
829
  }
722
-
723
- // 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
724
832
  // request. See CURLOPT_POSTFIELDS in the libcurl docs.
725
833
  //rbce->postdata_buffer = post_body;
726
834
  rb_easy_set("postdata_buffer", post_body);
727
-
835
+
728
836
  curl_easy_setopt(curl, CURLOPT_POST, 1);
729
837
  curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
730
838
  curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len);
731
-
839
+
732
840
  return post_body;
733
841
  }
734
-
842
+
735
843
  return Qnil;
736
844
  }
737
845
 
@@ -748,7 +856,7 @@ static VALUE ruby_curl_easy_post_body_get(VALUE self) {
748
856
  /*
749
857
  * call-seq:
750
858
  * easy.put_data = data => ""
751
- *
859
+ *
752
860
  * Points this Curl::Easy instance to data to be uploaded via PUT. This
753
861
  * sets the request to a PUT type request - useful if you want to PUT via
754
862
  * a multi handle.
@@ -772,9 +880,15 @@ static VALUE ruby_curl_easy_put_data_set(VALUE self, VALUE data) {
772
880
  curl_easy_setopt(curl, CURLOPT_NOBODY, 0);
773
881
  curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
774
882
  curl_easy_setopt(curl, CURLOPT_READFUNCTION, (curl_read_callback)read_data_handler);
883
+ #if HAVE_CURLOPT_SEEKFUNCTION
884
+ curl_easy_setopt(curl, CURLOPT_SEEKFUNCTION, (curl_seek_callback)seek_data_handler);
885
+ #endif
775
886
  curl_easy_setopt(curl, CURLOPT_READDATA, rbce);
887
+ #if HAVE_CURLOPT_SEEKDATA
888
+ curl_easy_setopt(curl, CURLOPT_SEEKDATA, rbce);
889
+ #endif
776
890
 
777
- /*
891
+ /*
778
892
  * we need to set specific headers for the PUT to work... so
779
893
  * convert the internal headers structure to a HASH if one is set
780
894
  */
@@ -788,7 +902,7 @@ static VALUE ruby_curl_easy_put_data_set(VALUE self, VALUE data) {
788
902
  if (NIL_P(data)) { return data; }
789
903
 
790
904
  headers = rb_easy_get("headers");
791
- if( headers == Qnil ) {
905
+ if( headers == Qnil ) {
792
906
  headers = rb_hash_new();
793
907
  }
794
908
 
@@ -800,14 +914,14 @@ static VALUE ruby_curl_easy_put_data_set(VALUE self, VALUE data) {
800
914
  rb_hash_aset(headers, rb_str_new2("Expect"), rb_str_new2(""));
801
915
  }
802
916
  size = rb_funcall(stat, rb_intern("size"), 0);
803
- curl_easy_setopt(curl, CURLOPT_INFILESIZE, FIX2LONG(size));
917
+ curl_easy_setopt(curl, CURLOPT_INFILESIZE, NUM2LONG(size));
804
918
  }
805
919
  else if( rb_hash_aref(headers, rb_str_new2("Content-Length")) == Qnil && rb_hash_aref(headers, rb_str_new2("Transfer-Encoding")) == Qnil ) {
806
920
  rb_hash_aset(headers, rb_str_new2("Transfer-Encoding"), rb_str_new2("chunked"));
807
921
  }
808
922
  else if( rb_hash_aref(headers, rb_str_new2("Content-Length")) ) {
809
923
  VALUE size = rb_funcall(rb_hash_aref(headers, rb_str_new2("Content-Length")), rb_intern("to_i"), 0);
810
- curl_easy_setopt(curl, CURLOPT_INFILESIZE, FIX2LONG(size));
924
+ curl_easy_setopt(curl, CURLOPT_INFILESIZE, NUM2LONG(size));
811
925
  }
812
926
  }
813
927
  else if (rb_respond_to(data, rb_intern("to_s"))) {
@@ -843,6 +957,25 @@ static VALUE ruby_curl_easy_ftp_commands_get(VALUE self) {
843
957
  CURB_OBJECT_HGETTER(ruby_curl_easy, ftp_commands);
844
958
  }
845
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
+
846
979
  /* ================== IMMED ATTRS ==================*/
847
980
 
848
981
  /*
@@ -951,16 +1084,16 @@ static VALUE ruby_curl_easy_proxy_type_get(VALUE self) {
951
1084
  (!strncmp("digest",node,6)) ? CURLAUTH_DIGEST : \
952
1085
  (!strncmp("gssnegotiate",node,12)) ? CURLAUTH_GSSNEGOTIATE : \
953
1086
  (!strncmp("ntlm",node,4)) ? CURLAUTH_NTLM : \
954
- (!strncmp("any",node,3)) ? CURLAUTH_ANY : \
955
- (!strncmp("anysafe",node,7)) ? CURLAUTH_ANYSAFE : 0
956
- #else
1087
+ (!strncmp("anysafe",node,7)) ? CURLAUTH_ANYSAFE : \
1088
+ (!strncmp("any",node,3)) ? CURLAUTH_ANY : 0
1089
+ #else
957
1090
  #define CURL_HTTPAUTH_STR_TO_NUM(node) \
958
1091
  (!strncmp("basic",node,5)) ? CURLAUTH_BASIC : \
959
1092
  (!strncmp("digest",node,6)) ? CURLAUTH_DIGEST : \
960
1093
  (!strncmp("gssnegotiate",node,12)) ? CURLAUTH_GSSNEGOTIATE : \
961
1094
  (!strncmp("ntlm",node,4)) ? CURLAUTH_NTLM : \
962
- (!strncmp("any",node,3)) ? CURLAUTH_ANY : \
963
- (!strncmp("anysafe",node,7)) ? CURLAUTH_ANYSAFE : 0
1095
+ (!strncmp("anysafe",node,7)) ? CURLAUTH_ANYSAFE : \
1096
+ (!strncmp("any",node,3)) ? CURLAUTH_ANY : 0
964
1097
  #endif
965
1098
  /*
966
1099
  * call-seq:
@@ -974,21 +1107,22 @@ static VALUE ruby_curl_easy_proxy_type_get(VALUE self) {
974
1107
  static VALUE ruby_curl_easy_http_auth_types_set(int argc, VALUE *argv, VALUE self) {//VALUE self, VALUE http_auth_types) {
975
1108
  ruby_curl_easy *rbce;
976
1109
  VALUE args_ary;
977
- int i, len;
1110
+ long i, len;
978
1111
  char* node = NULL;
979
- long mask = 0x000000;
1112
+ long mask = 0;
980
1113
 
981
1114
  rb_scan_args(argc, argv, "*", &args_ary);
982
1115
  Data_Get_Struct(self, ruby_curl_easy, rbce);
983
1116
 
984
- len = (int)RARRAY_LEN(args_ary);
1117
+ len = RARRAY_LEN(args_ary);
985
1118
 
986
- if (len == 1 && (TYPE(rb_ary_entry(args_ary,0)) == T_FIXNUM || rb_ary_entry(args_ary,0) == Qnil)) {
987
- if (rb_ary_entry(args_ary,0) == Qnil) {
1119
+ if (len == 1 && (rb_ary_entry(args_ary,0) == Qnil || TYPE(rb_ary_entry(args_ary,0)) == T_FIXNUM ||
1120
+ TYPE(rb_ary_entry(args_ary,0)) == T_BIGNUM)) {
1121
+ if (rb_ary_entry(args_ary,0) == Qnil) {
988
1122
  rbce->http_auth_types = 0;
989
1123
  }
990
1124
  else {
991
- rbce->http_auth_types = NUM2INT(rb_ary_entry(args_ary,0));
1125
+ rbce->http_auth_types = NUM2LONG(rb_ary_entry(args_ary,0));
992
1126
  }
993
1127
  }
994
1128
  else {
@@ -1001,7 +1135,7 @@ static VALUE ruby_curl_easy_http_auth_types_set(int argc, VALUE *argv, VALUE sel
1001
1135
  }
1002
1136
  rbce->http_auth_types = mask;
1003
1137
  }
1004
- return INT2NUM(rbce->http_auth_types);
1138
+ return LONG2NUM(rbce->http_auth_types);
1005
1139
  }
1006
1140
 
1007
1141
  /*
@@ -1066,7 +1200,7 @@ static VALUE ruby_curl_easy_max_redirects_get(VALUE self) {
1066
1200
 
1067
1201
  /*
1068
1202
  * call-seq:
1069
- * easy.timeout = fixnum or nil => fixnum or nil
1203
+ * easy.timeout = float, fixnum or nil => numeric
1070
1204
  *
1071
1205
  * Set the maximum time in seconds that you allow the libcurl transfer
1072
1206
  * operation to take. Normally, name lookups can take a considerable time
@@ -1075,20 +1209,77 @@ static VALUE ruby_curl_easy_max_redirects_get(VALUE self) {
1075
1209
  *
1076
1210
  * Set to nil (or zero) to disable timeout (it will then only timeout
1077
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
+ *
1078
1217
  */
1079
- static VALUE ruby_curl_easy_timeout_set(VALUE self, VALUE timeout) {
1080
- 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);
1081
1229
  }
1082
1230
 
1083
1231
  /*
1084
1232
  * call-seq:
1085
- * easy.timeout => fixnum or nil
1233
+ * easy.timeout => numeric
1086
1234
  *
1087
1235
  * Obtain the maximum time in seconds that you allow the libcurl transfer
1088
1236
  * operation to take.
1237
+ *
1238
+ * Uses timeout_ms internally instead of timeout.
1239
+ *
1240
+ */
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);
1245
+ }
1246
+
1247
+ /*
1248
+ * call-seq:
1249
+ * easy.timeout_ms = fixnum or nil => fixnum or nil
1250
+ *
1251
+ * Set the maximum time in milliseconds that you allow the libcurl transfer
1252
+ * operation to take. Normally, name lookups can take a considerable time
1253
+ * and limiting operations to less than a few minutes risk aborting
1254
+ * perfectly normal operations.
1255
+ *
1256
+ * Set to nil (or zero) to disable timeout (it will then only timeout
1257
+ * on the system's internal timeouts).
1258
+ */
1259
+ static VALUE ruby_curl_easy_timeout_ms_set(VALUE self, VALUE timeout_ms) {
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);
1270
+ }
1271
+
1272
+ /*
1273
+ * call-seq:
1274
+ * easy.timeout_ms => fixnum or nil
1275
+ *
1276
+ * Obtain the maximum time in milliseconds that you allow the libcurl transfer
1277
+ * operation to take.
1089
1278
  */
1090
- static VALUE ruby_curl_easy_timeout_get(VALUE self, VALUE timeout) {
1091
- CURB_IMMED_GETTER(ruby_curl_easy, timeout, 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);
1092
1283
  }
1093
1284
 
1094
1285
  /*
@@ -1117,6 +1308,32 @@ static VALUE ruby_curl_easy_connect_timeout_get(VALUE self, VALUE connect_timeou
1117
1308
  CURB_IMMED_GETTER(ruby_curl_easy, connect_timeout, 0);
1118
1309
  }
1119
1310
 
1311
+ /*
1312
+ * call-seq:
1313
+ * easy.connect_timeout_ms = fixnum or nil => fixnum or nil
1314
+ *
1315
+ * Set the maximum time in milliseconds that you allow the connection to the
1316
+ * server to take. This only limits the connection phase, once it has
1317
+ * connected, this option is of no more use.
1318
+ *
1319
+ * Set to nil (or zero) to disable connection timeout (it will then only
1320
+ * timeout on the system's internal timeouts).
1321
+ */
1322
+ static VALUE ruby_curl_easy_connect_timeout_ms_set(VALUE self, VALUE connect_timeout_ms) {
1323
+ CURB_IMMED_SETTER(ruby_curl_easy, connect_timeout_ms, 0);
1324
+ }
1325
+
1326
+ /*
1327
+ * call-seq:
1328
+ * easy.connect_timeout_ms => fixnum or nil
1329
+ *
1330
+ * Obtain the maximum time in milliseconds that you allow the connection to the
1331
+ * server to take.
1332
+ */
1333
+ static VALUE ruby_curl_easy_connect_timeout_ms_get(VALUE self, VALUE connect_timeout_ms) {
1334
+ CURB_IMMED_GETTER(ruby_curl_easy, connect_timeout_ms, 0);
1335
+ }
1336
+
1120
1337
  /*
1121
1338
  * call-seq:
1122
1339
  * easy.dns_cache_timeout = fixnum or nil => fixnum or nil
@@ -1212,6 +1429,46 @@ static VALUE ruby_curl_easy_low_speed_time_get(VALUE self, VALUE low_speed_time)
1212
1429
  CURB_IMMED_GETTER(ruby_curl_easy, low_speed_time, 0);
1213
1430
  }
1214
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
+
1215
1472
  /*
1216
1473
  * call-seq:
1217
1474
  * easy.username = string => string
@@ -1229,7 +1486,7 @@ static VALUE ruby_curl_easy_username_set(VALUE self, VALUE username) {
1229
1486
  /*
1230
1487
  * call-seq:
1231
1488
  * easy.username => string
1232
- *
1489
+ *
1233
1490
  * Get the current username
1234
1491
  */
1235
1492
  static VALUE ruby_curl_easy_username_get(VALUE self, VALUE username) {
@@ -1257,7 +1514,7 @@ static VALUE ruby_curl_easy_password_set(VALUE self, VALUE password) {
1257
1514
  /*
1258
1515
  * call-seq:
1259
1516
  * easy.password => string
1260
- *
1517
+ *
1261
1518
  * Get the current password
1262
1519
  */
1263
1520
  static VALUE ruby_curl_easy_password_get(VALUE self, VALUE password) {
@@ -1273,8 +1530,15 @@ static VALUE ruby_curl_easy_password_get(VALUE self, VALUE password) {
1273
1530
  * easy.ssl_version = value => fixnum or nil
1274
1531
  *
1275
1532
  * Sets the version of SSL/TLS that libcurl will attempt to use. Valid
1276
- * options are Curl::CURL_SSLVERSION_TLSv1, Curl::CURL_SSLVERSION::SSLv2,
1277
- * Curl::CURL_SSLVERSION_SSLv3 and Curl::CURL_SSLVERSION_DEFAULT
1533
+ * options are:
1534
+ *
1535
+ * Curl::CURL_SSLVERSION_DEFAULT
1536
+ * Curl::CURL_SSLVERSION_TLSv1 (TLS 1.x)
1537
+ * Curl::CURL_SSLVERSION_SSLv2
1538
+ * Curl::CURL_SSLVERSION_SSLv3
1539
+ * Curl::CURL_SSLVERSION_TLSv1_0
1540
+ * Curl::CURL_SSLVERSION_TLSv1_1
1541
+ * Curl::CURL_SSLVERSION_TLSv1_2
1278
1542
  */
1279
1543
  static VALUE ruby_curl_easy_ssl_version_set(VALUE self, VALUE ssl_version) {
1280
1544
  CURB_IMMED_SETTER(ruby_curl_easy, ssl_version, -1);
@@ -1293,7 +1557,7 @@ static VALUE ruby_curl_easy_ssl_version_get(VALUE self, VALUE ssl_version) {
1293
1557
  /*
1294
1558
  * call-seq:
1295
1559
  * easy.use_ssl = value => fixnum or nil
1296
- *
1560
+ *
1297
1561
  * Ensure libcurl uses SSL for FTP connections. Valid options are Curl::CURL_USESSL_NONE,
1298
1562
  * Curl::CURL_USESSL_TRY, Curl::CURL_USESSL_CONTROL, and Curl::CURL_USESSL_ALL.
1299
1563
  */
@@ -1637,9 +1901,10 @@ static VALUE ruby_curl_easy_ignore_content_length_q(VALUE self) {
1637
1901
  */
1638
1902
  static VALUE ruby_curl_easy_resolve_mode(VALUE self) {
1639
1903
  ruby_curl_easy *rbce;
1904
+ unsigned short rm;
1640
1905
  Data_Get_Struct(self, ruby_curl_easy, rbce);
1641
1906
 
1642
- unsigned short rm = rbce->resolve_mode;
1907
+ rm = rbce->resolve_mode;
1643
1908
 
1644
1909
  switch(rm) {
1645
1910
  case CURL_IPRESOLVE_V4:
@@ -1668,9 +1933,10 @@ static VALUE ruby_curl_easy_resolve_mode_set(VALUE self, VALUE resolve_mode) {
1668
1933
  return Qnil;
1669
1934
  } else {
1670
1935
  ruby_curl_easy *rbce;
1936
+ ID resolve_mode_id;
1671
1937
  Data_Get_Struct(self, ruby_curl_easy, rbce);
1672
1938
 
1673
- ID resolve_mode_id = rb_to_id(resolve_mode);
1939
+ resolve_mode_id = rb_to_id(resolve_mode);
1674
1940
 
1675
1941
  if (resolve_mode_id == rb_intern("auto")) {
1676
1942
  rbce->resolve_mode = CURL_IPRESOLVE_WHATEVER;
@@ -1748,7 +2014,7 @@ static VALUE ruby_curl_easy_on_failure_set(int argc, VALUE *argv, VALUE self) {
1748
2014
  * To remove a previously-supplied handler, call this method with no attached
1749
2015
  * block.
1750
2016
  *
1751
- * 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
1752
2018
  * status of 40x
1753
2019
  */
1754
2020
  static VALUE ruby_curl_easy_on_missing_set(int argc, VALUE *argv, VALUE self) {
@@ -1763,7 +2029,7 @@ static VALUE ruby_curl_easy_on_missing_set(int argc, VALUE *argv, VALUE self) {
1763
2029
  * To remove a previously-supplied handler, call this method with no attached
1764
2030
  * block.
1765
2031
  *
1766
- * 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
1767
2033
  * status of 30x
1768
2034
  */
1769
2035
  static VALUE ruby_curl_easy_on_redirect_set(int argc, VALUE *argv, VALUE self) {
@@ -1850,10 +2116,10 @@ static VALUE ruby_curl_easy_on_debug_set(int argc, VALUE *argv, VALUE self) {
1850
2116
  */
1851
2117
  static VALUE cb_each_http_header(VALUE header, VALUE wrap) {
1852
2118
  struct curl_slist **list;
1853
- Data_Get_Struct(wrap, struct curl_slist *, list);
1854
-
1855
2119
  VALUE header_str = Qnil;
1856
2120
 
2121
+ Data_Get_Struct(wrap, struct curl_slist *, list);
2122
+
1857
2123
  //rb_p(header);
1858
2124
 
1859
2125
  if (rb_type(header) == T_ARRAY) {
@@ -1877,19 +2143,66 @@ static VALUE cb_each_http_header(VALUE header, VALUE wrap) {
1877
2143
  return header_str;
1878
2144
  }
1879
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
+
1880
2178
  /***********************************************
1881
2179
  * This is an rb_iterate callback used to set up ftp commands.
1882
2180
  */
1883
2181
  static VALUE cb_each_ftp_command(VALUE ftp_command, VALUE wrap) {
1884
2182
  struct curl_slist **list;
2183
+ VALUE ftp_command_string;
1885
2184
  Data_Get_Struct(wrap, struct curl_slist *, list);
1886
2185
 
1887
- VALUE ftp_command_string = rb_obj_as_string(ftp_command);
2186
+ ftp_command_string = rb_obj_as_string(ftp_command);
1888
2187
  *list = curl_slist_append(*list, StringValuePtr(ftp_command));
1889
2188
 
1890
2189
  return ftp_command_string;
1891
2190
  }
1892
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
+
1893
2206
  /***********************************************
1894
2207
  *
1895
2208
  * Setup a connection
@@ -1901,7 +2214,9 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
1901
2214
  CURL *curl;
1902
2215
  VALUE url, _url = rb_easy_get("url");
1903
2216
  struct curl_slist **hdrs = &(rbce->curl_headers);
2217
+ struct curl_slist **phdrs = &(rbce->curl_proxy_headers);
1904
2218
  struct curl_slist **cmds = &(rbce->curl_ftp_commands);
2219
+ struct curl_slist **rslv = &(rbce->curl_resolve);
1905
2220
 
1906
2221
  curl = rbce->curl;
1907
2222
 
@@ -1910,7 +2225,6 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
1910
2225
  }
1911
2226
 
1912
2227
  url = rb_check_string_type(_url);
1913
-
1914
2228
  curl_easy_setopt(curl, CURLOPT_URL, StringValuePtr(url));
1915
2229
 
1916
2230
  // network stuff and auth
@@ -2023,8 +2337,14 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2023
2337
 
2024
2338
  curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, rbce->unrestricted_auth);
2025
2339
 
2026
- curl_easy_setopt(curl, CURLOPT_TIMEOUT, rbce->timeout);
2340
+ #if HAVE_CURLOPT_TIMEOUT_MS
2341
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, rbce->timeout_ms);
2342
+ #endif
2343
+
2027
2344
  curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, rbce->connect_timeout);
2345
+ #if HAVE_CURLOPT_CONNECTTIMEOUT_MS
2346
+ curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, rbce->connect_timeout_ms);
2347
+ #endif
2028
2348
  curl_easy_setopt(curl, CURLOPT_DNS_CACHE_TIMEOUT, rbce->dns_cache_timeout);
2029
2349
 
2030
2350
  curl_easy_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, rbce->ignore_content_length);
@@ -2043,6 +2363,9 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2043
2363
  curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, rbce->low_speed_limit);
2044
2364
  curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, rbce->low_speed_time);
2045
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
+
2046
2369
  // Set up localport / proxy port
2047
2370
  // FIXME these won't get returned to default if they're unset Ruby
2048
2371
  if (rbce->proxy_port > 0) {
@@ -2075,21 +2398,20 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2075
2398
  #endif
2076
2399
  }
2077
2400
 
2078
- if (rbce->http_auth_types > 0) {
2401
+ /*
2402
+ * NOTE: we used to set CURLAUTH_ANY but see: http://curl.haxx.se/mail/lib-2015-06/0033.html
2403
+ */
2404
+ if (rbce->http_auth_types != 0) {
2079
2405
  #if LIBCURL_VERSION_NUM >= 0x070a06
2080
2406
  curl_easy_setopt(curl, CURLOPT_HTTPAUTH, rbce->http_auth_types);
2081
- } else {
2082
- curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
2083
2407
  #else
2084
2408
  rb_warn("Installed libcurl is too old to support http_auth_types");
2085
2409
  #endif
2086
2410
  }
2087
2411
 
2088
- if (rbce->proxy_auth_types > 0) {
2412
+ if (rbce->proxy_auth_types != 0) {
2089
2413
  #if LIBCURL_VERSION_NUM >= 0x070a07
2090
2414
  curl_easy_setopt(curl, CURLOPT_PROXYAUTH, rbce->proxy_auth_types);
2091
- } else {
2092
- curl_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
2093
2415
  #else
2094
2416
  rb_warn("Installed libcurl is too old to support proxy_auth_types");
2095
2417
  #endif
@@ -2150,7 +2472,7 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2150
2472
  rb_warn("libcurl is not configured with SSL support");
2151
2473
  }
2152
2474
  #endif
2153
-
2475
+
2154
2476
  if (rbce->ftp_filemethod > 0) {
2155
2477
  curl_easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, rbce->ftp_filemethod);
2156
2478
  }
@@ -2177,6 +2499,25 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2177
2499
  }
2178
2500
  }
2179
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
+
2180
2521
  /* Setup FTP commands if necessary */
2181
2522
  if (!rb_easy_nil("ftp_commands")) {
2182
2523
  if (rb_easy_type_check("ftp_commands", T_ARRAY)) {
@@ -2189,18 +2530,33 @@ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce) {
2189
2530
  }
2190
2531
  }
2191
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
+
2192
2547
  return Qnil;
2193
2548
  }
2194
2549
  /***********************************************
2195
2550
  *
2196
2551
  * Clean up a connection
2197
2552
  *
2198
- * Always returns Qtrue.
2553
+ * Always returns Qnil.
2199
2554
  */
2200
2555
  VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
2201
2556
 
2202
2557
  CURL *curl = rbce->curl;
2203
2558
  struct curl_slist *ftp_commands;
2559
+ struct curl_slist *resolve;
2204
2560
 
2205
2561
  /* Free everything up */
2206
2562
  if (rbce->curl_headers) {
@@ -2208,12 +2564,23 @@ VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
2208
2564
  rbce->curl_headers = NULL;
2209
2565
  }
2210
2566
 
2567
+ if (rbce->curl_proxy_headers) {
2568
+ curl_slist_free_all(rbce->curl_proxy_headers);
2569
+ rbce->curl_proxy_headers = NULL;
2570
+ }
2571
+
2211
2572
  ftp_commands = rbce->curl_ftp_commands;
2212
2573
  if (ftp_commands) {
2213
2574
  curl_slist_free_all(ftp_commands);
2214
2575
  rbce->curl_ftp_commands = NULL;
2215
2576
  }
2216
2577
 
2578
+ resolve = rbce->curl_resolve;
2579
+ if (resolve) {
2580
+ curl_slist_free_all(resolve);
2581
+ rbce->curl_resolve = NULL;
2582
+ }
2583
+
2217
2584
  /* clean up a PUT request's curl options. */
2218
2585
  if (!rb_easy_nil("upload")) {
2219
2586
  rb_easy_del("upload"); // set the upload object to Qnil to let the GC clean up
@@ -2223,6 +2590,9 @@ VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
2223
2590
  curl_easy_setopt(curl, CURLOPT_INFILESIZE, 0);
2224
2591
  }
2225
2592
 
2593
+ // set values on cleanup to nil
2594
+ rb_easy_del("multi");
2595
+
2226
2596
  return Qnil;
2227
2597
  }
2228
2598
 
@@ -2314,7 +2684,7 @@ static VALUE ruby_curl_easy_perform_post(int argc, VALUE *argv, VALUE self) {
2314
2684
  append_to_form(argv[i], &first, &last);
2315
2685
  } else if (rb_type(argv[i]) == T_ARRAY) {
2316
2686
  // see: https://github.com/rvanlieshout/curb/commit/8bcdefddc0162484681ebd1a92d52a642666a445
2317
- int c = 0, argv_len = (int)RARRAY_LEN(argv[i]);
2687
+ long c = 0, argv_len = RARRAY_LEN(argv[i]);
2318
2688
  for (; c < argv_len; ++c) {
2319
2689
  if (rb_obj_is_instance_of(rb_ary_entry(argv[i],c), cCurlPostField)) {
2320
2690
  append_to_form(rb_ary_entry(argv[i],c), &first, &last);
@@ -2467,7 +2837,7 @@ static VALUE ruby_curl_easy_response_code_get(VALUE self) {
2467
2837
  static VALUE ruby_curl_easy_primary_ip_get(VALUE self) {
2468
2838
  ruby_curl_easy *rbce;
2469
2839
  char* ip;
2470
-
2840
+
2471
2841
  Data_Get_Struct(self, ruby_curl_easy, rbce);
2472
2842
  curl_easy_getinfo(rbce->curl, CURLINFO_PRIMARY_IP, &ip);
2473
2843
 
@@ -2523,7 +2893,7 @@ static VALUE ruby_curl_easy_file_time_get(VALUE self) {
2523
2893
  return LONG2NUM(time);
2524
2894
  #else
2525
2895
  rb_warn("Installed libcurl is too old to support file_time");
2526
- return INT2FIX(0);
2896
+ return LONG2NUM(0);
2527
2897
  #endif
2528
2898
  }
2529
2899
 
@@ -2578,6 +2948,29 @@ static VALUE ruby_curl_easy_connect_time_get(VALUE self) {
2578
2948
  return rb_float_new(time);
2579
2949
  }
2580
2950
 
2951
+ /*
2952
+ * call-seq:
2953
+ * easy.app_connect_time => float
2954
+ *
2955
+ * Retrieve the time, in seconds, it took from the start until the SSL/SSH
2956
+ * connect/handshake to the remote host was completed. This time is most often
2957
+ * 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
2959
+ * for the pipeline and more.
2960
+ */
2961
+ #if defined(HAVE_CURLINFO_APPCONNECT_TIME)
2962
+ static VALUE ruby_curl_easy_app_connect_time_get(VALUE self) {
2963
+ ruby_curl_easy *rbce;
2964
+ double time;
2965
+
2966
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
2967
+ curl_easy_getinfo(rbce->curl, CURLINFO_APPCONNECT_TIME, &time);
2968
+
2969
+ return rb_float_new(time);
2970
+ }
2971
+ #endif
2972
+
2973
+
2581
2974
  /*
2582
2975
  * call-seq:
2583
2976
  * easy.pre_transfer_time => float
@@ -2660,7 +3053,7 @@ static VALUE ruby_curl_easy_redirect_count_get(VALUE self) {
2660
3053
  return LONG2NUM(count);
2661
3054
  #else
2662
3055
  rb_warn("Installed libcurl is too old to support redirect_count");
2663
- return INT2FIX(-1);
3056
+ return LONG2NUM(-1);
2664
3057
  #endif
2665
3058
 
2666
3059
  }
@@ -2669,7 +3062,7 @@ static VALUE ruby_curl_easy_redirect_count_get(VALUE self) {
2669
3062
  * call-seq:
2670
3063
  * easy.redirect_url => "http://some.url" or nil
2671
3064
  *
2672
- * Retrieve the URL a redirect would take you to if you
3065
+ * Retrieve the URL a redirect would take you to if you
2673
3066
  * would enable CURLOPT_FOLLOWLOCATION.
2674
3067
  *
2675
3068
  * Requires libcurl 7.18.2 or higher, otherwise -1 is always returned.
@@ -2689,7 +3082,7 @@ static VALUE ruby_curl_easy_redirect_url_get(VALUE self) {
2689
3082
  }
2690
3083
  #else
2691
3084
  rb_warn("Installed libcurl is too old to support redirect_url");
2692
- return INT2FIX(-1);
3085
+ return LONG2NUM(-1);
2693
3086
  #endif
2694
3087
  }
2695
3088
 
@@ -2918,7 +3311,7 @@ static VALUE ruby_curl_easy_os_errno_get(VALUE self) {
2918
3311
  return LONG2NUM(result);
2919
3312
  #else
2920
3313
  rb_warn("Installed libcurl is too old to support os_errno");
2921
- return INT2FIX(0);
3314
+ return LONG2NUM(0);
2922
3315
  #endif
2923
3316
  }
2924
3317
 
@@ -2947,17 +3340,45 @@ static VALUE ruby_curl_easy_num_connects_get(VALUE self) {
2947
3340
  return LONG2NUM(result);
2948
3341
  #else
2949
3342
  rb_warn("Installed libcurl is too old to support num_connects");
2950
- return INT2FIX(-1);
3343
+ return LONG2NUM(-1);
2951
3344
  #endif
2952
3345
  }
2953
3346
 
2954
3347
 
2955
- /* 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;
2956
3365
 
2957
- CURLINFO_COOKIELIST
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;
3375
+
3376
+ #else
3377
+ rb_warn("Installed libcurl is too old to support cookielist");
3378
+ return INT2FIX(-1);
3379
+ #endif
3380
+ }
2958
3381
 
2959
- 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)
2960
- */
2961
3382
 
2962
3383
  /* TODO this needs to be implemented. Could probably support CONNECT_ONLY by having this
2963
3384
  * return an open Socket or something.
@@ -3024,18 +3445,19 @@ static VALUE ruby_curl_easy_multi_set(VALUE self, VALUE multi) {
3024
3445
  static VALUE ruby_curl_easy_last_result(VALUE self) {
3025
3446
  ruby_curl_easy *rbce;
3026
3447
  Data_Get_Struct(self, ruby_curl_easy, rbce);
3027
- return INT2FIX(rbce->last_result);
3448
+ return LONG2NUM(rbce->last_result);
3028
3449
  }
3029
3450
 
3030
3451
  /*
3031
3452
  * call-seq:
3032
3453
  * easy.setopt Fixnum, value => value
3033
3454
  *
3034
- * Iniital access to libcurl curl_easy_setopt
3455
+ * Initial access to libcurl curl_easy_setopt
3035
3456
  */
3036
3457
  static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3037
3458
  ruby_curl_easy *rbce;
3038
- long option = FIX2LONG(opt);
3459
+ long option = NUM2LONG(opt);
3460
+ rb_io_t *open_f_ptr;
3039
3461
 
3040
3462
  Data_Get_Struct(self, ruby_curl_easy, rbce);
3041
3463
 
@@ -3060,7 +3482,7 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3060
3482
  curl_easy_setopt(rbce->curl, CURLOPT_CUSTOMREQUEST, NIL_P(val) ? NULL : StringValueCStr(val));
3061
3483
  break;
3062
3484
  case CURLOPT_HTTP_VERSION:
3063
- curl_easy_setopt(rbce->curl, CURLOPT_HTTP_VERSION, FIX2INT(val));
3485
+ curl_easy_setopt(rbce->curl, CURLOPT_HTTP_VERSION, NUM2LONG(val));
3064
3486
  break;
3065
3487
  case CURLOPT_PROXY: {
3066
3488
  VALUE proxy_url = val;
@@ -3073,6 +3495,12 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3073
3495
  case CURLOPT_HEADER:
3074
3496
  case CURLOPT_NOPROGRESS:
3075
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
3076
3504
  case CURLOPT_HTTPGET:
3077
3505
  case CURLOPT_NOBODY: {
3078
3506
  int type = rb_type(val);
@@ -3084,8 +3512,17 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3084
3512
  } else {
3085
3513
  value = rb_funcall(val, rb_intern("to_i"), 0);
3086
3514
  }
3087
- curl_easy_setopt(rbce->curl, option, FIX2INT(value));
3515
+ curl_easy_setopt(rbce->curl, option, NUM2LONG(value));
3088
3516
  } break;
3517
+ case CURLOPT_POST: {
3518
+ curl_easy_setopt(rbce->curl, CURLOPT_POST, rb_type(val) == T_TRUE);
3519
+ } break;
3520
+ case CURLOPT_MAXCONNECTS: {
3521
+ curl_easy_setopt(rbce->curl, CURLOPT_MAXCONNECTS, NUM2LONG(val));
3522
+ } break;
3523
+ case CURLOPT_POSTFIELDS: {
3524
+ curl_easy_setopt(rbce->curl, CURLOPT_POSTFIELDS, NIL_P(val) ? NULL : StringValueCStr(val));
3525
+ } break;
3089
3526
  case CURLOPT_USERPWD: {
3090
3527
  VALUE userpwd = val;
3091
3528
  CURB_OBJECT_HSETTER(ruby_curl_easy, userpwd);
@@ -3106,14 +3543,71 @@ static VALUE ruby_curl_easy_set_opt(VALUE self, VALUE opt, VALUE val) {
3106
3543
  VALUE cookiejar = val;
3107
3544
  CURB_OBJECT_HSETTER(ruby_curl_easy, cookiejar);
3108
3545
  } break;
3546
+ case CURLOPT_TCP_NODELAY: {
3547
+ curl_easy_setopt(rbce->curl, CURLOPT_TCP_NODELAY, NUM2LONG(val));
3548
+ } break;
3109
3549
  case CURLOPT_RESUME_FROM: {
3110
- curl_easy_setopt(rbce->curl, CURLOPT_RESUME_FROM, FIX2LONG(val));
3550
+ curl_easy_setopt(rbce->curl, CURLOPT_RESUME_FROM, NUM2LONG(val));
3111
3551
  } break;
3112
3552
  case CURLOPT_FAILONERROR: {
3113
- curl_easy_setopt(rbce->curl, CURLOPT_FAILONERROR, FIX2LONG(val));
3553
+ curl_easy_setopt(rbce->curl, CURLOPT_FAILONERROR, NUM2LONG(val));
3114
3554
  } break;
3115
- default:
3555
+ case CURLOPT_SSL_CIPHER_LIST: {
3556
+ curl_easy_setopt(rbce->curl, CURLOPT_SSL_CIPHER_LIST, StringValueCStr(val));
3557
+ } break;
3558
+ case CURLOPT_FORBID_REUSE: {
3559
+ curl_easy_setopt(rbce->curl, CURLOPT_FORBID_REUSE, NUM2LONG(val));
3560
+ } break;
3561
+ #if HAVE_CURLOPT_GSSAPI_DELEGATION
3562
+ case CURLOPT_GSSAPI_DELEGATION: {
3563
+ curl_easy_setopt(rbce->curl, CURLOPT_GSSAPI_DELEGATION, NUM2LONG(val));
3564
+ } break;
3565
+ #endif
3566
+ #if HAVE_CURLOPT_UNIX_SOCKET_PATH
3567
+ case CURLOPT_UNIX_SOCKET_PATH: {
3568
+ curl_easy_setopt(rbce->curl, CURLOPT_UNIX_SOCKET_PATH, StringValueCStr(val));
3569
+ } break;
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));
3116
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;
3609
+ default:
3610
+ rb_raise(rb_eTypeError, "Curb unsupported option");
3117
3611
  }
3118
3612
 
3119
3613
  return val;
@@ -3204,7 +3698,7 @@ static VALUE ruby_curl_easy_unescape(VALUE self, VALUE str) {
3204
3698
  #if (LIBCURL_VERSION_NUM >= 0x070f04)
3205
3699
  result = (char*)curl_easy_unescape(rbce->curl, StringValuePtr(str), (int)RSTRING_LEN(str), &rlen);
3206
3700
  #else
3207
- result = (char*)curl_unescape(StringValuePtr(str), RSTRING_LEN(str));
3701
+ result = (char*)curl_unescape(StringValuePtr(str), (int)RSTRING_LEN(str));
3208
3702
  rlen = strlen(result);
3209
3703
  #endif
3210
3704
 
@@ -3219,12 +3713,12 @@ static VALUE ruby_curl_easy_unescape(VALUE self, VALUE str) {
3219
3713
 
3220
3714
  /*
3221
3715
  * call-seq:
3222
- * Curl::Easy.error(code) => String
3716
+ * Curl::Easy.error(code) => [ErrCode, String]
3223
3717
  *
3224
3718
  * translate an internal libcurl error to ruby error class
3225
3719
  */
3226
3720
  static VALUE ruby_curl_easy_error_message(VALUE klass, VALUE code) {
3227
- return rb_curl_easy_error(FIX2INT(code));
3721
+ return rb_curl_easy_error(NUM2INT(code));
3228
3722
  }
3229
3723
 
3230
3724
  /* =================== INIT LIB =====================*/
@@ -3238,12 +3732,19 @@ void init_curb_easy() {
3238
3732
  cCurlEasy = rb_define_class_under(mCurl, "Easy", rb_cObject);
3239
3733
 
3240
3734
  /* Class methods */
3241
- rb_define_singleton_method(cCurlEasy, "new", ruby_curl_easy_new, -1);
3735
+ rb_define_alloc_func(cCurlEasy, ruby_curl_easy_allocate);
3242
3736
  rb_define_singleton_method(cCurlEasy, "error", ruby_curl_easy_error_message, 1);
3243
3737
 
3738
+ /* Initialize method */
3739
+ rb_define_method(cCurlEasy, "initialize", ruby_curl_easy_initialize, -1);
3740
+
3244
3741
  /* Attributes for config next perform */
3245
3742
  rb_define_method(cCurlEasy, "url", ruby_curl_easy_url_get, 0);
3246
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
+
3247
3748
  rb_define_method(cCurlEasy, "headers=", ruby_curl_easy_headers_set, 1);
3248
3749
  rb_define_method(cCurlEasy, "headers", ruby_curl_easy_headers_get, 0);
3249
3750
  rb_define_method(cCurlEasy, "interface", ruby_curl_easy_interface_get, 0);
@@ -3270,6 +3771,8 @@ void init_curb_easy() {
3270
3771
  rb_define_method(cCurlEasy, "put_data=", ruby_curl_easy_put_data_set, 1);
3271
3772
  rb_define_method(cCurlEasy, "ftp_commands=", ruby_curl_easy_ftp_commands_set, 1);
3272
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);
3273
3776
 
3274
3777
  rb_define_method(cCurlEasy, "local_port=", ruby_curl_easy_local_port_set, 1);
3275
3778
  rb_define_method(cCurlEasy, "local_port", ruby_curl_easy_local_port_get, 0);
@@ -3287,8 +3790,12 @@ void init_curb_easy() {
3287
3790
  rb_define_method(cCurlEasy, "max_redirects", ruby_curl_easy_max_redirects_get, 0);
3288
3791
  rb_define_method(cCurlEasy, "timeout=", ruby_curl_easy_timeout_set, 1);
3289
3792
  rb_define_method(cCurlEasy, "timeout", ruby_curl_easy_timeout_get, 0);
3793
+ rb_define_method(cCurlEasy, "timeout_ms=", ruby_curl_easy_timeout_ms_set, 1);
3794
+ rb_define_method(cCurlEasy, "timeout_ms", ruby_curl_easy_timeout_ms_get, 0);
3290
3795
  rb_define_method(cCurlEasy, "connect_timeout=", ruby_curl_easy_connect_timeout_set, 1);
3291
3796
  rb_define_method(cCurlEasy, "connect_timeout", ruby_curl_easy_connect_timeout_get, 0);
3797
+ rb_define_method(cCurlEasy, "connect_timeout_ms=", ruby_curl_easy_connect_timeout_ms_set, 1);
3798
+ rb_define_method(cCurlEasy, "connect_timeout_ms", ruby_curl_easy_connect_timeout_ms_get, 0);
3292
3799
  rb_define_method(cCurlEasy, "dns_cache_timeout=", ruby_curl_easy_dns_cache_timeout_set, 1);
3293
3800
  rb_define_method(cCurlEasy, "dns_cache_timeout", ruby_curl_easy_dns_cache_timeout_get, 0);
3294
3801
  rb_define_method(cCurlEasy, "ftp_response_timeout=", ruby_curl_easy_ftp_response_timeout_set, 1);
@@ -3297,6 +3804,10 @@ void init_curb_easy() {
3297
3804
  rb_define_method(cCurlEasy, "low_speed_limit", ruby_curl_easy_low_speed_limit_get, 0);
3298
3805
  rb_define_method(cCurlEasy, "low_speed_time=", ruby_curl_easy_low_speed_time_set, 1);
3299
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);
3300
3811
  rb_define_method(cCurlEasy, "ssl_version=", ruby_curl_easy_ssl_version_set, 1);
3301
3812
  rb_define_method(cCurlEasy, "ssl_version", ruby_curl_easy_ssl_version_get, 0);
3302
3813
  rb_define_method(cCurlEasy, "use_ssl=", ruby_curl_easy_use_ssl_set, 1);
@@ -3364,6 +3875,9 @@ void init_curb_easy() {
3364
3875
  rb_define_method(cCurlEasy, "total_time", ruby_curl_easy_total_time_get, 0);
3365
3876
  rb_define_method(cCurlEasy, "name_lookup_time", ruby_curl_easy_name_lookup_time_get, 0);
3366
3877
  rb_define_method(cCurlEasy, "connect_time", ruby_curl_easy_connect_time_get, 0);
3878
+ #if defined(HAVE_CURLINFO_APPCONNECT_TIME)
3879
+ rb_define_method(cCurlEasy, "app_connect_time", ruby_curl_easy_app_connect_time_get, 0);
3880
+ #endif
3367
3881
  rb_define_method(cCurlEasy, "pre_transfer_time", ruby_curl_easy_pre_transfer_time_get, 0);
3368
3882
  rb_define_method(cCurlEasy, "start_transfer_time", ruby_curl_easy_start_transfer_time_get, 0);
3369
3883
  rb_define_method(cCurlEasy, "redirect_time", ruby_curl_easy_redirect_time_get, 0);
@@ -3381,6 +3895,7 @@ void init_curb_easy() {
3381
3895
  rb_define_method(cCurlEasy, "content_type", ruby_curl_easy_content_type_get, 0);
3382
3896
  rb_define_method(cCurlEasy, "os_errno", ruby_curl_easy_os_errno_get, 0);
3383
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);
3384
3899
  rb_define_method(cCurlEasy, "ftp_entry_path", ruby_curl_easy_ftp_entry_path_get, 0);
3385
3900
 
3386
3901
  rb_define_method(cCurlEasy, "close", ruby_curl_easy_close, 0);