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