curb 0.7.15 → 1.0.0
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 +283 -0
- data/Rakefile +38 -26
- data/ext/banned.h +32 -0
- data/ext/curb.c +903 -46
- data/ext/curb.h +20 -11
- data/ext/curb_easy.c +1039 -565
- data/ext/curb_easy.h +12 -0
- data/ext/curb_errors.c +127 -18
- data/ext/curb_errors.h +8 -5
- data/ext/curb_macros.h +10 -6
- data/ext/curb_multi.c +245 -167
- data/ext/curb_multi.h +0 -1
- data/ext/curb_upload.c +2 -2
- data/ext/extconf.rb +314 -20
- data/lib/curb.rb +2 -308
- data/lib/curl/easy.rb +489 -0
- data/lib/curl/multi.rb +287 -0
- data/lib/curl.rb +68 -1
- data/tests/bug_crash_on_debug.rb +39 -0
- data/tests/bug_crash_on_progress.rb +73 -0
- data/tests/bug_curb_easy_blocks_ruby_threads.rb +2 -2
- data/tests/bug_issue102.rb +17 -0
- data/tests/bug_require_last_or_segfault.rb +1 -1
- data/tests/helper.rb +120 -16
- data/tests/signals.rb +33 -0
- data/tests/tc_curl.rb +69 -0
- data/tests/tc_curl_download.rb +4 -4
- data/tests/tc_curl_easy.rb +327 -43
- data/tests/tc_curl_easy_resolve.rb +16 -0
- data/tests/tc_curl_easy_setopt.rb +31 -0
- data/tests/tc_curl_maxfilesize.rb +12 -0
- data/tests/tc_curl_multi.rb +141 -15
- data/tests/tc_curl_postfield.rb +29 -29
- data/tests/tc_curl_protocols.rb +37 -0
- data/tests/timeout.rb +30 -6
- metadata +61 -58
- data/README +0 -177
data/ext/curb_multi.c
CHANGED
|
@@ -3,15 +3,18 @@
|
|
|
3
3
|
* Licensed under the Ruby License. See LICENSE for details.
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
|
-
|
|
7
6
|
#include "curb_config.h"
|
|
8
|
-
#
|
|
9
|
-
|
|
7
|
+
#include <ruby.h>
|
|
8
|
+
#ifdef HAVE_RUBY_ST_H
|
|
10
9
|
#include <ruby/st.h>
|
|
11
10
|
#else
|
|
12
|
-
#include <ruby.h>
|
|
13
11
|
#include <st.h>
|
|
14
12
|
#endif
|
|
13
|
+
|
|
14
|
+
#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
|
|
15
|
+
#include <ruby/thread.h>
|
|
16
|
+
#endif
|
|
17
|
+
|
|
15
18
|
#include "curb_easy.h"
|
|
16
19
|
#include "curb_errors.h"
|
|
17
20
|
#include "curb_postfield.h"
|
|
@@ -34,47 +37,29 @@ static VALUE idCall;
|
|
|
34
37
|
VALUE cCurlMulti;
|
|
35
38
|
|
|
36
39
|
static long cCurlMutiDefaulttimeout = 100; /* milliseconds */
|
|
40
|
+
static char cCurlMutiAutoClose = 0;
|
|
37
41
|
|
|
38
42
|
static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy);
|
|
39
43
|
static void rb_curl_multi_read_info(VALUE self, CURLM *mptr);
|
|
40
44
|
static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running);
|
|
41
45
|
|
|
42
|
-
static
|
|
43
|
-
|
|
46
|
+
static VALUE callback_exception(VALUE unused) {
|
|
47
|
+
return Qfalse;
|
|
44
48
|
}
|
|
45
49
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
50
|
+
void curl_multi_free(ruby_curl_multi *rbcm) {
|
|
51
|
+
curl_multi_cleanup(rbcm->handle);
|
|
52
|
+
free(rbcm);
|
|
49
53
|
}
|
|
50
54
|
|
|
51
|
-
static void
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
|
56
|
-
result = curl_multi_remove_handle(rbcm->handle, rbce->curl);
|
|
57
|
-
if (result != 0) {
|
|
58
|
-
raise_curl_multi_error_exception(result);
|
|
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");
|
|
59
59
|
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
static int
|
|
63
|
-
rb_hash_clear_i(VALUE key, VALUE value, VALUE dummy) {
|
|
64
|
-
return ST_DELETE;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
static void curl_multi_free(ruby_curl_multi *rbcm) {
|
|
68
60
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
rb_hash_foreach( rbcm->requests, (int (*)())curl_multi_flush_easy, (VALUE)rbcm );
|
|
72
|
-
|
|
73
|
-
rb_hash_foreach(rbcm->requests, rb_hash_clear_i, 0); //rb_hash_clear(rbcm->requests);
|
|
74
|
-
rbcm->requests = Qnil;
|
|
75
|
-
}
|
|
76
|
-
curl_multi_cleanup(rbcm->handle);
|
|
77
|
-
free(rbcm);
|
|
61
|
+
rbcm->active = 0;
|
|
62
|
+
rbcm->running = 0;
|
|
78
63
|
}
|
|
79
64
|
|
|
80
65
|
/*
|
|
@@ -84,23 +69,17 @@ static void curl_multi_free(ruby_curl_multi *rbcm) {
|
|
|
84
69
|
* Create a new Curl::Multi instance
|
|
85
70
|
*/
|
|
86
71
|
VALUE ruby_curl_multi_new(VALUE klass) {
|
|
87
|
-
VALUE new_curlm;
|
|
88
|
-
|
|
89
72
|
ruby_curl_multi *rbcm = ALLOC(ruby_curl_multi);
|
|
90
73
|
|
|
91
|
-
rbcm
|
|
92
|
-
if (!rbcm->handle) {
|
|
93
|
-
rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
rbcm->requests = rb_hash_new();
|
|
97
|
-
|
|
98
|
-
rbcm->active = 0;
|
|
99
|
-
rbcm->running = 0;
|
|
100
|
-
|
|
101
|
-
new_curlm = Data_Wrap_Struct(klass, curl_multi_mark, curl_multi_free, rbcm);
|
|
74
|
+
ruby_curl_multi_init(rbcm);
|
|
102
75
|
|
|
103
|
-
|
|
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);
|
|
104
83
|
}
|
|
105
84
|
|
|
106
85
|
/*
|
|
@@ -112,7 +91,7 @@ VALUE ruby_curl_multi_new(VALUE klass) {
|
|
|
112
91
|
*
|
|
113
92
|
*/
|
|
114
93
|
VALUE ruby_curl_multi_set_default_timeout(VALUE klass, VALUE timeout) {
|
|
115
|
-
cCurlMutiDefaulttimeout =
|
|
94
|
+
cCurlMutiDefaulttimeout = NUM2LONG(timeout);
|
|
116
95
|
return timeout;
|
|
117
96
|
}
|
|
118
97
|
|
|
@@ -124,55 +103,39 @@ VALUE ruby_curl_multi_set_default_timeout(VALUE klass, VALUE timeout) {
|
|
|
124
103
|
*
|
|
125
104
|
*/
|
|
126
105
|
VALUE ruby_curl_multi_get_default_timeout(VALUE klass) {
|
|
127
|
-
return
|
|
106
|
+
return LONG2NUM(cCurlMutiDefaulttimeout);
|
|
128
107
|
}
|
|
129
108
|
|
|
130
|
-
/*
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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;
|
|
135
120
|
}
|
|
136
121
|
|
|
137
122
|
/*
|
|
138
123
|
* call-seq:
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
124
|
+
* Curl::Multi.autoclose => true|false
|
|
125
|
+
*
|
|
126
|
+
* Get the global default autoclose setting for all Curl::Multi Handles.
|
|
127
|
+
*
|
|
142
128
|
*/
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
VALUE result_array;
|
|
146
|
-
|
|
147
|
-
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
|
148
|
-
|
|
149
|
-
result_array = rb_ary_new();
|
|
150
|
-
|
|
151
|
-
/* iterate over the requests hash, and stuff references into the array. */
|
|
152
|
-
rb_hash_foreach(rbcm->requests, ruby_curl_multi_requests_callback, result_array);
|
|
153
|
-
|
|
154
|
-
return result_array;
|
|
129
|
+
VALUE ruby_curl_multi_get_autoclose(VALUE klass) {
|
|
130
|
+
return cCurlMutiAutoClose == 1 ? Qtrue : Qfalse;
|
|
155
131
|
}
|
|
156
132
|
|
|
157
133
|
/*
|
|
158
134
|
* call-seq:
|
|
159
|
-
* multi.
|
|
135
|
+
* multi.requests => [#<Curl::Easy...>, ...]
|
|
160
136
|
*
|
|
161
|
-
* Returns
|
|
162
|
-
* true when multi.requests.length == 0.
|
|
137
|
+
* Returns an array containing all the active requests on this Curl::Multi object.
|
|
163
138
|
*/
|
|
164
|
-
static VALUE ruby_curl_multi_idle(VALUE self) {
|
|
165
|
-
ruby_curl_multi *rbcm;
|
|
166
|
-
|
|
167
|
-
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
|
168
|
-
|
|
169
|
-
if ( FIX2INT( rb_funcall(rbcm->requests, rb_intern("length"), 0) ) == 0 ) {
|
|
170
|
-
return Qtrue;
|
|
171
|
-
} else {
|
|
172
|
-
return Qfalse;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
139
|
/*
|
|
177
140
|
* call-seq:
|
|
178
141
|
* multi = Curl::Multi.new
|
|
@@ -185,7 +148,8 @@ static VALUE ruby_curl_multi_max_connects(VALUE self, VALUE count) {
|
|
|
185
148
|
ruby_curl_multi *rbcm;
|
|
186
149
|
|
|
187
150
|
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
|
188
|
-
|
|
151
|
+
|
|
152
|
+
curl_multi_setopt(rbcm->handle, CURLMOPT_MAXCONNECTS, NUM2LONG(count));
|
|
189
153
|
#endif
|
|
190
154
|
|
|
191
155
|
return count;
|
|
@@ -196,20 +160,31 @@ static VALUE ruby_curl_multi_max_connects(VALUE self, VALUE count) {
|
|
|
196
160
|
* multi = Curl::Multi.new
|
|
197
161
|
* multi.pipeline = true
|
|
198
162
|
*
|
|
199
|
-
* Pass a long set to 1
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
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)
|
|
203
168
|
*
|
|
204
169
|
*/
|
|
205
|
-
static VALUE ruby_curl_multi_pipeline(VALUE self, VALUE
|
|
170
|
+
static VALUE ruby_curl_multi_pipeline(VALUE self, VALUE method) {
|
|
206
171
|
#ifdef HAVE_CURLMOPT_PIPELINING
|
|
207
172
|
ruby_curl_multi *rbcm;
|
|
208
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
|
+
|
|
209
184
|
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
|
210
|
-
curl_multi_setopt(rbcm->handle, CURLMOPT_PIPELINING,
|
|
185
|
+
curl_multi_setopt(rbcm->handle, CURLMOPT_PIPELINING, value);
|
|
211
186
|
#endif
|
|
212
|
-
return
|
|
187
|
+
return method == Qtrue ? 1 : 0;
|
|
213
188
|
}
|
|
214
189
|
|
|
215
190
|
/*
|
|
@@ -243,9 +218,8 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
|
|
243
218
|
* If this number is not correct, the next call to curl_multi_perform will correct it. */
|
|
244
219
|
rbcm->running++;
|
|
245
220
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
|
221
|
+
/* track a reference to associated multi handle */
|
|
222
|
+
rbce->multi = self;
|
|
249
223
|
|
|
250
224
|
return self;
|
|
251
225
|
}
|
|
@@ -264,25 +238,20 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
|
|
264
238
|
*
|
|
265
239
|
* Will raise an exception if the easy handle is not found
|
|
266
240
|
*/
|
|
267
|
-
VALUE ruby_curl_multi_remove(VALUE self, VALUE
|
|
241
|
+
VALUE ruby_curl_multi_remove(VALUE self, VALUE rb_easy_handle) {
|
|
268
242
|
ruby_curl_multi *rbcm;
|
|
269
|
-
ruby_curl_easy *rbce;
|
|
270
243
|
|
|
271
244
|
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
|
272
245
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
rb_curl_multi_remove(rbcm,easy);
|
|
246
|
+
rb_curl_multi_remove(rbcm, rb_easy_handle);
|
|
276
247
|
|
|
277
248
|
return self;
|
|
278
249
|
}
|
|
279
250
|
static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
|
|
280
251
|
CURLMcode result;
|
|
281
252
|
ruby_curl_easy *rbce;
|
|
282
|
-
VALUE r;
|
|
283
253
|
|
|
284
254
|
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
|
285
|
-
|
|
286
255
|
result = curl_multi_remove_handle(rbcm->handle, rbce->curl);
|
|
287
256
|
if (result != 0) {
|
|
288
257
|
raise_curl_multi_error_exception(result);
|
|
@@ -291,43 +260,21 @@ static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
|
|
|
291
260
|
rbcm->active--;
|
|
292
261
|
|
|
293
262
|
ruby_curl_easy_cleanup( easy, rbce );
|
|
294
|
-
|
|
295
|
-
// active should equal INT2FIX(RHASH(rbcm->requests)->tbl->num_entries)
|
|
296
|
-
r = rb_hash_delete( rbcm->requests, easy );
|
|
297
|
-
if( r != easy || r == Qnil ) {
|
|
298
|
-
rb_warn("Possibly lost track of Curl::Easy VALUE, it may not be reclaimed by GC");
|
|
299
|
-
}
|
|
300
263
|
}
|
|
301
264
|
|
|
302
|
-
|
|
303
|
-
static
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
return ST_CONTINUE;
|
|
265
|
+
// on_success, on_failure, on_complete
|
|
266
|
+
static VALUE call_status_handler1(VALUE ary) {
|
|
267
|
+
return rb_funcall(rb_ary_entry(ary, 0), idCall, 1, rb_ary_entry(ary, 1));
|
|
307
268
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
* call-seq:
|
|
311
|
-
* multi.cancel!
|
|
312
|
-
*
|
|
313
|
-
* Cancels all requests currently being made on this Curl::Multi handle.
|
|
314
|
-
*/
|
|
315
|
-
static VALUE ruby_curl_multi_cancel(VALUE self) {
|
|
316
|
-
ruby_curl_multi *rbcm;
|
|
317
|
-
|
|
318
|
-
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
|
319
|
-
|
|
320
|
-
rb_hash_foreach( rbcm->requests, ruby_curl_multi_cancel_callback, (VALUE)rbcm );
|
|
321
|
-
|
|
322
|
-
/* for chaining */
|
|
323
|
-
return self;
|
|
269
|
+
static VALUE call_status_handler2(VALUE ary) {
|
|
270
|
+
return rb_funcall(rb_ary_entry(ary, 0), idCall, 2, rb_ary_entry(ary, 1), rb_ary_entry(ary, 2));
|
|
324
271
|
}
|
|
325
272
|
|
|
326
273
|
static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int result) {
|
|
327
|
-
|
|
328
274
|
long response_code = -1;
|
|
329
275
|
VALUE easy;
|
|
330
276
|
ruby_curl_easy *rbce = NULL;
|
|
277
|
+
VALUE callargs, val = Qtrue;
|
|
331
278
|
|
|
332
279
|
CURLcode ecode = curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, (char**)&easy);
|
|
333
280
|
|
|
@@ -335,7 +282,8 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
|
|
|
335
282
|
|
|
336
283
|
rbce->last_result = result; /* save the last easy result code */
|
|
337
284
|
|
|
338
|
-
|
|
285
|
+
// remove the easy handle from multi on completion so it can be reused again
|
|
286
|
+
rb_funcall(self, rb_intern("remove"), 1, easy);
|
|
339
287
|
|
|
340
288
|
/* after running a request cleanup the headers, these are set before each request */
|
|
341
289
|
if (rbce->curl_headers) {
|
|
@@ -348,42 +296,80 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
|
|
|
348
296
|
}
|
|
349
297
|
|
|
350
298
|
if (!rb_easy_nil("complete_proc")) {
|
|
351
|
-
|
|
299
|
+
callargs = rb_ary_new3(2, rb_easy_get("complete_proc"), easy);
|
|
300
|
+
rbce->callback_active = 1;
|
|
301
|
+
val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
|
|
302
|
+
rbce->callback_active = 0;
|
|
303
|
+
//rb_funcall( rb_easy_get("complete_proc"), idCall, 1, easy );
|
|
352
304
|
}
|
|
353
305
|
|
|
354
306
|
curl_easy_getinfo(rbce->curl, CURLINFO_RESPONSE_CODE, &response_code);
|
|
355
307
|
|
|
356
308
|
if (result != 0) {
|
|
357
309
|
if (!rb_easy_nil("failure_proc")) {
|
|
358
|
-
|
|
310
|
+
callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
|
|
311
|
+
rbce->callback_active = 1;
|
|
312
|
+
val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
|
|
313
|
+
rbce->callback_active = 0;
|
|
314
|
+
//rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
|
|
359
315
|
}
|
|
360
316
|
}
|
|
361
317
|
else if (!rb_easy_nil("success_proc") &&
|
|
362
318
|
((response_code >= 200 && response_code < 300) || response_code == 0)) {
|
|
363
319
|
/* NOTE: we allow response_code == 0, in the case of non http requests e.g. reading from disk */
|
|
364
|
-
|
|
320
|
+
callargs = rb_ary_new3(2, rb_easy_get("success_proc"), easy);
|
|
321
|
+
rbce->callback_active = 1;
|
|
322
|
+
val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
|
|
323
|
+
rbce->callback_active = 0;
|
|
324
|
+
//rb_funcall( rb_easy_get("success_proc"), idCall, 1, easy );
|
|
325
|
+
}
|
|
326
|
+
else if (!rb_easy_nil("redirect_proc") &&
|
|
327
|
+
(response_code >= 300 && response_code < 400)) {
|
|
328
|
+
rbce->callback_active = 1;
|
|
329
|
+
callargs = rb_ary_new3(3, rb_easy_get("redirect_proc"), easy, rb_curl_easy_error(result));
|
|
330
|
+
rbce->callback_active = 0;
|
|
331
|
+
val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
|
|
332
|
+
}
|
|
333
|
+
else if (!rb_easy_nil("missing_proc") &&
|
|
334
|
+
(response_code >= 400 && response_code < 500)) {
|
|
335
|
+
rbce->callback_active = 1;
|
|
336
|
+
callargs = rb_ary_new3(3, rb_easy_get("missing_proc"), easy, rb_curl_easy_error(result));
|
|
337
|
+
rbce->callback_active = 0;
|
|
338
|
+
val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
|
|
365
339
|
}
|
|
366
340
|
else if (!rb_easy_nil("failure_proc") &&
|
|
367
|
-
(response_code >=
|
|
368
|
-
|
|
341
|
+
(response_code >= 500 && response_code <= 999)) {
|
|
342
|
+
callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
|
|
343
|
+
rbce->callback_active = 1;
|
|
344
|
+
val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
|
|
345
|
+
rbce->callback_active = 0;
|
|
346
|
+
//rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
|
|
369
347
|
}
|
|
370
348
|
|
|
371
349
|
}
|
|
372
350
|
|
|
373
351
|
static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
|
|
374
|
-
int msgs_left
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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);
|
|
387
373
|
}
|
|
388
374
|
}
|
|
389
375
|
|
|
@@ -391,14 +377,32 @@ static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
|
|
|
391
377
|
static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running) {
|
|
392
378
|
CURLMcode mcode;
|
|
393
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
|
+
*/
|
|
394
387
|
do {
|
|
395
388
|
mcode = curl_multi_perform(multi_handle, still_running);
|
|
396
389
|
} while (mcode == CURLM_CALL_MULTI_PERFORM);
|
|
397
390
|
|
|
391
|
+
/*
|
|
392
|
+
* Nothing more to do, check if an error occured in the loop above and raise an exception if necessary.
|
|
393
|
+
*/
|
|
394
|
+
|
|
398
395
|
if (mcode != CURLM_OK) {
|
|
399
396
|
raise_curl_multi_error_exception(mcode);
|
|
400
397
|
}
|
|
401
|
-
|
|
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
|
+
*/
|
|
402
406
|
}
|
|
403
407
|
|
|
404
408
|
#ifdef _WIN32
|
|
@@ -430,6 +434,20 @@ void cleanup_crt_fd(fd_set *os_set, fd_set *crt_set)
|
|
|
430
434
|
}
|
|
431
435
|
#endif
|
|
432
436
|
|
|
437
|
+
#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
|
|
438
|
+
struct _select_set {
|
|
439
|
+
int maxfd;
|
|
440
|
+
fd_set *fdread, *fdwrite, *fdexcep;
|
|
441
|
+
struct timeval *tv;
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
static VALUE curb_select(void *args) {
|
|
445
|
+
struct _select_set* set = args;
|
|
446
|
+
int rc = select(set->maxfd, set->fdread, set->fdwrite, set->fdexcep, set->tv);
|
|
447
|
+
return INT2FIX(rc);
|
|
448
|
+
}
|
|
449
|
+
#endif
|
|
450
|
+
|
|
433
451
|
/*
|
|
434
452
|
* call-seq:
|
|
435
453
|
* multi = Curl::Multi.new
|
|
@@ -448,15 +466,18 @@ void cleanup_crt_fd(fd_set *os_set, fd_set *crt_set)
|
|
|
448
466
|
VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
449
467
|
CURLMcode mcode;
|
|
450
468
|
ruby_curl_multi *rbcm;
|
|
451
|
-
int maxfd, rc;
|
|
469
|
+
int maxfd, rc = -1;
|
|
452
470
|
fd_set fdread, fdwrite, fdexcep;
|
|
453
471
|
#ifdef _WIN32
|
|
454
472
|
fd_set crt_fdread, crt_fdwrite, crt_fdexcep;
|
|
455
473
|
#endif
|
|
456
|
-
|
|
457
474
|
long timeout_milliseconds;
|
|
458
475
|
struct timeval tv = {0, 0};
|
|
476
|
+
struct timeval tv_100ms = {0, 100000};
|
|
459
477
|
VALUE block = Qnil;
|
|
478
|
+
#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
|
|
479
|
+
struct _select_set fdset_args;
|
|
480
|
+
#endif
|
|
460
481
|
|
|
461
482
|
rb_scan_args(argc, argv, "0&", &block);
|
|
462
483
|
|
|
@@ -464,8 +485,23 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
464
485
|
|
|
465
486
|
timeout_milliseconds = cCurlMutiDefaulttimeout;
|
|
466
487
|
|
|
488
|
+
// Run curl_multi_perform for the first time to get the ball rolling
|
|
467
489
|
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
|
468
|
-
|
|
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.
|
|
496
|
+
rb_curl_multi_read_info( self, rbcm->handle );
|
|
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
|
+
|
|
469
505
|
do {
|
|
470
506
|
while (rbcm->running) {
|
|
471
507
|
|
|
@@ -505,13 +541,39 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
505
541
|
raise_curl_multi_error_exception(mcode);
|
|
506
542
|
}
|
|
507
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
|
+
|
|
508
553
|
#ifdef _WIN32
|
|
509
554
|
create_crt_fd(&fdread, &crt_fdread);
|
|
510
555
|
create_crt_fd(&fdwrite, &crt_fdwrite);
|
|
511
556
|
create_crt_fd(&fdexcep, &crt_fdexcep);
|
|
512
557
|
#endif
|
|
513
558
|
|
|
559
|
+
|
|
560
|
+
#if (defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL))
|
|
561
|
+
fdset_args.maxfd = maxfd+1;
|
|
562
|
+
fdset_args.fdread = &fdread;
|
|
563
|
+
fdset_args.fdwrite = &fdwrite;
|
|
564
|
+
fdset_args.fdexcep = &fdexcep;
|
|
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
|
|
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);
|
|
574
|
+
#else
|
|
514
575
|
rc = rb_thread_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
|
|
576
|
+
#endif
|
|
515
577
|
|
|
516
578
|
#ifdef _WIN32
|
|
517
579
|
cleanup_crt_fd(&fdread, &crt_fdread);
|
|
@@ -521,8 +583,10 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
521
583
|
|
|
522
584
|
switch(rc) {
|
|
523
585
|
case -1:
|
|
524
|
-
|
|
525
|
-
|
|
586
|
+
if(errno != EINTR) {
|
|
587
|
+
rb_raise(rb_eRuntimeError, "select(): %s", strerror(errno));
|
|
588
|
+
break;
|
|
589
|
+
}
|
|
526
590
|
case 0: /* timeout */
|
|
527
591
|
default: /* action */
|
|
528
592
|
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
|
@@ -536,30 +600,44 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
|
|
536
600
|
|
|
537
601
|
rb_curl_multi_read_info( self, rbcm->handle );
|
|
538
602
|
if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
|
|
539
|
-
|
|
603
|
+
if (cCurlMutiAutoClose == 1) {
|
|
604
|
+
rb_funcall(self, rb_intern("close"), 0);
|
|
605
|
+
}
|
|
540
606
|
return Qtrue;
|
|
541
607
|
}
|
|
542
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
|
+
|
|
543
625
|
/* =================== INIT LIB =====================*/
|
|
544
626
|
void init_curb_multi() {
|
|
545
627
|
idCall = rb_intern("call");
|
|
546
|
-
|
|
547
628
|
cCurlMulti = rb_define_class_under(mCurl, "Multi", rb_cObject);
|
|
548
629
|
|
|
549
630
|
/* Class methods */
|
|
550
631
|
rb_define_singleton_method(cCurlMulti, "new", ruby_curl_multi_new, 0);
|
|
551
632
|
rb_define_singleton_method(cCurlMulti, "default_timeout=", ruby_curl_multi_set_default_timeout, 1);
|
|
552
633
|
rb_define_singleton_method(cCurlMulti, "default_timeout", ruby_curl_multi_get_default_timeout, 0);
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
rb_define_method(cCurlMulti, "idle?", ruby_curl_multi_idle, 0);
|
|
557
|
-
|
|
558
|
-
/* 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 */
|
|
559
637
|
rb_define_method(cCurlMulti, "max_connects=", ruby_curl_multi_max_connects, 1);
|
|
560
638
|
rb_define_method(cCurlMulti, "pipeline=", ruby_curl_multi_pipeline, 1);
|
|
561
|
-
rb_define_method(cCurlMulti, "
|
|
562
|
-
rb_define_method(cCurlMulti, "
|
|
563
|
-
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);
|
|
564
641
|
rb_define_method(cCurlMulti, "perform", ruby_curl_multi_perform, -1);
|
|
642
|
+
rb_define_method(cCurlMulti, "_close", ruby_curl_multi_close, 0);
|
|
565
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 =====================*/
|