curb 0.9.3 → 1.0.1
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 +76 -21
- data/Rakefile +33 -20
- data/ext/banned.h +32 -0
- data/ext/curb.c +186 -14
- data/ext/curb.h +18 -5
- data/ext/curb_easy.c +493 -84
- data/ext/curb_easy.h +7 -0
- data/ext/curb_errors.c +86 -0
- data/ext/curb_multi.c +141 -218
- data/ext/curb_multi.h +0 -1
- data/ext/curb_postfield.c +7 -7
- data/ext/extconf.rb +74 -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_issue277.rb +32 -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 +163 -42
- data/tests/tc_curl_easy_resolve.rb +16 -0
- data/tests/tc_curl_maxfilesize.rb +12 -0
- data/tests/tc_curl_multi.rb +118 -15
- data/tests/tc_curl_postfield.rb +29 -29
- data/tests/tc_curl_protocols.rb +37 -0
- data/tests/timeout.rb +21 -5
- metadata +33 -25
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,71 +45,41 @@ 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
|
/*
|
89
66
|
* call-seq:
|
90
|
-
* Curl::Multi.new =>
|
67
|
+
* Curl::Multi.new => #<Curl::Easy...>
|
91
68
|
*
|
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;
|
108
|
-
|
109
|
-
new_curlm = Data_Wrap_Struct(klass, curl_multi_mark, curl_multi_free, rbcm);
|
74
|
+
ruby_curl_multi_init(rbcm);
|
110
75
|
|
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) {
|
@@ -428,10 +300,14 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
|
|
428
300
|
rbce->callback_active = 1;
|
429
301
|
val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
|
430
302
|
rbce->callback_active = 0;
|
431
|
-
//rb_funcall( rb_easy_get("complete_proc"), idCall, 1, easy );
|
432
303
|
}
|
433
304
|
|
305
|
+
#ifdef HAVE_CURLINFO_RESPONSE_CODE
|
434
306
|
curl_easy_getinfo(rbce->curl, CURLINFO_RESPONSE_CODE, &response_code);
|
307
|
+
#else
|
308
|
+
// old libcurl
|
309
|
+
curl_easy_getinfo(rbce->curl, CURLINFO_HTTP_CODE, &response_code);
|
310
|
+
#endif
|
435
311
|
|
436
312
|
if (result != 0) {
|
437
313
|
if (!rb_easy_nil("failure_proc")) {
|
@@ -441,8 +317,7 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
|
|
441
317
|
rbce->callback_active = 0;
|
442
318
|
//rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
|
443
319
|
}
|
444
|
-
}
|
445
|
-
else if (!rb_easy_nil("success_proc") &&
|
320
|
+
} else if (!rb_easy_nil("success_proc") &&
|
446
321
|
((response_code >= 200 && response_code < 300) || response_code == 0)) {
|
447
322
|
/* NOTE: we allow response_code == 0, in the case of non http requests e.g. reading from disk */
|
448
323
|
callargs = rb_ary_new3(2, rb_easy_get("success_proc"), easy);
|
@@ -450,22 +325,19 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
|
|
450
325
|
val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
|
451
326
|
rbce->callback_active = 0;
|
452
327
|
//rb_funcall( rb_easy_get("success_proc"), idCall, 1, easy );
|
453
|
-
}
|
454
|
-
else if (!rb_easy_nil("redirect_proc") &&
|
328
|
+
} else if (!rb_easy_nil("redirect_proc") &&
|
455
329
|
(response_code >= 300 && response_code < 400)) {
|
456
330
|
rbce->callback_active = 1;
|
457
331
|
callargs = rb_ary_new3(3, rb_easy_get("redirect_proc"), easy, rb_curl_easy_error(result));
|
458
332
|
rbce->callback_active = 0;
|
459
333
|
val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
|
460
|
-
}
|
461
|
-
else if (!rb_easy_nil("missing_proc") &&
|
334
|
+
} else if (!rb_easy_nil("missing_proc") &&
|
462
335
|
(response_code >= 400 && response_code < 500)) {
|
463
336
|
rbce->callback_active = 1;
|
464
337
|
callargs = rb_ary_new3(3, rb_easy_get("missing_proc"), easy, rb_curl_easy_error(result));
|
465
338
|
rbce->callback_active = 0;
|
466
339
|
val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
|
467
|
-
}
|
468
|
-
else if (!rb_easy_nil("failure_proc") &&
|
340
|
+
} else if (!rb_easy_nil("failure_proc") &&
|
469
341
|
(response_code >= 500 && response_code <= 999)) {
|
470
342
|
callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
|
471
343
|
rbce->callback_active = 1;
|
@@ -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
|
}
|
data/ext/curb_multi.h
CHANGED
data/ext/curb_postfield.c
CHANGED
@@ -195,9 +195,9 @@ void curl_postfield_free(ruby_curl_postfield *rbcpf) {
|
|
195
195
|
|
196
196
|
/*
|
197
197
|
* call-seq:
|
198
|
-
* Curl::PostField.content(name, content) =>
|
199
|
-
* Curl::PostField.content(name, content, content_type = nil) =>
|
200
|
-
* Curl::PostField.content(name, content_type = nil) { |field| ... } =>
|
198
|
+
* Curl::PostField.content(name, content) => #<Curl::PostField...>
|
199
|
+
* Curl::PostField.content(name, content, content_type = nil) => #<Curl::PostField...>
|
200
|
+
* Curl::PostField.content(name, content_type = nil) { |field| ... } => #<Curl::PostField...>
|
201
201
|
*
|
202
202
|
* Create a new Curl::PostField, supplying the field name, content,
|
203
203
|
* and, optionally, Content-type (curl will attempt to determine this if
|
@@ -241,9 +241,9 @@ static VALUE ruby_curl_postfield_new_content(int argc, VALUE *argv, VALUE klass)
|
|
241
241
|
|
242
242
|
/*
|
243
243
|
* call-seq:
|
244
|
-
* Curl::PostField.file(name, local_file_name) =>
|
245
|
-
* Curl::PostField.file(name, local_file_name, remote_file_name = local_file_name) =>
|
246
|
-
* Curl::PostField.file(name, remote_file_name) { |field| ... } =>
|
244
|
+
* Curl::PostField.file(name, local_file_name) => #<Curl::PostField...>
|
245
|
+
* Curl::PostField.file(name, local_file_name, remote_file_name = local_file_name) => #<Curl::PostField...>
|
246
|
+
* Curl::PostField.file(name, remote_file_name) { |field| ... } => #<Curl::PostField...>
|
247
247
|
*
|
248
248
|
* Create a new Curl::PostField for a file upload field, supplying the local filename
|
249
249
|
* to read from, and optionally the remote filename (defaults to the local name).
|
@@ -399,7 +399,7 @@ static VALUE ruby_curl_postfield_remote_file_get(VALUE self) {
|
|
399
399
|
|
400
400
|
/*
|
401
401
|
* call-seq:
|
402
|
-
* field.set_content_proc { |field| ... } =>
|
402
|
+
* field.set_content_proc { |field| ... } => <old proc>
|
403
403
|
*
|
404
404
|
* Set a content proc for this field. This proc will be called during the
|
405
405
|
* perform to supply the content for this field, overriding any setting
|