curb 0.9.3 → 1.0.2
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 +78 -22
- data/Rakefile +26 -20
- data/ext/banned.h +32 -0
- data/ext/curb.c +190 -14
- data/ext/curb.h +18 -5
- data/ext/curb_easy.c +499 -84
- data/ext/curb_easy.h +7 -0
- data/ext/curb_errors.c +86 -0
- data/ext/curb_macros.h +6 -0
- data/ext/curb_multi.c +172 -233
- data/ext/curb_multi.h +0 -1
- data/ext/curb_postfield.c +9 -7
- data/ext/curb_upload.c +1 -0
- data/ext/extconf.rb +76 -6
- data/lib/curb.rb +1 -0
- data/lib/curl/easy.rb +16 -9
- data/lib/curl/multi.rb +52 -13
- data/lib/curl.rb +20 -12
- data/tests/bug_crash_on_debug.rb +14 -4
- data/tests/bug_crash_on_progress.rb +4 -2
- data/tests/bug_follow_redirect_288.rb +91 -0
- data/tests/bug_issue277.rb +32 -0
- data/tests/bug_raise_on_callback.rb +40 -0
- data/tests/helper.rb +96 -13
- data/tests/tc_curl.rb +31 -1
- data/tests/tc_curl_download.rb +3 -3
- data/tests/tc_curl_easy.rb +167 -46
- data/tests/tc_curl_easy_resolve.rb +16 -0
- data/tests/tc_curl_maxfilesize.rb +12 -0
- data/tests/tc_curl_multi.rb +123 -18
- data/tests/tc_curl_postfield.rb +29 -29
- data/tests/tc_curl_protocols.rb +37 -0
- data/tests/timeout.rb +21 -5
- metadata +20 -8
data/ext/curb_multi.c
CHANGED
@@ -37,78 +37,45 @@ 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);
|
43
44
|
static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running);
|
44
45
|
|
45
|
-
static VALUE callback_exception(VALUE unused) {
|
46
|
-
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
|
-
}
|
70
|
-
|
71
46
|
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); */
|
47
|
+
curl_multi_cleanup(rbcm->handle);
|
48
|
+
free(rbcm);
|
49
|
+
}
|
78
50
|
|
79
|
-
|
51
|
+
static void ruby_curl_multi_init(ruby_curl_multi *rbcm) {
|
52
|
+
rbcm->handle = curl_multi_init();
|
53
|
+
if (!rbcm->handle) {
|
54
|
+
rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
|
80
55
|
}
|
81
56
|
|
82
|
-
|
83
|
-
|
84
|
-
}
|
85
|
-
free(rbcm);
|
57
|
+
rbcm->active = 0;
|
58
|
+
rbcm->running = 0;
|
86
59
|
}
|
87
60
|
|
88
61
|
/*
|
89
62
|
* call-seq:
|
90
|
-
* Curl::Multi.new =>
|
63
|
+
* Curl::Multi.new => #<Curl::Easy...>
|
91
64
|
*
|
92
65
|
* Create a new Curl::Multi instance
|
93
66
|
*/
|
94
67
|
VALUE ruby_curl_multi_new(VALUE klass) {
|
95
|
-
VALUE new_curlm;
|
96
|
-
|
97
68
|
ruby_curl_multi *rbcm = ALLOC(ruby_curl_multi);
|
98
69
|
|
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;
|
70
|
+
ruby_curl_multi_init(rbcm);
|
108
71
|
|
109
|
-
|
110
|
-
|
111
|
-
|
72
|
+
/*
|
73
|
+
* The mark routine will be called by the garbage collector during its ``mark'' phase.
|
74
|
+
* If your structure references other Ruby objects, then your mark function needs to
|
75
|
+
* identify these objects using rb_gc_mark(value). If the structure doesn't reference
|
76
|
+
* other Ruby objects, you can simply pass 0 as a function pointer.
|
77
|
+
*/
|
78
|
+
return Data_Wrap_Struct(klass, 0, curl_multi_free, rbcm);
|
112
79
|
}
|
113
80
|
|
114
81
|
/*
|
@@ -135,52 +102,36 @@ VALUE ruby_curl_multi_get_default_timeout(VALUE klass) {
|
|
135
102
|
return LONG2NUM(cCurlMutiDefaulttimeout);
|
136
103
|
}
|
137
104
|
|
138
|
-
/*
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
105
|
+
/*
|
106
|
+
* call-seq:
|
107
|
+
* Curl::Multi.autoclose = true => true
|
108
|
+
*
|
109
|
+
* Automatically close open connections after each request. Otherwise, the connection will remain open
|
110
|
+
* for reuse until the next GC
|
111
|
+
*
|
112
|
+
*/
|
113
|
+
VALUE ruby_curl_multi_set_autoclose(VALUE klass, VALUE onoff) {
|
114
|
+
cCurlMutiAutoClose = ((onoff == Qtrue) ? 1 : 0);
|
115
|
+
return onoff;
|
143
116
|
}
|
144
117
|
|
145
118
|
/*
|
146
119
|
* call-seq:
|
147
|
-
*
|
148
|
-
*
|
149
|
-
*
|
120
|
+
* Curl::Multi.autoclose => true|false
|
121
|
+
*
|
122
|
+
* Get the global default autoclose setting for all Curl::Multi Handles.
|
123
|
+
*
|
150
124
|
*/
|
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;
|
125
|
+
VALUE ruby_curl_multi_get_autoclose(VALUE klass) {
|
126
|
+
return cCurlMutiAutoClose == 1 ? Qtrue : Qfalse;
|
163
127
|
}
|
164
128
|
|
165
129
|
/*
|
166
130
|
* call-seq:
|
167
|
-
* multi.
|
131
|
+
* multi.requests => [#<Curl::Easy...>, ...]
|
168
132
|
*
|
169
|
-
* Returns
|
170
|
-
* true when multi.requests.length == 0.
|
133
|
+
* Returns an array containing all the active requests on this Curl::Multi object.
|
171
134
|
*/
|
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
135
|
/*
|
185
136
|
* call-seq:
|
186
137
|
* multi = Curl::Multi.new
|
@@ -194,13 +145,6 @@ static VALUE ruby_curl_multi_max_connects(VALUE self, VALUE count) {
|
|
194
145
|
|
195
146
|
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
196
147
|
|
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
148
|
curl_multi_setopt(rbcm->handle, CURLMOPT_MAXCONNECTS, NUM2LONG(count));
|
205
149
|
#endif
|
206
150
|
|
@@ -234,14 +178,6 @@ static VALUE ruby_curl_multi_pipeline(VALUE self, VALUE method) {
|
|
234
178
|
}
|
235
179
|
|
236
180
|
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
181
|
curl_multi_setopt(rbcm->handle, CURLMOPT_PIPELINING, value);
|
246
182
|
#endif
|
247
183
|
return method == Qtrue ? 1 : 0;
|
@@ -258,29 +194,15 @@ static VALUE ruby_curl_multi_pipeline(VALUE self, VALUE method) {
|
|
258
194
|
*/
|
259
195
|
VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
260
196
|
CURLMcode mcode;
|
261
|
-
VALUE r;
|
262
197
|
ruby_curl_easy *rbce;
|
263
198
|
ruby_curl_multi *rbcm;
|
264
199
|
|
265
200
|
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
266
201
|
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
267
202
|
|
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
203
|
/* setup the easy handle */
|
275
204
|
ruby_curl_easy_setup( rbce );
|
276
205
|
|
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
206
|
mcode = curl_multi_add_handle(rbcm->handle, rbce->curl);
|
285
207
|
if (mcode != CURLM_CALL_MULTI_PERFORM && mcode != CURLM_OK) {
|
286
208
|
raise_curl_multi_error_exception(mcode);
|
@@ -295,8 +217,6 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
|
295
217
|
/* track a reference to associated multi handle */
|
296
218
|
rbce->multi = self;
|
297
219
|
|
298
|
-
rb_hash_aset( rbcm->requests, LONG2NUM((long)rbce->curl), easy );
|
299
|
-
|
300
220
|
return self;
|
301
221
|
}
|
302
222
|
|
@@ -314,35 +234,20 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
|
314
234
|
*
|
315
235
|
* Will raise an exception if the easy handle is not found
|
316
236
|
*/
|
317
|
-
VALUE ruby_curl_multi_remove(VALUE self, VALUE
|
237
|
+
VALUE ruby_curl_multi_remove(VALUE self, VALUE rb_easy_handle) {
|
318
238
|
ruby_curl_multi *rbcm;
|
319
239
|
|
320
240
|
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
321
241
|
|
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);
|
242
|
+
rb_curl_multi_remove(rbcm, rb_easy_handle);
|
330
243
|
|
331
244
|
return self;
|
332
245
|
}
|
333
246
|
static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
|
334
247
|
CURLMcode result;
|
335
248
|
ruby_curl_easy *rbce;
|
336
|
-
VALUE r;
|
337
249
|
|
338
250
|
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
251
|
result = curl_multi_remove_handle(rbcm->handle, rbce->curl);
|
347
252
|
if (result != 0) {
|
348
253
|
raise_curl_multi_error_exception(result);
|
@@ -351,43 +256,6 @@ static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
|
|
351
256
|
rbcm->active--;
|
352
257
|
|
353
258
|
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
259
|
}
|
392
260
|
|
393
261
|
// on_success, on_failure, on_complete
|
@@ -399,11 +267,10 @@ static VALUE call_status_handler2(VALUE ary) {
|
|
399
267
|
}
|
400
268
|
|
401
269
|
static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int result) {
|
402
|
-
|
403
270
|
long response_code = -1;
|
404
271
|
VALUE easy;
|
405
272
|
ruby_curl_easy *rbce = NULL;
|
406
|
-
VALUE callargs
|
273
|
+
VALUE callargs;
|
407
274
|
|
408
275
|
CURLcode ecode = curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, (char**)&easy);
|
409
276
|
|
@@ -411,7 +278,8 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
|
|
411
278
|
|
412
279
|
rbce->last_result = result; /* save the last easy result code */
|
413
280
|
|
414
|
-
|
281
|
+
// remove the easy handle from multi on completion so it can be reused again
|
282
|
+
rb_funcall(self, rb_intern("remove"), 1, easy);
|
415
283
|
|
416
284
|
/* after running a request cleanup the headers, these are set before each request */
|
417
285
|
if (rbce->curl_headers) {
|
@@ -423,77 +291,99 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
|
|
423
291
|
raise_curl_easy_error_exception(ecode);
|
424
292
|
}
|
425
293
|
|
294
|
+
int status;
|
295
|
+
|
426
296
|
if (!rb_easy_nil("complete_proc")) {
|
427
297
|
callargs = rb_ary_new3(2, rb_easy_get("complete_proc"), easy);
|
428
298
|
rbce->callback_active = 1;
|
429
|
-
|
299
|
+
rb_protect(call_status_handler1, callargs, &status);
|
430
300
|
rbce->callback_active = 0;
|
431
|
-
|
301
|
+
if (status) {
|
302
|
+
CURB_RB_CALLBACK_RAISE("complete")
|
303
|
+
}
|
432
304
|
}
|
433
305
|
|
306
|
+
#ifdef HAVE_CURLINFO_RESPONSE_CODE
|
434
307
|
curl_easy_getinfo(rbce->curl, CURLINFO_RESPONSE_CODE, &response_code);
|
308
|
+
#else
|
309
|
+
// old libcurl
|
310
|
+
curl_easy_getinfo(rbce->curl, CURLINFO_HTTP_CODE, &response_code);
|
311
|
+
#endif
|
312
|
+
long redirect_count;
|
313
|
+
curl_easy_getinfo(rbce->curl, CURLINFO_REDIRECT_COUNT, &redirect_count);
|
435
314
|
|
436
315
|
if (result != 0) {
|
437
316
|
if (!rb_easy_nil("failure_proc")) {
|
438
317
|
callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
|
439
318
|
rbce->callback_active = 1;
|
440
|
-
|
319
|
+
rb_protect(call_status_handler2, callargs, &status);
|
441
320
|
rbce->callback_active = 0;
|
442
|
-
|
321
|
+
if (status) {
|
322
|
+
CURB_RB_CALLBACK_RAISE("failure")
|
323
|
+
}
|
443
324
|
}
|
444
|
-
}
|
445
|
-
else if (!rb_easy_nil("success_proc") &&
|
325
|
+
} else if (!rb_easy_nil("success_proc") &&
|
446
326
|
((response_code >= 200 && response_code < 300) || response_code == 0)) {
|
447
327
|
/* NOTE: we allow response_code == 0, in the case of non http requests e.g. reading from disk */
|
448
328
|
callargs = rb_ary_new3(2, rb_easy_get("success_proc"), easy);
|
449
329
|
rbce->callback_active = 1;
|
450
|
-
|
330
|
+
rb_protect(call_status_handler1, callargs, &status);
|
451
331
|
rbce->callback_active = 0;
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
332
|
+
if (status) {
|
333
|
+
CURB_RB_CALLBACK_RAISE("success")
|
334
|
+
}
|
335
|
+
} else if (!rb_easy_nil("redirect_proc") && ((response_code >= 300 && response_code < 400) || redirect_count > 0) ) {
|
456
336
|
rbce->callback_active = 1;
|
457
337
|
callargs = rb_ary_new3(3, rb_easy_get("redirect_proc"), easy, rb_curl_easy_error(result));
|
458
338
|
rbce->callback_active = 0;
|
459
|
-
|
460
|
-
|
461
|
-
|
339
|
+
rb_protect(call_status_handler2, callargs, &status);
|
340
|
+
if (status) {
|
341
|
+
CURB_RB_CALLBACK_RAISE("redirect")
|
342
|
+
}
|
343
|
+
} else if (!rb_easy_nil("missing_proc") &&
|
462
344
|
(response_code >= 400 && response_code < 500)) {
|
463
345
|
rbce->callback_active = 1;
|
464
346
|
callargs = rb_ary_new3(3, rb_easy_get("missing_proc"), easy, rb_curl_easy_error(result));
|
465
347
|
rbce->callback_active = 0;
|
466
|
-
|
467
|
-
|
468
|
-
|
348
|
+
rb_protect(call_status_handler2, callargs, &status);
|
349
|
+
if (status) {
|
350
|
+
CURB_RB_CALLBACK_RAISE("missing")
|
351
|
+
}
|
352
|
+
} else if (!rb_easy_nil("failure_proc") &&
|
469
353
|
(response_code >= 500 && response_code <= 999)) {
|
470
354
|
callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
|
471
355
|
rbce->callback_active = 1;
|
472
|
-
|
356
|
+
rb_protect(call_status_handler2, callargs, &status);
|
473
357
|
rbce->callback_active = 0;
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
if (val == Qfalse) {
|
478
|
-
rb_warn("uncaught exception from callback");
|
358
|
+
if (status) {
|
359
|
+
CURB_RB_CALLBACK_RAISE("failure")
|
360
|
+
}
|
479
361
|
}
|
480
362
|
|
481
363
|
}
|
482
364
|
|
483
365
|
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
|
-
|
366
|
+
int msgs_left;
|
367
|
+
|
368
|
+
CURLcode c_easy_result;
|
369
|
+
CURLMsg *c_multi_result; // for picking up messages with the transfer status
|
370
|
+
CURL *c_easy_handle;
|
371
|
+
|
372
|
+
/* Check for finished easy handles and remove from the multi handle.
|
373
|
+
* curl_multi_info_read will query for messages from individual handles.
|
374
|
+
*
|
375
|
+
* The messages fetched with this function are removed from the curl internal
|
376
|
+
* queue and when there are no messages left it will return NULL (and break
|
377
|
+
* the loop effectively).
|
378
|
+
*/
|
379
|
+
while ((c_multi_result = curl_multi_info_read(multi_handle, &msgs_left))) {
|
380
|
+
// A message is there, but we really care only about transfer completetion.
|
381
|
+
if (c_multi_result->msg != CURLMSG_DONE) continue;
|
382
|
+
|
383
|
+
c_easy_handle = c_multi_result->easy_handle;
|
384
|
+
c_easy_result = c_multi_result->data.result; /* return code for transfer */
|
385
|
+
|
386
|
+
rb_curl_mutli_handle_complete(self, c_easy_handle, c_easy_result);
|
497
387
|
}
|
498
388
|
}
|
499
389
|
|
@@ -501,15 +391,32 @@ static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
|
|
501
391
|
static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running) {
|
502
392
|
CURLMcode mcode;
|
503
393
|
|
394
|
+
/*
|
395
|
+
* curl_multi_perform will return CURLM_CALL_MULTI_PERFORM only when it wants to be called again immediately.
|
396
|
+
* When things are fine and there is nothing immediate it wants done, it'll return CURLM_OK.
|
397
|
+
*
|
398
|
+
* It will perform all pending actions on all added easy handles attached to this multi handle. We will loop
|
399
|
+
* here as long as mcode is CURLM_CALL_MULTIPERFORM.
|
400
|
+
*/
|
504
401
|
do {
|
505
402
|
mcode = curl_multi_perform(multi_handle, still_running);
|
506
403
|
} while (mcode == CURLM_CALL_MULTI_PERFORM);
|
507
404
|
|
405
|
+
/*
|
406
|
+
* Nothing more to do, check if an error occured in the loop above and raise an exception if necessary.
|
407
|
+
*/
|
508
408
|
|
509
409
|
if (mcode != CURLM_OK) {
|
510
410
|
raise_curl_multi_error_exception(mcode);
|
511
411
|
}
|
512
412
|
|
413
|
+
/*
|
414
|
+
* Everything is ok, but this does not mean all the transfers are completed.
|
415
|
+
* There is no data to read or write available for Curl at the moment.
|
416
|
+
*
|
417
|
+
* At this point we can return control to the caller to do something else while
|
418
|
+
* curl is waiting for more actions to queue.
|
419
|
+
*/
|
513
420
|
}
|
514
421
|
|
515
422
|
#ifdef _WIN32
|
@@ -580,6 +487,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
580
487
|
#endif
|
581
488
|
long timeout_milliseconds;
|
582
489
|
struct timeval tv = {0, 0};
|
490
|
+
struct timeval tv_100ms = {0, 100000};
|
583
491
|
VALUE block = Qnil;
|
584
492
|
#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
|
585
493
|
struct _select_set fdset_args;
|
@@ -591,10 +499,23 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
591
499
|
|
592
500
|
timeout_milliseconds = cCurlMutiDefaulttimeout;
|
593
501
|
|
502
|
+
// Run curl_multi_perform for the first time to get the ball rolling
|
594
503
|
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
504
|
+
|
505
|
+
// Check the easy handles for new messages one more time before yielding
|
506
|
+
// control to passed ruby block.
|
507
|
+
//
|
508
|
+
// This call will block until all queued messages are processed and if any
|
509
|
+
// handle completed the transfer we will run the on_complete callback here too.
|
595
510
|
rb_curl_multi_read_info( self, rbcm->handle );
|
596
|
-
|
597
|
-
|
511
|
+
|
512
|
+
// There are no more messages to handle by curl and we can run the ruby block
|
513
|
+
// passed to perform method.
|
514
|
+
// When the block completes curl will resume.
|
515
|
+
if (block != Qnil) {
|
516
|
+
rb_funcall(block, rb_intern("call"), 1, self);
|
517
|
+
}
|
518
|
+
|
598
519
|
do {
|
599
520
|
while (rbcm->running) {
|
600
521
|
|
@@ -634,18 +555,30 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
634
555
|
raise_curl_multi_error_exception(mcode);
|
635
556
|
}
|
636
557
|
|
558
|
+
if (maxfd == -1) {
|
559
|
+
/* libcurl recommends sleeping for 100ms */
|
560
|
+
rb_thread_wait_for(tv_100ms);
|
561
|
+
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
562
|
+
rb_curl_multi_read_info( self, rbcm->handle );
|
563
|
+
if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
|
564
|
+
continue;
|
565
|
+
}
|
566
|
+
|
637
567
|
#ifdef _WIN32
|
638
568
|
create_crt_fd(&fdread, &crt_fdread);
|
639
569
|
create_crt_fd(&fdwrite, &crt_fdwrite);
|
640
570
|
create_crt_fd(&fdexcep, &crt_fdexcep);
|
641
571
|
#endif
|
642
572
|
|
643
|
-
|
573
|
+
|
574
|
+
#if (defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL))
|
644
575
|
fdset_args.maxfd = maxfd+1;
|
645
576
|
fdset_args.fdread = &fdread;
|
646
577
|
fdset_args.fdwrite = &fdwrite;
|
647
578
|
fdset_args.fdexcep = &fdexcep;
|
648
579
|
fdset_args.tv = &tv;
|
580
|
+
#endif
|
581
|
+
|
649
582
|
#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
|
650
583
|
rc = (int)(VALUE) rb_thread_call_without_gvl((void *(*)(void *))curb_select, &fdset_args, RUBY_UBF_IO, 0);
|
651
584
|
#elif HAVE_RB_THREAD_BLOCKING_REGION
|
@@ -656,8 +589,6 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
656
589
|
rc = rb_thread_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
|
657
590
|
#endif
|
658
591
|
|
659
|
-
#endif
|
660
|
-
|
661
592
|
#ifdef _WIN32
|
662
593
|
cleanup_crt_fd(&fdread, &crt_fdread);
|
663
594
|
cleanup_crt_fd(&fdwrite, &crt_fdwrite);
|
@@ -683,38 +614,46 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
683
614
|
|
684
615
|
rb_curl_multi_read_info( self, rbcm->handle );
|
685
616
|
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);
|
617
|
+
if (cCurlMutiAutoClose == 1) {
|
618
|
+
rb_funcall(self, rb_intern("close"), 0);
|
691
619
|
}
|
692
|
-
curl_multi_cleanup(rbcm->handle);
|
693
|
-
rbcm->handle = NULL;
|
694
|
-
|
695
620
|
return Qtrue;
|
696
621
|
}
|
697
622
|
|
623
|
+
/*
|
624
|
+
* call-seq:
|
625
|
+
*
|
626
|
+
* multi.close
|
627
|
+
* after closing the multi handle all connections will be closed and the handle will no longer be usable
|
628
|
+
*
|
629
|
+
*/
|
630
|
+
VALUE ruby_curl_multi_close(VALUE self) {
|
631
|
+
ruby_curl_multi *rbcm;
|
632
|
+
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
633
|
+
curl_multi_cleanup(rbcm->handle);
|
634
|
+
ruby_curl_multi_init(rbcm);
|
635
|
+
return self;
|
636
|
+
}
|
637
|
+
|
638
|
+
|
698
639
|
/* =================== INIT LIB =====================*/
|
699
640
|
void init_curb_multi() {
|
700
641
|
idCall = rb_intern("call");
|
701
|
-
|
702
642
|
cCurlMulti = rb_define_class_under(mCurl, "Multi", rb_cObject);
|
703
643
|
|
644
|
+
rb_undef_alloc_func(cCurlMulti);
|
645
|
+
|
704
646
|
/* Class methods */
|
705
647
|
rb_define_singleton_method(cCurlMulti, "new", ruby_curl_multi_new, 0);
|
706
648
|
rb_define_singleton_method(cCurlMulti, "default_timeout=", ruby_curl_multi_set_default_timeout, 1);
|
707
649
|
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
|
-
|
650
|
+
rb_define_singleton_method(cCurlMulti, "autoclose=", ruby_curl_multi_set_autoclose, 1);
|
651
|
+
rb_define_singleton_method(cCurlMulti, "autoclose", ruby_curl_multi_get_autoclose, 0);
|
713
652
|
/* Instance methods */
|
714
653
|
rb_define_method(cCurlMulti, "max_connects=", ruby_curl_multi_max_connects, 1);
|
715
654
|
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);
|
655
|
+
rb_define_method(cCurlMulti, "_add", ruby_curl_multi_add, 1);
|
656
|
+
rb_define_method(cCurlMulti, "_remove", ruby_curl_multi_remove, 1);
|
719
657
|
rb_define_method(cCurlMulti, "perform", ruby_curl_multi_perform, -1);
|
658
|
+
rb_define_method(cCurlMulti, "_close", ruby_curl_multi_close, 0);
|
720
659
|
}
|