curb 0.8.4 → 0.9.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.markdown +277 -0
- data/Rakefile +29 -18
- data/ext/banned.h +32 -0
- data/ext/curb.c +161 -46
- data/ext/curb.h +14 -10
- data/ext/curb_easy.c +612 -97
- data/ext/curb_easy.h +6 -0
- data/ext/curb_errors.c +113 -16
- data/ext/curb_errors.h +8 -5
- data/ext/curb_macros.h +7 -7
- data/ext/curb_multi.c +175 -167
- data/ext/curb_multi.h +0 -1
- data/ext/curb_upload.c +2 -2
- data/ext/extconf.rb +52 -17
- data/lib/curb.rb +1 -0
- data/lib/curl/easy.rb +79 -59
- data/lib/curl/multi.rb +59 -20
- data/lib/curl.rb +15 -9
- data/tests/bug_crash_on_progress.rb +41 -1
- data/tests/bug_issue102.rb +1 -1
- data/tests/bug_issue277.rb +32 -0
- data/tests/helper.rb +102 -13
- data/tests/tc_curl.rb +31 -1
- data/tests/tc_curl_download.rb +4 -4
- data/tests/tc_curl_easy.rb +197 -42
- data/tests/tc_curl_easy_resolve.rb +16 -0
- data/tests/tc_curl_maxfilesize.rb +12 -0
- data/tests/tc_curl_multi.rb +82 -16
- data/tests/tc_curl_postfield.rb +29 -29
- data/tests/timeout.rb +10 -2
- metadata +51 -52
- data/README +0 -194
data/ext/curb_multi.c
CHANGED
|
@@ -4,13 +4,17 @@
|
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
6
|
#include "curb_config.h"
|
|
7
|
-
#
|
|
8
|
-
|
|
7
|
+
#include <ruby.h>
|
|
8
|
+
#ifdef HAVE_RUBY_ST_H
|
|
9
9
|
#include <ruby/st.h>
|
|
10
10
|
#else
|
|
11
|
-
#include <ruby.h>
|
|
12
11
|
#include <st.h>
|
|
13
12
|
#endif
|
|
13
|
+
|
|
14
|
+
#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
|
|
15
|
+
#include <ruby/thread.h>
|
|
16
|
+
#endif
|
|
17
|
+
|
|
14
18
|
#include "curb_easy.h"
|
|
15
19
|
#include "curb_errors.h"
|
|
16
20
|
#include "curb_postfield.h"
|
|
@@ -33,6 +37,7 @@ static VALUE idCall;
|
|
|
33
37
|
VALUE cCurlMulti;
|
|
34
38
|
|
|
35
39
|
static long cCurlMutiDefaulttimeout = 100; /* milliseconds */
|
|
40
|
+
static char cCurlMutiAutoClose = 0;
|
|
36
41
|
|
|
37
42
|
static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy);
|
|
38
43
|
static void rb_curl_multi_read_info(VALUE self, CURLM *mptr);
|
|
@@ -40,39 +45,21 @@ static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_runnin
|
|
|
40
45
|
|
|
41
46
|
static VALUE callback_exception(VALUE unused) {
|
|
42
47
|
return Qfalse;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
static void curl_multi_mark(ruby_curl_multi *rbcm) {
|
|
46
|
-
rb_gc_mark(rbcm->requests);
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
|
54
|
-
result = curl_multi_remove_handle(rbcm->handle, rbce->curl);
|
|
55
|
-
if (result != 0) {
|
|
56
|
-
raise_curl_multi_error_exception(result);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
static int
|
|
61
|
-
rb_hash_clear_i(VALUE key, VALUE value, VALUE dummy) {
|
|
62
|
-
return ST_DELETE;
|
|
50
|
+
void curl_multi_free(ruby_curl_multi *rbcm) {
|
|
51
|
+
curl_multi_cleanup(rbcm->handle);
|
|
52
|
+
free(rbcm);
|
|
63
53
|
}
|
|
64
54
|
|
|
65
|
-
static void
|
|
66
|
-
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
rb_hash_foreach( rbcm->requests, (int (*)())curl_multi_flush_easy, (VALUE)rbcm );
|
|
70
|
-
|
|
71
|
-
rb_hash_foreach(rbcm->requests, rb_hash_clear_i, 0); //rb_hash_clear(rbcm->requests);
|
|
72
|
-
rbcm->requests = Qnil;
|
|
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");
|
|
73
59
|
}
|
|
74
|
-
|
|
75
|
-
|
|
60
|
+
|
|
61
|
+
rbcm->active = 0;
|
|
62
|
+
rbcm->running = 0;
|
|
76
63
|
}
|
|
77
64
|
|
|
78
65
|
/*
|
|
@@ -82,23 +69,17 @@ static void curl_multi_free(ruby_curl_multi *rbcm) {
|
|
|
82
69
|
* Create a new Curl::Multi instance
|
|
83
70
|
*/
|
|
84
71
|
VALUE ruby_curl_multi_new(VALUE klass) {
|
|
85
|
-
VALUE new_curlm;
|
|
86
|
-
|
|
87
72
|
ruby_curl_multi *rbcm = ALLOC(ruby_curl_multi);
|
|
88
73
|
|
|
89
|
-
rbcm
|
|
90
|
-
if (!rbcm->handle) {
|
|
91
|
-
rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
rbcm->requests = rb_hash_new();
|
|
95
|
-
|
|
96
|
-
rbcm->active = 0;
|
|
97
|
-
rbcm->running = 0;
|
|
74
|
+
ruby_curl_multi_init(rbcm);
|
|
98
75
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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);
|
|
102
83
|
}
|
|
103
84
|
|
|
104
85
|
/*
|
|
@@ -110,7 +91,7 @@ VALUE ruby_curl_multi_new(VALUE klass) {
|
|
|
110
91
|
*
|
|
111
92
|
*/
|
|
112
93
|
VALUE ruby_curl_multi_set_default_timeout(VALUE klass, VALUE timeout) {
|
|
113
|
-
cCurlMutiDefaulttimeout =
|
|
94
|
+
cCurlMutiDefaulttimeout = NUM2LONG(timeout);
|
|
114
95
|
return timeout;
|
|
115
96
|
}
|
|
116
97
|
|
|
@@ -122,55 +103,39 @@ VALUE ruby_curl_multi_set_default_timeout(VALUE klass, VALUE timeout) {
|
|
|
122
103
|
*
|
|
123
104
|
*/
|
|
124
105
|
VALUE ruby_curl_multi_get_default_timeout(VALUE klass) {
|
|
125
|
-
return
|
|
106
|
+
return LONG2NUM(cCurlMutiDefaulttimeout);
|
|
126
107
|
}
|
|
127
108
|
|
|
128
|
-
/*
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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;
|
|
133
120
|
}
|
|
134
121
|
|
|
135
122
|
/*
|
|
136
123
|
* call-seq:
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
124
|
+
* Curl::Multi.autoclose => true|false
|
|
125
|
+
*
|
|
126
|
+
* Get the global default autoclose setting for all Curl::Multi Handles.
|
|
127
|
+
*
|
|
140
128
|
*/
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
VALUE result_array;
|
|
144
|
-
|
|
145
|
-
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
|
146
|
-
|
|
147
|
-
result_array = rb_ary_new();
|
|
148
|
-
|
|
149
|
-
/* iterate over the requests hash, and stuff references into the array. */
|
|
150
|
-
rb_hash_foreach(rbcm->requests, ruby_curl_multi_requests_callback, result_array);
|
|
151
|
-
|
|
152
|
-
return result_array;
|
|
129
|
+
VALUE ruby_curl_multi_get_autoclose(VALUE klass) {
|
|
130
|
+
return cCurlMutiAutoClose == 1 ? Qtrue : Qfalse;
|
|
153
131
|
}
|
|
154
132
|
|
|
155
133
|
/*
|
|
156
134
|
* call-seq:
|
|
157
|
-
* multi.
|
|
135
|
+
* multi.requests => [#<Curl::Easy...>, ...]
|
|
158
136
|
*
|
|
159
|
-
* Returns
|
|
160
|
-
* true when multi.requests.length == 0.
|
|
137
|
+
* Returns an array containing all the active requests on this Curl::Multi object.
|
|
161
138
|
*/
|
|
162
|
-
static VALUE ruby_curl_multi_idle(VALUE self) {
|
|
163
|
-
ruby_curl_multi *rbcm;
|
|
164
|
-
|
|
165
|
-
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
|
166
|
-
|
|
167
|
-
if ( FIX2INT( rb_funcall(rbcm->requests, rb_intern("length"), 0) ) == 0 ) {
|
|
168
|
-
return Qtrue;
|
|
169
|
-
} else {
|
|
170
|
-
return Qfalse;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
139
|
/*
|
|
175
140
|
* call-seq:
|
|
176
141
|
* multi = Curl::Multi.new
|
|
@@ -183,7 +148,8 @@ static VALUE ruby_curl_multi_max_connects(VALUE self, VALUE count) {
|
|
|
183
148
|
ruby_curl_multi *rbcm;
|
|
184
149
|
|
|
185
150
|
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
|
186
|
-
|
|
151
|
+
|
|
152
|
+
curl_multi_setopt(rbcm->handle, CURLMOPT_MAXCONNECTS, NUM2LONG(count));
|
|
187
153
|
#endif
|
|
188
154
|
|
|
189
155
|
return count;
|
|
@@ -194,20 +160,31 @@ static VALUE ruby_curl_multi_max_connects(VALUE self, VALUE count) {
|
|
|
194
160
|
* multi = Curl::Multi.new
|
|
195
161
|
* multi.pipeline = true
|
|
196
162
|
*
|
|
197
|
-
* Pass a long set to 1
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
163
|
+
* Pass a long set to 1 for HTTP/1.1 pipelining, 2 for HTTP/2 multiplexing, or 0 to disable.
|
|
164
|
+
* Enabling pipelining on a multi handle will make it attempt to perform HTTP Pipelining as
|
|
165
|
+
* far as possible for transfers using this handle. This means that if you add a second request
|
|
166
|
+
* that can use an already existing connection, the second request will be "piped" on the same
|
|
167
|
+
* connection rather than being executed in parallel. (Added in 7.16.0, multiplex added in 7.43.0)
|
|
201
168
|
*
|
|
202
169
|
*/
|
|
203
|
-
static VALUE ruby_curl_multi_pipeline(VALUE self, VALUE
|
|
170
|
+
static VALUE ruby_curl_multi_pipeline(VALUE self, VALUE method) {
|
|
204
171
|
#ifdef HAVE_CURLMOPT_PIPELINING
|
|
205
172
|
ruby_curl_multi *rbcm;
|
|
206
173
|
|
|
174
|
+
long value;
|
|
175
|
+
|
|
176
|
+
if (method == Qtrue) {
|
|
177
|
+
value = 1;
|
|
178
|
+
} else if (method == Qfalse) {
|
|
179
|
+
value = 0;
|
|
180
|
+
} else {
|
|
181
|
+
value = NUM2LONG(method);
|
|
182
|
+
}
|
|
183
|
+
|
|
207
184
|
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
|
208
|
-
curl_multi_setopt(rbcm->handle, CURLMOPT_PIPELINING,
|
|
185
|
+
curl_multi_setopt(rbcm->handle, CURLMOPT_PIPELINING, value);
|
|
209
186
|
#endif
|
|
210
|
-
return
|
|
187
|
+
return method == Qtrue ? 1 : 0;
|
|
211
188
|
}
|
|
212
189
|
|
|
213
190
|
/*
|
|
@@ -241,7 +218,8 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
|
|
241
218
|
* If this number is not correct, the next call to curl_multi_perform will correct it. */
|
|
242
219
|
rbcm->running++;
|
|
243
220
|
|
|
244
|
-
|
|
221
|
+
/* track a reference to associated multi handle */
|
|
222
|
+
rbce->multi = self;
|
|
245
223
|
|
|
246
224
|
return self;
|
|
247
225
|
}
|
|
@@ -260,25 +238,20 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
|
|
260
238
|
*
|
|
261
239
|
* Will raise an exception if the easy handle is not found
|
|
262
240
|
*/
|
|
263
|
-
VALUE ruby_curl_multi_remove(VALUE self, VALUE
|
|
241
|
+
VALUE ruby_curl_multi_remove(VALUE self, VALUE rb_easy_handle) {
|
|
264
242
|
ruby_curl_multi *rbcm;
|
|
265
|
-
ruby_curl_easy *rbce;
|
|
266
243
|
|
|
267
244
|
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
|
268
245
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
rb_curl_multi_remove(rbcm,easy);
|
|
246
|
+
rb_curl_multi_remove(rbcm, rb_easy_handle);
|
|
272
247
|
|
|
273
248
|
return self;
|
|
274
249
|
}
|
|
275
250
|
static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
|
|
276
251
|
CURLMcode result;
|
|
277
252
|
ruby_curl_easy *rbce;
|
|
278
|
-
VALUE r;
|
|
279
253
|
|
|
280
254
|
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
|
281
|
-
|
|
282
255
|
result = curl_multi_remove_handle(rbcm->handle, rbce->curl);
|
|
283
256
|
if (result != 0) {
|
|
284
257
|
raise_curl_multi_error_exception(result);
|
|
@@ -287,36 +260,6 @@ static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
|
|
|
287
260
|
rbcm->active--;
|
|
288
261
|
|
|
289
262
|
ruby_curl_easy_cleanup( easy, rbce );
|
|
290
|
-
|
|
291
|
-
// active should equal INT2FIX(RHASH(rbcm->requests)->tbl->num_entries)
|
|
292
|
-
r = rb_hash_delete( rbcm->requests, easy );
|
|
293
|
-
if( r != easy || r == Qnil ) {
|
|
294
|
-
rb_warn("Possibly lost track of Curl::Easy VALUE, it may not be reclaimed by GC");
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/* Hash#foreach callback for ruby_curl_multi_cancel */
|
|
299
|
-
static int ruby_curl_multi_cancel_callback(VALUE key, VALUE value, ruby_curl_multi *rbcm) {
|
|
300
|
-
rb_curl_multi_remove(rbcm, value);
|
|
301
|
-
|
|
302
|
-
return ST_CONTINUE;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/*
|
|
306
|
-
* call-seq:
|
|
307
|
-
* multi.cancel!
|
|
308
|
-
*
|
|
309
|
-
* Cancels all requests currently being made on this Curl::Multi handle.
|
|
310
|
-
*/
|
|
311
|
-
static VALUE ruby_curl_multi_cancel(VALUE self) {
|
|
312
|
-
ruby_curl_multi *rbcm;
|
|
313
|
-
|
|
314
|
-
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
|
315
|
-
|
|
316
|
-
rb_hash_foreach( rbcm->requests, ruby_curl_multi_cancel_callback, (VALUE)rbcm );
|
|
317
|
-
|
|
318
|
-
/* for chaining */
|
|
319
|
-
return self;
|
|
320
263
|
}
|
|
321
264
|
|
|
322
265
|
// on_success, on_failure, on_complete
|
|
@@ -328,7 +271,6 @@ static VALUE call_status_handler2(VALUE ary) {
|
|
|
328
271
|
}
|
|
329
272
|
|
|
330
273
|
static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int result) {
|
|
331
|
-
|
|
332
274
|
long response_code = -1;
|
|
333
275
|
VALUE easy;
|
|
334
276
|
ruby_curl_easy *rbce = NULL;
|
|
@@ -340,7 +282,8 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
|
|
|
340
282
|
|
|
341
283
|
rbce->last_result = result; /* save the last easy result code */
|
|
342
284
|
|
|
343
|
-
|
|
285
|
+
// remove the easy handle from multi on completion so it can be reused again
|
|
286
|
+
rb_funcall(self, rb_intern("remove"), 1, easy);
|
|
344
287
|
|
|
345
288
|
/* after running a request cleanup the headers, these are set before each request */
|
|
346
289
|
if (rbce->curl_headers) {
|
|
@@ -403,28 +346,30 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
|
|
|
403
346
|
//rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
|
|
404
347
|
}
|
|
405
348
|
|
|
406
|
-
if (val == Qfalse) {
|
|
407
|
-
rb_warn("uncaught exception from callback");
|
|
408
|
-
// exception was raised?
|
|
409
|
-
//fprintf(stderr, "exception raised from callback\n");
|
|
410
|
-
}
|
|
411
|
-
|
|
412
349
|
}
|
|
413
350
|
|
|
414
351
|
static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
|
|
415
|
-
int msgs_left
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
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);
|
|
428
373
|
}
|
|
429
374
|
}
|
|
430
375
|
|
|
@@ -432,14 +377,32 @@ static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
|
|
|
432
377
|
static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running) {
|
|
433
378
|
CURLMcode mcode;
|
|
434
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
|
+
*/
|
|
435
387
|
do {
|
|
436
388
|
mcode = curl_multi_perform(multi_handle, still_running);
|
|
437
389
|
} while (mcode == CURLM_CALL_MULTI_PERFORM);
|
|
438
390
|
|
|
391
|
+
/*
|
|
392
|
+
* Nothing more to do, check if an error occured in the loop above and raise an exception if necessary.
|
|
393
|
+
*/
|
|
394
|
+
|
|
439
395
|
if (mcode != CURLM_OK) {
|
|
440
396
|
raise_curl_multi_error_exception(mcode);
|
|
441
397
|
}
|
|
442
|
-
|
|
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
|
+
*/
|
|
443
406
|
}
|
|
444
407
|
|
|
445
408
|
#ifdef _WIN32
|
|
@@ -471,7 +434,7 @@ void cleanup_crt_fd(fd_set *os_set, fd_set *crt_set)
|
|
|
471
434
|
}
|
|
472
435
|
#endif
|
|
473
436
|
|
|
474
|
-
#
|
|
437
|
+
#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
|
|
475
438
|
struct _select_set {
|
|
476
439
|
int maxfd;
|
|
477
440
|
fd_set *fdread, *fdwrite, *fdexcep;
|
|
@@ -503,15 +466,16 @@ static VALUE curb_select(void *args) {
|
|
|
503
466
|
VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
504
467
|
CURLMcode mcode;
|
|
505
468
|
ruby_curl_multi *rbcm;
|
|
506
|
-
int maxfd, rc;
|
|
469
|
+
int maxfd, rc = -1;
|
|
507
470
|
fd_set fdread, fdwrite, fdexcep;
|
|
508
471
|
#ifdef _WIN32
|
|
509
472
|
fd_set crt_fdread, crt_fdwrite, crt_fdexcep;
|
|
510
473
|
#endif
|
|
511
474
|
long timeout_milliseconds;
|
|
512
475
|
struct timeval tv = {0, 0};
|
|
476
|
+
struct timeval tv_100ms = {0, 100000};
|
|
513
477
|
VALUE block = Qnil;
|
|
514
|
-
#
|
|
478
|
+
#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
|
|
515
479
|
struct _select_set fdset_args;
|
|
516
480
|
#endif
|
|
517
481
|
|
|
@@ -521,10 +485,23 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
521
485
|
|
|
522
486
|
timeout_milliseconds = cCurlMutiDefaulttimeout;
|
|
523
487
|
|
|
488
|
+
// Run curl_multi_perform for the first time to get the ball rolling
|
|
524
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.
|
|
525
496
|
rb_curl_multi_read_info( self, rbcm->handle );
|
|
526
|
-
|
|
527
|
-
|
|
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
|
+
|
|
528
505
|
do {
|
|
529
506
|
while (rbcm->running) {
|
|
530
507
|
|
|
@@ -564,19 +541,36 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
564
541
|
raise_curl_multi_error_exception(mcode);
|
|
565
542
|
}
|
|
566
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
|
+
|
|
567
553
|
#ifdef _WIN32
|
|
568
554
|
create_crt_fd(&fdread, &crt_fdread);
|
|
569
555
|
create_crt_fd(&fdwrite, &crt_fdwrite);
|
|
570
556
|
create_crt_fd(&fdexcep, &crt_fdexcep);
|
|
571
557
|
#endif
|
|
572
558
|
|
|
573
|
-
|
|
559
|
+
|
|
560
|
+
#if (defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL))
|
|
574
561
|
fdset_args.maxfd = maxfd+1;
|
|
575
562
|
fdset_args.fdread = &fdread;
|
|
576
563
|
fdset_args.fdwrite = &fdwrite;
|
|
577
564
|
fdset_args.fdexcep = &fdexcep;
|
|
578
565
|
fdset_args.tv = &tv;
|
|
566
|
+
#endif
|
|
567
|
+
|
|
568
|
+
#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
|
|
569
|
+
rc = (int)(VALUE) rb_thread_call_without_gvl((void *(*)(void *))curb_select, &fdset_args, RUBY_UBF_IO, 0);
|
|
570
|
+
#elif HAVE_RB_THREAD_BLOCKING_REGION
|
|
579
571
|
rc = rb_thread_blocking_region(curb_select, &fdset_args, RUBY_UBF_IO, 0);
|
|
572
|
+
#elif HAVE_RB_THREAD_FD_SELECT
|
|
573
|
+
rc = rb_thread_fd_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
|
|
580
574
|
#else
|
|
581
575
|
rc = rb_thread_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
|
|
582
576
|
#endif
|
|
@@ -606,30 +600,44 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
606
600
|
|
|
607
601
|
rb_curl_multi_read_info( self, rbcm->handle );
|
|
608
602
|
if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
|
|
609
|
-
|
|
603
|
+
if (cCurlMutiAutoClose == 1) {
|
|
604
|
+
rb_funcall(self, rb_intern("close"), 0);
|
|
605
|
+
}
|
|
610
606
|
return Qtrue;
|
|
611
607
|
}
|
|
612
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
|
+
|
|
613
625
|
/* =================== INIT LIB =====================*/
|
|
614
626
|
void init_curb_multi() {
|
|
615
627
|
idCall = rb_intern("call");
|
|
616
|
-
|
|
617
628
|
cCurlMulti = rb_define_class_under(mCurl, "Multi", rb_cObject);
|
|
618
629
|
|
|
619
630
|
/* Class methods */
|
|
620
631
|
rb_define_singleton_method(cCurlMulti, "new", ruby_curl_multi_new, 0);
|
|
621
632
|
rb_define_singleton_method(cCurlMulti, "default_timeout=", ruby_curl_multi_set_default_timeout, 1);
|
|
622
633
|
rb_define_singleton_method(cCurlMulti, "default_timeout", ruby_curl_multi_get_default_timeout, 0);
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
rb_define_method(cCurlMulti, "idle?", ruby_curl_multi_idle, 0);
|
|
627
|
-
|
|
628
|
-
/* Instnace methods */
|
|
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);
|
|
636
|
+
/* Instance methods */
|
|
629
637
|
rb_define_method(cCurlMulti, "max_connects=", ruby_curl_multi_max_connects, 1);
|
|
630
638
|
rb_define_method(cCurlMulti, "pipeline=", ruby_curl_multi_pipeline, 1);
|
|
631
|
-
rb_define_method(cCurlMulti, "
|
|
632
|
-
rb_define_method(cCurlMulti, "
|
|
633
|
-
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);
|
|
634
641
|
rb_define_method(cCurlMulti, "perform", ruby_curl_multi_perform, -1);
|
|
642
|
+
rb_define_method(cCurlMulti, "_close", ruby_curl_multi_close, 0);
|
|
635
643
|
}
|
data/ext/curb_multi.h
CHANGED
data/ext/curb_upload.c
CHANGED
|
@@ -56,7 +56,7 @@ VALUE ruby_curl_upload_stream_get(VALUE self) {
|
|
|
56
56
|
VALUE ruby_curl_upload_offset_set(VALUE self, VALUE offset) {
|
|
57
57
|
ruby_curl_upload *rbcu;
|
|
58
58
|
Data_Get_Struct(self, ruby_curl_upload, rbcu);
|
|
59
|
-
rbcu->offset =
|
|
59
|
+
rbcu->offset = NUM2LONG(offset);
|
|
60
60
|
return offset;
|
|
61
61
|
}
|
|
62
62
|
/*
|
|
@@ -66,7 +66,7 @@ VALUE ruby_curl_upload_offset_set(VALUE self, VALUE offset) {
|
|
|
66
66
|
VALUE ruby_curl_upload_offset_get(VALUE self) {
|
|
67
67
|
ruby_curl_upload *rbcu;
|
|
68
68
|
Data_Get_Struct(self, ruby_curl_upload, rbcu);
|
|
69
|
-
return
|
|
69
|
+
return LONG2NUM(rbcu->offset);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
/* =================== INIT LIB =====================*/
|