ghazel-curb 0.5.9.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.
- data/LICENSE +51 -0
- data/README +157 -0
- data/Rakefile +297 -0
- data/doc.rb +42 -0
- data/ext/curb.c +339 -0
- data/ext/curb.h +50 -0
- data/ext/curb_easy.c +2932 -0
- data/ext/curb_easy.h +103 -0
- data/ext/curb_errors.c +630 -0
- data/ext/curb_errors.h +128 -0
- data/ext/curb_macros.h +114 -0
- data/ext/curb_multi.c +487 -0
- data/ext/curb_multi.h +26 -0
- data/ext/curb_postfield.c +511 -0
- data/ext/curb_postfield.h +40 -0
- data/ext/curb_upload.c +80 -0
- data/ext/curb_upload.h +30 -0
- data/ext/extconf.rb +162 -0
- data/lib/curb.rb +184 -0
- data/lib/curl.rb +2 -0
- data/tests/alltests.rb +3 -0
- data/tests/bug_curb_easy_blocks_ruby_threads.rb +52 -0
- data/tests/bug_instance_post_differs_from_class_post.rb +53 -0
- data/tests/bug_multi_segfault.rb +10 -0
- data/tests/bug_require_last_or_segfault.rb +40 -0
- data/tests/helper.rb +168 -0
- data/tests/require_last_or_segfault_script.rb +36 -0
- data/tests/tc_curl_download.rb +32 -0
- data/tests/tc_curl_easy.rb +664 -0
- data/tests/tc_curl_multi.rb +421 -0
- data/tests/tc_curl_postfield.rb +141 -0
- data/tests/unittests.rb +2 -0
- metadata +89 -0
data/ext/curb_multi.c
ADDED
@@ -0,0 +1,487 @@
|
|
1
|
+
/* curb_multi.c - Curl multi mode
|
2
|
+
* Copyright (c)2008 Todd A. Fisher.
|
3
|
+
* Licensed under the Ruby License. See LICENSE for details.
|
4
|
+
*
|
5
|
+
*/
|
6
|
+
|
7
|
+
#include "curb_config.h"
|
8
|
+
#ifdef HAVE_RUBY19_ST_H
|
9
|
+
#include <ruby.h>
|
10
|
+
#include <ruby/st.h>
|
11
|
+
#else
|
12
|
+
#include <ruby.h>
|
13
|
+
#include <st.h>
|
14
|
+
#endif
|
15
|
+
#include "curb_easy.h"
|
16
|
+
#include "curb_errors.h"
|
17
|
+
#include "curb_postfield.h"
|
18
|
+
#include "curb_multi.h"
|
19
|
+
|
20
|
+
#include <errno.h>
|
21
|
+
|
22
|
+
extern VALUE mCurl;
|
23
|
+
static VALUE idCall;
|
24
|
+
|
25
|
+
#ifdef RDOC_NEVER_DEFINED
|
26
|
+
mCurl = rb_define_module("Curl");
|
27
|
+
#endif
|
28
|
+
|
29
|
+
VALUE cCurlMulti;
|
30
|
+
|
31
|
+
static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy);
|
32
|
+
static void rb_curl_multi_read_info(VALUE self, CURLM *mptr);
|
33
|
+
|
34
|
+
static void rb_curl_multi_mark_all_easy(VALUE key, VALUE rbeasy, ruby_curl_multi *rbcm) {
|
35
|
+
//printf( "mark easy: 0x%X\n", (long)rbeasy );
|
36
|
+
rb_gc_mark(rbeasy);
|
37
|
+
}
|
38
|
+
|
39
|
+
static void curl_multi_mark(ruby_curl_multi *rbcm) {
|
40
|
+
rb_gc_mark(rbcm->requests);
|
41
|
+
rb_hash_foreach( rbcm->requests, (int (*)())rb_curl_multi_mark_all_easy, (VALUE)rbcm );
|
42
|
+
}
|
43
|
+
|
44
|
+
static void curl_multi_flush_easy(VALUE key, VALUE easy, ruby_curl_multi *rbcm) {
|
45
|
+
//rb_curl_multi_remove(rbcm, easy);
|
46
|
+
CURLMcode result;
|
47
|
+
ruby_curl_easy *rbce;
|
48
|
+
VALUE r;
|
49
|
+
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
50
|
+
result = curl_multi_remove_handle(rbcm->handle, rbce->curl);
|
51
|
+
if (result != 0) {
|
52
|
+
raise_curl_multi_error_exception(result);
|
53
|
+
}
|
54
|
+
// XXX: easy handle may not be finished yet... so don't clean it GC pass will get it next time
|
55
|
+
r = rb_hash_delete( rbcm->requests, easy );
|
56
|
+
if( r != easy || r == Qnil ) {
|
57
|
+
rb_raise(rb_eRuntimeError, "Critical:: Unable to remove easy from requests");
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
static int
|
62
|
+
rb_hash_clear_i(VALUE key, VALUE value, VALUE dummy) {
|
63
|
+
return ST_DELETE;
|
64
|
+
}
|
65
|
+
|
66
|
+
static void curl_multi_free(ruby_curl_multi *rbcm) {
|
67
|
+
|
68
|
+
//printf("hash entries: %d\n", RHASH(rbcm->requests)->tbl->num_entries );
|
69
|
+
if (rbcm && !rbcm->requests == Qnil && rb_type(rbcm->requests) == T_HASH && RHASH_LEN(rbcm->requests) > 0) {
|
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);
|
78
|
+
}
|
79
|
+
|
80
|
+
/*
|
81
|
+
* call-seq:
|
82
|
+
* Curl::Multi.new => #<Curl::Easy...>
|
83
|
+
*
|
84
|
+
* Create a new Curl::Multi instance
|
85
|
+
*/
|
86
|
+
VALUE ruby_curl_multi_new(VALUE klass) {
|
87
|
+
VALUE new_curlm;
|
88
|
+
|
89
|
+
ruby_curl_multi *rbcm = ALLOC(ruby_curl_multi);
|
90
|
+
|
91
|
+
rbcm->handle = curl_multi_init();
|
92
|
+
|
93
|
+
rbcm->requests = rb_hash_new();
|
94
|
+
|
95
|
+
rbcm->active = 0;
|
96
|
+
rbcm->running = 0;
|
97
|
+
|
98
|
+
new_curlm = Data_Wrap_Struct(klass, curl_multi_mark, curl_multi_free, rbcm);
|
99
|
+
|
100
|
+
return new_curlm;
|
101
|
+
}
|
102
|
+
|
103
|
+
// Hash#foreach callback for ruby_curl_multi_requests
|
104
|
+
static int ruby_curl_multi_requests_callback(VALUE key, VALUE value, VALUE result_array) {
|
105
|
+
rb_ary_push(result_array, value);
|
106
|
+
|
107
|
+
return ST_CONTINUE;
|
108
|
+
}
|
109
|
+
|
110
|
+
/*
|
111
|
+
* call-seq:
|
112
|
+
* multi.requests => [#<Curl::Easy...>, ...]
|
113
|
+
*
|
114
|
+
* Returns an array containing all the active requests on this Curl::Multi object.
|
115
|
+
*/
|
116
|
+
static VALUE ruby_curl_multi_requests(VALUE self) {
|
117
|
+
ruby_curl_multi *rbcm;
|
118
|
+
VALUE result_array;
|
119
|
+
|
120
|
+
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
121
|
+
|
122
|
+
result_array = rb_ary_new();
|
123
|
+
|
124
|
+
// iterate over the requests hash, and stuff references into the array.
|
125
|
+
rb_hash_foreach( rbcm->requests, ruby_curl_multi_requests_callback, result_array );
|
126
|
+
|
127
|
+
return result_array;
|
128
|
+
}
|
129
|
+
|
130
|
+
/*
|
131
|
+
* call-seq:
|
132
|
+
* multi.idle? => true or false
|
133
|
+
*
|
134
|
+
* Returns whether or not this Curl::Multi handle is processing any requests. E.g. this returns
|
135
|
+
* true when multi.requests.length == 0.
|
136
|
+
*/
|
137
|
+
static VALUE ruby_curl_multi_idle(VALUE self) {
|
138
|
+
ruby_curl_multi *rbcm;
|
139
|
+
|
140
|
+
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
141
|
+
|
142
|
+
if ( FIX2INT( rb_funcall(rbcm->requests, rb_intern("length"), 0) ) == 0 ) {
|
143
|
+
return Qtrue;
|
144
|
+
} else {
|
145
|
+
return Qfalse;
|
146
|
+
}
|
147
|
+
}
|
148
|
+
|
149
|
+
/*
|
150
|
+
* call-seq:
|
151
|
+
* multi = Curl::Multi.new
|
152
|
+
* multi.max_connects = 800
|
153
|
+
*
|
154
|
+
* Set the max connections in the cache for a multi handle
|
155
|
+
*/
|
156
|
+
static VALUE ruby_curl_multi_max_connects(VALUE self, VALUE count) {
|
157
|
+
#ifdef HAVE_CURLMOPT_MAXCONNECTS
|
158
|
+
ruby_curl_multi *rbcm;
|
159
|
+
|
160
|
+
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
161
|
+
curl_multi_setopt(rbcm->handle, CURLMOPT_MAXCONNECTS, NUM2INT(count));
|
162
|
+
#endif
|
163
|
+
|
164
|
+
return count;
|
165
|
+
}
|
166
|
+
|
167
|
+
/*
|
168
|
+
* call-seq:
|
169
|
+
* multi = Curl::Multi.new
|
170
|
+
* multi.pipeline = true
|
171
|
+
*
|
172
|
+
* Pass a long set to 1 to enable or 0 to disable. Enabling pipelining on a multi handle will make it
|
173
|
+
* attempt to perform HTTP Pipelining as far as possible for transfers using this handle. This means
|
174
|
+
* that if you add a second request that can use an already existing connection, the second request will
|
175
|
+
* be "piped" on the same connection rather than being executed in parallel. (Added in 7.16.0)
|
176
|
+
*
|
177
|
+
*/
|
178
|
+
static VALUE ruby_curl_multi_pipeline(VALUE self, VALUE onoff) {
|
179
|
+
#ifdef HAVE_CURLMOPT_PIPELINING
|
180
|
+
ruby_curl_multi *rbcm;
|
181
|
+
|
182
|
+
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
183
|
+
curl_multi_setopt(rbcm->handle, CURLMOPT_PIPELINING, onoff == Qtrue ? 1 : 0);
|
184
|
+
#endif
|
185
|
+
return onoff;
|
186
|
+
}
|
187
|
+
|
188
|
+
/*
|
189
|
+
* call-seq:
|
190
|
+
* multi = Curl::Multi.new
|
191
|
+
* easy = Curl::Easy.new('url')
|
192
|
+
*
|
193
|
+
* multi.add(easy)
|
194
|
+
*
|
195
|
+
* Add an easy handle to the multi stack
|
196
|
+
*/
|
197
|
+
VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
198
|
+
CURLMcode mcode;
|
199
|
+
ruby_curl_easy *rbce;
|
200
|
+
ruby_curl_multi *rbcm;
|
201
|
+
|
202
|
+
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
203
|
+
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
204
|
+
|
205
|
+
mcode = curl_multi_add_handle(rbcm->handle, rbce->curl);
|
206
|
+
if (mcode != CURLM_CALL_MULTI_PERFORM && mcode != CURLM_OK) {
|
207
|
+
raise_curl_multi_error_exception(mcode);
|
208
|
+
}
|
209
|
+
|
210
|
+
/* save a pointer to self */
|
211
|
+
rbce->self = easy;
|
212
|
+
|
213
|
+
/* setup the easy handle */
|
214
|
+
ruby_curl_easy_setup( rbce, &(rbce->bodybuf), &(rbce->headerbuf), &(rbce->curl_headers) );
|
215
|
+
|
216
|
+
rbcm->active++;
|
217
|
+
|
218
|
+
/* Increase the running count, so that the perform loop keeps running.
|
219
|
+
* If this number is not correct, the next call to curl_multi_perform will correct it. */
|
220
|
+
rbcm->running++;
|
221
|
+
|
222
|
+
rb_hash_aset( rbcm->requests, easy, easy );
|
223
|
+
|
224
|
+
return self;
|
225
|
+
}
|
226
|
+
|
227
|
+
/*
|
228
|
+
* call-seq:
|
229
|
+
* multi = Curl::Multi.new
|
230
|
+
* easy = Curl::Easy.new('url')
|
231
|
+
*
|
232
|
+
* multi.add(easy)
|
233
|
+
*
|
234
|
+
* # sometime later
|
235
|
+
* multi.remove(easy)
|
236
|
+
*
|
237
|
+
* Remove an easy handle from a multi stack.
|
238
|
+
*
|
239
|
+
* Will raise an exception if the easy handle is not found
|
240
|
+
*/
|
241
|
+
VALUE ruby_curl_multi_remove(VALUE self, VALUE easy) {
|
242
|
+
ruby_curl_multi *rbcm;
|
243
|
+
ruby_curl_easy *rbce;
|
244
|
+
|
245
|
+
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
246
|
+
|
247
|
+
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
248
|
+
|
249
|
+
rb_curl_multi_remove(rbcm,easy);
|
250
|
+
|
251
|
+
return self;
|
252
|
+
}
|
253
|
+
static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
|
254
|
+
CURLMcode result;
|
255
|
+
ruby_curl_easy *rbce;
|
256
|
+
VALUE r;
|
257
|
+
|
258
|
+
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
259
|
+
|
260
|
+
result = curl_multi_remove_handle(rbcm->handle, rbce->curl);
|
261
|
+
if (result != 0) {
|
262
|
+
raise_curl_multi_error_exception(result);
|
263
|
+
}
|
264
|
+
|
265
|
+
rbcm->active--;
|
266
|
+
|
267
|
+
ruby_curl_easy_cleanup( easy, rbce, rbce->bodybuf, rbce->headerbuf, rbce->curl_headers );
|
268
|
+
rbce->headerbuf = Qnil;
|
269
|
+
rbce->bodybuf = Qnil;
|
270
|
+
|
271
|
+
// active should equal INT2FIX(RHASH(rbcm->requests)->tbl->num_entries)
|
272
|
+
r = rb_hash_delete( rbcm->requests, easy );
|
273
|
+
if( r != easy || r == Qnil ) {
|
274
|
+
rb_raise(rb_eRuntimeError, "Critical:: Unable to remove easy from requests");
|
275
|
+
}
|
276
|
+
}
|
277
|
+
|
278
|
+
// Hash#foreach callback for ruby_curl_multi_cancel
|
279
|
+
static int ruby_curl_multi_cancel_callback(VALUE key, VALUE value, ruby_curl_multi *rbcm) {
|
280
|
+
rb_curl_multi_remove(rbcm, value);
|
281
|
+
|
282
|
+
return ST_CONTINUE;
|
283
|
+
}
|
284
|
+
|
285
|
+
/*
|
286
|
+
* call-seq:
|
287
|
+
* multi.cancel!
|
288
|
+
*
|
289
|
+
* Cancels all requests currently being made on this Curl::Multi handle.
|
290
|
+
*/
|
291
|
+
static VALUE ruby_curl_multi_cancel(VALUE self) {
|
292
|
+
ruby_curl_multi *rbcm;
|
293
|
+
|
294
|
+
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
295
|
+
|
296
|
+
rb_hash_foreach( rbcm->requests, ruby_curl_multi_cancel_callback, (VALUE)rbcm );
|
297
|
+
|
298
|
+
// for chaining
|
299
|
+
return self;
|
300
|
+
}
|
301
|
+
|
302
|
+
static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int result) {
|
303
|
+
|
304
|
+
long response_code = -1;
|
305
|
+
ruby_curl_easy *rbce = NULL;
|
306
|
+
VALUE ref;
|
307
|
+
CURLcode ecode = curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, (char**)&rbce);
|
308
|
+
|
309
|
+
if (ecode != 0) {
|
310
|
+
raise_curl_easy_error_exception(ecode);
|
311
|
+
}
|
312
|
+
|
313
|
+
rbce->last_result = result; /* save the last easy result code */
|
314
|
+
|
315
|
+
ruby_curl_multi_remove( self, rbce->self );
|
316
|
+
|
317
|
+
if (rbce->complete_proc != Qnil) {
|
318
|
+
rb_funcall( rbce->complete_proc, idCall, 1, rbce->self );
|
319
|
+
}
|
320
|
+
|
321
|
+
curl_easy_getinfo(rbce->curl, CURLINFO_RESPONSE_CODE, &response_code);
|
322
|
+
|
323
|
+
ref = rbce->self;
|
324
|
+
/* break reference */
|
325
|
+
rbce->self = Qnil;
|
326
|
+
|
327
|
+
if (result != 0) {
|
328
|
+
if (rbce->failure_proc != Qnil) {
|
329
|
+
rb_funcall( rbce->failure_proc, idCall, 2, ref, rb_curl_easy_error(result) );
|
330
|
+
}
|
331
|
+
}
|
332
|
+
else if (rbce->success_proc != Qnil &&
|
333
|
+
((response_code >= 200 && response_code < 300) || response_code == 0)) {
|
334
|
+
/* NOTE: we allow response_code == 0, in the case of non http requests e.g. reading from disk */
|
335
|
+
rb_funcall( rbce->success_proc, idCall, 1, ref );
|
336
|
+
}
|
337
|
+
else if (rbce->failure_proc != Qnil &&
|
338
|
+
(response_code >= 300 && response_code <= 999)) {
|
339
|
+
rb_funcall( rbce->failure_proc, idCall, 2, ref, rb_curl_easy_error(result) );
|
340
|
+
}
|
341
|
+
}
|
342
|
+
|
343
|
+
static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
|
344
|
+
int msgs_left, result;
|
345
|
+
CURLMsg *msg;
|
346
|
+
CURL *easy_handle;
|
347
|
+
|
348
|
+
/* check for finished easy handles and remove from the multi handle */
|
349
|
+
while ((msg = curl_multi_info_read(multi_handle, &msgs_left))) {
|
350
|
+
if (msg->msg == CURLMSG_DONE) {
|
351
|
+
easy_handle = msg->easy_handle;
|
352
|
+
result = msg->data.result;
|
353
|
+
if (easy_handle) {
|
354
|
+
rb_curl_mutli_handle_complete(self, easy_handle, result);
|
355
|
+
}
|
356
|
+
}
|
357
|
+
}
|
358
|
+
}
|
359
|
+
|
360
|
+
/* called within ruby_curl_multi_perform */
|
361
|
+
static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running) {
|
362
|
+
CURLMcode mcode;
|
363
|
+
|
364
|
+
do {
|
365
|
+
mcode = curl_multi_perform(multi_handle, still_running);
|
366
|
+
} while (mcode == CURLM_CALL_MULTI_PERFORM);
|
367
|
+
|
368
|
+
if (mcode != CURLM_OK) {
|
369
|
+
raise_curl_multi_error_exception(mcode);
|
370
|
+
}
|
371
|
+
|
372
|
+
rb_curl_multi_read_info( self, multi_handle );
|
373
|
+
}
|
374
|
+
|
375
|
+
/*
|
376
|
+
* call-seq:
|
377
|
+
* multi = Curl::Multi.new
|
378
|
+
* easy1 = Curl::Easy.new('url')
|
379
|
+
* easy2 = Curl::Easy.new('url')
|
380
|
+
*
|
381
|
+
* multi.add(easy1)
|
382
|
+
* multi.add(easy2)
|
383
|
+
*
|
384
|
+
* multi.perform do
|
385
|
+
* # while idle other code my execute here
|
386
|
+
* end
|
387
|
+
*
|
388
|
+
* Run multi handles, looping selecting when data can be transfered
|
389
|
+
*/
|
390
|
+
VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
|
391
|
+
CURLMcode mcode;
|
392
|
+
ruby_curl_multi *rbcm;
|
393
|
+
int maxfd, rc;
|
394
|
+
fd_set fdread, fdwrite, fdexcep;
|
395
|
+
|
396
|
+
long timeout_milliseconds;
|
397
|
+
struct timeval tv = {0, 0};
|
398
|
+
VALUE block = Qnil;
|
399
|
+
|
400
|
+
rb_scan_args(argc, argv, "0&", &block);
|
401
|
+
|
402
|
+
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
403
|
+
|
404
|
+
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
405
|
+
|
406
|
+
while(rbcm->running) {
|
407
|
+
FD_ZERO(&fdread);
|
408
|
+
FD_ZERO(&fdwrite);
|
409
|
+
FD_ZERO(&fdexcep);
|
410
|
+
|
411
|
+
/* load the fd sets from the multi handle */
|
412
|
+
mcode = curl_multi_fdset(rbcm->handle, &fdread, &fdwrite, &fdexcep, &maxfd);
|
413
|
+
if (mcode != CURLM_OK) {
|
414
|
+
raise_curl_multi_error_exception(mcode);
|
415
|
+
}
|
416
|
+
|
417
|
+
#ifdef HAVE_CURL_MULTI_TIMEOUT
|
418
|
+
/* get the curl suggested time out */
|
419
|
+
mcode = curl_multi_timeout(rbcm->handle, &timeout_milliseconds);
|
420
|
+
if (mcode != CURLM_OK) {
|
421
|
+
raise_curl_multi_error_exception(mcode);
|
422
|
+
}
|
423
|
+
#else
|
424
|
+
/* libcurl doesn't have a timeout method defined... make a wild guess */
|
425
|
+
timeout_milliseconds = -1;
|
426
|
+
#endif
|
427
|
+
//printf("libcurl says wait: %ld ms or %ld s\n", timeout_milliseconds, timeout_milliseconds/1000);
|
428
|
+
|
429
|
+
if (timeout_milliseconds == 0) { /* no delay */
|
430
|
+
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
431
|
+
continue;
|
432
|
+
}
|
433
|
+
else if(timeout_milliseconds < 0) {
|
434
|
+
timeout_milliseconds = 500; /* wait half a second, libcurl doesn't know how long to wait */
|
435
|
+
}
|
436
|
+
#ifdef __APPLE_CC__
|
437
|
+
if(timeout_milliseconds > 1000) {
|
438
|
+
timeout_milliseconds = 1000; /* apple libcurl sometimes reports huge timeouts... let's cap it */
|
439
|
+
}
|
440
|
+
#endif
|
441
|
+
|
442
|
+
tv.tv_sec = timeout_milliseconds / 1000; // convert milliseconds to seconds
|
443
|
+
tv.tv_usec = (timeout_milliseconds % 1000) * 1000; // get the remainder of milliseconds and convert to micro seconds
|
444
|
+
|
445
|
+
rc = rb_thread_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
|
446
|
+
switch(rc) {
|
447
|
+
case -1:
|
448
|
+
rb_raise(rb_eRuntimeError, "select(): %s", strerror(errno));
|
449
|
+
break;
|
450
|
+
case 0:
|
451
|
+
if (block != Qnil) {
|
452
|
+
rb_funcall(block, rb_intern("call"), 1, self);
|
453
|
+
}
|
454
|
+
// if (rb_block_given_p()) {
|
455
|
+
// rb_yield(self);
|
456
|
+
// }
|
457
|
+
default:
|
458
|
+
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
459
|
+
break;
|
460
|
+
}
|
461
|
+
|
462
|
+
}
|
463
|
+
|
464
|
+
return Qtrue;
|
465
|
+
}
|
466
|
+
|
467
|
+
/* =================== INIT LIB =====================*/
|
468
|
+
void init_curb_multi() {
|
469
|
+
idCall = rb_intern("call");
|
470
|
+
|
471
|
+
cCurlMulti = rb_define_class_under(mCurl, "Multi", rb_cObject);
|
472
|
+
|
473
|
+
/* Class methods */
|
474
|
+
rb_define_singleton_method(cCurlMulti, "new", ruby_curl_multi_new, 0);
|
475
|
+
|
476
|
+
/* "Attributes" */
|
477
|
+
rb_define_method(cCurlMulti, "requests", ruby_curl_multi_requests, 0);
|
478
|
+
rb_define_method(cCurlMulti, "idle?", ruby_curl_multi_idle, 0);
|
479
|
+
|
480
|
+
/* Instnace methods */
|
481
|
+
rb_define_method(cCurlMulti, "max_connects=", ruby_curl_multi_max_connects, 1);
|
482
|
+
rb_define_method(cCurlMulti, "pipeline=", ruby_curl_multi_pipeline, 1);
|
483
|
+
rb_define_method(cCurlMulti, "add", ruby_curl_multi_add, 1);
|
484
|
+
rb_define_method(cCurlMulti, "remove", ruby_curl_multi_remove, 1);
|
485
|
+
rb_define_method(cCurlMulti, "cancel!", ruby_curl_multi_cancel, 0);
|
486
|
+
rb_define_method(cCurlMulti, "perform", ruby_curl_multi_perform, -1);
|
487
|
+
}
|
data/ext/curb_multi.h
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
/* curb_multi.h - Curl easy mode
|
2
|
+
* Copyright (c)2008 Todd A. Fisher.
|
3
|
+
* Licensed under the Ruby License. See LICENSE for details.
|
4
|
+
*
|
5
|
+
* $Id$
|
6
|
+
*/
|
7
|
+
#ifndef __CURB_MULTI_H
|
8
|
+
#define __CURB_MULTI_H
|
9
|
+
|
10
|
+
#include "curb.h"
|
11
|
+
#include "curb_easy.h"
|
12
|
+
#include <curl/multi.h>
|
13
|
+
|
14
|
+
typedef struct {
|
15
|
+
int active;
|
16
|
+
int running;
|
17
|
+
VALUE requests; /* hash of handles currently added */
|
18
|
+
CURLM *handle;
|
19
|
+
} ruby_curl_multi;
|
20
|
+
|
21
|
+
extern VALUE cCurlMulti;
|
22
|
+
void init_curb_multi();
|
23
|
+
VALUE ruby_curl_multi_new(VALUE klass);
|
24
|
+
|
25
|
+
|
26
|
+
#endif
|