curb 0.9.7 → 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 +54 -5
- data/Rakefile +33 -20
- data/ext/banned.h +32 -0
- data/ext/curb.c +170 -8
- data/ext/curb.h +18 -5
- data/ext/curb_easy.c +321 -43
- data/ext/curb_easy.h +6 -0
- data/ext/curb_multi.c +136 -171
- data/ext/curb_multi.h +0 -1
- data/ext/curb_postfield.c +7 -7
- data/ext/extconf.rb +45 -0
- data/lib/curb.rb +1 -0
- data/lib/curl/easy.rb +14 -7
- data/lib/curl/multi.rb +42 -3
- data/lib/curl.rb +12 -3
- data/tests/bug_issue277.rb +32 -0
- data/tests/helper.rb +79 -1
- data/tests/tc_curl_easy.rb +118 -16
- data/tests/tc_curl_maxfilesize.rb +12 -0
- data/tests/tc_curl_multi.rb +109 -5
- data/tests/tc_curl_postfield.rb +29 -29
- data/tests/tc_curl_protocols.rb +37 -0
- data/tests/timeout.rb +21 -5
- metadata +14 -8
data/ext/curb_easy.h
CHANGED
@@ -36,6 +36,9 @@ typedef struct {
|
|
36
36
|
/* The handler */
|
37
37
|
CURL *curl;
|
38
38
|
|
39
|
+
/* Buffer for error details from CURLOPT_ERRORBUFFER */
|
40
|
+
char err_buf[CURL_ERROR_SIZE];
|
41
|
+
|
39
42
|
VALUE opts; /* rather then allocate everything we might need to store, allocate a Hash and only store objects we actually use... */
|
40
43
|
VALUE multi; /* keep a multi handle alive for each easy handle not being used by a multi handle. This improves easy performance when not within a multi context */
|
41
44
|
|
@@ -55,6 +58,8 @@ typedef struct {
|
|
55
58
|
unsigned long ftp_response_timeout;
|
56
59
|
long low_speed_limit;
|
57
60
|
long low_speed_time;
|
61
|
+
long max_send_speed_large;
|
62
|
+
long max_recv_speed_large;
|
58
63
|
long ssl_version;
|
59
64
|
long use_ssl;
|
60
65
|
long ftp_filemethod;
|
@@ -76,6 +81,7 @@ typedef struct {
|
|
76
81
|
char callback_active;
|
77
82
|
|
78
83
|
struct curl_slist *curl_headers;
|
84
|
+
struct curl_slist *curl_proxy_headers;
|
79
85
|
struct curl_slist *curl_ftp_commands;
|
80
86
|
struct curl_slist *curl_resolve;
|
81
87
|
|
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,69 +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
|
-
|
61
|
+
rbcm->active = 0;
|
62
|
+
rbcm->running = 0;
|
84
63
|
}
|
85
64
|
|
86
65
|
/*
|
87
66
|
* call-seq:
|
88
|
-
* Curl::Multi.new =>
|
67
|
+
* Curl::Multi.new => #<Curl::Easy...>
|
89
68
|
*
|
90
69
|
* Create a new Curl::Multi instance
|
91
70
|
*/
|
92
71
|
VALUE ruby_curl_multi_new(VALUE klass) {
|
93
|
-
VALUE new_curlm;
|
94
|
-
|
95
72
|
ruby_curl_multi *rbcm = ALLOC(ruby_curl_multi);
|
96
73
|
|
97
|
-
rbcm
|
98
|
-
if (!rbcm->handle) {
|
99
|
-
rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
|
100
|
-
}
|
101
|
-
|
102
|
-
rbcm->requests = rb_hash_new();
|
103
|
-
|
104
|
-
rbcm->active = 0;
|
105
|
-
rbcm->running = 0;
|
74
|
+
ruby_curl_multi_init(rbcm);
|
106
75
|
|
107
|
-
|
108
|
-
|
109
|
-
|
76
|
+
/*
|
77
|
+
* The mark routine will be called by the garbage collector during its ``mark'' phase.
|
78
|
+
* If your structure references other Ruby objects, then your mark function needs to
|
79
|
+
* identify these objects using rb_gc_mark(value). If the structure doesn't reference
|
80
|
+
* other Ruby objects, you can simply pass 0 as a function pointer.
|
81
|
+
*/
|
82
|
+
return Data_Wrap_Struct(klass, 0, curl_multi_free, rbcm);
|
110
83
|
}
|
111
84
|
|
112
85
|
/*
|
@@ -133,52 +106,36 @@ VALUE ruby_curl_multi_get_default_timeout(VALUE klass) {
|
|
133
106
|
return LONG2NUM(cCurlMutiDefaulttimeout);
|
134
107
|
}
|
135
108
|
|
136
|
-
/*
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
109
|
+
/*
|
110
|
+
* call-seq:
|
111
|
+
* Curl::Multi.autoclose = true => true
|
112
|
+
*
|
113
|
+
* Automatically close open connections after each request. Otherwise, the connection will remain open
|
114
|
+
* for reuse until the next GC
|
115
|
+
*
|
116
|
+
*/
|
117
|
+
VALUE ruby_curl_multi_set_autoclose(VALUE klass, VALUE onoff) {
|
118
|
+
cCurlMutiAutoClose = ((onoff == Qtrue) ? 1 : 0);
|
119
|
+
return onoff;
|
141
120
|
}
|
142
121
|
|
143
122
|
/*
|
144
123
|
* call-seq:
|
145
|
-
*
|
146
|
-
*
|
147
|
-
*
|
124
|
+
* Curl::Multi.autoclose => true|false
|
125
|
+
*
|
126
|
+
* Get the global default autoclose setting for all Curl::Multi Handles.
|
127
|
+
*
|
148
128
|
*/
|
149
|
-
|
150
|
-
|
151
|
-
VALUE result_array;
|
152
|
-
|
153
|
-
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
154
|
-
|
155
|
-
result_array = rb_ary_new();
|
156
|
-
|
157
|
-
/* iterate over the requests hash, and stuff references into the array. */
|
158
|
-
rb_hash_foreach(rbcm->requests, ruby_curl_multi_requests_callback, result_array);
|
159
|
-
|
160
|
-
return result_array;
|
129
|
+
VALUE ruby_curl_multi_get_autoclose(VALUE klass) {
|
130
|
+
return cCurlMutiAutoClose == 1 ? Qtrue : Qfalse;
|
161
131
|
}
|
162
132
|
|
163
133
|
/*
|
164
134
|
* call-seq:
|
165
|
-
* multi.
|
135
|
+
* multi.requests => [#<Curl::Easy...>, ...]
|
166
136
|
*
|
167
|
-
* Returns
|
168
|
-
* true when multi.requests.length == 0.
|
137
|
+
* Returns an array containing all the active requests on this Curl::Multi object.
|
169
138
|
*/
|
170
|
-
static VALUE ruby_curl_multi_idle(VALUE self) {
|
171
|
-
ruby_curl_multi *rbcm;
|
172
|
-
|
173
|
-
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
174
|
-
|
175
|
-
if (RHASH_SIZE(rbcm->requests) == 0) {
|
176
|
-
return Qtrue;
|
177
|
-
} else {
|
178
|
-
return Qfalse;
|
179
|
-
}
|
180
|
-
}
|
181
|
-
|
182
139
|
/*
|
183
140
|
* call-seq:
|
184
141
|
* multi = Curl::Multi.new
|
@@ -241,19 +198,12 @@ static VALUE ruby_curl_multi_pipeline(VALUE self, VALUE method) {
|
|
241
198
|
*/
|
242
199
|
VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
243
200
|
CURLMcode mcode;
|
244
|
-
VALUE r;
|
245
201
|
ruby_curl_easy *rbce;
|
246
202
|
ruby_curl_multi *rbcm;
|
247
203
|
|
248
204
|
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
249
205
|
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
250
206
|
|
251
|
-
// check if this curl handle has been added before adding again
|
252
|
-
r = rb_hash_aref(rbcm->requests, LONG2NUM((long)rbce->curl));
|
253
|
-
if ( r != Qnil ) {
|
254
|
-
return Qnil;
|
255
|
-
}
|
256
|
-
|
257
207
|
/* setup the easy handle */
|
258
208
|
ruby_curl_easy_setup( rbce );
|
259
209
|
|
@@ -271,8 +221,6 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
|
271
221
|
/* track a reference to associated multi handle */
|
272
222
|
rbce->multi = self;
|
273
223
|
|
274
|
-
rb_hash_aset( rbcm->requests, LONG2NUM((long)rbce->curl), easy );
|
275
|
-
|
276
224
|
return self;
|
277
225
|
}
|
278
226
|
|
@@ -290,28 +238,20 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
|
290
238
|
*
|
291
239
|
* Will raise an exception if the easy handle is not found
|
292
240
|
*/
|
293
|
-
VALUE ruby_curl_multi_remove(VALUE self, VALUE
|
241
|
+
VALUE ruby_curl_multi_remove(VALUE self, VALUE rb_easy_handle) {
|
294
242
|
ruby_curl_multi *rbcm;
|
295
243
|
|
296
244
|
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
297
245
|
|
298
|
-
rb_curl_multi_remove(rbcm,
|
246
|
+
rb_curl_multi_remove(rbcm, rb_easy_handle);
|
299
247
|
|
300
248
|
return self;
|
301
249
|
}
|
302
250
|
static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
|
303
251
|
CURLMcode result;
|
304
252
|
ruby_curl_easy *rbce;
|
305
|
-
VALUE r;
|
306
253
|
|
307
254
|
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
308
|
-
|
309
|
-
// check if this curl handle has been added before removing
|
310
|
-
r = rb_hash_aref(rbcm->requests, LONG2NUM((long)rbce->curl));
|
311
|
-
if ( r == Qnil ) {
|
312
|
-
return;
|
313
|
-
}
|
314
|
-
|
315
255
|
result = curl_multi_remove_handle(rbcm->handle, rbce->curl);
|
316
256
|
if (result != 0) {
|
317
257
|
raise_curl_multi_error_exception(result);
|
@@ -320,36 +260,6 @@ static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
|
|
320
260
|
rbcm->active--;
|
321
261
|
|
322
262
|
ruby_curl_easy_cleanup( easy, rbce );
|
323
|
-
|
324
|
-
// active should equal LONG2NUM(RHASH(rbcm->requests)->tbl->num_entries)
|
325
|
-
r = rb_hash_delete( rbcm->requests, LONG2NUM((long)rbce->curl) );
|
326
|
-
if( r != easy || r == Qnil ) {
|
327
|
-
rb_warn("Possibly lost track of Curl::Easy VALUE, it may not be reclaimed by GC");
|
328
|
-
}
|
329
|
-
}
|
330
|
-
|
331
|
-
/* Hash#foreach callback for ruby_curl_multi_cancel */
|
332
|
-
static int ruby_curl_multi_cancel_callback(VALUE key, VALUE value, ruby_curl_multi *rbcm) {
|
333
|
-
rb_curl_multi_remove(rbcm, value);
|
334
|
-
|
335
|
-
return ST_CONTINUE;
|
336
|
-
}
|
337
|
-
|
338
|
-
/*
|
339
|
-
* call-seq:
|
340
|
-
* multi.cancel!
|
341
|
-
*
|
342
|
-
* Cancels all requests currently being made on this Curl::Multi handle.
|
343
|
-
*/
|
344
|
-
static VALUE ruby_curl_multi_cancel(VALUE self) {
|
345
|
-
ruby_curl_multi *rbcm;
|
346
|
-
|
347
|
-
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
348
|
-
|
349
|
-
rb_hash_foreach( rbcm->requests, ruby_curl_multi_cancel_callback, (VALUE)rbcm );
|
350
|
-
|
351
|
-
/* for chaining */
|
352
|
-
return self;
|
353
263
|
}
|
354
264
|
|
355
265
|
// on_success, on_failure, on_complete
|
@@ -361,7 +271,6 @@ static VALUE call_status_handler2(VALUE ary) {
|
|
361
271
|
}
|
362
272
|
|
363
273
|
static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int result) {
|
364
|
-
|
365
274
|
long response_code = -1;
|
366
275
|
VALUE easy;
|
367
276
|
ruby_curl_easy *rbce = NULL;
|
@@ -373,7 +282,8 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
|
|
373
282
|
|
374
283
|
rbce->last_result = result; /* save the last easy result code */
|
375
284
|
|
376
|
-
|
285
|
+
// remove the easy handle from multi on completion so it can be reused again
|
286
|
+
rb_funcall(self, rb_intern("remove"), 1, easy);
|
377
287
|
|
378
288
|
/* after running a request cleanup the headers, these are set before each request */
|
379
289
|
if (rbce->curl_headers) {
|
@@ -390,10 +300,14 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
|
|
390
300
|
rbce->callback_active = 1;
|
391
301
|
val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
|
392
302
|
rbce->callback_active = 0;
|
393
|
-
//rb_funcall( rb_easy_get("complete_proc"), idCall, 1, easy );
|
394
303
|
}
|
395
304
|
|
305
|
+
#ifdef HAVE_CURLINFO_RESPONSE_CODE
|
396
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
|
397
311
|
|
398
312
|
if (result != 0) {
|
399
313
|
if (!rb_easy_nil("failure_proc")) {
|
@@ -403,8 +317,7 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
|
|
403
317
|
rbce->callback_active = 0;
|
404
318
|
//rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
|
405
319
|
}
|
406
|
-
}
|
407
|
-
else if (!rb_easy_nil("success_proc") &&
|
320
|
+
} else if (!rb_easy_nil("success_proc") &&
|
408
321
|
((response_code >= 200 && response_code < 300) || response_code == 0)) {
|
409
322
|
/* NOTE: we allow response_code == 0, in the case of non http requests e.g. reading from disk */
|
410
323
|
callargs = rb_ary_new3(2, rb_easy_get("success_proc"), easy);
|
@@ -412,22 +325,19 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
|
|
412
325
|
val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
|
413
326
|
rbce->callback_active = 0;
|
414
327
|
//rb_funcall( rb_easy_get("success_proc"), idCall, 1, easy );
|
415
|
-
}
|
416
|
-
else if (!rb_easy_nil("redirect_proc") &&
|
328
|
+
} else if (!rb_easy_nil("redirect_proc") &&
|
417
329
|
(response_code >= 300 && response_code < 400)) {
|
418
330
|
rbce->callback_active = 1;
|
419
331
|
callargs = rb_ary_new3(3, rb_easy_get("redirect_proc"), easy, rb_curl_easy_error(result));
|
420
332
|
rbce->callback_active = 0;
|
421
333
|
val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
|
422
|
-
}
|
423
|
-
else if (!rb_easy_nil("missing_proc") &&
|
334
|
+
} else if (!rb_easy_nil("missing_proc") &&
|
424
335
|
(response_code >= 400 && response_code < 500)) {
|
425
336
|
rbce->callback_active = 1;
|
426
337
|
callargs = rb_ary_new3(3, rb_easy_get("missing_proc"), easy, rb_curl_easy_error(result));
|
427
338
|
rbce->callback_active = 0;
|
428
339
|
val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
|
429
|
-
}
|
430
|
-
else if (!rb_easy_nil("failure_proc") &&
|
340
|
+
} else if (!rb_easy_nil("failure_proc") &&
|
431
341
|
(response_code >= 500 && response_code <= 999)) {
|
432
342
|
callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
|
433
343
|
rbce->callback_active = 1;
|
@@ -439,19 +349,27 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
|
|
439
349
|
}
|
440
350
|
|
441
351
|
static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
|
442
|
-
int msgs_left
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
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);
|
455
373
|
}
|
456
374
|
}
|
457
375
|
|
@@ -459,14 +377,32 @@ static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
|
|
459
377
|
static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running) {
|
460
378
|
CURLMcode mcode;
|
461
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
|
+
*/
|
462
387
|
do {
|
463
388
|
mcode = curl_multi_perform(multi_handle, still_running);
|
464
389
|
} while (mcode == CURLM_CALL_MULTI_PERFORM);
|
465
390
|
|
391
|
+
/*
|
392
|
+
* Nothing more to do, check if an error occured in the loop above and raise an exception if necessary.
|
393
|
+
*/
|
394
|
+
|
466
395
|
if (mcode != CURLM_OK) {
|
467
396
|
raise_curl_multi_error_exception(mcode);
|
468
397
|
}
|
469
|
-
|
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
|
+
*/
|
470
406
|
}
|
471
407
|
|
472
408
|
#ifdef _WIN32
|
@@ -537,6 +473,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
537
473
|
#endif
|
538
474
|
long timeout_milliseconds;
|
539
475
|
struct timeval tv = {0, 0};
|
476
|
+
struct timeval tv_100ms = {0, 100000};
|
540
477
|
VALUE block = Qnil;
|
541
478
|
#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
|
542
479
|
struct _select_set fdset_args;
|
@@ -548,10 +485,23 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
548
485
|
|
549
486
|
timeout_milliseconds = cCurlMutiDefaulttimeout;
|
550
487
|
|
488
|
+
// Run curl_multi_perform for the first time to get the ball rolling
|
551
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.
|
552
496
|
rb_curl_multi_read_info( self, rbcm->handle );
|
553
|
-
|
554
|
-
|
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
|
+
|
555
505
|
do {
|
556
506
|
while (rbcm->running) {
|
557
507
|
|
@@ -593,7 +543,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
593
543
|
|
594
544
|
if (maxfd == -1) {
|
595
545
|
/* libcurl recommends sleeping for 100ms */
|
596
|
-
rb_thread_wait_for(
|
546
|
+
rb_thread_wait_for(tv_100ms);
|
597
547
|
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
598
548
|
rb_curl_multi_read_info( self, rbcm->handle );
|
599
549
|
if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
|
@@ -606,12 +556,15 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
606
556
|
create_crt_fd(&fdexcep, &crt_fdexcep);
|
607
557
|
#endif
|
608
558
|
|
609
|
-
|
559
|
+
|
560
|
+
#if (defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL))
|
610
561
|
fdset_args.maxfd = maxfd+1;
|
611
562
|
fdset_args.fdread = &fdread;
|
612
563
|
fdset_args.fdwrite = &fdwrite;
|
613
564
|
fdset_args.fdexcep = &fdexcep;
|
614
565
|
fdset_args.tv = &tv;
|
566
|
+
#endif
|
567
|
+
|
615
568
|
#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
|
616
569
|
rc = (int)(VALUE) rb_thread_call_without_gvl((void *(*)(void *))curb_select, &fdset_args, RUBY_UBF_IO, 0);
|
617
570
|
#elif HAVE_RB_THREAD_BLOCKING_REGION
|
@@ -622,8 +575,6 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
622
575
|
rc = rb_thread_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
|
623
576
|
#endif
|
624
577
|
|
625
|
-
#endif
|
626
|
-
|
627
578
|
#ifdef _WIN32
|
628
579
|
cleanup_crt_fd(&fdread, &crt_fdread);
|
629
580
|
cleanup_crt_fd(&fdwrite, &crt_fdwrite);
|
@@ -649,30 +600,44 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
649
600
|
|
650
601
|
rb_curl_multi_read_info( self, rbcm->handle );
|
651
602
|
if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
|
652
|
-
|
603
|
+
if (cCurlMutiAutoClose == 1) {
|
604
|
+
rb_funcall(self, rb_intern("close"), 0);
|
605
|
+
}
|
653
606
|
return Qtrue;
|
654
607
|
}
|
655
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
|
+
|
656
625
|
/* =================== INIT LIB =====================*/
|
657
626
|
void init_curb_multi() {
|
658
627
|
idCall = rb_intern("call");
|
659
|
-
|
660
628
|
cCurlMulti = rb_define_class_under(mCurl, "Multi", rb_cObject);
|
661
629
|
|
662
630
|
/* Class methods */
|
663
631
|
rb_define_singleton_method(cCurlMulti, "new", ruby_curl_multi_new, 0);
|
664
632
|
rb_define_singleton_method(cCurlMulti, "default_timeout=", ruby_curl_multi_set_default_timeout, 1);
|
665
633
|
rb_define_singleton_method(cCurlMulti, "default_timeout", ruby_curl_multi_get_default_timeout, 0);
|
666
|
-
|
667
|
-
|
668
|
-
rb_define_method(cCurlMulti, "requests", ruby_curl_multi_requests, 0);
|
669
|
-
rb_define_method(cCurlMulti, "idle?", ruby_curl_multi_idle, 0);
|
670
|
-
|
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);
|
671
636
|
/* Instance methods */
|
672
637
|
rb_define_method(cCurlMulti, "max_connects=", ruby_curl_multi_max_connects, 1);
|
673
638
|
rb_define_method(cCurlMulti, "pipeline=", ruby_curl_multi_pipeline, 1);
|
674
|
-
rb_define_method(cCurlMulti, "
|
675
|
-
rb_define_method(cCurlMulti, "
|
676
|
-
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);
|
677
641
|
rb_define_method(cCurlMulti, "perform", ruby_curl_multi_perform, -1);
|
642
|
+
rb_define_method(cCurlMulti, "_close", ruby_curl_multi_close, 0);
|
678
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
|