curb 0.9.2 → 0.9.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/README.markdown +45 -19
- data/Rakefile +26 -10
- data/ext/banned.h +32 -0
- data/ext/curb.c +45 -3
- data/ext/curb.h +12 -4
- data/ext/curb_easy.c +421 -61
- data/ext/curb_easy.h +4 -0
- data/ext/curb_errors.c +86 -0
- data/ext/curb_multi.c +131 -208
- data/ext/curb_multi.h +0 -1
- data/ext/extconf.rb +35 -0
- data/lib/curb.rb +1 -0
- data/lib/curl/easy.rb +10 -4
- data/lib/curl/multi.rb +52 -13
- data/lib/curl.rb +9 -9
- data/tests/bug_issue277.rb +32 -0
- data/tests/helper.rb +96 -12
- data/tests/tc_curl.rb +31 -1
- data/tests/tc_curl_download.rb +3 -3
- data/tests/tc_curl_easy.rb +155 -42
- data/tests/tc_curl_easy_resolve.rb +16 -0
- data/tests/tc_curl_maxfilesize.rb +12 -0
- data/tests/tc_curl_multi.rb +77 -15
- data/tests/tc_curl_postfield.rb +29 -29
- metadata +31 -25
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,45 +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
|
-
|
84
|
-
}
|
85
|
-
free(rbcm);
|
61
|
+
rbcm->active = 0;
|
62
|
+
rbcm->running = 0;
|
86
63
|
}
|
87
64
|
|
88
65
|
/*
|
@@ -92,23 +69,17 @@ void curl_multi_free(ruby_curl_multi *rbcm) {
|
|
92
69
|
* Create a new Curl::Multi instance
|
93
70
|
*/
|
94
71
|
VALUE ruby_curl_multi_new(VALUE klass) {
|
95
|
-
VALUE new_curlm;
|
96
|
-
|
97
72
|
ruby_curl_multi *rbcm = ALLOC(ruby_curl_multi);
|
98
73
|
|
99
|
-
rbcm
|
100
|
-
if (!rbcm->handle) {
|
101
|
-
rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
|
102
|
-
}
|
103
|
-
|
104
|
-
rbcm->requests = rb_hash_new();
|
105
|
-
|
106
|
-
rbcm->active = 0;
|
107
|
-
rbcm->running = 0;
|
74
|
+
ruby_curl_multi_init(rbcm);
|
108
75
|
|
109
|
-
|
110
|
-
|
111
|
-
|
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);
|
112
83
|
}
|
113
84
|
|
114
85
|
/*
|
@@ -135,52 +106,36 @@ VALUE ruby_curl_multi_get_default_timeout(VALUE klass) {
|
|
135
106
|
return LONG2NUM(cCurlMutiDefaulttimeout);
|
136
107
|
}
|
137
108
|
|
138
|
-
/*
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
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;
|
143
120
|
}
|
144
121
|
|
145
122
|
/*
|
146
123
|
* call-seq:
|
147
|
-
*
|
148
|
-
*
|
149
|
-
*
|
124
|
+
* Curl::Multi.autoclose => true|false
|
125
|
+
*
|
126
|
+
* Get the global default autoclose setting for all Curl::Multi Handles.
|
127
|
+
*
|
150
128
|
*/
|
151
|
-
|
152
|
-
|
153
|
-
VALUE result_array;
|
154
|
-
|
155
|
-
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
156
|
-
|
157
|
-
result_array = rb_ary_new();
|
158
|
-
|
159
|
-
/* iterate over the requests hash, and stuff references into the array. */
|
160
|
-
rb_hash_foreach(rbcm->requests, ruby_curl_multi_requests_callback, result_array);
|
161
|
-
|
162
|
-
return result_array;
|
129
|
+
VALUE ruby_curl_multi_get_autoclose(VALUE klass) {
|
130
|
+
return cCurlMutiAutoClose == 1 ? Qtrue : Qfalse;
|
163
131
|
}
|
164
132
|
|
165
133
|
/*
|
166
134
|
* call-seq:
|
167
|
-
* multi.
|
135
|
+
* multi.requests => [#<Curl::Easy...>, ...]
|
168
136
|
*
|
169
|
-
* Returns
|
170
|
-
* true when multi.requests.length == 0.
|
137
|
+
* Returns an array containing all the active requests on this Curl::Multi object.
|
171
138
|
*/
|
172
|
-
static VALUE ruby_curl_multi_idle(VALUE self) {
|
173
|
-
ruby_curl_multi *rbcm;
|
174
|
-
|
175
|
-
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
176
|
-
|
177
|
-
if (RHASH_SIZE(rbcm->requests) == 0) {
|
178
|
-
return Qtrue;
|
179
|
-
} else {
|
180
|
-
return Qfalse;
|
181
|
-
}
|
182
|
-
}
|
183
|
-
|
184
139
|
/*
|
185
140
|
* call-seq:
|
186
141
|
* multi = Curl::Multi.new
|
@@ -194,13 +149,6 @@ static VALUE ruby_curl_multi_max_connects(VALUE self, VALUE count) {
|
|
194
149
|
|
195
150
|
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
196
151
|
|
197
|
-
if (!rbcm->handle) {
|
198
|
-
rbcm->handle = curl_multi_init();
|
199
|
-
if (!rbcm->handle) {
|
200
|
-
rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
|
201
|
-
}
|
202
|
-
}
|
203
|
-
|
204
152
|
curl_multi_setopt(rbcm->handle, CURLMOPT_MAXCONNECTS, NUM2LONG(count));
|
205
153
|
#endif
|
206
154
|
|
@@ -234,14 +182,6 @@ static VALUE ruby_curl_multi_pipeline(VALUE self, VALUE method) {
|
|
234
182
|
}
|
235
183
|
|
236
184
|
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
237
|
-
|
238
|
-
if (!rbcm->handle) {
|
239
|
-
rbcm->handle = curl_multi_init();
|
240
|
-
if (!rbcm->handle) {
|
241
|
-
rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
|
242
|
-
}
|
243
|
-
}
|
244
|
-
|
245
185
|
curl_multi_setopt(rbcm->handle, CURLMOPT_PIPELINING, value);
|
246
186
|
#endif
|
247
187
|
return method == Qtrue ? 1 : 0;
|
@@ -258,29 +198,15 @@ static VALUE ruby_curl_multi_pipeline(VALUE self, VALUE method) {
|
|
258
198
|
*/
|
259
199
|
VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
260
200
|
CURLMcode mcode;
|
261
|
-
VALUE r;
|
262
201
|
ruby_curl_easy *rbce;
|
263
202
|
ruby_curl_multi *rbcm;
|
264
203
|
|
265
204
|
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
266
205
|
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
267
206
|
|
268
|
-
// check if this curl handle has been added before adding again
|
269
|
-
r = rb_hash_aref(rbcm->requests, LONG2NUM((long)rbce->curl));
|
270
|
-
if ( r != Qnil ) {
|
271
|
-
return Qnil;
|
272
|
-
}
|
273
|
-
|
274
207
|
/* setup the easy handle */
|
275
208
|
ruby_curl_easy_setup( rbce );
|
276
209
|
|
277
|
-
if (!rbcm->handle) {
|
278
|
-
rbcm->handle = curl_multi_init();
|
279
|
-
if (!rbcm->handle) {
|
280
|
-
rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
|
281
|
-
}
|
282
|
-
}
|
283
|
-
|
284
210
|
mcode = curl_multi_add_handle(rbcm->handle, rbce->curl);
|
285
211
|
if (mcode != CURLM_CALL_MULTI_PERFORM && mcode != CURLM_OK) {
|
286
212
|
raise_curl_multi_error_exception(mcode);
|
@@ -295,8 +221,6 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
|
295
221
|
/* track a reference to associated multi handle */
|
296
222
|
rbce->multi = self;
|
297
223
|
|
298
|
-
rb_hash_aset( rbcm->requests, LONG2NUM((long)rbce->curl), easy );
|
299
|
-
|
300
224
|
return self;
|
301
225
|
}
|
302
226
|
|
@@ -314,35 +238,20 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
|
314
238
|
*
|
315
239
|
* Will raise an exception if the easy handle is not found
|
316
240
|
*/
|
317
|
-
VALUE ruby_curl_multi_remove(VALUE self, VALUE
|
241
|
+
VALUE ruby_curl_multi_remove(VALUE self, VALUE rb_easy_handle) {
|
318
242
|
ruby_curl_multi *rbcm;
|
319
243
|
|
320
244
|
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
321
245
|
|
322
|
-
|
323
|
-
rbcm->handle = curl_multi_init();
|
324
|
-
if (!rbcm->handle) {
|
325
|
-
rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
|
326
|
-
}
|
327
|
-
}
|
328
|
-
|
329
|
-
rb_curl_multi_remove(rbcm,easy);
|
246
|
+
rb_curl_multi_remove(rbcm, rb_easy_handle);
|
330
247
|
|
331
248
|
return self;
|
332
249
|
}
|
333
250
|
static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
|
334
251
|
CURLMcode result;
|
335
252
|
ruby_curl_easy *rbce;
|
336
|
-
VALUE r;
|
337
253
|
|
338
254
|
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
339
|
-
|
340
|
-
// check if this curl handle has been added before removing
|
341
|
-
r = rb_hash_aref(rbcm->requests, LONG2NUM((long)rbce->curl));
|
342
|
-
if ( r == Qnil ) {
|
343
|
-
return;
|
344
|
-
}
|
345
|
-
|
346
255
|
result = curl_multi_remove_handle(rbcm->handle, rbce->curl);
|
347
256
|
if (result != 0) {
|
348
257
|
raise_curl_multi_error_exception(result);
|
@@ -351,43 +260,6 @@ static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
|
|
351
260
|
rbcm->active--;
|
352
261
|
|
353
262
|
ruby_curl_easy_cleanup( easy, rbce );
|
354
|
-
|
355
|
-
// active should equal LONG2NUM(RHASH(rbcm->requests)->tbl->num_entries)
|
356
|
-
r = rb_hash_delete( rbcm->requests, LONG2NUM((long)rbce->curl) );
|
357
|
-
if( r != easy || r == Qnil ) {
|
358
|
-
rb_warn("Possibly lost track of Curl::Easy VALUE, it may not be reclaimed by GC");
|
359
|
-
}
|
360
|
-
}
|
361
|
-
|
362
|
-
/* Hash#foreach callback for ruby_curl_multi_cancel */
|
363
|
-
static int ruby_curl_multi_cancel_callback(VALUE key, VALUE value, ruby_curl_multi *rbcm) {
|
364
|
-
rb_curl_multi_remove(rbcm, value);
|
365
|
-
|
366
|
-
return ST_CONTINUE;
|
367
|
-
}
|
368
|
-
|
369
|
-
/*
|
370
|
-
* call-seq:
|
371
|
-
* multi.cancel!
|
372
|
-
*
|
373
|
-
* Cancels all requests currently being made on this Curl::Multi handle.
|
374
|
-
*/
|
375
|
-
static VALUE ruby_curl_multi_cancel(VALUE self) {
|
376
|
-
ruby_curl_multi *rbcm;
|
377
|
-
|
378
|
-
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
379
|
-
|
380
|
-
if (!rbcm->handle) {
|
381
|
-
rbcm->handle = curl_multi_init();
|
382
|
-
if (!rbcm->handle) {
|
383
|
-
rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
|
384
|
-
}
|
385
|
-
}
|
386
|
-
|
387
|
-
rb_hash_foreach( rbcm->requests, ruby_curl_multi_cancel_callback, (VALUE)rbcm );
|
388
|
-
|
389
|
-
/* for chaining */
|
390
|
-
return self;
|
391
263
|
}
|
392
264
|
|
393
265
|
// on_success, on_failure, on_complete
|
@@ -399,7 +271,6 @@ static VALUE call_status_handler2(VALUE ary) {
|
|
399
271
|
}
|
400
272
|
|
401
273
|
static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int result) {
|
402
|
-
|
403
274
|
long response_code = -1;
|
404
275
|
VALUE easy;
|
405
276
|
ruby_curl_easy *rbce = NULL;
|
@@ -411,7 +282,8 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
|
|
411
282
|
|
412
283
|
rbce->last_result = result; /* save the last easy result code */
|
413
284
|
|
414
|
-
|
285
|
+
// remove the easy handle from multi on completion so it can be reused again
|
286
|
+
rb_funcall(self, rb_intern("remove"), 1, easy);
|
415
287
|
|
416
288
|
/* after running a request cleanup the headers, these are set before each request */
|
417
289
|
if (rbce->curl_headers) {
|
@@ -474,26 +346,30 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
|
|
474
346
|
//rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
|
475
347
|
}
|
476
348
|
|
477
|
-
if (val == Qfalse) {
|
478
|
-
rb_warn("uncaught exception from callback");
|
479
|
-
}
|
480
|
-
|
481
349
|
}
|
482
350
|
|
483
351
|
static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
|
484
|
-
int msgs_left
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
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);
|
497
373
|
}
|
498
374
|
}
|
499
375
|
|
@@ -501,15 +377,32 @@ static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
|
|
501
377
|
static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running) {
|
502
378
|
CURLMcode mcode;
|
503
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
|
+
*/
|
504
387
|
do {
|
505
388
|
mcode = curl_multi_perform(multi_handle, still_running);
|
506
389
|
} while (mcode == CURLM_CALL_MULTI_PERFORM);
|
507
390
|
|
391
|
+
/*
|
392
|
+
* Nothing more to do, check if an error occured in the loop above and raise an exception if necessary.
|
393
|
+
*/
|
508
394
|
|
509
395
|
if (mcode != CURLM_OK) {
|
510
396
|
raise_curl_multi_error_exception(mcode);
|
511
397
|
}
|
512
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
|
+
*/
|
513
406
|
}
|
514
407
|
|
515
408
|
#ifdef _WIN32
|
@@ -580,6 +473,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
580
473
|
#endif
|
581
474
|
long timeout_milliseconds;
|
582
475
|
struct timeval tv = {0, 0};
|
476
|
+
struct timeval tv_100ms = {0, 100000};
|
583
477
|
VALUE block = Qnil;
|
584
478
|
#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
|
585
479
|
struct _select_set fdset_args;
|
@@ -591,10 +485,23 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
591
485
|
|
592
486
|
timeout_milliseconds = cCurlMutiDefaulttimeout;
|
593
487
|
|
488
|
+
// Run curl_multi_perform for the first time to get the ball rolling
|
594
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.
|
595
496
|
rb_curl_multi_read_info( self, rbcm->handle );
|
596
|
-
|
597
|
-
|
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
|
+
|
598
505
|
do {
|
599
506
|
while (rbcm->running) {
|
600
507
|
|
@@ -634,18 +541,30 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
634
541
|
raise_curl_multi_error_exception(mcode);
|
635
542
|
}
|
636
543
|
|
544
|
+
if (maxfd == -1) {
|
545
|
+
/* libcurl recommends sleeping for 100ms */
|
546
|
+
rb_thread_wait_for(tv_100ms);
|
547
|
+
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
548
|
+
rb_curl_multi_read_info( self, rbcm->handle );
|
549
|
+
if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
|
550
|
+
continue;
|
551
|
+
}
|
552
|
+
|
637
553
|
#ifdef _WIN32
|
638
554
|
create_crt_fd(&fdread, &crt_fdread);
|
639
555
|
create_crt_fd(&fdwrite, &crt_fdwrite);
|
640
556
|
create_crt_fd(&fdexcep, &crt_fdexcep);
|
641
557
|
#endif
|
642
558
|
|
643
|
-
|
559
|
+
|
560
|
+
#if (defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL))
|
644
561
|
fdset_args.maxfd = maxfd+1;
|
645
562
|
fdset_args.fdread = &fdread;
|
646
563
|
fdset_args.fdwrite = &fdwrite;
|
647
564
|
fdset_args.fdexcep = &fdexcep;
|
648
565
|
fdset_args.tv = &tv;
|
566
|
+
#endif
|
567
|
+
|
649
568
|
#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
|
650
569
|
rc = (int)(VALUE) rb_thread_call_without_gvl((void *(*)(void *))curb_select, &fdset_args, RUBY_UBF_IO, 0);
|
651
570
|
#elif HAVE_RB_THREAD_BLOCKING_REGION
|
@@ -656,8 +575,6 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
656
575
|
rc = rb_thread_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
|
657
576
|
#endif
|
658
577
|
|
659
|
-
#endif
|
660
|
-
|
661
578
|
#ifdef _WIN32
|
662
579
|
cleanup_crt_fd(&fdread, &crt_fdread);
|
663
580
|
cleanup_crt_fd(&fdwrite, &crt_fdwrite);
|
@@ -683,38 +600,44 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
683
600
|
|
684
601
|
rb_curl_multi_read_info( self, rbcm->handle );
|
685
602
|
if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
|
686
|
-
|
687
|
-
|
688
|
-
VALUE hash = rbcm->requests;
|
689
|
-
if (!NIL_P(hash) && rb_type(hash) == T_HASH && RHASH_SIZE(hash) > 0) {
|
690
|
-
rb_hash_foreach(hash, curl_multi_flush_easy, (VALUE)rbcm);
|
603
|
+
if (cCurlMutiAutoClose == 1) {
|
604
|
+
rb_funcall(self, rb_intern("close"), 0);
|
691
605
|
}
|
692
|
-
curl_multi_cleanup(rbcm->handle);
|
693
|
-
rbcm->handle = NULL;
|
694
|
-
|
695
606
|
return Qtrue;
|
696
607
|
}
|
697
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
|
+
|
698
625
|
/* =================== INIT LIB =====================*/
|
699
626
|
void init_curb_multi() {
|
700
627
|
idCall = rb_intern("call");
|
701
|
-
|
702
628
|
cCurlMulti = rb_define_class_under(mCurl, "Multi", rb_cObject);
|
703
629
|
|
704
630
|
/* Class methods */
|
705
631
|
rb_define_singleton_method(cCurlMulti, "new", ruby_curl_multi_new, 0);
|
706
632
|
rb_define_singleton_method(cCurlMulti, "default_timeout=", ruby_curl_multi_set_default_timeout, 1);
|
707
633
|
rb_define_singleton_method(cCurlMulti, "default_timeout", ruby_curl_multi_get_default_timeout, 0);
|
708
|
-
|
709
|
-
|
710
|
-
rb_define_method(cCurlMulti, "requests", ruby_curl_multi_requests, 0);
|
711
|
-
rb_define_method(cCurlMulti, "idle?", ruby_curl_multi_idle, 0);
|
712
|
-
|
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);
|
713
636
|
/* Instance methods */
|
714
637
|
rb_define_method(cCurlMulti, "max_connects=", ruby_curl_multi_max_connects, 1);
|
715
638
|
rb_define_method(cCurlMulti, "pipeline=", ruby_curl_multi_pipeline, 1);
|
716
|
-
rb_define_method(cCurlMulti, "
|
717
|
-
rb_define_method(cCurlMulti, "
|
718
|
-
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);
|
719
641
|
rb_define_method(cCurlMulti, "perform", ruby_curl_multi_perform, -1);
|
642
|
+
rb_define_method(cCurlMulti, "_close", ruby_curl_multi_close, 0);
|
720
643
|
}
|