curb 0.9.4 → 0.9.10
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.
- checksums.yaml +5 -5
- data/README.markdown +39 -19
- data/Rakefile +26 -10
- data/ext/curb.c +33 -6
- data/ext/curb.h +11 -4
- data/ext/curb_easy.c +375 -57
- data/ext/curb_easy.h +4 -0
- data/ext/curb_errors.c +86 -0
- data/ext/curb_multi.c +126 -165
- data/ext/curb_multi.h +0 -1
- data/ext/extconf.rb +31 -6
- data/lib/curb.rb +1 -0
- data/lib/curl.rb +7 -2
- data/lib/curl/easy.rb +10 -4
- data/lib/curl/multi.rb +50 -11
- data/tests/bug_issue277.rb +32 -0
- data/tests/helper.rb +89 -7
- data/tests/tc_curl.rb +31 -1
- data/tests/tc_curl_easy.rb +120 -16
- data/tests/tc_curl_easy_resolve.rb +16 -0
- data/tests/tc_curl_maxfilesize.rb +12 -0
- data/tests/tc_curl_multi.rb +68 -5
- metadata +27 -21
data/ext/curb_easy.h
CHANGED
@@ -55,6 +55,8 @@ typedef struct {
|
|
55
55
|
unsigned long ftp_response_timeout;
|
56
56
|
long low_speed_limit;
|
57
57
|
long low_speed_time;
|
58
|
+
long max_send_speed_large;
|
59
|
+
long max_recv_speed_large;
|
58
60
|
long ssl_version;
|
59
61
|
long use_ssl;
|
60
62
|
long ftp_filemethod;
|
@@ -76,7 +78,9 @@ typedef struct {
|
|
76
78
|
char callback_active;
|
77
79
|
|
78
80
|
struct curl_slist *curl_headers;
|
81
|
+
struct curl_slist *curl_proxy_headers;
|
79
82
|
struct curl_slist *curl_ftp_commands;
|
83
|
+
struct curl_slist *curl_resolve;
|
80
84
|
|
81
85
|
int last_result; /* last result code from multi loop */
|
82
86
|
|
data/ext/curb_errors.c
CHANGED
@@ -21,6 +21,7 @@ VALUE eCurlErrFileError;
|
|
21
21
|
VALUE eCurlErrLDAPError;
|
22
22
|
VALUE eCurlErrTelnetError;
|
23
23
|
VALUE eCurlErrTFTPError;
|
24
|
+
VALUE eCurlErrRTSPError;
|
24
25
|
|
25
26
|
/* Specific libcurl errors */
|
26
27
|
VALUE eCurlErrOK; /* not really an error but a return code */
|
@@ -127,6 +128,18 @@ VALUE mCurlErrUnknownOption;
|
|
127
128
|
VALUE eCurlErrInvalidPostField;
|
128
129
|
|
129
130
|
|
131
|
+
/* new errors */
|
132
|
+
VALUE eCurlErrFTPPRETFailed;
|
133
|
+
VALUE eCurlErrRTSPCseqError;
|
134
|
+
VALUE eCurlErrRTSPSessionError;
|
135
|
+
VALUE eCurlErrFTPBadFileList;
|
136
|
+
VALUE eCurlErrChunkFailed;
|
137
|
+
VALUE eCurlErrNoConnectionAvailable;
|
138
|
+
VALUE eCurlErrSSLPinnedPubKeyNotMatch;
|
139
|
+
VALUE eCurlErrSSLInvalidCertStatus;
|
140
|
+
VALUE eCurlErrHTTP2Stream;
|
141
|
+
|
142
|
+
|
130
143
|
VALUE rb_curl_easy_error(CURLcode code) {
|
131
144
|
VALUE exclz;
|
132
145
|
const char *exmsg = NULL;
|
@@ -294,9 +307,11 @@ VALUE rb_curl_easy_error(CURLcode code) {
|
|
294
307
|
exclz = eCurlErrObsolete;
|
295
308
|
break;
|
296
309
|
#endif
|
310
|
+
#if LIBCURL_VERSION_NUM < 0x073e00
|
297
311
|
case CURLE_SSL_PEER_CERTIFICATE: /* 51 - peer's certificate wasn't ok */
|
298
312
|
exclz = eCurlErrSSLPeerCertificate;
|
299
313
|
break;
|
314
|
+
#endif
|
300
315
|
case CURLE_GOT_NOTHING: /* 52 - when this is a specific error */
|
301
316
|
exclz = eCurlErrGotNothing;
|
302
317
|
break;
|
@@ -321,8 +336,13 @@ VALUE rb_curl_easy_error(CURLcode code) {
|
|
321
336
|
case CURLE_SSL_CIPHER: /* 59 - couldn't use specified cipher */
|
322
337
|
exclz = eCurlErrSSLCipher;
|
323
338
|
break;
|
339
|
+
#if LIBCURL_VERSION_NUM >= 0x073e00
|
340
|
+
case CURLE_PEER_FAILED_VERIFICATION: /* 60 - problem with the CA cert (path?) */
|
341
|
+
exclz = eCurlErrSSLPeerCertificate;
|
342
|
+
#else
|
324
343
|
case CURLE_SSL_CACERT: /* 60 - problem with the CA cert (path?) */
|
325
344
|
exclz = eCurlErrSSLCACertificate;
|
345
|
+
#endif
|
326
346
|
break;
|
327
347
|
case CURLE_BAD_CONTENT_ENCODING: /* 61 - Unrecognized transfer encoding */
|
328
348
|
exclz = eCurlErrBadContentEncoding;
|
@@ -445,6 +465,61 @@ VALUE rb_curl_easy_error(CURLcode code) {
|
|
445
465
|
exclz = eCurlErrSSLIssuerError;
|
446
466
|
break;
|
447
467
|
#endif
|
468
|
+
|
469
|
+
#ifdef HAVE_CURLE_FTP_PRET_FAILED
|
470
|
+
case CURLE_FTP_PRET_FAILED: /* 84 */
|
471
|
+
exclz = eCurlErrFTPPRETFailed;
|
472
|
+
break;
|
473
|
+
#endif
|
474
|
+
|
475
|
+
#ifdef HAVE_CURLE_RTSP_CSEQ_ERROR
|
476
|
+
case CURLE_RTSP_CSEQ_ERROR: /* 85 */
|
477
|
+
exclz = eCurlErrRTSPCseqError;
|
478
|
+
break;
|
479
|
+
#endif
|
480
|
+
|
481
|
+
#ifdef HAVE_CURLE_RTSP_SESSION_ERROR
|
482
|
+
case CURLE_RTSP_SESSION_ERROR: /* 86 */
|
483
|
+
exclz = eCurlErrRTSPSessionError;
|
484
|
+
break;
|
485
|
+
#endif
|
486
|
+
|
487
|
+
#ifdef HAVE_CURLE_FTP_BAD_FILE_LIST
|
488
|
+
case CURLE_FTP_BAD_FILE_LIST: /* 87 */
|
489
|
+
exclz = eCurlErrFTPBadFileList;
|
490
|
+
break;
|
491
|
+
#endif
|
492
|
+
|
493
|
+
#ifdef HAVE_CURLE_CHUNK_FAILED
|
494
|
+
case CURLE_CHUNK_FAILED: /* 88 */
|
495
|
+
exclz = eCurlErrChunkFailed;
|
496
|
+
break;
|
497
|
+
#endif
|
498
|
+
|
499
|
+
#ifdef HAVE_CURLE_NO_CONNECTION_AVAILABLE
|
500
|
+
case CURLE_NO_CONNECTION_AVAILABLE: /* 89 */
|
501
|
+
exclz = eCurlErrNoConnectionAvailable;
|
502
|
+
break;
|
503
|
+
#endif
|
504
|
+
|
505
|
+
#ifdef HAVE_CURLE_SSL_PINNEDPUBKEYNOTMATCH
|
506
|
+
case CURLE_SSL_PINNEDPUBKEYNOTMATCH: /* 90 */
|
507
|
+
exclz = eCurlErrSSLPinnedPubKeyNotMatch;
|
508
|
+
break;
|
509
|
+
#endif
|
510
|
+
|
511
|
+
#ifdef HAVE_CURLE_SSL_INVALIDCERTSTATUS
|
512
|
+
case CURLE_SSL_INVALIDCERTSTATUS: /* 91 */
|
513
|
+
exclz = eCurlErrSSLInvalidCertStatus;
|
514
|
+
break;
|
515
|
+
#endif
|
516
|
+
|
517
|
+
#ifdef HAVE_CURLE_HTTP2_STREAM
|
518
|
+
case CURLE_HTTP2_STREAM: /* 92 */
|
519
|
+
exclz = eCurlErrHTTP2Stream;
|
520
|
+
break;
|
521
|
+
#endif
|
522
|
+
|
448
523
|
default:
|
449
524
|
exclz = eCurlErrError;
|
450
525
|
exmsg = "Unknown error result from libcurl";
|
@@ -532,6 +607,7 @@ void init_curb_errors() {
|
|
532
607
|
eCurlErrLDAPError = rb_define_class_under(mCurlErr, "LDAPError", eCurlErrError);
|
533
608
|
eCurlErrTelnetError = rb_define_class_under(mCurlErr, "TelnetError", eCurlErrError);
|
534
609
|
eCurlErrTFTPError = rb_define_class_under(mCurlErr, "TFTPError", eCurlErrError);
|
610
|
+
eCurlErrRTSPError = rb_define_class_under(mCurlErr, "RTSPError", eCurlErrError);
|
535
611
|
|
536
612
|
eCurlErrOK = rb_define_class_under(mCurlErr, "CurlOK", eCurlErrError);
|
537
613
|
eCurlErrUnsupportedProtocol = rb_define_class_under(mCurlErr, "UnsupportedProtocolError", eCurlErrError);
|
@@ -657,4 +733,14 @@ void init_curb_errors() {
|
|
657
733
|
eCurlErrTFTPNoSuchUser = rb_define_class_under(mCurlErr, "NoSuchUserError", eCurlErrTFTPError);
|
658
734
|
|
659
735
|
eCurlErrInvalidPostField = rb_define_class_under(mCurlErr, "InvalidPostFieldError", eCurlErrError);
|
736
|
+
|
737
|
+
eCurlErrFTPPRETFailed = rb_define_class_under(mCurlErr, "PPRETFailedError", eCurlErrFTPError);
|
738
|
+
eCurlErrRTSPCseqError = rb_define_class_under(mCurlErr, "CseqError", eCurlErrRTSPError);
|
739
|
+
eCurlErrRTSPSessionError = rb_define_class_under(mCurlErr, "SessionError", eCurlErrRTSPError);
|
740
|
+
eCurlErrFTPBadFileList = rb_define_class_under(mCurlErr, "BadFileListError", eCurlErrFTPError);
|
741
|
+
eCurlErrChunkFailed = rb_define_class_under(mCurlErr, "ChunkFailedError", eCurlErrError);
|
742
|
+
eCurlErrNoConnectionAvailable = rb_define_class_under(mCurlErr, "NoConnectionAvailableError", eCurlErrError);
|
743
|
+
eCurlErrSSLPinnedPubKeyNotMatch = rb_define_class_under(mCurlErr, "SSLPinnedPubKeyNotMatchError", eCurlErrError);
|
744
|
+
eCurlErrSSLInvalidCertStatus = rb_define_class_under(mCurlErr, "SSLInvalidCertStatusError", eCurlErrError);
|
745
|
+
eCurlErrHTTP2Stream = rb_define_class_under(mCurlErr, "HTTP2StreamError", eCurlErrHTTPError);
|
660
746
|
}
|
data/ext/curb_multi.c
CHANGED
@@ -37,6 +37,7 @@ static VALUE idCall;
|
|
37
37
|
VALUE cCurlMulti;
|
38
38
|
|
39
39
|
static long cCurlMutiDefaulttimeout = 100; /* milliseconds */
|
40
|
+
static char cCurlMutiAutoClose = 0;
|
40
41
|
|
41
42
|
static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy);
|
42
43
|
static void rb_curl_multi_read_info(VALUE self, CURLM *mptr);
|
@@ -44,43 +45,21 @@ static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_runnin
|
|
44
45
|
|
45
46
|
static VALUE callback_exception(VALUE unused) {
|
46
47
|
return Qfalse;
|
47
|
-
}
|
48
|
-
|
49
|
-
static void curl_multi_mark(ruby_curl_multi *rbcm) {
|
50
|
-
if (!NIL_P(rbcm->requests)) rb_gc_mark(rbcm->requests);
|
51
|
-
}
|
52
|
-
|
53
|
-
/* Hash#foreach callback for curl_multi_free */
|
54
|
-
static int curl_multi_flush_easy(VALUE key, VALUE easy, ruby_curl_multi *rbcm) {
|
55
|
-
CURLMcode result;
|
56
|
-
ruby_curl_easy *rbce;
|
57
|
-
|
58
|
-
// sometimes the type is T_ZOMBIE, e.g. after Ruby has received the SIGTERM signal
|
59
|
-
if (rb_type(easy) == T_DATA) {
|
60
|
-
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
61
|
-
|
62
|
-
result = curl_multi_remove_handle(rbcm->handle, rbce->curl);
|
63
|
-
if (result != 0) {
|
64
|
-
raise_curl_multi_error_exception(result);
|
65
|
-
}
|
66
|
-
}
|
67
|
-
|
68
|
-
return ST_DELETE;
|
69
48
|
}
|
70
49
|
|
71
50
|
void curl_multi_free(ruby_curl_multi *rbcm) {
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
rb_hash_foreach(hash, curl_multi_flush_easy, (VALUE)rbcm);
|
77
|
-
/* rb_hash_clear(rbcm->requests); */
|
51
|
+
curl_multi_cleanup(rbcm->handle);
|
52
|
+
free(rbcm);
|
53
|
+
}
|
78
54
|
|
79
|
-
|
55
|
+
static void ruby_curl_multi_init(ruby_curl_multi *rbcm) {
|
56
|
+
rbcm->handle = curl_multi_init();
|
57
|
+
if (!rbcm->handle) {
|
58
|
+
rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
|
80
59
|
}
|
81
60
|
|
82
|
-
|
83
|
-
|
61
|
+
rbcm->active = 0;
|
62
|
+
rbcm->running = 0;
|
84
63
|
}
|
85
64
|
|
86
65
|
/*
|
@@ -90,23 +69,17 @@ void curl_multi_free(ruby_curl_multi *rbcm) {
|
|
90
69
|
* Create a new Curl::Multi instance
|
91
70
|
*/
|
92
71
|
VALUE ruby_curl_multi_new(VALUE klass) {
|
93
|
-
VALUE new_curlm;
|
94
|
-
|
95
72
|
ruby_curl_multi *rbcm = ALLOC(ruby_curl_multi);
|
96
73
|
|
97
|
-
rbcm
|
98
|
-
if (!rbcm->handle) {
|
99
|
-
rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
|
100
|
-
}
|
74
|
+
ruby_curl_multi_init(rbcm);
|
101
75
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
return new_curlm;
|
76
|
+
/*
|
77
|
+
* The mark routine will be called by the garbage collector during its ``mark'' phase.
|
78
|
+
* If your structure references other Ruby objects, then your mark function needs to
|
79
|
+
* identify these objects using rb_gc_mark(value). If the structure doesn't reference
|
80
|
+
* other Ruby objects, you can simply pass 0 as a function pointer.
|
81
|
+
*/
|
82
|
+
return Data_Wrap_Struct(klass, 0, curl_multi_free, rbcm);
|
110
83
|
}
|
111
84
|
|
112
85
|
/*
|
@@ -133,52 +106,36 @@ VALUE ruby_curl_multi_get_default_timeout(VALUE klass) {
|
|
133
106
|
return LONG2NUM(cCurlMutiDefaulttimeout);
|
134
107
|
}
|
135
108
|
|
136
|
-
/*
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
109
|
+
/*
|
110
|
+
* call-seq:
|
111
|
+
* Curl::Multi.autoclose = true => true
|
112
|
+
*
|
113
|
+
* Automatically close open connections after each request. Otherwise, the connection will remain open
|
114
|
+
* for reuse until the next GC
|
115
|
+
*
|
116
|
+
*/
|
117
|
+
VALUE ruby_curl_multi_set_autoclose(VALUE klass, VALUE onoff) {
|
118
|
+
cCurlMutiAutoClose = ((onoff == Qtrue) ? 1 : 0);
|
119
|
+
return onoff;
|
141
120
|
}
|
142
121
|
|
143
122
|
/*
|
144
123
|
* call-seq:
|
145
|
-
*
|
146
|
-
*
|
147
|
-
*
|
124
|
+
* Curl::Multi.autoclose => true|false
|
125
|
+
*
|
126
|
+
* Get the global default autoclose setting for all Curl::Multi Handles.
|
127
|
+
*
|
148
128
|
*/
|
149
|
-
|
150
|
-
|
151
|
-
VALUE result_array;
|
152
|
-
|
153
|
-
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
154
|
-
|
155
|
-
result_array = rb_ary_new();
|
156
|
-
|
157
|
-
/* iterate over the requests hash, and stuff references into the array. */
|
158
|
-
rb_hash_foreach(rbcm->requests, ruby_curl_multi_requests_callback, result_array);
|
159
|
-
|
160
|
-
return result_array;
|
129
|
+
VALUE ruby_curl_multi_get_autoclose(VALUE klass) {
|
130
|
+
return cCurlMutiAutoClose == 1 ? Qtrue : Qfalse;
|
161
131
|
}
|
162
132
|
|
163
133
|
/*
|
164
134
|
* call-seq:
|
165
|
-
* multi.
|
135
|
+
* multi.requests => [#<Curl::Easy...>, ...]
|
166
136
|
*
|
167
|
-
* Returns
|
168
|
-
* true when multi.requests.length == 0.
|
137
|
+
* Returns an array containing all the active requests on this Curl::Multi object.
|
169
138
|
*/
|
170
|
-
static VALUE ruby_curl_multi_idle(VALUE self) {
|
171
|
-
ruby_curl_multi *rbcm;
|
172
|
-
|
173
|
-
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
174
|
-
|
175
|
-
if (RHASH_SIZE(rbcm->requests) == 0) {
|
176
|
-
return Qtrue;
|
177
|
-
} else {
|
178
|
-
return Qfalse;
|
179
|
-
}
|
180
|
-
}
|
181
|
-
|
182
139
|
/*
|
183
140
|
* call-seq:
|
184
141
|
* multi = Curl::Multi.new
|
@@ -241,19 +198,12 @@ static VALUE ruby_curl_multi_pipeline(VALUE self, VALUE method) {
|
|
241
198
|
*/
|
242
199
|
VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
243
200
|
CURLMcode mcode;
|
244
|
-
VALUE r;
|
245
201
|
ruby_curl_easy *rbce;
|
246
202
|
ruby_curl_multi *rbcm;
|
247
203
|
|
248
204
|
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
249
205
|
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
250
206
|
|
251
|
-
// check if this curl handle has been added before adding again
|
252
|
-
r = rb_hash_aref(rbcm->requests, LONG2NUM((long)rbce->curl));
|
253
|
-
if ( r != Qnil ) {
|
254
|
-
return Qnil;
|
255
|
-
}
|
256
|
-
|
257
207
|
/* setup the easy handle */
|
258
208
|
ruby_curl_easy_setup( rbce );
|
259
209
|
|
@@ -271,8 +221,6 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
|
271
221
|
/* track a reference to associated multi handle */
|
272
222
|
rbce->multi = self;
|
273
223
|
|
274
|
-
rb_hash_aset( rbcm->requests, LONG2NUM((long)rbce->curl), easy );
|
275
|
-
|
276
224
|
return self;
|
277
225
|
}
|
278
226
|
|
@@ -290,28 +238,20 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
|
290
238
|
*
|
291
239
|
* Will raise an exception if the easy handle is not found
|
292
240
|
*/
|
293
|
-
VALUE ruby_curl_multi_remove(VALUE self, VALUE
|
241
|
+
VALUE ruby_curl_multi_remove(VALUE self, VALUE rb_easy_handle) {
|
294
242
|
ruby_curl_multi *rbcm;
|
295
243
|
|
296
244
|
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
297
245
|
|
298
|
-
rb_curl_multi_remove(rbcm,
|
246
|
+
rb_curl_multi_remove(rbcm, rb_easy_handle);
|
299
247
|
|
300
248
|
return self;
|
301
249
|
}
|
302
250
|
static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
|
303
251
|
CURLMcode result;
|
304
252
|
ruby_curl_easy *rbce;
|
305
|
-
VALUE r;
|
306
253
|
|
307
254
|
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
308
|
-
|
309
|
-
// check if this curl handle has been added before removing
|
310
|
-
r = rb_hash_aref(rbcm->requests, LONG2NUM((long)rbce->curl));
|
311
|
-
if ( r == Qnil ) {
|
312
|
-
return;
|
313
|
-
}
|
314
|
-
|
315
255
|
result = curl_multi_remove_handle(rbcm->handle, rbce->curl);
|
316
256
|
if (result != 0) {
|
317
257
|
raise_curl_multi_error_exception(result);
|
@@ -320,36 +260,6 @@ static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
|
|
320
260
|
rbcm->active--;
|
321
261
|
|
322
262
|
ruby_curl_easy_cleanup( easy, rbce );
|
323
|
-
|
324
|
-
// active should equal LONG2NUM(RHASH(rbcm->requests)->tbl->num_entries)
|
325
|
-
r = rb_hash_delete( rbcm->requests, LONG2NUM((long)rbce->curl) );
|
326
|
-
if( r != easy || r == Qnil ) {
|
327
|
-
rb_warn("Possibly lost track of Curl::Easy VALUE, it may not be reclaimed by GC");
|
328
|
-
}
|
329
|
-
}
|
330
|
-
|
331
|
-
/* Hash#foreach callback for ruby_curl_multi_cancel */
|
332
|
-
static int ruby_curl_multi_cancel_callback(VALUE key, VALUE value, ruby_curl_multi *rbcm) {
|
333
|
-
rb_curl_multi_remove(rbcm, value);
|
334
|
-
|
335
|
-
return ST_CONTINUE;
|
336
|
-
}
|
337
|
-
|
338
|
-
/*
|
339
|
-
* call-seq:
|
340
|
-
* multi.cancel!
|
341
|
-
*
|
342
|
-
* Cancels all requests currently being made on this Curl::Multi handle.
|
343
|
-
*/
|
344
|
-
static VALUE ruby_curl_multi_cancel(VALUE self) {
|
345
|
-
ruby_curl_multi *rbcm;
|
346
|
-
|
347
|
-
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
348
|
-
|
349
|
-
rb_hash_foreach( rbcm->requests, ruby_curl_multi_cancel_callback, (VALUE)rbcm );
|
350
|
-
|
351
|
-
/* for chaining */
|
352
|
-
return self;
|
353
263
|
}
|
354
264
|
|
355
265
|
// on_success, on_failure, on_complete
|
@@ -361,7 +271,6 @@ static VALUE call_status_handler2(VALUE ary) {
|
|
361
271
|
}
|
362
272
|
|
363
273
|
static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int result) {
|
364
|
-
|
365
274
|
long response_code = -1;
|
366
275
|
VALUE easy;
|
367
276
|
ruby_curl_easy *rbce = NULL;
|
@@ -373,7 +282,8 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
|
|
373
282
|
|
374
283
|
rbce->last_result = result; /* save the last easy result code */
|
375
284
|
|
376
|
-
|
285
|
+
// remove the easy handle from multi on completion so it can be reused again
|
286
|
+
rb_funcall(self, rb_intern("remove"), 1, easy);
|
377
287
|
|
378
288
|
/* after running a request cleanup the headers, these are set before each request */
|
379
289
|
if (rbce->curl_headers) {
|
@@ -436,26 +346,30 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
|
|
436
346
|
//rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
|
437
347
|
}
|
438
348
|
|
439
|
-
if (val == Qfalse) {
|
440
|
-
rb_warn("uncaught exception from callback");
|
441
|
-
}
|
442
|
-
|
443
349
|
}
|
444
350
|
|
445
351
|
static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
|
446
|
-
int msgs_left
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
352
|
+
int msgs_left;
|
353
|
+
|
354
|
+
CURLcode c_easy_result;
|
355
|
+
CURLMsg *c_multi_result; // for picking up messages with the transfer status
|
356
|
+
CURL *c_easy_handle;
|
357
|
+
|
358
|
+
/* Check for finished easy handles and remove from the multi handle.
|
359
|
+
* curl_multi_info_read will query for messages from individual handles.
|
360
|
+
*
|
361
|
+
* The messages fetched with this function are removed from the curl internal
|
362
|
+
* queue and when there are no messages left it will return NULL (and break
|
363
|
+
* the loop effectively).
|
364
|
+
*/
|
365
|
+
while ((c_multi_result = curl_multi_info_read(multi_handle, &msgs_left))) {
|
366
|
+
// A message is there, but we really care only about transfer completetion.
|
367
|
+
if (c_multi_result->msg != CURLMSG_DONE) continue;
|
368
|
+
|
369
|
+
c_easy_handle = c_multi_result->easy_handle;
|
370
|
+
c_easy_result = c_multi_result->data.result; /* return code for transfer */
|
371
|
+
|
372
|
+
rb_curl_mutli_handle_complete(self, c_easy_handle, c_easy_result);
|
459
373
|
}
|
460
374
|
}
|
461
375
|
|
@@ -463,14 +377,32 @@ static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
|
|
463
377
|
static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running) {
|
464
378
|
CURLMcode mcode;
|
465
379
|
|
380
|
+
/*
|
381
|
+
* curl_multi_perform will return CURLM_CALL_MULTI_PERFORM only when it wants to be called again immediately.
|
382
|
+
* When things are fine and there is nothing immediate it wants done, it'll return CURLM_OK.
|
383
|
+
*
|
384
|
+
* It will perform all pending actions on all added easy handles attached to this multi handle. We will loop
|
385
|
+
* here as long as mcode is CURLM_CALL_MULTIPERFORM.
|
386
|
+
*/
|
466
387
|
do {
|
467
388
|
mcode = curl_multi_perform(multi_handle, still_running);
|
468
389
|
} while (mcode == CURLM_CALL_MULTI_PERFORM);
|
469
390
|
|
391
|
+
/*
|
392
|
+
* Nothing more to do, check if an error occured in the loop above and raise an exception if necessary.
|
393
|
+
*/
|
394
|
+
|
470
395
|
if (mcode != CURLM_OK) {
|
471
396
|
raise_curl_multi_error_exception(mcode);
|
472
397
|
}
|
473
|
-
|
398
|
+
|
399
|
+
/*
|
400
|
+
* Everything is ok, but this does not mean all the transfers are completed.
|
401
|
+
* There is no data to read or write available for Curl at the moment.
|
402
|
+
*
|
403
|
+
* At this point we can return control to the caller to do something else while
|
404
|
+
* curl is waiting for more actions to queue.
|
405
|
+
*/
|
474
406
|
}
|
475
407
|
|
476
408
|
#ifdef _WIN32
|
@@ -541,6 +473,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
541
473
|
#endif
|
542
474
|
long timeout_milliseconds;
|
543
475
|
struct timeval tv = {0, 0};
|
476
|
+
struct timeval tv_100ms = {0, 100000};
|
544
477
|
VALUE block = Qnil;
|
545
478
|
#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
|
546
479
|
struct _select_set fdset_args;
|
@@ -552,10 +485,23 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
552
485
|
|
553
486
|
timeout_milliseconds = cCurlMutiDefaulttimeout;
|
554
487
|
|
488
|
+
// Run curl_multi_perform for the first time to get the ball rolling
|
555
489
|
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
490
|
+
|
491
|
+
// Check the easy handles for new messages one more time before yielding
|
492
|
+
// control to passed ruby block.
|
493
|
+
//
|
494
|
+
// This call will block until all queued messages are processed and if any
|
495
|
+
// handle completed the transfer we will run the on_complete callback here too.
|
556
496
|
rb_curl_multi_read_info( self, rbcm->handle );
|
557
|
-
|
558
|
-
|
497
|
+
|
498
|
+
// There are no more messages to handle by curl and we can run the ruby block
|
499
|
+
// passed to perform method.
|
500
|
+
// When the block completes curl will resume.
|
501
|
+
if (block != Qnil) {
|
502
|
+
rb_funcall(block, rb_intern("call"), 1, self);
|
503
|
+
}
|
504
|
+
|
559
505
|
do {
|
560
506
|
while (rbcm->running) {
|
561
507
|
|
@@ -597,7 +543,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
597
543
|
|
598
544
|
if (maxfd == -1) {
|
599
545
|
/* libcurl recommends sleeping for 100ms */
|
600
|
-
rb_thread_wait_for(
|
546
|
+
rb_thread_wait_for(tv_100ms);
|
601
547
|
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
602
548
|
rb_curl_multi_read_info( self, rbcm->handle );
|
603
549
|
if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
|
@@ -610,12 +556,15 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
610
556
|
create_crt_fd(&fdexcep, &crt_fdexcep);
|
611
557
|
#endif
|
612
558
|
|
613
|
-
|
559
|
+
|
560
|
+
#if (defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL))
|
614
561
|
fdset_args.maxfd = maxfd+1;
|
615
562
|
fdset_args.fdread = &fdread;
|
616
563
|
fdset_args.fdwrite = &fdwrite;
|
617
564
|
fdset_args.fdexcep = &fdexcep;
|
618
565
|
fdset_args.tv = &tv;
|
566
|
+
#endif
|
567
|
+
|
619
568
|
#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
|
620
569
|
rc = (int)(VALUE) rb_thread_call_without_gvl((void *(*)(void *))curb_select, &fdset_args, RUBY_UBF_IO, 0);
|
621
570
|
#elif HAVE_RB_THREAD_BLOCKING_REGION
|
@@ -626,8 +575,6 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
626
575
|
rc = rb_thread_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
|
627
576
|
#endif
|
628
577
|
|
629
|
-
#endif
|
630
|
-
|
631
578
|
#ifdef _WIN32
|
632
579
|
cleanup_crt_fd(&fdread, &crt_fdread);
|
633
580
|
cleanup_crt_fd(&fdwrite, &crt_fdwrite);
|
@@ -653,30 +600,44 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
653
600
|
|
654
601
|
rb_curl_multi_read_info( self, rbcm->handle );
|
655
602
|
if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
|
656
|
-
|
603
|
+
if (cCurlMutiAutoClose == 1) {
|
604
|
+
rb_funcall(self, rb_intern("close"), 0);
|
605
|
+
}
|
657
606
|
return Qtrue;
|
658
607
|
}
|
659
608
|
|
609
|
+
/*
|
610
|
+
* call-seq:
|
611
|
+
*
|
612
|
+
* multi.close
|
613
|
+
* after closing the multi handle all connections will be closed and the handle will no longer be usable
|
614
|
+
*
|
615
|
+
*/
|
616
|
+
VALUE ruby_curl_multi_close(VALUE self) {
|
617
|
+
ruby_curl_multi *rbcm;
|
618
|
+
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
619
|
+
curl_multi_cleanup(rbcm->handle);
|
620
|
+
ruby_curl_multi_init(rbcm);
|
621
|
+
return self;
|
622
|
+
}
|
623
|
+
|
624
|
+
|
660
625
|
/* =================== INIT LIB =====================*/
|
661
626
|
void init_curb_multi() {
|
662
627
|
idCall = rb_intern("call");
|
663
|
-
|
664
628
|
cCurlMulti = rb_define_class_under(mCurl, "Multi", rb_cObject);
|
665
629
|
|
666
630
|
/* Class methods */
|
667
631
|
rb_define_singleton_method(cCurlMulti, "new", ruby_curl_multi_new, 0);
|
668
632
|
rb_define_singleton_method(cCurlMulti, "default_timeout=", ruby_curl_multi_set_default_timeout, 1);
|
669
633
|
rb_define_singleton_method(cCurlMulti, "default_timeout", ruby_curl_multi_get_default_timeout, 0);
|
670
|
-
|
671
|
-
|
672
|
-
rb_define_method(cCurlMulti, "requests", ruby_curl_multi_requests, 0);
|
673
|
-
rb_define_method(cCurlMulti, "idle?", ruby_curl_multi_idle, 0);
|
674
|
-
|
634
|
+
rb_define_singleton_method(cCurlMulti, "autoclose=", ruby_curl_multi_set_autoclose, 1);
|
635
|
+
rb_define_singleton_method(cCurlMulti, "autoclose", ruby_curl_multi_get_autoclose, 0);
|
675
636
|
/* Instance methods */
|
676
637
|
rb_define_method(cCurlMulti, "max_connects=", ruby_curl_multi_max_connects, 1);
|
677
638
|
rb_define_method(cCurlMulti, "pipeline=", ruby_curl_multi_pipeline, 1);
|
678
|
-
rb_define_method(cCurlMulti, "
|
679
|
-
rb_define_method(cCurlMulti, "
|
680
|
-
rb_define_method(cCurlMulti, "cancel!", ruby_curl_multi_cancel, 0);
|
639
|
+
rb_define_method(cCurlMulti, "_add", ruby_curl_multi_add, 1);
|
640
|
+
rb_define_method(cCurlMulti, "_remove", ruby_curl_multi_remove, 1);
|
681
641
|
rb_define_method(cCurlMulti, "perform", ruby_curl_multi_perform, -1);
|
642
|
+
rb_define_method(cCurlMulti, "_close", ruby_curl_multi_close, 0);
|
682
643
|
}
|