curb 0.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of curb might be problematic. Click here for more details.

@@ -0,0 +1,2138 @@
1
+ /* curb_easy.c - Curl easy mode
2
+ * Copyright (c)2006 Ross Bamford.
3
+ * Licensed under the Ruby License. See LICENSE for details.
4
+ *
5
+ * $Id: curb_easy.c 30 2006-12-09 12:30:24Z roscopeco $
6
+ */
7
+ #include "curb_easy.h"
8
+ #include "curb_errors.h"
9
+ #include "curb_postfield.h"
10
+
11
+ extern VALUE mCurl;
12
+
13
+ static VALUE idCall;
14
+ static VALUE idJoin;
15
+ static VALUE rbstrAmp;
16
+
17
+ #ifdef RDOC_NEVER_DEFINED
18
+ mCurl = rb_define_module("Curl");
19
+ #endif
20
+
21
+ VALUE cCurlEasy;
22
+
23
+
24
+ /* ================== CURL HANDLER FUNCS ==============*/
25
+
26
+ /* These handle both body and header data */
27
+ static size_t default_data_handler(char *stream,
28
+ size_t size,
29
+ size_t nmemb,
30
+ VALUE out) {
31
+ rb_str_buf_cat(out, stream, size * nmemb);
32
+ return size * nmemb;
33
+ }
34
+
35
+ static size_t proc_data_handler(char *stream,
36
+ size_t size,
37
+ size_t nmemb,
38
+ VALUE proc) {
39
+ VALUE procret;
40
+
41
+ procret = rb_funcall(proc, idCall, 1, rb_str_new(stream, size * nmemb));
42
+
43
+ switch (rb_type(procret)) {
44
+ case T_FIXNUM:
45
+ return FIX2LONG(procret);
46
+ case T_BIGNUM:
47
+ return NUM2LONG(procret);
48
+ default:
49
+ rb_warn("Curl data handlers should return the number of bytes read as an Integer");
50
+ return size * nmemb;
51
+ }
52
+ }
53
+
54
+ static int proc_progress_handler(VALUE proc,
55
+ double dltotal,
56
+ double dlnow,
57
+ double ultotal,
58
+ double ulnow) {
59
+ VALUE procret;
60
+
61
+ procret = rb_funcall(proc, idCall, 4, rb_float_new(dltotal),
62
+ rb_float_new(dlnow),
63
+ rb_float_new(ultotal),
64
+ rb_float_new(ulnow));
65
+
66
+ return(((procret == Qfalse) || (procret == Qnil)) ? -1 : 0);
67
+ }
68
+
69
+ static int proc_debug_handler(CURL *curl,
70
+ curl_infotype type,
71
+ char *data,
72
+ size_t data_len,
73
+ VALUE proc) {
74
+ rb_funcall(proc, idCall, 2, INT2FIX(type), rb_str_new(data, data_len));
75
+ return 0;
76
+ }
77
+
78
+ /* ================== MARK/FREE FUNC ==================*/
79
+ void curl_easy_mark(ruby_curl_easy *rbce) {
80
+ rb_gc_mark(rbce->url);
81
+ rb_gc_mark(rbce->body_proc);
82
+ rb_gc_mark(rbce->body_data);
83
+ rb_gc_mark(rbce->header_proc);
84
+ rb_gc_mark(rbce->header_data);
85
+ rb_gc_mark(rbce->progress_proc);
86
+ rb_gc_mark(rbce->debug_proc);
87
+ rb_gc_mark(rbce->interface);
88
+ rb_gc_mark(rbce->userpwd);
89
+ rb_gc_mark(rbce->proxypwd);
90
+ rb_gc_mark(rbce->headers);
91
+ rb_gc_mark(rbce->cookiejar);
92
+
93
+ rb_gc_mark(rbce->postdata_buffer);
94
+ }
95
+
96
+ void curl_easy_free(ruby_curl_easy *rbce) {
97
+ curl_easy_cleanup(rbce->curl);
98
+ free(rbce);
99
+ }
100
+
101
+
102
+ /* ================= ALLOC METHODS ====================*/
103
+
104
+ /*
105
+ * call-seq:
106
+ * Curl::Easy.new => #<Curl::Easy...>
107
+ * Curl::Easy.new(url = nil) => #<Curl::Easy...>
108
+ * Curl::Easy.new(url = nil) { |self| ... } => #<Curl::Easy...>
109
+ *
110
+ * Create a new Curl::Easy instance, optionally supplying the URL.
111
+ * The block form allows further configuration to be supplied before
112
+ * the instance is returned.
113
+ */
114
+ static VALUE ruby_curl_easy_new(int argc, VALUE *argv, VALUE klass) {
115
+ VALUE url, blk;
116
+ VALUE new_curl;
117
+
118
+ rb_scan_args(argc, argv, "01&", &url, &blk);
119
+
120
+ ruby_curl_easy *rbce = ALLOC(ruby_curl_easy);
121
+
122
+ /* handler */
123
+ rbce->curl = curl_easy_init();
124
+
125
+ /* assoc objects */
126
+ rbce->url = url;
127
+ rbce->body_data = Qnil;
128
+ rbce->body_proc = Qnil;
129
+ rbce->header_data = Qnil;
130
+ rbce->header_proc = Qnil;
131
+ rbce->progress_proc = Qnil;
132
+ rbce->debug_proc = Qnil;
133
+ rbce->proxy_addr = Qnil;
134
+ rbce->interface = Qnil;
135
+ rbce->userpwd = Qnil;
136
+ rbce->proxypwd = Qnil;
137
+ rbce->headers = rb_hash_new();
138
+ rbce->cookiejar = Qnil;
139
+
140
+ /* various-typed opts */
141
+ rbce->local_port = 0;
142
+ rbce->local_port_range = 0;
143
+ rbce->proxy_port = 0;
144
+ rbce->proxy_type = -1;
145
+ rbce->http_auth_types = 0;
146
+ rbce->proxy_auth_types = 0;
147
+ rbce->max_redirs = -1;
148
+ rbce->timeout = 0;
149
+ rbce->connect_timeout = 0;
150
+ rbce->dns_cache_timeout = 60;
151
+ rbce->ftp_response_timeout = 0;
152
+
153
+ /* bool opts */
154
+ rbce->proxy_tunnel = 0;
155
+ rbce->fetch_file_time = 0;
156
+ rbce->ssl_verify_peer = 0;
157
+ rbce->header_in_body = 0;
158
+ rbce->use_netrc = 0;
159
+ rbce->follow_location = 0;
160
+ rbce->unrestricted_auth = 0;
161
+ rbce->verbose = 0;
162
+ rbce->multipart_form_post = 0;
163
+ rbce->enable_cookies = 0;
164
+
165
+ /* buffers */
166
+ rbce->postdata_buffer = Qnil;
167
+
168
+ new_curl = Data_Wrap_Struct(cCurlEasy, curl_easy_mark, curl_easy_free, rbce);
169
+
170
+ if (blk != Qnil) {
171
+ rb_funcall(blk, idCall, 1, new_curl);
172
+ }
173
+
174
+ return new_curl;
175
+ }
176
+
177
+ /*
178
+ * call-seq:
179
+ * easy.clone => #<easy clone>
180
+ * easy.dup => #<easy clone>
181
+ *
182
+ * Clone this Curl::Easy instance, creating a new instance.
183
+ * This method duplicates the underlying CURL* handle.
184
+ */
185
+ static VALUE ruby_curl_easy_clone(VALUE self) {
186
+ ruby_curl_easy *rbce, *newrbce;
187
+
188
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
189
+
190
+ newrbce = ALLOC(ruby_curl_easy);
191
+ memcpy(newrbce, rbce, sizeof(ruby_curl_easy));
192
+ newrbce->curl = curl_easy_duphandle(rbce->curl);
193
+
194
+ return Data_Wrap_Struct(cCurlEasy, curl_easy_mark, curl_easy_free, newrbce);
195
+ }
196
+
197
+
198
+ /* ================ OBJ ATTRIBUTES ==================*/
199
+
200
+ /*
201
+ * call-seq:
202
+ * easy.url = "http://some.url/" => "http://some.url/"
203
+ *
204
+ * Set the URL for subsequent calls to +perform+. It is acceptable
205
+ * (and even recommended) to reuse Curl::Easy instances by reassigning
206
+ * the URL between calls to +perform+.
207
+ */
208
+ static VALUE ruby_curl_easy_url_set(VALUE self, VALUE url) {
209
+ CURB_OBJECT_SETTER(ruby_curl_easy, url);
210
+ }
211
+
212
+ /*
213
+ * call-seq:
214
+ * easy.url => "http://some.url/"
215
+ *
216
+ * Obtain the URL that will be used by subsequent calls to +perform+.
217
+ */
218
+ static VALUE ruby_curl_easy_url_get(VALUE self) {
219
+ CURB_OBJECT_GETTER(ruby_curl_easy, url);
220
+ }
221
+
222
+ /*
223
+ * call-seq:
224
+ * easy.headers = "Header: val" => ["Header: val", ...]
225
+ * easy.headers = {"Header" => "val" ..., "Header" => "val"} => ["Header: val", ...]
226
+ * easy.headers = ["Header: val" ..., "Header: val"] => ["Header: val", ...]
227
+ *
228
+ * Set custom HTTP headers for following requests. This can be used to add
229
+ * custom headers, or override standard headers used by libcurl. It defaults to a
230
+ * Hash.
231
+ *
232
+ * For example to set a standard or custom header:
233
+ *
234
+ * easy.headers["MyHeader"] = "myval"
235
+ *
236
+ * To remove a standard header (this is useful when removing libcurls default
237
+ * 'Expect: 100-Continue' header when using HTTP form posts):
238
+ *
239
+ * easy.headers["Expect:"] = ''
240
+ *
241
+ * Anything passed to libcurl as a header will be converted to a string during
242
+ * the perform step.
243
+ */
244
+ static VALUE ruby_curl_easy_headers_set(VALUE self, VALUE headers) {
245
+ CURB_OBJECT_SETTER(ruby_curl_easy, headers);
246
+ }
247
+
248
+ /*
249
+ * call-seq:
250
+ * easy.headers => Hash, Array or Str
251
+ *
252
+ * Obtain the custom HTTP headers for following requests.
253
+ */
254
+ static VALUE ruby_curl_easy_headers_get(VALUE self) {
255
+ CURB_OBJECT_GETTER(ruby_curl_easy, headers);
256
+ }
257
+
258
+ /*
259
+ * call-seq:
260
+ * easy.interface = "interface" => "interface"
261
+ *
262
+ * Set the interface name to use as the outgoing network interface.
263
+ * The name can be an interface name, an IP address or a host name.
264
+ */
265
+ static VALUE ruby_curl_easy_interface_set(VALUE self, VALUE interface) {
266
+ CURB_OBJECT_SETTER(ruby_curl_easy, interface);
267
+ }
268
+
269
+ /*
270
+ * call-seq:
271
+ * easy.interface => "interface"
272
+ *
273
+ * Obtain the interface name that is used as the outgoing network interface.
274
+ * The name can be an interface name, an IP address or a host name.
275
+ */
276
+ static VALUE ruby_curl_easy_interface_get(VALUE self) {
277
+ CURB_OBJECT_GETTER(ruby_curl_easy, interface);
278
+ }
279
+
280
+ /*
281
+ * call-seq:
282
+ * easy.userpwd = "pwd string" => "pwd string"
283
+ *
284
+ * Set the username/password string to use for subsequent calls to +perform+.
285
+ * The supplied string should have the form "username:password"
286
+ */
287
+ static VALUE ruby_curl_easy_userpwd_set(VALUE self, VALUE userpwd) {
288
+ CURB_OBJECT_SETTER(ruby_curl_easy, userpwd);
289
+ }
290
+
291
+ /*
292
+ * call-seq:
293
+ * easy.userpwd => "pwd string"
294
+ *
295
+ * Obtain the username/password string that will be used for subsequent
296
+ * calls to +perform+.
297
+ */
298
+ static VALUE ruby_curl_easy_userpwd_get(VALUE self) {
299
+ CURB_OBJECT_GETTER(ruby_curl_easy, userpwd);
300
+ }
301
+
302
+ /*
303
+ * call-seq:
304
+ * easy.proxypwd = "pwd string" => "pwd string"
305
+ *
306
+ * Set the username/password string to use for proxy connection during
307
+ * subsequent calls to +perform+. The supplied string should have the
308
+ * form "username:password"
309
+ */
310
+ static VALUE ruby_curl_easy_proxypwd_set(VALUE self, VALUE proxypwd) {
311
+ CURB_OBJECT_SETTER(ruby_curl_easy, proxypwd);
312
+ }
313
+
314
+ /*
315
+ * call-seq:
316
+ * easy.proxypwd => "pwd string"
317
+ *
318
+ * Obtain the username/password string that will be used for proxy
319
+ * connection during subsequent calls to +perform+. The supplied string
320
+ * should have the form "username:password"
321
+ */
322
+ static VALUE ruby_curl_easy_proxypwd_get(VALUE self) {
323
+ CURB_OBJECT_GETTER(ruby_curl_easy, proxypwd);
324
+ }
325
+
326
+ /*
327
+ * call-seq:
328
+ * easy.cookiejar = "cookiejar.file" => "pwd string"
329
+ *
330
+ * Set a cookiejar file to use for this Curl::Easy instance. This file
331
+ * will be used to persist cookies.
332
+ *
333
+ * *Note* that you must set enable_cookies true to enable the cookie
334
+ * engine, or this option will be ignored.
335
+ */
336
+ static VALUE ruby_curl_easy_cookiejar_set(VALUE self, VALUE cookiejar) {
337
+ CURB_OBJECT_SETTER(ruby_curl_easy, cookiejar);
338
+ }
339
+
340
+ /*
341
+ * call-seq:
342
+ * easy.cookiejar => "cookiejar.file"
343
+ *
344
+ * Obtain the cookiejar file to use for this Curl::Easy instance.
345
+ */
346
+ static VALUE ruby_curl_easy_cookiejar_get(VALUE self) {
347
+ CURB_OBJECT_GETTER(ruby_curl_easy, cookiejar);
348
+ }
349
+
350
+
351
+ /* ================== IMMED ATTRS ==================*/
352
+
353
+ /*
354
+ * call-seq:
355
+ * easy.local_port = fixnum or nil => fixnum or nil
356
+ *
357
+ * Set the local port that will be used for the following +perform+ calls.
358
+ *
359
+ * Passing +nil+ will return to the default behaviour (no local port
360
+ * preference).
361
+ *
362
+ * This option is ignored if compiled against libcurl < 7.15.2.
363
+ */
364
+ static VALUE ruby_curl_easy_local_port_set(VALUE self, VALUE local_port) {
365
+ CURB_IMMED_PORT_SETTER(ruby_curl_easy, local_port, "port");
366
+ }
367
+
368
+ /*
369
+ * call-seq:
370
+ * easy.local_port => fixnum or nil
371
+ *
372
+ * Obtain the local port that will be used for the following +perform+ calls.
373
+ *
374
+ * This option is ignored if compiled against libcurl < 7.15.2.
375
+ */
376
+ static VALUE ruby_curl_easy_local_port_get(VALUE self) {
377
+ CURB_IMMED_PORT_GETTER(ruby_curl_easy, local_port);
378
+ }
379
+
380
+ /*
381
+ * call-seq:
382
+ * easy.local_port_range = fixnum or nil => fixnum or nil
383
+ *
384
+ * Set the local port range that will be used for the following +perform+
385
+ * calls. This is a number (between 0 and 65535) that determines how far
386
+ * libcurl may deviate from the supplied +local_port+ in order to find
387
+ * an available port.
388
+ *
389
+ * If you set +local_port+ it's also recommended that you set this, since
390
+ * it is fairly likely that your specified port will be unavailable.
391
+ *
392
+ * This option is ignored if compiled against libcurl < 7.15.2.
393
+ */
394
+ static VALUE ruby_curl_easy_local_port_range_set(VALUE self, VALUE local_port_range) {
395
+ CURB_IMMED_PORT_SETTER(ruby_curl_easy, local_port_range, "port range");
396
+ }
397
+
398
+ /*
399
+ * call-seq:
400
+ * easy.local_port_range => fixnum or nil
401
+ *
402
+ * Obtain the local port range that will be used for the following +perform+
403
+ * calls.
404
+ *
405
+ * This option is ignored if compiled against libcurl < 7.15.2.
406
+ */
407
+ static VALUE ruby_curl_easy_local_port_range_get(VALUE self) {
408
+ CURB_IMMED_PORT_GETTER(ruby_curl_easy, local_port_range);
409
+ }
410
+
411
+ /*
412
+ * call-seq:
413
+ * easy.proxy_port = fixnum or nil => fixnum or nil
414
+ *
415
+ * Set the proxy port that will be used for the following +perform+ calls.
416
+ */
417
+ static VALUE ruby_curl_easy_proxy_port_set(VALUE self, VALUE proxy_port) {
418
+ CURB_IMMED_PORT_SETTER(ruby_curl_easy, proxy_port, "port");
419
+ }
420
+
421
+ /*
422
+ * call-seq:
423
+ * easy.proxy_port => fixnum or nil
424
+ *
425
+ * Obtain the proxy port that will be used for the following +perform+ calls.
426
+ */
427
+ static VALUE ruby_curl_easy_proxy_port_get(VALUE self) {
428
+ CURB_IMMED_PORT_GETTER(ruby_curl_easy, proxy_port);
429
+ }
430
+
431
+ /*
432
+ * call-seq:
433
+ * easy.proxy_type = fixnum or nil => fixnum or nil
434
+ *
435
+ * Set the proxy type that will be used for the following +perform+ calls.
436
+ * This should be one of the Curl::CURLPROXY constants.
437
+ */
438
+ static VALUE ruby_curl_easy_proxy_type_set(VALUE self, VALUE proxy_type) {
439
+ CURB_IMMED_SETTER(ruby_curl_easy, proxy_type, -1);
440
+ }
441
+
442
+ /*
443
+ * call-seq:
444
+ * easy.proxy_type => fixnum or nil
445
+ *
446
+ * Obtain the proxy type that will be used for the following +perform+ calls.
447
+ */
448
+ static VALUE ruby_curl_easy_proxy_type_get(VALUE self) {
449
+ CURB_IMMED_GETTER(ruby_curl_easy, proxy_type, -1);
450
+ }
451
+
452
+ /*
453
+ * call-seq:
454
+ * easy.http_auth_types = fixnum or nil => fixnum or nil
455
+ *
456
+ * Set the HTTP authentication types that may be used for the following
457
+ * +perform+ calls. This is a bitmap made by ORing together the
458
+ * Curl::CURLAUTH constants.
459
+ */
460
+ static VALUE ruby_curl_easy_http_auth_types_set(VALUE self, VALUE http_auth_types) {
461
+ CURB_IMMED_SETTER(ruby_curl_easy, http_auth_types, 0);
462
+ }
463
+
464
+ /*
465
+ * call-seq:
466
+ * easy.http_auth_types => fixnum or nil
467
+ *
468
+ * Obtain the HTTP authentication types that may be used for the following
469
+ * +perform+ calls.
470
+ */
471
+ static VALUE ruby_curl_easy_http_auth_types_get(VALUE self) {
472
+ CURB_IMMED_GETTER(ruby_curl_easy, http_auth_types, 0);
473
+ }
474
+
475
+ /*
476
+ * call-seq:
477
+ * easy.proxy_auth_types = fixnum or nil => fixnum or nil
478
+ *
479
+ * Set the proxy authentication types that may be used for the following
480
+ * +perform+ calls. This is a bitmap made by ORing together the
481
+ * Curl::CURLAUTH constants.
482
+ */
483
+ static VALUE ruby_curl_easy_proxy_auth_types_set(VALUE self, VALUE proxy_auth_types) {
484
+ CURB_IMMED_SETTER(ruby_curl_easy, proxy_auth_types, 0);
485
+ }
486
+
487
+ /*
488
+ * call-seq:
489
+ * easy.proxy_auth_types => fixnum or nil
490
+ *
491
+ * Obtain the proxy authentication types that may be used for the following
492
+ * +perform+ calls.
493
+ */
494
+ static VALUE ruby_curl_easy_proxy_auth_types_get(VALUE self) {
495
+ CURB_IMMED_GETTER(ruby_curl_easy, proxy_auth_types, 0);
496
+ }
497
+
498
+ /*
499
+ * call-seq:
500
+ * easy.max_redirects = fixnum or nil => fixnum or nil
501
+ *
502
+ * Set the maximum number of redirections to follow in the following +perform+
503
+ * calls. Set to nil or -1 allow an infinite number (the default). Setting this
504
+ * option only makes sense if +follow_location+ is also set true.
505
+ *
506
+ * With libcurl >= 7.15.1, setting this to 0 will cause libcurl to refuse any
507
+ * redirect.
508
+ */
509
+ static VALUE ruby_curl_easy_max_redirects_set(VALUE self, VALUE max_redirs) {
510
+ CURB_IMMED_SETTER(ruby_curl_easy, max_redirs, -1);
511
+ }
512
+
513
+ /*
514
+ * call-seq:
515
+ * easy.max_redirects => fixnum or nil
516
+ *
517
+ * Obtain the maximum number of redirections to follow in the following
518
+ * +perform+ calls.
519
+ */
520
+ static VALUE ruby_curl_easy_max_redirects_get(VALUE self) {
521
+ CURB_IMMED_GETTER(ruby_curl_easy, max_redirs, -1);
522
+ }
523
+
524
+ /*
525
+ * call-seq:
526
+ * easy.timeout = fixnum or nil => fixnum or nil
527
+ *
528
+ * Set the maximum time in seconds that you allow the libcurl transfer
529
+ * operation to take. Normally, name lookups can take a considerable time
530
+ * and limiting operations to less than a few minutes risk aborting
531
+ * perfectly normal operations.
532
+ *
533
+ * Set to nil (or zero) to disable timeout (it will then only timeout
534
+ * on the system's internal timeouts).
535
+ */
536
+ static VALUE ruby_curl_easy_timeout_set(VALUE self, VALUE timeout) {
537
+ CURB_IMMED_SETTER(ruby_curl_easy, timeout, 0);
538
+ }
539
+
540
+ /*
541
+ * call-seq:
542
+ * easy.timeout => fixnum or nil
543
+ *
544
+ * Obtain the maximum time in seconds that you allow the libcurl transfer
545
+ * operation to take.
546
+ */
547
+ static VALUE ruby_curl_easy_timeout_get(VALUE self, VALUE timeout) {
548
+ CURB_IMMED_GETTER(ruby_curl_easy, timeout, 0);
549
+ }
550
+
551
+ /*
552
+ * call-seq:
553
+ * easy.connect_timeout = fixnum or nil => fixnum or nil
554
+ *
555
+ * Set the maximum time in seconds that you allow the connection to the
556
+ * server to take. This only limits the connection phase, once it has
557
+ * connected, this option is of no more use.
558
+ *
559
+ * Set to nil (or zero) to disable connection timeout (it will then only
560
+ * timeout on the system's internal timeouts).
561
+ */
562
+ static VALUE ruby_curl_easy_connect_timeout_set(VALUE self, VALUE connect_timeout) {
563
+ CURB_IMMED_SETTER(ruby_curl_easy, connect_timeout, 0);
564
+ }
565
+
566
+ /*
567
+ * call-seq:
568
+ * easy.connect_timeout => fixnum or nil
569
+ *
570
+ * Obtain the maximum time in seconds that you allow the connection to the
571
+ * server to take.
572
+ */
573
+ static VALUE ruby_curl_easy_connect_timeout_get(VALUE self, VALUE connect_timeout) {
574
+ CURB_IMMED_GETTER(ruby_curl_easy, connect_timeout, 0);
575
+ }
576
+
577
+ /*
578
+ * call-seq:
579
+ * easy.dns_cache_timeout = fixnum or nil => fixnum or nil
580
+ *
581
+ * Set the dns cache timeout in seconds. Name resolves will be kept in
582
+ * memory for this number of seconds. Set to zero (0) to completely disable
583
+ * caching, or set to nil (or -1) to make the cached entries remain forever.
584
+ * By default, libcurl caches this info for 60 seconds.
585
+ */
586
+ static VALUE ruby_curl_easy_dns_cache_timeout_set(VALUE self, VALUE dns_cache_timeout) {
587
+ CURB_IMMED_SETTER(ruby_curl_easy, dns_cache_timeout, -1);
588
+ }
589
+
590
+ /*
591
+ * call-seq:
592
+ * easy.dns_cache_timeout => fixnum or nil
593
+ *
594
+ * Obtain the dns cache timeout in seconds.
595
+ */
596
+ static VALUE ruby_curl_easy_dns_cache_timeout_get(VALUE self, VALUE dns_cache_timeout) {
597
+ CURB_IMMED_GETTER(ruby_curl_easy, dns_cache_timeout, -1);
598
+ }
599
+
600
+ /*
601
+ * call-seq:
602
+ * easy.ftp_response_timeout = fixnum or nil => fixnum or nil
603
+ *
604
+ * Set a timeout period (in seconds) on the amount of time that the server
605
+ * is allowed to take in order to generate a response message for a command
606
+ * before the session is considered hung. While curl is waiting for a
607
+ * response, this value overrides +timeout+. It is recommended that if used
608
+ * in conjunction with +timeout+, you set +ftp_response_timeout+ to a value
609
+ * smaller than +timeout+.
610
+ *
611
+ * Ignored if libcurl version is < 7.10.8.
612
+ */
613
+ static VALUE ruby_curl_easy_ftp_response_timeout_set(VALUE self, VALUE ftp_response_timeout) {
614
+ CURB_IMMED_SETTER(ruby_curl_easy, ftp_response_timeout, 0);
615
+ }
616
+
617
+ /*
618
+ * call-seq:
619
+ * easy.ftp_response_timeout => fixnum or nil
620
+ *
621
+ * Obtain the maximum time that libcurl will wait for FTP command responses.
622
+ */
623
+ static VALUE ruby_curl_easy_ftp_response_timeout_get(VALUE self, VALUE ftp_response_timeout) {
624
+ CURB_IMMED_GETTER(ruby_curl_easy, ftp_response_timeout, 0);
625
+ }
626
+
627
+ /* ================== BOOL ATTRS ===================*/
628
+
629
+ /*
630
+ * call-seq:
631
+ * proxy_tunnel = boolean => boolean
632
+ *
633
+ * Configure whether this Curl instance will use proxy tunneling.
634
+ */
635
+ static VALUE ruby_curl_easy_proxy_tunnel_set(VALUE self, VALUE proxy_tunnel) {
636
+ CURB_BOOLEAN_SETTER(ruby_curl_easy, proxy_tunnel);
637
+ }
638
+
639
+ /*
640
+ * call-seq:
641
+ * proxy_tunnel? => boolean
642
+ *
643
+ * Determine whether this Curl instance will use proxy tunneling.
644
+ */
645
+ static VALUE ruby_curl_easy_proxy_tunnel_q(VALUE self) {
646
+ CURB_BOOLEAN_GETTER(ruby_curl_easy, proxy_tunnel);
647
+ }
648
+
649
+ /*
650
+ * call-seq:
651
+ * fetch_file_time = boolean => boolean
652
+ *
653
+ * Configure whether this Curl instance will fetch remote file
654
+ * times, if available.
655
+ */
656
+ static VALUE ruby_curl_easy_fetch_file_time_set(VALUE self, VALUE fetch_file_time) {
657
+ CURB_BOOLEAN_SETTER(ruby_curl_easy, fetch_file_time);
658
+ }
659
+
660
+ /*
661
+ * call-seq:
662
+ * fetch_file_time? => boolean
663
+ *
664
+ * Determine whether this Curl instance will fetch remote file
665
+ * times, if available.
666
+ */
667
+ static VALUE ruby_curl_easy_fetch_file_time_q(VALUE self) {
668
+ CURB_BOOLEAN_GETTER(ruby_curl_easy, fetch_file_time);
669
+ }
670
+
671
+ /*
672
+ * call-seq:
673
+ * ssl_verify_peer = boolean => boolean
674
+ *
675
+ * Configure whether this Curl instance will verify the SSL peer
676
+ * certificate.
677
+ */
678
+ static VALUE ruby_curl_easy_ssl_verify_peer_set(VALUE self, VALUE ssl_verify_peer) {
679
+ CURB_BOOLEAN_SETTER(ruby_curl_easy, ssl_verify_peer);
680
+ }
681
+
682
+ /*
683
+ * call-seq:
684
+ * ssl_verify_peer? => boolean
685
+ *
686
+ * Determine whether this Curl instance will verify the SSL peer
687
+ * certificate.
688
+ */
689
+ static VALUE ruby_curl_easy_ssl_verify_peer_q(VALUE self) {
690
+ CURB_BOOLEAN_GETTER(ruby_curl_easy, ssl_verify_peer);
691
+ }
692
+
693
+ /*
694
+ * call-seq:
695
+ * header_in_body = boolean => boolean
696
+ *
697
+ * Configure whether this Curl instance will return HTTP headers
698
+ * combined with body data. If this option is set true, both header
699
+ * and body data will go to +body_str+ (or the configured +on_body+ handler).
700
+ */
701
+ static VALUE ruby_curl_easy_header_in_body_set(VALUE self, VALUE header_in_body) {
702
+ CURB_BOOLEAN_SETTER(ruby_curl_easy, header_in_body);
703
+ }
704
+
705
+ /*
706
+ * call-seq:
707
+ * header_in_body? => boolean
708
+ *
709
+ * Determine whether this Curl instance will verify the SSL peer
710
+ * certificate.
711
+ */
712
+ static VALUE ruby_curl_easy_header_in_body_q(VALUE self) {
713
+ CURB_BOOLEAN_GETTER(ruby_curl_easy, header_in_body);
714
+ }
715
+
716
+ /*
717
+ * call-seq:
718
+ * use_netrc = boolean => boolean
719
+ *
720
+ * Configure whether this Curl instance will use data from the user's
721
+ * .netrc file for FTP connections.
722
+ */
723
+ static VALUE ruby_curl_easy_use_netrc_set(VALUE self, VALUE use_netrc) {
724
+ CURB_BOOLEAN_SETTER(ruby_curl_easy, use_netrc);
725
+ }
726
+
727
+ /*
728
+ * call-seq:
729
+ * use_netrc? => boolean
730
+ *
731
+ * Determine whether this Curl instance will use data from the user's
732
+ * .netrc file for FTP connections.
733
+ */
734
+ static VALUE ruby_curl_easy_use_netrc_q(VALUE self) {
735
+ CURB_BOOLEAN_GETTER(ruby_curl_easy, use_netrc);
736
+ }
737
+
738
+ /*
739
+ * call-seq:
740
+ * follow_location = boolean => boolean
741
+ *
742
+ * Configure whether this Curl instance will follow Location: headers
743
+ * in HTTP responses. Redirects will only be followed to the extent
744
+ * specified by +max_redirects+.
745
+ */
746
+ static VALUE ruby_curl_easy_follow_location_set(VALUE self, VALUE follow_location) {
747
+ CURB_BOOLEAN_SETTER(ruby_curl_easy, follow_location);
748
+ }
749
+
750
+ /*
751
+ * call-seq:
752
+ * follow_location? => boolean
753
+ *
754
+ * Determine whether this Curl instance will follow Location: headers
755
+ * in HTTP responses.
756
+ */
757
+ static VALUE ruby_curl_easy_follow_location_q(VALUE self) {
758
+ CURB_BOOLEAN_GETTER(ruby_curl_easy, follow_location);
759
+ }
760
+
761
+ /*
762
+ * call-seq:
763
+ * unrestricted_auth = boolean => boolean
764
+ *
765
+ * Configure whether this Curl instance may use any HTTP authentication
766
+ * method available when necessary.
767
+ */
768
+ static VALUE ruby_curl_easy_unrestricted_auth_set(VALUE self, VALUE unrestricted_auth) {
769
+ CURB_BOOLEAN_SETTER(ruby_curl_easy, unrestricted_auth);
770
+ }
771
+
772
+ /*
773
+ * call-seq:
774
+ * unrestricted_auth? => boolean
775
+ *
776
+ * Determine whether this Curl instance may use any HTTP authentication
777
+ * method available when necessary.
778
+ */
779
+ static VALUE ruby_curl_easy_unrestricted_auth_q(VALUE self) {
780
+ CURB_BOOLEAN_GETTER(ruby_curl_easy, unrestricted_auth);
781
+ }
782
+
783
+ /*
784
+ * call-seq:
785
+ * easy.verbose = boolean => boolean
786
+ *
787
+ * Configure whether this Curl instance gives verbose output to STDERR
788
+ * during transfers. Ignored if this instance has an on_debug handler.
789
+ */
790
+ static VALUE ruby_curl_easy_verbose_set(VALUE self, VALUE verbose) {
791
+ CURB_BOOLEAN_SETTER(ruby_curl_easy, verbose);
792
+ }
793
+
794
+ /*
795
+ * call-seq:
796
+ * easy.verbose? => boolean
797
+ *
798
+ * Determine whether this Curl instance gives verbose output to STDERR
799
+ * during transfers.
800
+ */
801
+ static VALUE ruby_curl_easy_verbose_q(VALUE self) {
802
+ CURB_BOOLEAN_GETTER(ruby_curl_easy, verbose);
803
+ }
804
+
805
+ /*
806
+ * call-seq:
807
+ * easy.multipart_form_post = boolean => boolean
808
+ *
809
+ * Configure whether this Curl instance uses multipart/formdata content
810
+ * type for HTTP POST requests. If this is false (the default), then the
811
+ * application/x-www-form-urlencoded content type is used for the form
812
+ * data.
813
+ *
814
+ * If this is set true, you must pass one or more PostField instances
815
+ * to the http_post method - no support for posting multipart forms from
816
+ * a string is provided.
817
+ */
818
+ static VALUE ruby_curl_easy_multipart_form_post_set(VALUE self, VALUE multipart_form_post)
819
+ {
820
+ CURB_BOOLEAN_SETTER(ruby_curl_easy, multipart_form_post);
821
+ }
822
+
823
+ /*
824
+ * call-seq:
825
+ * easy.multipart_form_post? => boolean
826
+ *
827
+ * Determine whether this Curl instance uses multipart/formdata content
828
+ * type for HTTP POST requests.
829
+ */
830
+ static VALUE ruby_curl_easy_multipart_form_post_q(VALUE self) {
831
+ CURB_BOOLEAN_GETTER(ruby_curl_easy, multipart_form_post);
832
+ }
833
+
834
+ /*
835
+ * call-seq:
836
+ * easy.enable_cookies = boolean => boolean
837
+ *
838
+ * Configure whether the libcurl cookie engine is enabled for this Curl::Easy
839
+ * instance.
840
+ */
841
+ static VALUE ruby_curl_easy_enable_cookies_set(VALUE self, VALUE enable_cookies)
842
+ {
843
+ CURB_BOOLEAN_SETTER(ruby_curl_easy, enable_cookies);
844
+ }
845
+
846
+ /*
847
+ * call-seq:
848
+ * easy.enable_cookies? => boolean
849
+ *
850
+ * Determine whether the libcurl cookie engine is enabled for this
851
+ * Curl::Easy instance.
852
+ */
853
+ static VALUE ruby_curl_easy_enable_cookies_q(VALUE self) {
854
+ CURB_BOOLEAN_GETTER(ruby_curl_easy, enable_cookies);
855
+ }
856
+
857
+ /* ================= EVENT PROCS ================== */
858
+
859
+ /*
860
+ * call-seq:
861
+ * easy.on_body { |body_data| ... } => &lt;old handler&gt;
862
+ *
863
+ * Assign or remove the +on_body+ handler for this Curl::Easy instance.
864
+ * To remove a previously-supplied handler, call this method with no
865
+ * attached block.
866
+ *
867
+ * The +on_body+ handler is called for each chunk of response body passed back
868
+ * by libcurl during +perform+. It should perform any processing necessary,
869
+ * and return the actual number of bytes handled. Normally, this will
870
+ * equal the length of the data string, and CURL will continue processing.
871
+ * If the returned length does not equal the input length, CURL will abort
872
+ * the processing with a Curl::Err::AbortedByCallbackError.
873
+ */
874
+ static VALUE ruby_curl_easy_on_body_set(int argc, VALUE *argv, VALUE self) {
875
+ CURB_HANDLER_PROC_SETTER(ruby_curl_easy, body_proc);
876
+ }
877
+
878
+ /*
879
+ * call-seq:
880
+ * easy.on_header { |header_data| ... } => &lt;old handler&gt;
881
+ *
882
+ * Assign or remove the +on_header+ handler for this Curl::Easy instance.
883
+ * To remove a previously-supplied handler, call this method with no
884
+ * attached block.
885
+ *
886
+ * The +on_header+ handler is called for each chunk of response header passed
887
+ * back by libcurl during +perform+. The semantics are the same as for the
888
+ * block supplied to +on_body+.
889
+ */
890
+ static VALUE ruby_curl_easy_on_header_set(int argc, VALUE *argv, VALUE self) {
891
+ CURB_HANDLER_PROC_SETTER(ruby_curl_easy, header_proc);
892
+ }
893
+
894
+ /*
895
+ * call-seq:
896
+ * easy.on_progress { |dl_total, dl_now, ul_total, ul_now| ... } => &lt;old handler&gt;
897
+ *
898
+ * Assign or remove the +on_progress+ handler for this Curl::Easy instance.
899
+ * To remove a previously-supplied handler, call this method with no
900
+ * attached block.
901
+ *
902
+ * The +on_progress+ handler is called regularly by libcurl (approximately once
903
+ * per second) during transfers to allow the application to receive progress
904
+ * information. There is no guarantee that the reported progress will change
905
+ * between calls.
906
+ *
907
+ * The result of the block call determines whether libcurl continues the transfer.
908
+ * Returning a non-true value (i.e. nil or false) will cause the transfer to abort,
909
+ * throwing a Curl::Err::AbortedByCallbackError.
910
+ */
911
+ static VALUE ruby_curl_easy_on_progress_set(int argc, VALUE *argv, VALUE self) {
912
+ CURB_HANDLER_PROC_SETTER(ruby_curl_easy, progress_proc);
913
+ }
914
+
915
+ /*
916
+ * call-seq:
917
+ * easy.on_debug { |type, data| ... } => &lt;old handler&gt;
918
+ *
919
+ * Assign or remove the +on_debug+ handler for this Curl::Easy instance.
920
+ * To remove a previously-supplied handler, call this method with no
921
+ * attached block.
922
+ *
923
+ * The +on_debug+ handler, if configured, will receive detailed information
924
+ * from libcurl during the perform call. This can be useful for debugging.
925
+ * Setting a debug handler overrides libcurl's internal handler, disabling
926
+ * any output from +verbose+, if set.
927
+ *
928
+ * The type argument will match one of the Curl::Easy::CURLINFO_XXXX
929
+ * constants, and specifies the kind of information contained in the
930
+ * data. The data is passed as a String.
931
+ */
932
+ static VALUE ruby_curl_easy_on_debug_set(int argc, VALUE *argv, VALUE self) {
933
+ CURB_HANDLER_PROC_SETTER(ruby_curl_easy, debug_proc);
934
+ }
935
+
936
+
937
+ /* =================== PERFORM =====================*/
938
+
939
+ /***********************************************
940
+ * This is an rb_iterate callback used to set up http headers.
941
+ */
942
+ static VALUE cb_each_http_header(VALUE header, struct curl_slist **list) {
943
+ VALUE header_str = Qnil;
944
+
945
+ rb_p(header);
946
+
947
+ if (rb_type(header) == T_ARRAY) {
948
+ // we're processing a hash, header is [name, val]
949
+ VALUE name, value;
950
+
951
+ name = rb_obj_as_string(rb_ary_entry(header, 0));
952
+ value = rb_obj_as_string(rb_ary_entry(header, 1));
953
+
954
+ // This is a bit inefficient, but we don't want to be modifying
955
+ // the actual values in the original hash.
956
+ header_str = rb_str_plus(name, rb_str_new2(": "));
957
+ header_str = rb_str_plus(header_str, value);
958
+ } else {
959
+ header_str = rb_obj_as_string(header);
960
+ }
961
+
962
+ rb_p(header_str);
963
+
964
+ *list = curl_slist_append(*list, StringValuePtr(header_str));
965
+ return header_str;
966
+ }
967
+
968
+ /***********************************************
969
+ *
970
+ * This is the main worker for the perform methods (get, post, head, put).
971
+ * It's not surfaced as a Ruby method - instead, the individual request
972
+ * methods are responsible for setting up stuff specific to that type,
973
+ * then calling this to handle common stuff and do the perform.
974
+ *
975
+ * Always returns Qtrue, rb_raise on error.
976
+ *
977
+ */
978
+ static VALUE handle_perform(ruby_curl_easy *rbce) {
979
+ // TODO this could do with a bit of refactoring...
980
+ CURL *curl;
981
+ CURLcode result = -1;
982
+ struct curl_slist *headers = NULL;
983
+
984
+ curl = rbce->curl;
985
+
986
+ if (rbce->url != Qnil) {
987
+ VALUE url = rb_check_string_type(rbce->url);
988
+ VALUE bodybuf, headerbuf;
989
+
990
+ // Need to configure the handler as per settings in rbce
991
+ curl_easy_setopt(curl, CURLOPT_URL, StringValuePtr(url));
992
+
993
+ // network stuff and auth
994
+ if (rbce->interface != Qnil) {
995
+ curl_easy_setopt(curl, CURLOPT_INTERFACE, StringValuePtr(rbce->interface));
996
+ } else {
997
+ curl_easy_setopt(curl, CURLOPT_INTERFACE, NULL);
998
+ }
999
+
1000
+ if (rbce->userpwd != Qnil) {
1001
+ curl_easy_setopt(curl, CURLOPT_USERPWD, StringValuePtr(rbce->userpwd));
1002
+ } else {
1003
+ curl_easy_setopt(curl, CURLOPT_USERPWD, NULL);
1004
+ }
1005
+
1006
+ if (rbce->proxypwd != Qnil) {
1007
+ curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, StringValuePtr(rbce->proxypwd));
1008
+ } else {
1009
+ curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, NULL);
1010
+ }
1011
+
1012
+ // body/header procs
1013
+ if (rbce->body_proc != Qnil) {
1014
+ bodybuf = Qnil;
1015
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &proc_data_handler);
1016
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, rbce->body_proc);
1017
+ } else {
1018
+ bodybuf = rb_str_buf_new(32768);
1019
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &default_data_handler);
1020
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, bodybuf);
1021
+ }
1022
+
1023
+ if (rbce->header_proc != Qnil) {
1024
+ headerbuf = Qnil;
1025
+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, &proc_data_handler);
1026
+ curl_easy_setopt(curl, CURLOPT_HEADERDATA, rbce->header_proc);
1027
+ } else {
1028
+ headerbuf = rb_str_buf_new(32768);
1029
+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, &default_data_handler);
1030
+ curl_easy_setopt(curl, CURLOPT_HEADERDATA, headerbuf);
1031
+ }
1032
+
1033
+ // progress and debug procs
1034
+ if (rbce->progress_proc != Qnil) {
1035
+ curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, &proc_progress_handler);
1036
+ curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, rbce->progress_proc);
1037
+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
1038
+ } else {
1039
+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
1040
+ }
1041
+
1042
+ if (rbce->debug_proc != Qnil) {
1043
+ curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, &proc_debug_handler);
1044
+ curl_easy_setopt(curl, CURLOPT_DEBUGDATA, rbce->debug_proc);
1045
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
1046
+ } else {
1047
+ // have to remove handler to re-enable standard verbosity
1048
+ curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, NULL);
1049
+ curl_easy_setopt(curl, CURLOPT_DEBUGDATA, NULL);
1050
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, rbce->verbose);
1051
+ }
1052
+
1053
+ /* general opts */
1054
+
1055
+ curl_easy_setopt(curl, CURLOPT_HEADER, rbce->header_in_body);
1056
+
1057
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, rbce->follow_location);
1058
+ curl_easy_setopt(curl, CURLOPT_MAXREDIRS, rbce->max_redirs);
1059
+
1060
+ curl_easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, rbce->proxy_tunnel);
1061
+ curl_easy_setopt(curl, CURLOPT_FILETIME, rbce->fetch_file_time);
1062
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, rbce->ssl_verify_peer);
1063
+
1064
+ if ((rbce->use_netrc != Qnil) && (rbce->use_netrc != Qfalse)) {
1065
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, CURL_NETRC_OPTIONAL);
1066
+ } else {
1067
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, CURL_NETRC_IGNORED);
1068
+ }
1069
+
1070
+ curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, rbce->unrestricted_auth);
1071
+
1072
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, rbce->timeout);
1073
+ curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, rbce->connect_timeout);
1074
+ curl_easy_setopt(curl, CURLOPT_DNS_CACHE_TIMEOUT, rbce->dns_cache_timeout);
1075
+
1076
+ #if LIBCURL_VERSION_NUM >= 0x070a08
1077
+ curl_easy_setopt(curl, CURLOPT_FTP_RESPONSE_TIMEOUT, rbce->ftp_response_timeout);
1078
+ #else
1079
+ if (rbce->ftp_response_timeout > 0) {
1080
+ rb_warn("Installed libcurl is too old to support ftp_response_timeout");
1081
+ }
1082
+ #endif
1083
+
1084
+ // Set up localport / proxy port
1085
+ // FIXME these won't get returned to default if they're unset Ruby
1086
+ if (rbce->proxy_port > 0) {
1087
+ curl_easy_setopt(curl, CURLOPT_PROXYPORT, rbce->proxy_port);
1088
+ }
1089
+
1090
+ if (rbce->local_port > 0) {
1091
+ #if LIBCURL_VERSION_NUM >= 0x070f02
1092
+ curl_easy_setopt(curl, CURLOPT_LOCALPORT, rbce->local_port);
1093
+
1094
+ if (rbce->local_port_range > 0) {
1095
+ curl_easy_setopt(curl, CURLOPT_LOCALPORTRANGE, rbce->local_port_range);
1096
+ }
1097
+ #else
1098
+ rb_warn("Installed libcurl is too old to support local_port");
1099
+ #endif
1100
+ }
1101
+
1102
+ if (rbce->proxy_type != -1) {
1103
+ #if LIBCURL_VERSION_NUM >= 0x070a00
1104
+ if (rbce->proxy_type == -2) {
1105
+ rb_warn("Installed libcurl is too old to support the selected proxy type");
1106
+ } else {
1107
+ curl_easy_setopt(curl, CURLOPT_PROXYTYPE, rbce->proxy_type);
1108
+ }
1109
+ } else {
1110
+ curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
1111
+ #else
1112
+ rb_warn("Installed libcurl is too old to support proxy_type");
1113
+ #endif
1114
+ }
1115
+
1116
+ if (rbce->http_auth_types > 0) {
1117
+ #if LIBCURL_VERSION_NUM >= 0x070a06
1118
+ curl_easy_setopt(curl, CURLOPT_HTTPAUTH, rbce->http_auth_types);
1119
+ } else {
1120
+ curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
1121
+ #else
1122
+ rb_warn("Installed libcurl is too old to support http_auth_types");
1123
+ #endif
1124
+ }
1125
+
1126
+ if (rbce->proxy_auth_types > 0) {
1127
+ #if LIBCURL_VERSION_NUM >= 0x070a07
1128
+ curl_easy_setopt(curl, CURLOPT_PROXYAUTH, rbce->proxy_auth_types);
1129
+ } else {
1130
+ curl_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
1131
+ #else
1132
+ rb_warn("Installed libcurl is too old to support proxy_auth_types");
1133
+ #endif
1134
+ }
1135
+
1136
+ // Set up HTTP cookie handling if necessary
1137
+ // FIXME this may not get disabled if it's enabled, the disabled again from ruby.
1138
+ if (rbce->enable_cookies) {
1139
+ if (rbce->cookiejar != Qnil) {
1140
+ curl_easy_setopt(curl, CURLOPT_COOKIEJAR, StringValuePtr(rbce->cookiejar));
1141
+ } else {
1142
+ curl_easy_setopt(curl, CURLOPT_COOKIEFILE, ""); /* "" = magic to just enable */
1143
+ }
1144
+ }
1145
+
1146
+ // Setup HTTP headers if necessary
1147
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, NULL); // clear
1148
+
1149
+ if (rbce->headers != Qnil) {
1150
+ if ((rb_type(rbce->headers) == T_ARRAY) || (rb_type(rbce->headers) == T_HASH)) {
1151
+ rb_iterate(rb_each, rbce->headers, cb_each_http_header, (VALUE)&headers);
1152
+ } else {
1153
+ VALUE headers_str = rb_obj_as_string(rbce->headers);
1154
+ headers = curl_slist_append(headers, StringValuePtr(headers_str));
1155
+ }
1156
+
1157
+ if (headers) {
1158
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
1159
+ }
1160
+ }
1161
+
1162
+ // Okay, do it.
1163
+ result = curl_easy_perform(curl);
1164
+
1165
+ // Free everything up
1166
+ if (headers) {
1167
+ curl_slist_free_all(headers);
1168
+ }
1169
+
1170
+ // Sort out the built-in body/header data.
1171
+ if (bodybuf != Qnil) {
1172
+ rbce->body_data = rb_str_to_str(bodybuf);
1173
+ } else {
1174
+ rbce->body_data = Qnil;
1175
+ }
1176
+
1177
+ if (headerbuf != Qnil) {
1178
+ rbce->header_data = rb_str_to_str(headerbuf);
1179
+ } else {
1180
+ rbce->header_data = Qnil;
1181
+ }
1182
+
1183
+ if (result != 0) {
1184
+ raise_curl_easy_error_exception(result);
1185
+ }
1186
+ } else {
1187
+ rb_raise(eCurlErrError, "No URL supplied");
1188
+ }
1189
+
1190
+ return Qtrue;
1191
+ }
1192
+
1193
+ /*
1194
+ * call-seq:
1195
+ * easy.http_get => true
1196
+ *
1197
+ * GET the currently configured URL using the current options set for
1198
+ * this Curl::Easy instance. This method always returns true, or raises
1199
+ * an exception (defined under Curl::Err) on error.
1200
+ */
1201
+ static VALUE ruby_curl_easy_perform_get(VALUE self) {
1202
+ ruby_curl_easy *rbce;
1203
+ CURL *curl;
1204
+
1205
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1206
+ curl = rbce->curl;
1207
+
1208
+ curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
1209
+
1210
+ return handle_perform(rbce);
1211
+ }
1212
+
1213
+ /*
1214
+ * call-seq:
1215
+ * easy.perform => true
1216
+ *
1217
+ * Transfer the currently configured URL using the options set for this
1218
+ * Curl::Easy instance. If this is a HTTP URL, it will be transferred via
1219
+ * the GET request method.
1220
+ */
1221
+ static VALUE ruby_curl_easy_perform(VALUE self) {
1222
+ return ruby_curl_easy_perform_get(self);
1223
+ }
1224
+
1225
+ /*
1226
+ * call-seq:
1227
+ * easy.http_post("url=encoded%20form%20data;and=so%20on") => true
1228
+ * easy.http_post("url=encoded%20form%20data", "and=so%20on", ...) => true
1229
+ * easy.http_post("url=encoded%20form%20data", Curl::PostField, "and=so%20on", ...) => true
1230
+ * easy.http_post(Curl::PostField, Curl::PostField ..., Curl::PostField) => true
1231
+ *
1232
+ * POST the specified formdata to the currently configured URL using
1233
+ * the current options set for this Curl::Easy instance. This method
1234
+ * always returns true, or raises an exception (defined under
1235
+ * Curl::Err) on error.
1236
+ *
1237
+ * The Content-type of the POST is determined by the current setting
1238
+ * of multipart_form_post? , according to the following rules:
1239
+ * * When false (the default): the form will be POSTed with a
1240
+ * content-type of 'application/x-www-form-urlencoded', and any of the
1241
+ * four calling forms may be used.
1242
+ * * When true: the form will be POSTed with a content-type of
1243
+ * 'multipart/formdata'. Only the last calling form may be used,
1244
+ * i.e. only PostField instances may be POSTed. In this mode,
1245
+ * individual fields' content-types are recognised, and file upload
1246
+ * fields are supported.
1247
+ *
1248
+ */
1249
+ static VALUE ruby_curl_easy_perform_post(int argc, VALUE *argv, VALUE self) {
1250
+ ruby_curl_easy *rbce;
1251
+ CURL *curl;
1252
+ int i;
1253
+ VALUE args_ary;
1254
+
1255
+ rb_scan_args(argc, argv, "*", &args_ary);
1256
+
1257
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1258
+ curl = rbce->curl;
1259
+
1260
+ if (rbce->multipart_form_post) {
1261
+ VALUE ret;
1262
+ struct curl_httppost *first = NULL, *last = NULL;
1263
+
1264
+ // Make the multipart form
1265
+ for (i = 0; i < argc; i++) {
1266
+ if (rb_obj_is_instance_of(argv[i], cCurlPostField)) {
1267
+ append_to_form(argv[i], &first, &last);
1268
+ } else {
1269
+ rb_raise(eCurlErrInvalidPostField, "You must use PostFields only with multipart form posts");
1270
+ return Qnil;
1271
+ }
1272
+ }
1273
+
1274
+ curl_easy_setopt(curl, CURLOPT_POST, 0);
1275
+ curl_easy_setopt(curl, CURLOPT_HTTPPOST, first);
1276
+ ret = handle_perform(rbce);
1277
+ curl_formfree(first);
1278
+
1279
+ return ret;
1280
+ } else {
1281
+ long len;
1282
+ char *data;
1283
+
1284
+ if ((rbce->postdata_buffer = rb_funcall(args_ary, idJoin, 1, rbstrAmp)) == Qnil) {
1285
+ rb_raise(eCurlErrError, "Failed to join arguments");
1286
+ return Qnil;
1287
+ } else {
1288
+ data = StringValuePtr(rbce->postdata_buffer);
1289
+ len = RSTRING_LEN(rbce->postdata_buffer);
1290
+
1291
+ curl_easy_setopt(curl, CURLOPT_POST, 1);
1292
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
1293
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len);
1294
+
1295
+ return handle_perform(rbce);
1296
+ }
1297
+ }
1298
+ }
1299
+
1300
+ /*
1301
+ * call-seq:
1302
+ * easy.http_head => true
1303
+ *
1304
+ * Request headers from the currently configured URL using the HEAD
1305
+ * method and current options set for this Curl::Easy instance. This
1306
+ * method always returns true, or raises an exception (defined under
1307
+ * Curl::Err) on error.
1308
+ *
1309
+ * TODO Not yet implemented
1310
+ */
1311
+ static VALUE ruby_curl_easy_perform_head(VALUE self) {
1312
+ rb_raise(eCurlErrError, "Not yet implemented");
1313
+ }
1314
+
1315
+ /*
1316
+ * call-seq:
1317
+ * easy.http_put(data) => true
1318
+ *
1319
+ * PUT the supplied data to the currently configured URL using the
1320
+ * current options set for this Curl::Easy instance. This method always
1321
+ * returns true, or raises an exception (defined under Curl::Err) on error.
1322
+ *
1323
+ * TODO Not yet implemented
1324
+ */
1325
+ static VALUE ruby_curl_easy_perform_put(VALUE self) {
1326
+ rb_raise(eCurlErrError, "Not yet implemented");
1327
+ }
1328
+
1329
+ /* =================== DATA FUNCS =============== */
1330
+
1331
+ /*
1332
+ * call-seq:
1333
+ * easy.body_str => "response body"
1334
+ *
1335
+ * Return the response body from the previous call to +perform+. This
1336
+ * is populated by the default +on_body+ handler - if you supply
1337
+ * your own body handler, this string will be empty.
1338
+ */
1339
+ static VALUE ruby_curl_easy_body_str_get(VALUE self) {
1340
+ CURB_OBJECT_GETTER(ruby_curl_easy, body_data);
1341
+ }
1342
+
1343
+ /*
1344
+ * call-seq:
1345
+ * easy.header_str => "response header"
1346
+ *
1347
+ * Return the response header from the previous call to +perform+. This
1348
+ * is populated by the default +on_header+ handler - if you supply
1349
+ * your own header handler, this string will be empty.
1350
+ */
1351
+ static VALUE ruby_curl_easy_header_str_get(VALUE self) {
1352
+ CURB_OBJECT_GETTER(ruby_curl_easy, header_data);
1353
+ }
1354
+
1355
+
1356
+ /* ============== LASTCONN INFO FUNCS ============ */
1357
+
1358
+ /*
1359
+ * call-seq:
1360
+ * easy.last_effective_url => "http://some.url" or nil
1361
+ *
1362
+ * Retrieve the last effective URL used by this instance.
1363
+ * This is the URL used in the last +perform+ call,
1364
+ * and may differ from the value of easy.url.
1365
+ */
1366
+ static VALUE ruby_curl_easy_last_effective_url_get(VALUE self) {
1367
+ ruby_curl_easy *rbce;
1368
+ char* url;
1369
+
1370
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1371
+ curl_easy_getinfo(rbce->curl, CURLINFO_EFFECTIVE_URL, &url);
1372
+
1373
+ if (url && url[0]) { // curl returns empty string if none
1374
+ return rb_str_new2(url);
1375
+ } else {
1376
+ return Qnil;
1377
+ }
1378
+ }
1379
+
1380
+ /*
1381
+ * call-seq:
1382
+ * easy.response_code => fixnum
1383
+ *
1384
+ * Retrieve the last received HTTP or FTP code. This will be zero
1385
+ * if no server response code has been received. Note that a proxy's
1386
+ * CONNECT response should be read with +http_connect_code+
1387
+ * and not this method.
1388
+ */
1389
+ static VALUE ruby_curl_easy_response_code_get(VALUE self) {
1390
+ ruby_curl_easy *rbce;
1391
+ long code;
1392
+
1393
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1394
+ #ifdef CURLINFO_RESPONSE_CODE
1395
+ curl_easy_getinfo(rbce->curl, CURLINFO_RESPONSE_CODE, &code);
1396
+ #else
1397
+ // old libcurl
1398
+ curl_easy_getinfo(rbce->curl, CURLINFO_HTTP_CODE, &code);
1399
+ #endif
1400
+
1401
+ return LONG2NUM(code);
1402
+ }
1403
+
1404
+ /*
1405
+ * call-seq:
1406
+ * easy.http_connect_code => fixnum
1407
+ *
1408
+ * Retrieve the last received proxy response code to a CONNECT request.
1409
+ */
1410
+ static VALUE ruby_curl_easy_http_connect_code_get(VALUE self) {
1411
+ ruby_curl_easy *rbce;
1412
+ long code;
1413
+
1414
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1415
+ curl_easy_getinfo(rbce->curl, CURLINFO_HTTP_CONNECTCODE, &code);
1416
+
1417
+ return LONG2NUM(code);
1418
+ }
1419
+
1420
+ /*
1421
+ * call-seq:
1422
+ * easy.file_time => fixnum
1423
+ *
1424
+ * Retrieve the remote time of the retrieved document (in number of
1425
+ * seconds since 1 jan 1970 in the GMT/UTC time zone). If you get -1,
1426
+ * it can be because of many reasons (unknown, the server hides it
1427
+ * or the server doesn't support the command that tells document time
1428
+ * etc) and the time of the document is unknown.
1429
+ *
1430
+ * Note that you must tell the server to collect this information
1431
+ * before the transfer is made, by setting +fetch_file_time?+ to true,
1432
+ * or you will unconditionally get a -1 back.
1433
+ *
1434
+ * This requires libcurl 7.5 or higher - otherwise -1 is unconditionally
1435
+ * returned.
1436
+ */
1437
+ static VALUE ruby_curl_easy_file_time_get(VALUE self) {
1438
+ #ifdef CURLINFO_FILETIME
1439
+ ruby_curl_easy *rbce;
1440
+ long time;
1441
+
1442
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1443
+ curl_easy_getinfo(rbce->curl, CURLINFO_FILETIME, &time);
1444
+
1445
+ return LONG2NUM(time);
1446
+ #else
1447
+ rb_warn("Installed libcurl is too old to support file_time");
1448
+ return INT2FIX(0);
1449
+ #endif
1450
+ }
1451
+
1452
+ /*
1453
+ * call-seq:
1454
+ * easy.total_time => float
1455
+ *
1456
+ * Retrieve the total time in seconds for the previous transfer,
1457
+ * including name resolving, TCP connect etc.
1458
+ */
1459
+ static VALUE ruby_curl_easy_total_time_get(VALUE self) {
1460
+ ruby_curl_easy *rbce;
1461
+ double time;
1462
+
1463
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1464
+ curl_easy_getinfo(rbce->curl, CURLINFO_TOTAL_TIME, &time);
1465
+
1466
+ return rb_float_new(time);
1467
+ }
1468
+
1469
+ /*
1470
+ * call-seq:
1471
+ * easy.name_lookup_time => float
1472
+ *
1473
+ * Retrieve the time, in seconds, it took from the start until the
1474
+ * name resolving was completed.
1475
+ */
1476
+ static VALUE ruby_curl_easy_name_lookup_time_get(VALUE self) {
1477
+ ruby_curl_easy *rbce;
1478
+ double time;
1479
+
1480
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1481
+ curl_easy_getinfo(rbce->curl, CURLINFO_NAMELOOKUP_TIME, &time);
1482
+
1483
+ return rb_float_new(time);
1484
+ }
1485
+
1486
+ /*
1487
+ * call-seq:
1488
+ * easy.connect_time => float
1489
+ *
1490
+ * Retrieve the time, in seconds, it took from the start until the
1491
+ * connect to the remote host (or proxy) was completed.
1492
+ */
1493
+ static VALUE ruby_curl_easy_connect_time_get(VALUE self) {
1494
+ ruby_curl_easy *rbce;
1495
+ double time;
1496
+
1497
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1498
+ curl_easy_getinfo(rbce->curl, CURLINFO_CONNECT_TIME, &time);
1499
+
1500
+ return rb_float_new(time);
1501
+ }
1502
+
1503
+ /*
1504
+ * call-seq:
1505
+ * easy.pre_transfer_time => float
1506
+ *
1507
+ * Retrieve the time, in seconds, it took from the start until the
1508
+ * file transfer is just about to begin. This includes all pre-transfer
1509
+ * commands and negotiations that are specific to the particular protocol(s)
1510
+ * involved.
1511
+ */
1512
+ static VALUE ruby_curl_easy_pre_transfer_time_get(VALUE self) {
1513
+ ruby_curl_easy *rbce;
1514
+ double time;
1515
+
1516
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1517
+ curl_easy_getinfo(rbce->curl, CURLINFO_PRETRANSFER_TIME, &time);
1518
+
1519
+ return rb_float_new(time);
1520
+ }
1521
+
1522
+ /*
1523
+ * call-seq:
1524
+ * easy.start_transfer_time => float
1525
+ *
1526
+ * Retrieve the time, in seconds, it took from the start until the first byte
1527
+ * is just about to be transferred. This includes the +pre_transfer_time+ and
1528
+ * also the time the server needs to calculate the result.
1529
+ */
1530
+ static VALUE ruby_curl_easy_start_transfer_time_get(VALUE self) {
1531
+ ruby_curl_easy *rbce;
1532
+ double time;
1533
+
1534
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1535
+ curl_easy_getinfo(rbce->curl, CURLINFO_STARTTRANSFER_TIME, &time);
1536
+
1537
+ return rb_float_new(time);
1538
+ }
1539
+
1540
+ /*
1541
+ * call-seq:
1542
+ * easy.redirect_time => float
1543
+ *
1544
+ * Retrieve the total time, in seconds, it took for all redirection steps
1545
+ * include name lookup, connect, pretransfer and transfer before final
1546
+ * transaction was started. +redirect_time+ contains the complete
1547
+ * execution time for multiple redirections.
1548
+ *
1549
+ * Requires libcurl 7.9.7 or higher, otherwise -1 is always returned.
1550
+ */
1551
+ static VALUE ruby_curl_easy_redirect_time_get(VALUE self) {
1552
+ #ifdef CURLINFO_REDIRECT_TIME
1553
+ ruby_curl_easy *rbce;
1554
+ double time;
1555
+
1556
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1557
+ curl_easy_getinfo(rbce->curl, CURLINFO_REDIRECT_TIME, &time);
1558
+
1559
+ return rb_float_new(time);
1560
+ #else
1561
+ rb_warn("Installed libcurl is too old to support redirect_time");
1562
+ return rb_float_new(-1);
1563
+ #endif
1564
+ }
1565
+
1566
+ /*
1567
+ * call-seq:
1568
+ * easy.redirect_count => integer
1569
+ *
1570
+ * Retrieve the total number of redirections that were actually followed.
1571
+ *
1572
+ * Requires libcurl 7.9.7 or higher, otherwise -1 is always returned.
1573
+ */
1574
+ static VALUE ruby_curl_easy_redirect_count_get(VALUE self) {
1575
+ #ifdef CURLINFO_REDIRECT_COUNT
1576
+ ruby_curl_easy *rbce;
1577
+ long count;
1578
+
1579
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1580
+ curl_easy_getinfo(rbce->curl, CURLINFO_REDIRECT_COUNT, &count);
1581
+
1582
+ return LONG2NUM(count);
1583
+ #else
1584
+ rb_warn("Installed libcurl is too old to support redirect_count");
1585
+ return INT2FIX(-1);
1586
+ #endif
1587
+
1588
+ }
1589
+
1590
+ /*
1591
+ * call-seq:
1592
+ * easy.uploaded_bytes => float
1593
+ *
1594
+ * Retrieve the total amount of bytes that were uploaded in the
1595
+ * preceeding transfer.
1596
+ */
1597
+ static VALUE ruby_curl_easy_uploaded_bytes_get(VALUE self) {
1598
+ ruby_curl_easy *rbce;
1599
+ double bytes;
1600
+
1601
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1602
+ curl_easy_getinfo(rbce->curl, CURLINFO_SIZE_UPLOAD, &bytes);
1603
+
1604
+ return rb_float_new(bytes);
1605
+ }
1606
+
1607
+ /*
1608
+ * call-seq:
1609
+ * easy.downloaded_bytes => float
1610
+ *
1611
+ * Retrieve the total amount of bytes that were downloaded in the
1612
+ * preceeding transfer.
1613
+ */
1614
+ static VALUE ruby_curl_easy_downloaded_bytes_get(VALUE self) {
1615
+ ruby_curl_easy *rbce;
1616
+ double bytes;
1617
+
1618
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1619
+ curl_easy_getinfo(rbce->curl, CURLINFO_SIZE_DOWNLOAD, &bytes);
1620
+
1621
+ return rb_float_new(bytes);
1622
+ }
1623
+
1624
+ /*
1625
+ * call-seq:
1626
+ * easy.upload_speed => float
1627
+ *
1628
+ * Retrieve the average upload speed that curl measured for the
1629
+ * preceeding complete upload.
1630
+ */
1631
+ static VALUE ruby_curl_easy_upload_speed_get(VALUE self) {
1632
+ ruby_curl_easy *rbce;
1633
+ double bytes;
1634
+
1635
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1636
+ curl_easy_getinfo(rbce->curl, CURLINFO_SPEED_UPLOAD, &bytes);
1637
+
1638
+ return rb_float_new(bytes);
1639
+ }
1640
+
1641
+ /*
1642
+ * call-seq:
1643
+ * easy.download_speed => float
1644
+ *
1645
+ * Retrieve the average download speed that curl measured for
1646
+ * the preceeding complete download.
1647
+ */
1648
+ static VALUE ruby_curl_easy_download_speed_get(VALUE self) {
1649
+ ruby_curl_easy *rbce;
1650
+ double bytes;
1651
+
1652
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1653
+ curl_easy_getinfo(rbce->curl, CURLINFO_SPEED_DOWNLOAD, &bytes);
1654
+
1655
+ return rb_float_new(bytes);
1656
+ }
1657
+
1658
+ /*
1659
+ * call-seq:
1660
+ * easy.header_size => fixnum
1661
+ *
1662
+ * Retrieve the total size of all the headers received in the
1663
+ * preceeding transfer.
1664
+ */
1665
+ static VALUE ruby_curl_easy_header_size_get(VALUE self) {
1666
+ ruby_curl_easy *rbce;
1667
+ long size;
1668
+
1669
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1670
+ curl_easy_getinfo(rbce->curl, CURLINFO_HEADER_SIZE, &size);
1671
+
1672
+ return LONG2NUM(size);
1673
+ }
1674
+
1675
+ /*
1676
+ * call-seq:
1677
+ * easy.request_size => fixnum
1678
+ *
1679
+ * Retrieve the total size of the issued requests. This is so far
1680
+ * only for HTTP requests. Note that this may be more than one request
1681
+ * if +follow_location?+ is true.
1682
+ */
1683
+ static VALUE ruby_curl_easy_request_size_get(VALUE self) {
1684
+ ruby_curl_easy *rbce;
1685
+ long size;
1686
+
1687
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1688
+ curl_easy_getinfo(rbce->curl, CURLINFO_REQUEST_SIZE, &size);
1689
+
1690
+ return LONG2NUM(size);
1691
+ }
1692
+
1693
+ /*
1694
+ * call-seq:
1695
+ * easy.ssl_verify_result => integer
1696
+ *
1697
+ * Retrieve the result of the certification verification that was requested
1698
+ * (by setting +ssl_verify_peer?+ to +true+).
1699
+ */
1700
+ static VALUE ruby_curl_easy_ssl_verify_result_get(VALUE self) {
1701
+ ruby_curl_easy *rbce;
1702
+ long result;
1703
+
1704
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1705
+ curl_easy_getinfo(rbce->curl, CURLINFO_SSL_VERIFYRESULT, &result);
1706
+
1707
+ return LONG2NUM(result);
1708
+ }
1709
+
1710
+ /* TODO CURLINFO_SSL_ENGINES
1711
+
1712
+ Pass the address of a 'struct curl_slist *' to receive a linked-list of OpenSSL crypto-engines supported. Note that engines are normally implemented in separate dynamic libraries. Hence not all the returned engines may be available at run-time. NOTE: you must call curl_slist_free_all(3) on the list pointer once you're done with it, as libcurl will not free the data for you. (Added in 7.12.3)
1713
+ */
1714
+
1715
+ /*
1716
+ * call-seq:
1717
+ * easy.downloaded_content_length => float
1718
+ *
1719
+ * Retrieve the content-length of the download. This is the value read
1720
+ * from the Content-Length: field.
1721
+ */
1722
+ static VALUE ruby_curl_easy_downloaded_content_length_get(VALUE self) {
1723
+ ruby_curl_easy *rbce;
1724
+ double bytes;
1725
+
1726
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1727
+ curl_easy_getinfo(rbce->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &bytes);
1728
+
1729
+ return rb_float_new(bytes);
1730
+ }
1731
+
1732
+ /*
1733
+ * call-seq:
1734
+ * easy.uploaded_content_length => float
1735
+ *
1736
+ * Retrieve the content-length of the upload.
1737
+ */
1738
+ static VALUE ruby_curl_easy_uploaded_content_length_get(VALUE self) {
1739
+ ruby_curl_easy *rbce;
1740
+ double bytes;
1741
+
1742
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1743
+ curl_easy_getinfo(rbce->curl, CURLINFO_CONTENT_LENGTH_UPLOAD, &bytes);
1744
+
1745
+ return rb_float_new(bytes);
1746
+ }
1747
+
1748
+ /*
1749
+ * call-seq:
1750
+ * easy.content_type => "content/type" or nil
1751
+ *
1752
+ * Retrieve the content-type of the downloaded object. This is the value read
1753
+ * from the Content-Type: field. If you get +nil+, it means that the server
1754
+ * didn't send a valid Content-Type header or that the protocol used doesn't
1755
+ * support this.
1756
+ */
1757
+ static VALUE ruby_curl_easy_content_type_get(VALUE self) {
1758
+ ruby_curl_easy *rbce;
1759
+ char* type;
1760
+
1761
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1762
+ curl_easy_getinfo(rbce->curl, CURLINFO_CONTENT_TYPE, &type);
1763
+
1764
+ if (type && type[0]) { // curl returns empty string if none
1765
+ return rb_str_new2(type);
1766
+ } else {
1767
+ return Qnil;
1768
+ }
1769
+ }
1770
+
1771
+
1772
+ /* NOT REQUIRED?
1773
+ CURLINFO_PRIVATE
1774
+
1775
+ Pass a pointer to a 'char *' to receive the pointer to the private data associated with the curl handle (set with the CURLOPT_PRIVATE option to curl_easy_setopt(3)). (Added in 7.10.3)
1776
+ */
1777
+
1778
+ /* TODO these will need constants setting up too for checking the bits.
1779
+ *
1780
+ * Alternatively, could return an object that wraps the long, and has
1781
+ * question methods to query the auth types. Could return long from to_i(nt)
1782
+ *
1783
+ CURLINFO_HTTPAUTH_AVAIL
1784
+
1785
+ Pass a pointer to a long to receive a bitmask indicating the authentication method(s) available. The meaning of the bits is explained in the CURLOPT_HTTPAUTH option for curl_easy_setopt(3). (Added in 7.10.8)
1786
+
1787
+ CURLINFO_PROXYAUTH_AVAIL
1788
+
1789
+ Pass a pointer to a long to receive a bitmask indicating the authentication method(s) available for your proxy authentication. (Added in 7.10.8)
1790
+ */
1791
+
1792
+ /*
1793
+ * call-seq:
1794
+ * easy.os_errno => integer
1795
+ *
1796
+ * Retrieve the errno variable from a connect failure (requires
1797
+ * libcurl 7.12.2 or higher, otherwise 0 is always returned).
1798
+ */
1799
+ static VALUE ruby_curl_easy_os_errno_get(VALUE self) {
1800
+ #ifdef CURLINFO_OS_ERRNO
1801
+ ruby_curl_easy *rbce;
1802
+ long result;
1803
+
1804
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1805
+ curl_easy_getinfo(rbce->curl, CURLINFO_OS_ERRNO, &result);
1806
+
1807
+ return LONG2NUM(result);
1808
+ #else
1809
+ rb_warn("Installed libcurl is too old to support os_errno");
1810
+ return INT2FIX(0);
1811
+ #endif
1812
+ }
1813
+
1814
+ /*
1815
+ * call-seq:
1816
+ * easy.os_errno => integer
1817
+ *
1818
+ * Retrieve the number of new connections libcurl had to create to achieve
1819
+ * the previous transfer (only the successful connects are counted).
1820
+ * Combined with +redirect_count+ you are able to know how many times libcurl
1821
+ * successfully reused existing connection(s) or not.
1822
+ *
1823
+ * See the Connection Options of curl_easy_setopt(3) to see how libcurl tries
1824
+ * to make persistent connections to save time.
1825
+ *
1826
+ * (requires libcurl 7.12.3 or higher, otherwise -1 is always returned).
1827
+ */
1828
+ static VALUE ruby_curl_easy_num_connects_get(VALUE self) {
1829
+ #ifdef CURLINFO_NUM_CONNECTS
1830
+ ruby_curl_easy *rbce;
1831
+ long result;
1832
+
1833
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1834
+ curl_easy_getinfo(rbce->curl, CURLINFO_NUM_CONNECTS, &result);
1835
+
1836
+ return LONG2NUM(result);
1837
+ #else
1838
+ rb_warn("Installed libcurl is too old to support num_connects");
1839
+ return INT2FIX(-1);
1840
+ #endif
1841
+ }
1842
+
1843
+
1844
+ /* TODO this needs to be implemented.
1845
+
1846
+ CURLINFO_COOKIELIST
1847
+
1848
+ 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)
1849
+ */
1850
+
1851
+ /* TODO this needs to be implemented. Could probably support CONNECT_ONLY by having this
1852
+ * return an open Socket or something.
1853
+ *
1854
+ CURLINFO_LASTSOCKET
1855
+
1856
+ Pass a pointer to a long to receive the last socket used by this curl session. If the socket is no longer valid, -1 is returned. When you finish working with the socket, you must call curl_easy_cleanup() as usual and let libcurl close the socket and cleanup other resources associated with the handle. This is typically used in combination with CURLOPT_CONNECT_ONLY. (Added in 7.15.2)
1857
+ */
1858
+
1859
+ /*
1860
+ * call-seq:
1861
+ * easy.content_type => "content/type" or nil
1862
+ *
1863
+ * Retrieve the path of the entry path. That is the initial path libcurl ended
1864
+ * up in when logging on to the remote FTP server. This returns +nil+ if
1865
+ * something is wrong.
1866
+ *
1867
+ * (requires libcurl 7.15.4 or higher, otherwise +nil+ is always returned).
1868
+ */
1869
+ static VALUE ruby_curl_easy_ftp_entry_path_get(VALUE self) {
1870
+ #ifdef CURLINFO_FTP_ENTRY_PATH
1871
+ ruby_curl_easy *rbce;
1872
+ char* path;
1873
+
1874
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1875
+ curl_easy_getinfo(rbce->curl, CURLINFO_FTP_ENTRY_PATH, &path);
1876
+
1877
+ if (type && type[0]) { // curl returns NULL or empty string if none
1878
+ return rb_str_new2(path);
1879
+ } else {
1880
+ return Qnil;
1881
+ }
1882
+ #else
1883
+ rb_warn("Installed libcurl is too old to support num_connects");
1884
+ return Qnil;
1885
+ #endif
1886
+ }
1887
+
1888
+
1889
+ /* ================== ESCAPING FUNCS ==============*/
1890
+
1891
+ /*
1892
+ * call-seq:
1893
+ * easy.escape("some text") => "some%20text"
1894
+ *
1895
+ * Convert the given input string to a URL encoded string and return
1896
+ * the result. All input characters that are not a-z, A-Z or 0-9 are
1897
+ * converted to their "URL escaped" version (%NN where NN is a
1898
+ * two-digit hexadecimal number).
1899
+ */
1900
+ static VALUE ruby_curl_easy_escape(VALUE self, VALUE str) {
1901
+ ruby_curl_easy *rbce;
1902
+ char *result;
1903
+ VALUE rresult;
1904
+
1905
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1906
+
1907
+ result = (char*)curl_easy_escape(rbce->curl, StringValuePtr(str), RSTRING_LEN(str));
1908
+ rresult = rb_str_new2(result);
1909
+ curl_free(result);
1910
+
1911
+ return rresult;
1912
+ }
1913
+
1914
+ /*
1915
+ * call-seq:
1916
+ * easy.unescape("some text") => "some%20text"
1917
+ *
1918
+ * Convert the given URL encoded input string to a "plain string" and return
1919
+ * the result. All input characters that are URL encoded (%XX where XX is a
1920
+ * two-digit hexadecimal number) are converted to their binary versions.
1921
+ */
1922
+ static VALUE ruby_curl_easy_unescape(VALUE self, VALUE str) {
1923
+ ruby_curl_easy *rbce;
1924
+ int rlen;
1925
+ char *result;
1926
+ VALUE rresult;
1927
+
1928
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
1929
+
1930
+ result = (char*)curl_easy_unescape(rbce->curl, StringValuePtr(str), RSTRING_LEN(str), &rlen);
1931
+ rresult = rb_str_new(result, rlen);
1932
+ curl_free(result);
1933
+
1934
+ return rresult;
1935
+ }
1936
+
1937
+
1938
+ /* ================= CLASS METHODS ==================*/
1939
+
1940
+ /*
1941
+ * call-seq:
1942
+ * Curl::Easy.perform(url) { |easy| ... } => #&lt;Curl::Easy...&gt;
1943
+ *
1944
+ * Convenience method that creates a new Curl::Easy instance with
1945
+ * the specified URL and calls the general +perform+ method, before returning
1946
+ * the new instance. For HTTP URLs, this is equivalent to calling +http_get+.
1947
+ *
1948
+ * If a block is supplied, the new instance will be yielded just prior to
1949
+ * the +http_get+ call.
1950
+ */
1951
+ static VALUE ruby_curl_easy_class_perform(int argc, VALUE *argv, VALUE klass) {
1952
+ VALUE c = ruby_curl_easy_new(argc, argv, klass);
1953
+
1954
+ if (rb_block_given_p()) {
1955
+ rb_yield(c);
1956
+ }
1957
+
1958
+ ruby_curl_easy_perform(c);
1959
+ return c;
1960
+ }
1961
+
1962
+ /*
1963
+ * call-seq:
1964
+ * Curl::Easy.http_get(url) { |easy| ... } => #&lt;Curl::Easy...&gt;
1965
+ *
1966
+ * Convenience method that creates a new Curl::Easy instance with
1967
+ * the specified URL and calls +http_get+, before returning the new instance.
1968
+ *
1969
+ * If a block is supplied, the new instance will be yielded just prior to
1970
+ * the +http_get+ call.
1971
+ */
1972
+ static VALUE ruby_curl_easy_class_perform_get(int argc, VALUE *argv, VALUE klass) {
1973
+ VALUE c = ruby_curl_easy_new(argc, argv, klass);
1974
+
1975
+ if (rb_block_given_p()) {
1976
+ rb_yield(c);
1977
+ }
1978
+
1979
+ ruby_curl_easy_perform_get(c);
1980
+ return c;
1981
+ }
1982
+
1983
+ /*
1984
+ * call-seq:
1985
+ * Curl::Easy.http_post("url=encoded%20form%20data;and=so%20on") => true
1986
+ * Curl::Easy.http_post("url=encoded%20form%20data", "and=so%20on", ...) => true
1987
+ * Curl::Easy.http_post("url=encoded%20form%20data", Curl::PostField, "and=so%20on", ...) => true
1988
+ * Curl::Easy.http_post(Curl::PostField, Curl::PostField ..., Curl::PostField) => true
1989
+ *
1990
+ * POST the specified formdata to the currently configured URL using
1991
+ * the current options set for this Curl::Easy instance. This method
1992
+ * always returns true, or raises an exception (defined under
1993
+ * Curl::Err) on error.
1994
+ *
1995
+ * If you wish to use multipart form encoding, you'll need to supply a block
1996
+ * in order to set multipart_form_post true. See #http_post for more
1997
+ * information.
1998
+ */
1999
+ static VALUE ruby_curl_easy_class_perform_post(int argc, VALUE *argv, VALUE klass) {
2000
+ VALUE url, fields;
2001
+
2002
+ rb_scan_args(argc, argv, "1*", &url, &fields);
2003
+
2004
+ VALUE c = ruby_curl_easy_new(1, &url, klass);
2005
+
2006
+ if (argc > 1) {
2007
+ ruby_curl_easy_perform_post(argc - 1, &argv[1], c);
2008
+ } else {
2009
+ ruby_curl_easy_perform_post(0, NULL, c);
2010
+ }
2011
+
2012
+ return c;
2013
+ }
2014
+
2015
+
2016
+ /* =================== INIT LIB =====================*/
2017
+ void init_curb_easy() {
2018
+ idCall = rb_intern("call");
2019
+ idJoin = rb_intern("join");
2020
+
2021
+ rbstrAmp = rb_str_new2("&");
2022
+ rb_global_variable(&rbstrAmp);
2023
+
2024
+ cCurlEasy = rb_define_class_under(mCurl, "Easy", rb_cObject);
2025
+
2026
+ /* Class methods */
2027
+ rb_define_singleton_method(cCurlEasy, "new", ruby_curl_easy_new, -1);
2028
+ rb_define_singleton_method(cCurlEasy, "perform", ruby_curl_easy_class_perform, -1);
2029
+ rb_define_singleton_method(cCurlEasy, "http_get", ruby_curl_easy_class_perform_get, -1);
2030
+ rb_define_singleton_method(cCurlEasy, "http_post", ruby_curl_easy_class_perform_post, -1);
2031
+
2032
+ /* Attributes for config next perform */
2033
+ rb_define_method(cCurlEasy, "url=", ruby_curl_easy_url_set, 1);
2034
+ rb_define_method(cCurlEasy, "url", ruby_curl_easy_url_get, 0);
2035
+ rb_define_method(cCurlEasy, "headers=", ruby_curl_easy_headers_set, 1);
2036
+ rb_define_method(cCurlEasy, "headers", ruby_curl_easy_headers_get, 0);
2037
+ rb_define_method(cCurlEasy, "interface=", ruby_curl_easy_interface_set, 1);
2038
+ rb_define_method(cCurlEasy, "interface", ruby_curl_easy_interface_get, 0);
2039
+ rb_define_method(cCurlEasy, "userpwd=", ruby_curl_easy_userpwd_set, 1);
2040
+ rb_define_method(cCurlEasy, "userpwd", ruby_curl_easy_userpwd_get, 0);
2041
+ rb_define_method(cCurlEasy, "proxypwd=", ruby_curl_easy_proxypwd_set, 1);
2042
+ rb_define_method(cCurlEasy, "proxypwd", ruby_curl_easy_proxypwd_get, 0);
2043
+ rb_define_method(cCurlEasy, "cookiejar=", ruby_curl_easy_cookiejar_set, 1);
2044
+ rb_define_method(cCurlEasy, "cookiejar", ruby_curl_easy_cookiejar_get, 0);
2045
+
2046
+ rb_define_method(cCurlEasy, "local_port=", ruby_curl_easy_local_port_set, 1);
2047
+ rb_define_method(cCurlEasy, "local_port", ruby_curl_easy_local_port_get, 0);
2048
+ rb_define_method(cCurlEasy, "local_port_range=", ruby_curl_easy_local_port_range_set, 1);
2049
+ rb_define_method(cCurlEasy, "local_port_range", ruby_curl_easy_local_port_range_get, 0);
2050
+ rb_define_method(cCurlEasy, "proxy_port=", ruby_curl_easy_proxy_port_set, 1);
2051
+ rb_define_method(cCurlEasy, "proxy_port", ruby_curl_easy_proxy_port_get, 0);
2052
+ rb_define_method(cCurlEasy, "proxy_type=", ruby_curl_easy_proxy_type_set, 1);
2053
+ rb_define_method(cCurlEasy, "proxy_type", ruby_curl_easy_proxy_type_get, 0);
2054
+ rb_define_method(cCurlEasy, "http_auth_types=", ruby_curl_easy_http_auth_types_set, 1);
2055
+ rb_define_method(cCurlEasy, "http_auth_types", ruby_curl_easy_http_auth_types_get, 0);
2056
+ rb_define_method(cCurlEasy, "proxy_auth_types=", ruby_curl_easy_proxy_auth_types_set, 1);
2057
+ rb_define_method(cCurlEasy, "proxy_auth_types", ruby_curl_easy_proxy_auth_types_get, 0);
2058
+ rb_define_method(cCurlEasy, "max_redirects=", ruby_curl_easy_max_redirects_set, 1);
2059
+ rb_define_method(cCurlEasy, "max_redirects", ruby_curl_easy_max_redirects_get, 0);
2060
+ rb_define_method(cCurlEasy, "timeout=", ruby_curl_easy_timeout_set, 1);
2061
+ rb_define_method(cCurlEasy, "timeout", ruby_curl_easy_timeout_get, 0);
2062
+ rb_define_method(cCurlEasy, "connect_timeout=", ruby_curl_easy_connect_timeout_set, 1);
2063
+ rb_define_method(cCurlEasy, "connect_timeout", ruby_curl_easy_connect_timeout_get, 0);
2064
+ rb_define_method(cCurlEasy, "dns_cache_timeout=", ruby_curl_easy_dns_cache_timeout_set, 1);
2065
+ rb_define_method(cCurlEasy, "dns_cache_timeout", ruby_curl_easy_dns_cache_timeout_get, 0);
2066
+ rb_define_method(cCurlEasy, "ftp_response_timeout=", ruby_curl_easy_ftp_response_timeout_set, 1);
2067
+ rb_define_method(cCurlEasy, "ftp_response_timeout", ruby_curl_easy_ftp_response_timeout_get, 0);
2068
+
2069
+ rb_define_method(cCurlEasy, "proxy_tunnel=", ruby_curl_easy_proxy_tunnel_set, 1);
2070
+ rb_define_method(cCurlEasy, "proxy_tunnel?", ruby_curl_easy_proxy_tunnel_q, 0);
2071
+ rb_define_method(cCurlEasy, "fetch_file_time=", ruby_curl_easy_fetch_file_time_set, 1);
2072
+ rb_define_method(cCurlEasy, "fetch_file_time?", ruby_curl_easy_fetch_file_time_q, 0);
2073
+ rb_define_method(cCurlEasy, "ssl_verify_peer=", ruby_curl_easy_ssl_verify_peer_set, 1);
2074
+ rb_define_method(cCurlEasy, "ssl_verify_peer?", ruby_curl_easy_ssl_verify_peer_q, 0);
2075
+ rb_define_method(cCurlEasy, "header_in_body=", ruby_curl_easy_header_in_body_set, 1);
2076
+ rb_define_method(cCurlEasy, "header_in_body?", ruby_curl_easy_header_in_body_q, 0);
2077
+ rb_define_method(cCurlEasy, "use_netrc=", ruby_curl_easy_use_netrc_set, 1);
2078
+ rb_define_method(cCurlEasy, "use_netrc?", ruby_curl_easy_use_netrc_q, 0);
2079
+ rb_define_method(cCurlEasy, "follow_location=", ruby_curl_easy_follow_location_set, 1);
2080
+ rb_define_method(cCurlEasy, "follow_location?", ruby_curl_easy_follow_location_q, 0);
2081
+ rb_define_method(cCurlEasy, "unrestricted_auth=", ruby_curl_easy_unrestricted_auth_set, 1);
2082
+ rb_define_method(cCurlEasy, "unrestricted_auth?", ruby_curl_easy_unrestricted_auth_q, 0);
2083
+ rb_define_method(cCurlEasy, "verbose=", ruby_curl_easy_verbose_set, 1);
2084
+ rb_define_method(cCurlEasy, "verbose?", ruby_curl_easy_verbose_q, 0);
2085
+ rb_define_method(cCurlEasy, "multipart_form_post=", ruby_curl_easy_multipart_form_post_set, 1);
2086
+ rb_define_method(cCurlEasy, "multipart_form_post?", ruby_curl_easy_multipart_form_post_q, 0);
2087
+ rb_define_method(cCurlEasy, "enable_cookies=", ruby_curl_easy_enable_cookies_set, 1);
2088
+ rb_define_method(cCurlEasy, "enable_cookies?", ruby_curl_easy_enable_cookies_q, 0);
2089
+
2090
+ rb_define_method(cCurlEasy, "on_body", ruby_curl_easy_on_body_set, -1);
2091
+ rb_define_method(cCurlEasy, "on_header", ruby_curl_easy_on_header_set, -1);
2092
+ rb_define_method(cCurlEasy, "on_progress", ruby_curl_easy_on_progress_set, -1);
2093
+ rb_define_method(cCurlEasy, "on_debug", ruby_curl_easy_on_debug_set, -1);
2094
+
2095
+ rb_define_method(cCurlEasy, "perform", ruby_curl_easy_perform, 0);
2096
+ rb_define_method(cCurlEasy, "http_get", ruby_curl_easy_perform_get, 0);
2097
+ rb_define_method(cCurlEasy, "http_post", ruby_curl_easy_perform_post, -1);
2098
+ rb_define_method(cCurlEasy, "http_head", ruby_curl_easy_perform_head, 0);
2099
+ rb_define_method(cCurlEasy, "http_put", ruby_curl_easy_perform_put, 0);
2100
+
2101
+ /* Post-perform info methods */
2102
+ rb_define_method(cCurlEasy, "body_str", ruby_curl_easy_body_str_get, 0);
2103
+ rb_define_method(cCurlEasy, "header_str", ruby_curl_easy_header_str_get, 0);
2104
+
2105
+ rb_define_method(cCurlEasy, "last_effective_url", ruby_curl_easy_last_effective_url_get, 0);
2106
+ rb_define_method(cCurlEasy, "response_code", ruby_curl_easy_response_code_get, 0);
2107
+ rb_define_method(cCurlEasy, "http_connect_code", ruby_curl_easy_http_connect_code_get, 0);
2108
+ rb_define_method(cCurlEasy, "file_time", ruby_curl_easy_file_time_get, 0);
2109
+ rb_define_method(cCurlEasy, "total_time", ruby_curl_easy_total_time_get, 0);
2110
+ rb_define_method(cCurlEasy, "total_time", ruby_curl_easy_total_time_get, 0);
2111
+ rb_define_method(cCurlEasy, "name_lookup_time", ruby_curl_easy_name_lookup_time_get, 0);
2112
+ rb_define_method(cCurlEasy, "connect_time", ruby_curl_easy_connect_time_get, 0);
2113
+ rb_define_method(cCurlEasy, "pre_transfer_time", ruby_curl_easy_pre_transfer_time_get, 0);
2114
+ rb_define_method(cCurlEasy, "start_transfer_time", ruby_curl_easy_start_transfer_time_get, 0);
2115
+ rb_define_method(cCurlEasy, "redirect_time", ruby_curl_easy_redirect_time_get, 0);
2116
+ rb_define_method(cCurlEasy, "redirect_count", ruby_curl_easy_redirect_count_get, 0);
2117
+ rb_define_method(cCurlEasy, "downloaded_bytes", ruby_curl_easy_downloaded_bytes_get, 0);
2118
+ rb_define_method(cCurlEasy, "uploaded_bytes", ruby_curl_easy_uploaded_bytes_get, 0);
2119
+ rb_define_method(cCurlEasy, "download_speed", ruby_curl_easy_download_speed_get, 0);
2120
+ rb_define_method(cCurlEasy, "upload_speed", ruby_curl_easy_upload_speed_get, 0);
2121
+ rb_define_method(cCurlEasy, "header_size", ruby_curl_easy_header_size_get, 0);
2122
+ rb_define_method(cCurlEasy, "request_size", ruby_curl_easy_request_size_get, 0);
2123
+ rb_define_method(cCurlEasy, "ssl_verify_result", ruby_curl_easy_ssl_verify_result_get, 0);
2124
+ rb_define_method(cCurlEasy, "downloaded_content_length", ruby_curl_easy_downloaded_content_length_get, 0);
2125
+ rb_define_method(cCurlEasy, "uploaded_content_length", ruby_curl_easy_uploaded_content_length_get, 0);
2126
+ rb_define_method(cCurlEasy, "content_type", ruby_curl_easy_content_type_get, 0);
2127
+ rb_define_method(cCurlEasy, "os_errno", ruby_curl_easy_os_errno_get, 0);
2128
+ rb_define_method(cCurlEasy, "num_connects", ruby_curl_easy_num_connects_get, 0);
2129
+ rb_define_method(cCurlEasy, "ftp_entry_path", ruby_curl_easy_ftp_entry_path_get, 0);
2130
+
2131
+ /* Curl utils */
2132
+ rb_define_method(cCurlEasy, "escape", ruby_curl_easy_escape, 1);
2133
+ rb_define_method(cCurlEasy, "unescape", ruby_curl_easy_unescape, 1);
2134
+
2135
+ /* Runtime support */
2136
+ rb_define_method(cCurlEasy, "clone", ruby_curl_easy_clone, 0);
2137
+ rb_define_alias(cCurlEasy, "dup", "clone");
2138
+ }