curb 0.6.5.0 → 0.8.4
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/README +42 -5
- data/Rakefile +27 -4
- data/ext/curb.c +638 -0
- data/ext/curb.h +6 -4
- data/ext/curb_easy.c +954 -573
- data/ext/curb_easy.h +31 -2
- data/ext/curb_errors.c +16 -2
- data/ext/curb_errors.h +1 -0
- data/ext/curb_macros.h +4 -0
- data/ext/curb_multi.c +229 -67
- data/ext/curb_postfield.c +61 -49
- data/ext/curb_upload.c +2 -2
- data/ext/extconf.rb +232 -4
- data/lib/curb.rb +1 -186
- data/lib/curl/easy.rb +468 -0
- data/lib/curl/multi.rb +248 -0
- data/lib/curl.rb +61 -1
- data/tests/bug_crash_on_debug.rb +39 -0
- data/tests/bug_crash_on_progress.rb +33 -0
- data/tests/bug_curb_easy_blocks_ruby_threads.rb +2 -2
- data/tests/bug_issue102.rb +17 -0
- data/tests/bug_multi_segfault.rb +6 -2
- data/tests/bug_postfields_crash2.rb +57 -0
- data/tests/bug_require_last_or_segfault.rb +1 -1
- data/tests/bugtests.rb +9 -0
- data/tests/helper.rb +40 -10
- data/tests/signals.rb +33 -0
- data/tests/tc_curl.rb +39 -0
- data/tests/tc_curl_download.rb +44 -1
- data/tests/tc_curl_easy.rb +355 -22
- data/tests/tc_curl_easy_setopt.rb +31 -0
- data/tests/tc_curl_multi.rb +57 -5
- data/tests/tc_curl_postfield.rb +3 -1
- data/tests/timeout.rb +100 -0
- data/tests/timeout_server.rb +33 -0
- metadata +87 -48
data/ext/curb_easy.h
CHANGED
|
@@ -11,6 +11,24 @@
|
|
|
11
11
|
|
|
12
12
|
#include <curl/easy.h>
|
|
13
13
|
|
|
14
|
+
#ifdef CURL_VERSION_SSL
|
|
15
|
+
#if LIBCURL_VERSION_NUM >= 0x070b00
|
|
16
|
+
# if LIBCURL_VERSION_NUM <= 0x071004
|
|
17
|
+
# define CURB_FTPSSL CURLOPT_FTP_SSL
|
|
18
|
+
# define CURB_FTPSSL_ALL CURLFTPSSL_ALL
|
|
19
|
+
# define CURB_FTPSSL_TRY CURLFTPSSL_TRY
|
|
20
|
+
# define CURB_FTPSSL_CONTROL CURLFTPSSL_CONTROL
|
|
21
|
+
# define CURB_FTPSSL_NONE CURLFTPSSL_NONE
|
|
22
|
+
# else
|
|
23
|
+
# define CURB_FTPSSL CURLOPT_USE_SSL
|
|
24
|
+
# define CURB_FTPSSL_ALL CURLUSESSL_ALL
|
|
25
|
+
# define CURB_FTPSSL_TRY CURLUSESSL_TRY
|
|
26
|
+
# define CURB_FTPSSL_CONTROL CURLUSESSL_CONTROL
|
|
27
|
+
# define CURB_FTPSSL_NONE CURLUSESSL_NONE
|
|
28
|
+
# endif
|
|
29
|
+
#endif
|
|
30
|
+
#endif
|
|
31
|
+
|
|
14
32
|
/* a lot of this *could* be kept in the handler itself,
|
|
15
33
|
* but then we lose the ability to query it's status.
|
|
16
34
|
*/
|
|
@@ -19,6 +37,7 @@ typedef struct {
|
|
|
19
37
|
CURL *curl;
|
|
20
38
|
|
|
21
39
|
VALUE opts; /* rather then allocate everything we might need to store, allocate a Hash and only store objects we actually use... */
|
|
40
|
+
VALUE multi; /* keep a multi handle alive for each easy handle not being used by a multi handle. This improves easy performance when not within a multi context */
|
|
22
41
|
|
|
23
42
|
/* Other opts */
|
|
24
43
|
unsigned short local_port; // 0 is no port
|
|
@@ -32,6 +51,12 @@ typedef struct {
|
|
|
32
51
|
unsigned long connect_timeout;
|
|
33
52
|
long dns_cache_timeout;
|
|
34
53
|
unsigned long ftp_response_timeout;
|
|
54
|
+
long low_speed_limit;
|
|
55
|
+
long low_speed_time;
|
|
56
|
+
long ssl_version;
|
|
57
|
+
long use_ssl;
|
|
58
|
+
long ftp_filemethod;
|
|
59
|
+
unsigned short resolve_mode;
|
|
35
60
|
|
|
36
61
|
/* bool flags */
|
|
37
62
|
char proxy_tunnel;
|
|
@@ -45,7 +70,11 @@ typedef struct {
|
|
|
45
70
|
char verbose;
|
|
46
71
|
char multipart_form_post;
|
|
47
72
|
char enable_cookies;
|
|
73
|
+
char ignore_content_length;
|
|
74
|
+
char callback_active;
|
|
75
|
+
|
|
48
76
|
struct curl_slist *curl_headers;
|
|
77
|
+
struct curl_slist *curl_ftp_commands;
|
|
49
78
|
|
|
50
79
|
int last_result; /* last result code from multi loop */
|
|
51
80
|
|
|
@@ -53,8 +82,8 @@ typedef struct {
|
|
|
53
82
|
|
|
54
83
|
extern VALUE cCurlEasy;
|
|
55
84
|
|
|
56
|
-
VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce
|
|
57
|
-
VALUE ruby_curl_easy_cleanup(VALUE self, ruby_curl_easy *rbce
|
|
85
|
+
VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce);
|
|
86
|
+
VALUE ruby_curl_easy_cleanup(VALUE self, ruby_curl_easy *rbce);
|
|
58
87
|
|
|
59
88
|
void init_curb_easy();
|
|
60
89
|
|
data/ext/curb_errors.c
CHANGED
|
@@ -28,6 +28,7 @@ VALUE eCurlErrUnsupportedProtocol;
|
|
|
28
28
|
VALUE eCurlErrFailedInit;
|
|
29
29
|
VALUE eCurlErrMalformedURL;
|
|
30
30
|
VALUE eCurlErrMalformedURLUser;
|
|
31
|
+
VALUE eCurlErrNotBuiltIn;
|
|
31
32
|
VALUE eCurlErrProxyResolution;
|
|
32
33
|
VALUE eCurlErrHostResolution;
|
|
33
34
|
VALUE eCurlErrConnectFailed;
|
|
@@ -110,6 +111,7 @@ VALUE eCurlErrSSLCRLBadfile;
|
|
|
110
111
|
VALUE eCurlErrSSLIssuerError;
|
|
111
112
|
|
|
112
113
|
/* multi errors */
|
|
114
|
+
VALUE mCurlErrFailedInit;
|
|
113
115
|
VALUE mCurlErrCallMultiPerform;
|
|
114
116
|
VALUE mCurlErrBadHandle;
|
|
115
117
|
VALUE mCurlErrBadEasyHandle;
|
|
@@ -140,9 +142,15 @@ VALUE rb_curl_easy_error(CURLcode code) {
|
|
|
140
142
|
case CURLE_URL_MALFORMAT: /* 3 */
|
|
141
143
|
exclz = eCurlErrMalformedURL;
|
|
142
144
|
break;
|
|
145
|
+
#ifdef HAVE_CURLE_NOT_BUILT_IN
|
|
146
|
+
case CURLE_NOT_BUILT_IN: /* 4 - [was obsoleted in August 2007 for 7.17.0, reused in April 2011 for 7.21.5] */
|
|
147
|
+
exclz = eCurlErrNotBuiltIn;
|
|
148
|
+
break;
|
|
149
|
+
#else
|
|
143
150
|
case CURLE_URL_MALFORMAT_USER: /* 4 (NOT USED) */
|
|
144
151
|
exclz = eCurlErrMalformedURLUser;
|
|
145
152
|
break;
|
|
153
|
+
#endif
|
|
146
154
|
case CURLE_COULDNT_RESOLVE_PROXY: /* 5 */
|
|
147
155
|
exclz = eCurlErrProxyResolution;
|
|
148
156
|
break;
|
|
@@ -278,9 +286,11 @@ VALUE rb_curl_easy_error(CURLcode code) {
|
|
|
278
286
|
case CURLE_TELNET_OPTION_SYNTAX: /* 49 - Malformed telnet option */
|
|
279
287
|
exclz = eCurlErrTelnetBadOptionSyntax;
|
|
280
288
|
break;
|
|
289
|
+
#ifdef HAVE_CURLE_OBSOLETE
|
|
281
290
|
case CURLE_OBSOLETE: /* 50 - NOT USED */
|
|
282
291
|
exclz = eCurlErrObsolete;
|
|
283
292
|
break;
|
|
293
|
+
#endif
|
|
284
294
|
case CURLE_SSL_PEER_CERTIFICATE: /* 51 - peer's certificate wasn't ok */
|
|
285
295
|
exclz = eCurlErrSSLPeerCertificate;
|
|
286
296
|
break;
|
|
@@ -449,7 +459,8 @@ VALUE rb_curl_easy_error(CURLcode code) {
|
|
|
449
459
|
/* rb_raise an approriate exception for the supplied CURLcode */
|
|
450
460
|
void raise_curl_easy_error_exception(CURLcode code) {
|
|
451
461
|
VALUE obj = rb_curl_easy_error(code);
|
|
452
|
-
|
|
462
|
+
VALUE exmsg = rb_ary_entry(obj,1);
|
|
463
|
+
rb_raise(rb_ary_entry(obj,0), "CURLError: %s", StringValueCStr(exmsg));
|
|
453
464
|
}
|
|
454
465
|
VALUE rb_curl_multi_error(CURLMcode code) {
|
|
455
466
|
VALUE exclz;
|
|
@@ -499,7 +510,8 @@ VALUE rb_curl_multi_error(CURLMcode code) {
|
|
|
499
510
|
}
|
|
500
511
|
void raise_curl_multi_error_exception(CURLMcode code) {
|
|
501
512
|
VALUE obj = rb_curl_multi_error(code);
|
|
502
|
-
|
|
513
|
+
VALUE exmsg = rb_ary_entry(obj,1);
|
|
514
|
+
rb_raise(rb_ary_entry(obj,0), "CURLError: %s", StringValueCStr(exmsg));
|
|
503
515
|
}
|
|
504
516
|
|
|
505
517
|
void init_curb_errors() {
|
|
@@ -517,6 +529,7 @@ void init_curb_errors() {
|
|
|
517
529
|
eCurlErrUnsupportedProtocol = rb_define_class_under(mCurlErr, "UnsupportedProtocolError", eCurlErrError);
|
|
518
530
|
eCurlErrFailedInit = rb_define_class_under(mCurlErr, "FailedInitError", eCurlErrError);
|
|
519
531
|
eCurlErrMalformedURL = rb_define_class_under(mCurlErr, "MalformedURLError", eCurlErrError);
|
|
532
|
+
eCurlErrNotBuiltIn = rb_define_class_under(mCurlErr, "NotBuiltInError", eCurlErrError);
|
|
520
533
|
eCurlErrMalformedURLUser = rb_define_class_under(mCurlErr, "MalformedURLUserError", eCurlErrError);
|
|
521
534
|
eCurlErrProxyResolution = rb_define_class_under(mCurlErr, "ProxyResolutionError", eCurlErrError);
|
|
522
535
|
eCurlErrHostResolution = rb_define_class_under(mCurlErr, "HostResolutionError", eCurlErrError);
|
|
@@ -605,6 +618,7 @@ void init_curb_errors() {
|
|
|
605
618
|
eCurlErrSSLShutdownFailed = rb_define_class_under(mCurlErr, "SSLShutdownFailed", eCurlErrError);
|
|
606
619
|
eCurlErrSSH = rb_define_class_under(mCurlErr, "SSH", eCurlErrError);
|
|
607
620
|
|
|
621
|
+
mCurlErrFailedInit = rb_define_class_under(mCurlErr, "MultiInitError", eCurlErrError);
|
|
608
622
|
mCurlErrCallMultiPerform = rb_define_class_under(mCurlErr, "MultiPerform", eCurlErrError);
|
|
609
623
|
mCurlErrBadHandle = rb_define_class_under(mCurlErr, "MultiBadHandle", eCurlErrError);
|
|
610
624
|
mCurlErrBadEasyHandle = rb_define_class_under(mCurlErr, "MultiBadEasyHandle", eCurlErrError);
|
data/ext/curb_errors.h
CHANGED
|
@@ -108,6 +108,7 @@ extern VALUE eCurlErrSSLCRLBadfile;
|
|
|
108
108
|
extern VALUE eCurlErrSSLIssuerError;
|
|
109
109
|
|
|
110
110
|
/* multi errors */
|
|
111
|
+
extern VALUE mCurlErrFailedInit;
|
|
111
112
|
extern VALUE mCurlErrCallMultiPerform;
|
|
112
113
|
extern VALUE mCurlErrBadHandle;
|
|
113
114
|
extern VALUE mCurlErrBadEasyHandle;
|
data/ext/curb_macros.h
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
#ifndef __CURB_MACROS_H
|
|
9
9
|
#define __CURB_MACROS_H
|
|
10
10
|
|
|
11
|
+
#define rb_easy_sym(sym) ID2SYM(rb_intern(sym))
|
|
11
12
|
#define rb_easy_hkey(key) ID2SYM(rb_intern(key))
|
|
12
13
|
#define rb_easy_set(key,val) rb_hash_aset(rbce->opts, rb_easy_hkey(key) , val)
|
|
13
14
|
#define rb_easy_get(key) rb_hash_aref(rbce->opts, rb_easy_hkey(key))
|
|
@@ -152,4 +153,7 @@
|
|
|
152
153
|
return INT2FIX(ptr->attr); \
|
|
153
154
|
}
|
|
154
155
|
|
|
156
|
+
#define CURB_DEFINE(name) \
|
|
157
|
+
rb_define_const(mCurl, #name, INT2FIX(name))
|
|
158
|
+
|
|
155
159
|
#endif
|
data/ext/curb_multi.c
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* Licensed under the Ruby License. See LICENSE for details.
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
|
-
|
|
7
6
|
#include "curb_config.h"
|
|
8
7
|
#ifdef HAVE_RUBY19_ST_H
|
|
9
8
|
#include <ruby.h>
|
|
@@ -19,6 +18,11 @@
|
|
|
19
18
|
|
|
20
19
|
#include <errno.h>
|
|
21
20
|
|
|
21
|
+
#ifdef _WIN32
|
|
22
|
+
// for O_RDWR and O_BINARY
|
|
23
|
+
#include <fcntl.h>
|
|
24
|
+
#endif
|
|
25
|
+
|
|
22
26
|
extern VALUE mCurl;
|
|
23
27
|
static VALUE idCall;
|
|
24
28
|
|
|
@@ -28,16 +32,18 @@ static VALUE idCall;
|
|
|
28
32
|
|
|
29
33
|
VALUE cCurlMulti;
|
|
30
34
|
|
|
35
|
+
static long cCurlMutiDefaulttimeout = 100; /* milliseconds */
|
|
36
|
+
|
|
31
37
|
static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy);
|
|
32
38
|
static void rb_curl_multi_read_info(VALUE self, CURLM *mptr);
|
|
39
|
+
static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running);
|
|
33
40
|
|
|
34
|
-
static
|
|
35
|
-
|
|
36
|
-
}
|
|
41
|
+
static VALUE callback_exception(VALUE unused) {
|
|
42
|
+
return Qfalse;
|
|
43
|
+
}
|
|
37
44
|
|
|
38
45
|
static void curl_multi_mark(ruby_curl_multi *rbcm) {
|
|
39
46
|
rb_gc_mark(rbcm->requests);
|
|
40
|
-
rb_hash_foreach( rbcm->requests, (int (*)())rb_curl_multi_mark_all_easy, (VALUE)rbcm );
|
|
41
47
|
}
|
|
42
48
|
|
|
43
49
|
static void curl_multi_flush_easy(VALUE key, VALUE easy, ruby_curl_multi *rbcm) {
|
|
@@ -81,6 +87,9 @@ VALUE ruby_curl_multi_new(VALUE klass) {
|
|
|
81
87
|
ruby_curl_multi *rbcm = ALLOC(ruby_curl_multi);
|
|
82
88
|
|
|
83
89
|
rbcm->handle = curl_multi_init();
|
|
90
|
+
if (!rbcm->handle) {
|
|
91
|
+
rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
|
|
92
|
+
}
|
|
84
93
|
|
|
85
94
|
rbcm->requests = rb_hash_new();
|
|
86
95
|
|
|
@@ -92,6 +101,30 @@ VALUE ruby_curl_multi_new(VALUE klass) {
|
|
|
92
101
|
return new_curlm;
|
|
93
102
|
}
|
|
94
103
|
|
|
104
|
+
/*
|
|
105
|
+
* call-seq:
|
|
106
|
+
* Curl::Multi.default_timeout = 4 => 4
|
|
107
|
+
*
|
|
108
|
+
* Set the global default time out for all Curl::Multi Handles. This value is used
|
|
109
|
+
* when libcurl cannot determine a timeout value when calling curl_multi_timeout.
|
|
110
|
+
*
|
|
111
|
+
*/
|
|
112
|
+
VALUE ruby_curl_multi_set_default_timeout(VALUE klass, VALUE timeout) {
|
|
113
|
+
cCurlMutiDefaulttimeout = FIX2LONG(timeout);
|
|
114
|
+
return timeout;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/*
|
|
118
|
+
* call-seq:
|
|
119
|
+
* Curl::Multi.default_timeout = 4 => 4
|
|
120
|
+
*
|
|
121
|
+
* Get the global default time out for all Curl::Multi Handles.
|
|
122
|
+
*
|
|
123
|
+
*/
|
|
124
|
+
VALUE ruby_curl_multi_get_default_timeout(VALUE klass) {
|
|
125
|
+
return INT2FIX(cCurlMutiDefaulttimeout);
|
|
126
|
+
}
|
|
127
|
+
|
|
95
128
|
/* Hash#foreach callback for ruby_curl_multi_requests */
|
|
96
129
|
static int ruby_curl_multi_requests_callback(VALUE key, VALUE value, VALUE result_array) {
|
|
97
130
|
rb_ary_push(result_array, value);
|
|
@@ -194,14 +227,14 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
|
|
194
227
|
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
|
195
228
|
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
|
196
229
|
|
|
230
|
+
/* setup the easy handle */
|
|
231
|
+
ruby_curl_easy_setup( rbce );
|
|
232
|
+
|
|
197
233
|
mcode = curl_multi_add_handle(rbcm->handle, rbce->curl);
|
|
198
234
|
if (mcode != CURLM_CALL_MULTI_PERFORM && mcode != CURLM_OK) {
|
|
199
235
|
raise_curl_multi_error_exception(mcode);
|
|
200
236
|
}
|
|
201
237
|
|
|
202
|
-
/* setup the easy handle */
|
|
203
|
-
ruby_curl_easy_setup( rbce, &(rbce->curl_headers) );
|
|
204
|
-
|
|
205
238
|
rbcm->active++;
|
|
206
239
|
|
|
207
240
|
/* Increase the running count, so that the perform loop keeps running.
|
|
@@ -253,7 +286,7 @@ static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
|
|
|
253
286
|
|
|
254
287
|
rbcm->active--;
|
|
255
288
|
|
|
256
|
-
ruby_curl_easy_cleanup( easy, rbce
|
|
289
|
+
ruby_curl_easy_cleanup( easy, rbce );
|
|
257
290
|
|
|
258
291
|
// active should equal INT2FIX(RHASH(rbcm->requests)->tbl->num_entries)
|
|
259
292
|
r = rb_hash_delete( rbcm->requests, easy );
|
|
@@ -286,44 +319,96 @@ static VALUE ruby_curl_multi_cancel(VALUE self) {
|
|
|
286
319
|
return self;
|
|
287
320
|
}
|
|
288
321
|
|
|
322
|
+
// on_success, on_failure, on_complete
|
|
323
|
+
static VALUE call_status_handler1(VALUE ary) {
|
|
324
|
+
return rb_funcall(rb_ary_entry(ary, 0), idCall, 1, rb_ary_entry(ary, 1));
|
|
325
|
+
}
|
|
326
|
+
static VALUE call_status_handler2(VALUE ary) {
|
|
327
|
+
return rb_funcall(rb_ary_entry(ary, 0), idCall, 2, rb_ary_entry(ary, 1), rb_ary_entry(ary, 2));
|
|
328
|
+
}
|
|
329
|
+
|
|
289
330
|
static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int result) {
|
|
290
331
|
|
|
291
332
|
long response_code = -1;
|
|
292
333
|
VALUE easy;
|
|
293
334
|
ruby_curl_easy *rbce = NULL;
|
|
335
|
+
VALUE callargs, val = Qtrue;
|
|
294
336
|
|
|
295
337
|
CURLcode ecode = curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, (char**)&easy);
|
|
296
338
|
|
|
297
339
|
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
|
298
340
|
|
|
299
|
-
if (ecode != 0) {
|
|
300
|
-
raise_curl_easy_error_exception(ecode);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
341
|
rbce->last_result = result; /* save the last easy result code */
|
|
304
342
|
|
|
305
343
|
ruby_curl_multi_remove( self, easy );
|
|
306
344
|
|
|
345
|
+
/* after running a request cleanup the headers, these are set before each request */
|
|
346
|
+
if (rbce->curl_headers) {
|
|
347
|
+
curl_slist_free_all(rbce->curl_headers);
|
|
348
|
+
rbce->curl_headers = NULL;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (ecode != 0) {
|
|
352
|
+
raise_curl_easy_error_exception(ecode);
|
|
353
|
+
}
|
|
354
|
+
|
|
307
355
|
if (!rb_easy_nil("complete_proc")) {
|
|
308
|
-
|
|
356
|
+
callargs = rb_ary_new3(2, rb_easy_get("complete_proc"), easy);
|
|
357
|
+
rbce->callback_active = 1;
|
|
358
|
+
val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
|
|
359
|
+
rbce->callback_active = 0;
|
|
360
|
+
//rb_funcall( rb_easy_get("complete_proc"), idCall, 1, easy );
|
|
309
361
|
}
|
|
310
362
|
|
|
311
363
|
curl_easy_getinfo(rbce->curl, CURLINFO_RESPONSE_CODE, &response_code);
|
|
312
364
|
|
|
313
365
|
if (result != 0) {
|
|
314
366
|
if (!rb_easy_nil("failure_proc")) {
|
|
315
|
-
|
|
367
|
+
callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
|
|
368
|
+
rbce->callback_active = 1;
|
|
369
|
+
val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
|
|
370
|
+
rbce->callback_active = 0;
|
|
371
|
+
//rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
|
|
316
372
|
}
|
|
317
373
|
}
|
|
318
374
|
else if (!rb_easy_nil("success_proc") &&
|
|
319
375
|
((response_code >= 200 && response_code < 300) || response_code == 0)) {
|
|
320
376
|
/* NOTE: we allow response_code == 0, in the case of non http requests e.g. reading from disk */
|
|
321
|
-
|
|
377
|
+
callargs = rb_ary_new3(2, rb_easy_get("success_proc"), easy);
|
|
378
|
+
rbce->callback_active = 1;
|
|
379
|
+
val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
|
|
380
|
+
rbce->callback_active = 0;
|
|
381
|
+
//rb_funcall( rb_easy_get("success_proc"), idCall, 1, easy );
|
|
382
|
+
}
|
|
383
|
+
else if (!rb_easy_nil("redirect_proc") &&
|
|
384
|
+
(response_code >= 300 && response_code < 400)) {
|
|
385
|
+
rbce->callback_active = 1;
|
|
386
|
+
callargs = rb_ary_new3(3, rb_easy_get("redirect_proc"), easy, rb_curl_easy_error(result));
|
|
387
|
+
rbce->callback_active = 0;
|
|
388
|
+
val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
|
|
389
|
+
}
|
|
390
|
+
else if (!rb_easy_nil("missing_proc") &&
|
|
391
|
+
(response_code >= 400 && response_code < 500)) {
|
|
392
|
+
rbce->callback_active = 1;
|
|
393
|
+
callargs = rb_ary_new3(3, rb_easy_get("missing_proc"), easy, rb_curl_easy_error(result));
|
|
394
|
+
rbce->callback_active = 0;
|
|
395
|
+
val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
|
|
322
396
|
}
|
|
323
397
|
else if (!rb_easy_nil("failure_proc") &&
|
|
324
|
-
(response_code >=
|
|
325
|
-
|
|
398
|
+
(response_code >= 500 && response_code <= 999)) {
|
|
399
|
+
callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
|
|
400
|
+
rbce->callback_active = 1;
|
|
401
|
+
val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
|
|
402
|
+
rbce->callback_active = 0;
|
|
403
|
+
//rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
|
|
326
404
|
}
|
|
405
|
+
|
|
406
|
+
if (val == Qfalse) {
|
|
407
|
+
rb_warn("uncaught exception from callback");
|
|
408
|
+
// exception was raised?
|
|
409
|
+
//fprintf(stderr, "exception raised from callback\n");
|
|
410
|
+
}
|
|
411
|
+
|
|
327
412
|
}
|
|
328
413
|
|
|
329
414
|
static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
|
|
@@ -354,9 +439,51 @@ static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_runnin
|
|
|
354
439
|
if (mcode != CURLM_OK) {
|
|
355
440
|
raise_curl_multi_error_exception(mcode);
|
|
356
441
|
}
|
|
442
|
+
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
#ifdef _WIN32
|
|
446
|
+
void create_crt_fd(fd_set *os_set, fd_set *crt_set)
|
|
447
|
+
{
|
|
448
|
+
int i;
|
|
449
|
+
crt_set->fd_count = os_set->fd_count;
|
|
450
|
+
for (i = 0; i < os_set->fd_count; i++) {
|
|
451
|
+
WSAPROTOCOL_INFO wsa_pi;
|
|
452
|
+
// dupicate the SOCKET
|
|
453
|
+
int r = WSADuplicateSocket(os_set->fd_array[i], GetCurrentProcessId(), &wsa_pi);
|
|
454
|
+
SOCKET s = WSASocket(wsa_pi.iAddressFamily, wsa_pi.iSocketType, wsa_pi.iProtocol, &wsa_pi, 0, 0);
|
|
455
|
+
// create the CRT fd so ruby can get back to the SOCKET
|
|
456
|
+
int fd = _open_osfhandle(s, O_RDWR|O_BINARY);
|
|
457
|
+
os_set->fd_array[i] = s;
|
|
458
|
+
crt_set->fd_array[i] = fd;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
357
461
|
|
|
358
|
-
|
|
462
|
+
void cleanup_crt_fd(fd_set *os_set, fd_set *crt_set)
|
|
463
|
+
{
|
|
464
|
+
int i;
|
|
465
|
+
for (i = 0; i < os_set->fd_count; i++) {
|
|
466
|
+
// cleanup the CRT fd
|
|
467
|
+
_close(crt_set->fd_array[i]);
|
|
468
|
+
// cleanup the duplicated SOCKET
|
|
469
|
+
closesocket(os_set->fd_array[i]);
|
|
470
|
+
}
|
|
359
471
|
}
|
|
472
|
+
#endif
|
|
473
|
+
|
|
474
|
+
#ifdef HAVE_RB_THREAD_BLOCKING_REGION
|
|
475
|
+
struct _select_set {
|
|
476
|
+
int maxfd;
|
|
477
|
+
fd_set *fdread, *fdwrite, *fdexcep;
|
|
478
|
+
struct timeval *tv;
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
static VALUE curb_select(void *args) {
|
|
482
|
+
struct _select_set* set = args;
|
|
483
|
+
int rc = select(set->maxfd, set->fdread, set->fdwrite, set->fdexcep, set->tv);
|
|
484
|
+
return INT2FIX(rc);
|
|
485
|
+
}
|
|
486
|
+
#endif
|
|
360
487
|
|
|
361
488
|
/*
|
|
362
489
|
* call-seq:
|
|
@@ -378,75 +505,108 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
378
505
|
ruby_curl_multi *rbcm;
|
|
379
506
|
int maxfd, rc;
|
|
380
507
|
fd_set fdread, fdwrite, fdexcep;
|
|
381
|
-
|
|
508
|
+
#ifdef _WIN32
|
|
509
|
+
fd_set crt_fdread, crt_fdwrite, crt_fdexcep;
|
|
510
|
+
#endif
|
|
382
511
|
long timeout_milliseconds;
|
|
383
512
|
struct timeval tv = {0, 0};
|
|
384
513
|
VALUE block = Qnil;
|
|
514
|
+
#ifdef HAVE_RB_THREAD_BLOCKING_REGION
|
|
515
|
+
struct _select_set fdset_args;
|
|
516
|
+
#endif
|
|
385
517
|
|
|
386
518
|
rb_scan_args(argc, argv, "0&", &block);
|
|
387
519
|
|
|
388
520
|
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
|
389
521
|
|
|
522
|
+
timeout_milliseconds = cCurlMutiDefaulttimeout;
|
|
523
|
+
|
|
390
524
|
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
|
525
|
+
rb_curl_multi_read_info( self, rbcm->handle );
|
|
526
|
+
if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
|
|
527
|
+
|
|
528
|
+
do {
|
|
529
|
+
while (rbcm->running) {
|
|
530
|
+
|
|
531
|
+
#ifdef HAVE_CURL_MULTI_TIMEOUT
|
|
532
|
+
/* get the curl suggested time out */
|
|
533
|
+
mcode = curl_multi_timeout(rbcm->handle, &timeout_milliseconds);
|
|
534
|
+
if (mcode != CURLM_OK) {
|
|
535
|
+
raise_curl_multi_error_exception(mcode);
|
|
536
|
+
}
|
|
537
|
+
#else
|
|
538
|
+
/* libcurl doesn't have a timeout method defined, initialize to -1 we'll pick up the default later */
|
|
539
|
+
timeout_milliseconds = -1;
|
|
540
|
+
#endif
|
|
541
|
+
|
|
542
|
+
if (timeout_milliseconds == 0) { /* no delay */
|
|
543
|
+
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
|
544
|
+
rb_curl_multi_read_info( self, rbcm->handle );
|
|
545
|
+
if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
391
548
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
549
|
+
if (timeout_milliseconds < 0 || timeout_milliseconds > cCurlMutiDefaulttimeout) {
|
|
550
|
+
timeout_milliseconds = cCurlMutiDefaulttimeout; /* libcurl doesn't know how long to wait, use a default timeout */
|
|
551
|
+
/* or buggy versions libcurl sometimes reports huge timeouts... let's cap it */
|
|
552
|
+
}
|
|
396
553
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
if (mcode != CURLM_OK) {
|
|
400
|
-
raise_curl_multi_error_exception(mcode);
|
|
401
|
-
}
|
|
554
|
+
tv.tv_sec = 0; /* never wait longer than 1 second */
|
|
555
|
+
tv.tv_usec = (int)(timeout_milliseconds * 1000); /* XXX: int is the right type for OSX, what about linux? */
|
|
402
556
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
557
|
+
FD_ZERO(&fdread);
|
|
558
|
+
FD_ZERO(&fdwrite);
|
|
559
|
+
FD_ZERO(&fdexcep);
|
|
560
|
+
|
|
561
|
+
/* load the fd sets from the multi handle */
|
|
562
|
+
mcode = curl_multi_fdset(rbcm->handle, &fdread, &fdwrite, &fdexcep, &maxfd);
|
|
563
|
+
if (mcode != CURLM_OK) {
|
|
564
|
+
raise_curl_multi_error_exception(mcode);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
#ifdef _WIN32
|
|
568
|
+
create_crt_fd(&fdread, &crt_fdread);
|
|
569
|
+
create_crt_fd(&fdwrite, &crt_fdwrite);
|
|
570
|
+
create_crt_fd(&fdexcep, &crt_fdexcep);
|
|
571
|
+
#endif
|
|
572
|
+
|
|
573
|
+
#ifdef HAVE_RB_THREAD_BLOCKING_REGION
|
|
574
|
+
fdset_args.maxfd = maxfd+1;
|
|
575
|
+
fdset_args.fdread = &fdread;
|
|
576
|
+
fdset_args.fdwrite = &fdwrite;
|
|
577
|
+
fdset_args.fdexcep = &fdexcep;
|
|
578
|
+
fdset_args.tv = &tv;
|
|
579
|
+
rc = rb_thread_blocking_region(curb_select, &fdset_args, RUBY_UBF_IO, 0);
|
|
409
580
|
#else
|
|
410
|
-
|
|
411
|
-
timeout_milliseconds = -1;
|
|
581
|
+
rc = rb_thread_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
|
|
412
582
|
#endif
|
|
413
|
-
//printf("libcurl says wait: %ld ms or %ld s\n", timeout_milliseconds, timeout_milliseconds/1000);
|
|
414
583
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
else if(timeout_milliseconds < 0) {
|
|
420
|
-
timeout_milliseconds = 500; /* wait half a second, libcurl doesn't know how long to wait */
|
|
421
|
-
}
|
|
422
|
-
#ifdef __APPLE_CC__
|
|
423
|
-
if(timeout_milliseconds > 1000) {
|
|
424
|
-
timeout_milliseconds = 1000; /* apple libcurl sometimes reports huge timeouts... let's cap it */
|
|
425
|
-
}
|
|
584
|
+
#ifdef _WIN32
|
|
585
|
+
cleanup_crt_fd(&fdread, &crt_fdread);
|
|
586
|
+
cleanup_crt_fd(&fdwrite, &crt_fdwrite);
|
|
587
|
+
cleanup_crt_fd(&fdexcep, &crt_fdexcep);
|
|
426
588
|
#endif
|
|
427
589
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
rb_funcall(block, rb_intern("call"), 1, self);
|
|
590
|
+
switch(rc) {
|
|
591
|
+
case -1:
|
|
592
|
+
if(errno != EINTR) {
|
|
593
|
+
rb_raise(rb_eRuntimeError, "select(): %s", strerror(errno));
|
|
594
|
+
break;
|
|
595
|
+
}
|
|
596
|
+
case 0: /* timeout */
|
|
597
|
+
default: /* action */
|
|
598
|
+
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
|
599
|
+
rb_curl_multi_read_info( self, rbcm->handle );
|
|
600
|
+
if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
|
|
601
|
+
break;
|
|
439
602
|
}
|
|
440
|
-
// if (rb_block_given_p()) {
|
|
441
|
-
// rb_yield(self);
|
|
442
|
-
// }
|
|
443
|
-
default:
|
|
444
|
-
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
|
445
|
-
break;
|
|
446
603
|
}
|
|
447
604
|
|
|
448
|
-
}
|
|
605
|
+
} while( rbcm->running );
|
|
449
606
|
|
|
607
|
+
rb_curl_multi_read_info( self, rbcm->handle );
|
|
608
|
+
if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
|
|
609
|
+
|
|
450
610
|
return Qtrue;
|
|
451
611
|
}
|
|
452
612
|
|
|
@@ -458,6 +618,8 @@ void init_curb_multi() {
|
|
|
458
618
|
|
|
459
619
|
/* Class methods */
|
|
460
620
|
rb_define_singleton_method(cCurlMulti, "new", ruby_curl_multi_new, 0);
|
|
621
|
+
rb_define_singleton_method(cCurlMulti, "default_timeout=", ruby_curl_multi_set_default_timeout, 1);
|
|
622
|
+
rb_define_singleton_method(cCurlMulti, "default_timeout", ruby_curl_multi_get_default_timeout, 0);
|
|
461
623
|
|
|
462
624
|
/* "Attributes" */
|
|
463
625
|
rb_define_method(cCurlMulti, "requests", ruby_curl_multi_requests, 0);
|