curb 0.1.4 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of curb might be problematic. Click here for more details.
- data/README +26 -3
- data/Rakefile +20 -69
- data/doc.rb +1 -1
- data/ext/curb.c +21 -20
- data/ext/curb.h +12 -4
- data/ext/curb_config.h +42 -0
- data/ext/curb_easy.c +626 -207
- data/ext/curb_easy.h +23 -3
- data/ext/curb_errors.c +61 -8
- data/ext/curb_errors.h +11 -0
- data/ext/curb_multi.c +353 -0
- data/ext/curb_multi.h +25 -0
- data/ext/curb_postfield.c +14 -4
- data/ext/extconf.rb +104 -7
- data/{ext → lib}/curb.rb +2 -0
- data/{ext → lib}/curl.rb +0 -0
- data/tests/bug_curb_easy_blocks_ruby_threads.rb +52 -0
- data/tests/helper.rb +141 -0
- data/tests/tc_curl_download.rb +27 -0
- data/tests/tc_curl_easy.rb +67 -3
- data/tests/tc_curl_multi.rb +249 -0
- metadata +66 -61
- data/samples/gmail.rb +0 -48
data/ext/curb_easy.h
CHANGED
@@ -24,8 +24,8 @@ typedef struct {
|
|
24
24
|
|
25
25
|
VALUE body_proc;
|
26
26
|
VALUE header_proc;
|
27
|
-
VALUE body_data; /*
|
28
|
-
VALUE header_data; /* unless a block is supplied (
|
27
|
+
VALUE body_data; /* Holds the response body from the last call to curl_easy_perform */
|
28
|
+
VALUE header_data; /* unless a block is supplied (they'll be nil) */
|
29
29
|
VALUE progress_proc;
|
30
30
|
VALUE debug_proc;
|
31
31
|
VALUE interface;
|
@@ -33,7 +33,13 @@ typedef struct {
|
|
33
33
|
VALUE proxypwd;
|
34
34
|
VALUE headers; /* ruby array of strings with headers to set */
|
35
35
|
VALUE cookiejar; /* filename */
|
36
|
-
|
36
|
+
VALUE cert;
|
37
|
+
VALUE encoding;
|
38
|
+
|
39
|
+
VALUE success_proc;
|
40
|
+
VALUE failure_proc;
|
41
|
+
VALUE complete_proc;
|
42
|
+
|
37
43
|
/* Other opts */
|
38
44
|
unsigned short local_port; // 0 is no port
|
39
45
|
unsigned short local_port_range; // " " " "
|
@@ -65,10 +71,24 @@ typedef struct {
|
|
65
71
|
* and in case it's asked for before the next call.
|
66
72
|
*/
|
67
73
|
VALUE postdata_buffer;
|
74
|
+
|
75
|
+
/* when added to a multi handle these buffers are needed
|
76
|
+
* when the easy handle isn't supplied the body proc
|
77
|
+
* or a custom http header is passed.
|
78
|
+
*/
|
79
|
+
VALUE bodybuf;
|
80
|
+
VALUE headerbuf;
|
81
|
+
struct curl_slist *curl_headers;
|
82
|
+
|
83
|
+
VALUE self; /* pointer to self, used by multi interface */
|
84
|
+
|
68
85
|
} ruby_curl_easy;
|
69
86
|
|
70
87
|
extern VALUE cCurlEasy;
|
71
88
|
|
89
|
+
VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce, VALUE *bodybuf, VALUE *headerbuf, struct curl_slist **headers);
|
90
|
+
VALUE ruby_curl_easy_cleanup(VALUE self, ruby_curl_easy *rbce, VALUE bodybuf, VALUE headerbuf, struct curl_slist *headers);
|
91
|
+
|
72
92
|
void init_curb_easy();
|
73
93
|
|
74
94
|
#endif
|
data/ext/curb_errors.c
CHANGED
@@ -98,9 +98,19 @@ VALUE eCurlErrTFTPUnknownID;
|
|
98
98
|
VALUE eCurlErrTFTPFileExists;
|
99
99
|
VALUE eCurlErrTFTPNoSuchUser;
|
100
100
|
|
101
|
+
/* multi errors */
|
102
|
+
VALUE mCurlErrCallMultiPerform;
|
103
|
+
VALUE mCurlErrBadHandle;
|
104
|
+
VALUE mCurlErrBadEasyHandle;
|
105
|
+
VALUE mCurlErrOutOfMemory;
|
106
|
+
VALUE mCurlErrInternalError;
|
107
|
+
VALUE mCurlErrBadSocket;
|
108
|
+
VALUE mCurlErrUnknownOption;
|
109
|
+
|
101
110
|
/* binding errors */
|
102
111
|
VALUE eCurlErrInvalidPostField;
|
103
112
|
|
113
|
+
|
104
114
|
/* rb_raise an approriate exception for the supplied CURLcode */
|
105
115
|
void raise_curl_easy_error_exception(CURLcode code) {
|
106
116
|
VALUE exclz;
|
@@ -299,48 +309,54 @@ void raise_curl_easy_error_exception(CURLcode code) {
|
|
299
309
|
case CURLE_FTP_SSL_FAILED: /* 64 - Requested FTP SSL level failed */
|
300
310
|
exclz = eCurlErrFTPSSLFailed;
|
301
311
|
break;
|
312
|
+
#ifdef HAVE_CURLE_SEND_FAIL_REWIND
|
302
313
|
case CURLE_SEND_FAIL_REWIND: /* 65 - Sending the data requires a rewind that failed */
|
303
314
|
exclz = eCurlErrSendFailedRewind;
|
304
|
-
break;
|
315
|
+
break;
|
316
|
+
#endif
|
317
|
+
#ifdef HAVE_CURLE_SSL_ENGINE_INITFAILED
|
305
318
|
case CURLE_SSL_ENGINE_INITFAILED: /* 66 - failed to initialise ENGINE */
|
306
319
|
exclz = eCurlErrSSLEngineInitFailed;
|
307
320
|
break;
|
321
|
+
#endif
|
322
|
+
#ifdef HAVE_CURLE_LOGIN_DENIED
|
308
323
|
case CURLE_LOGIN_DENIED: /* 67 - user, password or similar was not accepted and we failed to login */
|
309
324
|
exclz = eCurlErrLoginDenied;
|
310
325
|
break;
|
326
|
+
#endif
|
311
327
|
|
312
328
|
// recent additions, may not be present in all supported versions
|
313
|
-
#ifdef
|
329
|
+
#ifdef HAVE_CURLE_TFTP_NOTFOUND
|
314
330
|
case CURLE_TFTP_NOTFOUND: /* 68 - file not found on server */
|
315
331
|
exclz = eCurlErrTFTPNotFound;
|
316
332
|
break;
|
317
333
|
#endif
|
318
|
-
#ifdef
|
334
|
+
#ifdef HAVE_CURLE_TFTP_PERM
|
319
335
|
case CURLE_TFTP_PERM: /* 69 - permission problem on server */
|
320
336
|
exclz = eCurlErrTFTPPermission;
|
321
337
|
break;
|
322
338
|
#endif
|
323
|
-
#ifdef
|
339
|
+
#ifdef HAVE_CURLE_TFTP_DISKFULL
|
324
340
|
case CURLE_TFTP_DISKFULL: /* 70 - out of disk space on server */
|
325
341
|
exclz = eCurlErrTFTPDiskFull;
|
326
342
|
break;
|
327
343
|
#endif
|
328
|
-
#ifdef
|
344
|
+
#ifdef HAVE_CURLE_TFTP_ILLEGAL
|
329
345
|
case CURLE_TFTP_ILLEGAL: /* 71 - Illegal TFTP operation */
|
330
346
|
exclz = eCurlErrTFTPIllegalOperation;
|
331
347
|
break;
|
332
348
|
#endif
|
333
|
-
#ifdef
|
349
|
+
#ifdef HAVE_CURLE_TFTP_UNKNOWNID
|
334
350
|
case CURLE_TFTP_UNKNOWNID: /* 72 - Unknown transfer ID */
|
335
351
|
exclz = eCurlErrTFTPUnknownID;
|
336
352
|
break;
|
337
353
|
#endif
|
338
|
-
#ifdef
|
354
|
+
#ifdef HAVE_CURLE_TFTP_EXISTS
|
339
355
|
case CURLE_TFTP_EXISTS: /* 73 - File already exists */
|
340
356
|
exclz = eCurlErrTFTPFileExists;
|
341
357
|
break;
|
342
358
|
#endif
|
343
|
-
#ifdef
|
359
|
+
#ifdef HAVE_CURLE_TFTP_NOSUCHUSER
|
344
360
|
case CURLE_TFTP_NOSUCHUSER: /* 74 - No such user */
|
345
361
|
exclz = eCurlErrTFTPNotFound;
|
346
362
|
break;
|
@@ -356,6 +372,43 @@ void raise_curl_easy_error_exception(CURLcode code) {
|
|
356
372
|
|
357
373
|
rb_raise(exclz, exmsg);
|
358
374
|
}
|
375
|
+
void raise_curl_multi_error_exception(CURLMcode code) {
|
376
|
+
VALUE exclz;
|
377
|
+
const char *exmsg = NULL;
|
378
|
+
|
379
|
+
switch(code) {
|
380
|
+
case CURLM_CALL_MULTI_PERFORM: /* -1 */
|
381
|
+
exclz = mCurlErrCallMultiPerform;
|
382
|
+
break;
|
383
|
+
case CURLM_BAD_HANDLE: /* 1 */
|
384
|
+
exclz = mCurlErrBadHandle;
|
385
|
+
break;
|
386
|
+
case CURLM_BAD_EASY_HANDLE: /* 2 */
|
387
|
+
exclz = mCurlErrBadEasyHandle;
|
388
|
+
break;
|
389
|
+
case CURLM_OUT_OF_MEMORY: /* 3 */
|
390
|
+
exclz = mCurlErrOutOfMemory;
|
391
|
+
break;
|
392
|
+
case CURLM_INTERNAL_ERROR: /* 4 */
|
393
|
+
exclz = mCurlErrInternalError;
|
394
|
+
break;
|
395
|
+
case CURLM_BAD_SOCKET: /* 5 */
|
396
|
+
exclz = mCurlErrBadSocket;
|
397
|
+
break;
|
398
|
+
case CURLM_UNKNOWN_OPTION: /* 6 */
|
399
|
+
exclz = mCurlErrUnknownOption;
|
400
|
+
break;
|
401
|
+
default:
|
402
|
+
exclz = eCurlErrError;
|
403
|
+
exmsg = "Unknown error result from libcurl";
|
404
|
+
}
|
405
|
+
|
406
|
+
if (!exmsg) {
|
407
|
+
exmsg = curl_multi_strerror(code);
|
408
|
+
}
|
409
|
+
|
410
|
+
rb_raise(exclz, exmsg);
|
411
|
+
}
|
359
412
|
|
360
413
|
void init_curb_errors() {
|
361
414
|
mCurlErr = rb_define_module_under(mCurl, "Err");
|
data/ext/curb_errors.h
CHANGED
@@ -12,6 +12,7 @@
|
|
12
12
|
/* base errors */
|
13
13
|
extern VALUE cCurlErr;
|
14
14
|
|
15
|
+
/* easy errors */
|
15
16
|
extern VALUE mCurlErr;
|
16
17
|
extern VALUE eCurlErrError;
|
17
18
|
extern VALUE eCurlErrFTPError;
|
@@ -97,10 +98,20 @@ extern VALUE eCurlErrTFTPUnknownID;
|
|
97
98
|
extern VALUE eCurlErrTFTPFileExists;
|
98
99
|
extern VALUE eCurlErrTFTPNoSuchUser;
|
99
100
|
|
101
|
+
/* multi errors */
|
102
|
+
extern VALUE mCurlErrCallMultiPerform;
|
103
|
+
extern VALUE mCurlErrBadHandle;
|
104
|
+
extern VALUE mCurlErrBadEasyHandle;
|
105
|
+
extern VALUE mCurlErrOutOfMemory;
|
106
|
+
extern VALUE mCurlErrInternalError;
|
107
|
+
extern VALUE mCurlErrBadSocket;
|
108
|
+
extern VALUE mCurlErrUnknownOption;
|
109
|
+
|
100
110
|
/* binding errors */
|
101
111
|
extern VALUE eCurlErrInvalidPostField;
|
102
112
|
|
103
113
|
void init_curb_errors();
|
104
114
|
void raise_curl_easy_error_exception(CURLcode code);
|
115
|
+
void raise_curl_multi_error_exception(CURLMcode code);
|
105
116
|
|
106
117
|
#endif
|
data/ext/curb_multi.c
ADDED
@@ -0,0 +1,353 @@
|
|
1
|
+
/* curb_easy.c - 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
|
+
|
8
|
+
#include "curb_config.h"
|
9
|
+
#ifdef HAVE_RUBY19_ST_H
|
10
|
+
#include <ruby/st.h>
|
11
|
+
#else
|
12
|
+
#include <st.h>
|
13
|
+
#endif
|
14
|
+
#include "curb_easy.h"
|
15
|
+
#include "curb_errors.h"
|
16
|
+
#include "curb_postfield.h"
|
17
|
+
#include "curb_multi.h"
|
18
|
+
|
19
|
+
#include <errno.h>
|
20
|
+
|
21
|
+
extern VALUE mCurl;
|
22
|
+
static VALUE idCall;
|
23
|
+
|
24
|
+
#ifdef RDOC_NEVER_DEFINED
|
25
|
+
mCurl = rb_define_module("Curl");
|
26
|
+
#endif
|
27
|
+
|
28
|
+
VALUE cCurlMulti;
|
29
|
+
|
30
|
+
static VALUE ruby_curl_multi_remove(VALUE , VALUE );
|
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
|
+
}
|
47
|
+
|
48
|
+
static void curl_multi_free(ruby_curl_multi *rbcm) {
|
49
|
+
//printf("hash entries: %d\n", RHASH(rbcm->requests)->tbl->num_entries );
|
50
|
+
if (rbcm && RHASH_LEN(rbcm->requests) > 0) {
|
51
|
+
rb_hash_foreach( rbcm->requests, (int (*)())curl_multi_flush_easy, (VALUE)rbcm );
|
52
|
+
|
53
|
+
curl_multi_cleanup(rbcm->handle);
|
54
|
+
//rb_hash_clear(rbcm->requests)
|
55
|
+
rbcm->requests = Qnil;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
/*
|
60
|
+
* call-seq:
|
61
|
+
* Curl::Multi.new => #<Curl::Easy...>
|
62
|
+
*
|
63
|
+
* Create a new Curl::Multi instance
|
64
|
+
*/
|
65
|
+
static VALUE ruby_curl_multi_new(VALUE self) {
|
66
|
+
VALUE new_curlm;
|
67
|
+
|
68
|
+
ruby_curl_multi *rbcm = ALLOC(ruby_curl_multi);
|
69
|
+
|
70
|
+
rbcm->handle = curl_multi_init();
|
71
|
+
|
72
|
+
rbcm->requests = rb_hash_new();
|
73
|
+
|
74
|
+
rbcm->active = 0;
|
75
|
+
rbcm->running = 0;
|
76
|
+
|
77
|
+
new_curlm = Data_Wrap_Struct(cCurlMulti, curl_multi_mark, curl_multi_free, rbcm);
|
78
|
+
|
79
|
+
return new_curlm;
|
80
|
+
}
|
81
|
+
|
82
|
+
/*
|
83
|
+
* call-seq:
|
84
|
+
* multi = Curl::Multi.new
|
85
|
+
* multi.max_connects = 800
|
86
|
+
*
|
87
|
+
* Set the max connections in the cache for a multi handle
|
88
|
+
*/
|
89
|
+
static VALUE ruby_curl_multi_max_connects(VALUE self, VALUE count) {
|
90
|
+
ruby_curl_multi *rbcm;
|
91
|
+
|
92
|
+
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
93
|
+
curl_multi_setopt(rbcm->handle, CURLMOPT_MAXCONNECTS, NUM2INT(count));
|
94
|
+
|
95
|
+
return self;
|
96
|
+
}
|
97
|
+
|
98
|
+
/*
|
99
|
+
* call-seq:
|
100
|
+
* multi = Curl::Multi.new
|
101
|
+
* easy = Curl::Easy.new('url')
|
102
|
+
*
|
103
|
+
* multi.add(easy)
|
104
|
+
*
|
105
|
+
* Add an easy handle to the multi stack
|
106
|
+
*/
|
107
|
+
static VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
|
108
|
+
CURLMcode mcode;
|
109
|
+
ruby_curl_easy *rbce;
|
110
|
+
ruby_curl_multi *rbcm;
|
111
|
+
|
112
|
+
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
113
|
+
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
114
|
+
|
115
|
+
mcode = curl_multi_add_handle(rbcm->handle, rbce->curl);
|
116
|
+
if (mcode != CURLM_CALL_MULTI_PERFORM && mcode != CURLM_OK) {
|
117
|
+
raise_curl_multi_error_exception(mcode);
|
118
|
+
}
|
119
|
+
|
120
|
+
/* save a pointer to self */
|
121
|
+
rbce->self = easy;
|
122
|
+
|
123
|
+
/* setup the easy handle */
|
124
|
+
ruby_curl_easy_setup( rbce, &(rbce->bodybuf), &(rbce->headerbuf), &(rbce->curl_headers) );
|
125
|
+
|
126
|
+
rbcm->active++;
|
127
|
+
if (mcode == CURLM_CALL_MULTI_PERFORM) {
|
128
|
+
curl_multi_perform(rbcm->handle, &(rbcm->running));
|
129
|
+
}
|
130
|
+
|
131
|
+
rb_hash_aset( rbcm->requests, rb_int_new((long)rbce->curl), easy );
|
132
|
+
// active should equal INT2FIX(RHASH(rbcm->requests)->tbl->num_entries)
|
133
|
+
|
134
|
+
if (rbcm->active > rbcm->running) {
|
135
|
+
rb_curl_multi_read_info(self, rbcm->handle);
|
136
|
+
}
|
137
|
+
|
138
|
+
return self;
|
139
|
+
}
|
140
|
+
|
141
|
+
/*
|
142
|
+
* call-seq:
|
143
|
+
* multi = Curl::Multi.new
|
144
|
+
* easy = Curl::Easy.new('url')
|
145
|
+
*
|
146
|
+
* multi.add(easy)
|
147
|
+
*
|
148
|
+
* # sometime later
|
149
|
+
* multi.remove(easy)
|
150
|
+
*
|
151
|
+
* Remove an easy handle from a multi stack
|
152
|
+
*
|
153
|
+
* Will raise an exception if the easy handle is not found
|
154
|
+
*/
|
155
|
+
static VALUE ruby_curl_multi_remove(VALUE self, VALUE easy) {
|
156
|
+
ruby_curl_multi *rbcm;
|
157
|
+
|
158
|
+
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
159
|
+
|
160
|
+
ruby_curl_easy *rbce;
|
161
|
+
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
162
|
+
|
163
|
+
rb_curl_multi_remove(rbcm,easy);
|
164
|
+
// active should equal INT2FIX(RHASH(rbcm->requests)->tbl->num_entries)
|
165
|
+
rb_hash_delete( rbcm->requests, rb_int_new((long)rbce->curl) );
|
166
|
+
|
167
|
+
return self;
|
168
|
+
}
|
169
|
+
static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
|
170
|
+
CURLMcode result;
|
171
|
+
ruby_curl_easy *rbce;
|
172
|
+
Data_Get_Struct(easy, ruby_curl_easy, rbce);
|
173
|
+
|
174
|
+
rbcm->active--;
|
175
|
+
|
176
|
+
//printf( "calling rb_curl_multi_remove: 0x%X, active: %d\n", (long)easy, rbcm->active );
|
177
|
+
|
178
|
+
result = curl_multi_remove_handle(rbcm->handle, rbce->curl);
|
179
|
+
if (result != 0) {
|
180
|
+
raise_curl_multi_error_exception(result);
|
181
|
+
}
|
182
|
+
|
183
|
+
ruby_curl_easy_cleanup( easy, rbce, rbce->bodybuf, rbce->headerbuf, rbce->curl_headers );
|
184
|
+
rbce->headerbuf = Qnil;
|
185
|
+
rbce->bodybuf = Qnil;
|
186
|
+
}
|
187
|
+
|
188
|
+
static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
|
189
|
+
int msgs_left, result;
|
190
|
+
CURLMsg *msg;
|
191
|
+
CURLcode ecode;
|
192
|
+
CURL *easy_handle;
|
193
|
+
ruby_curl_easy *rbce = NULL;
|
194
|
+
// VALUE finished = rb_ary_new();
|
195
|
+
|
196
|
+
/* check for finished easy handles and remove from the multi handle */
|
197
|
+
while ((msg = curl_multi_info_read(multi_handle, &msgs_left))) {
|
198
|
+
|
199
|
+
if (msg->msg != CURLMSG_DONE) {
|
200
|
+
continue;
|
201
|
+
}
|
202
|
+
|
203
|
+
easy_handle = msg->easy_handle;
|
204
|
+
result = msg->data.result;
|
205
|
+
if (easy_handle) {
|
206
|
+
ecode = curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, (char**)&rbce);
|
207
|
+
if (ecode != 0) {
|
208
|
+
raise_curl_easy_error_exception(ecode);
|
209
|
+
}
|
210
|
+
//printf( "finished: 0x%X\n", (long)rbce->self );
|
211
|
+
//rb_ary_push(finished, rbce->self);
|
212
|
+
ruby_curl_multi_remove( self, rbce->self );
|
213
|
+
|
214
|
+
if (rbce->complete_proc != Qnil) {
|
215
|
+
rb_funcall( rbce->complete_proc, idCall, 1, self );
|
216
|
+
}
|
217
|
+
|
218
|
+
long response_code = -1;
|
219
|
+
curl_easy_getinfo(rbce->curl, CURLINFO_RESPONSE_CODE, &response_code);
|
220
|
+
|
221
|
+
if (result != 0) {
|
222
|
+
if (rbce->failure_proc != Qnil) {
|
223
|
+
rb_funcall( rbce->failure_proc, idCall, 1, rbce->self );
|
224
|
+
}
|
225
|
+
}
|
226
|
+
else if (rbce->success_proc != Qnil &&
|
227
|
+
((response_code >= 200 && response_code < 300) || response_code == 0)) {
|
228
|
+
/* NOTE: we allow response_code == 0, in the case the file is being read from disk */
|
229
|
+
rb_funcall( rbce->success_proc, idCall, 1, rbce->self );
|
230
|
+
}
|
231
|
+
else if (rbce->failure_proc != Qnil &&
|
232
|
+
(response_code >= 300 && response_code <= 999)) {
|
233
|
+
rb_funcall( rbce->failure_proc, idCall, 1, rbce->self );
|
234
|
+
}
|
235
|
+
}
|
236
|
+
else {
|
237
|
+
//printf( "missing easy handle\n" );
|
238
|
+
}
|
239
|
+
}
|
240
|
+
|
241
|
+
/*
|
242
|
+
while (RARRAY(finished)->len > 0) {
|
243
|
+
//printf( "finished handle\n" );
|
244
|
+
ruby_curl_multi_remove( self, rb_ary_pop(finished) );
|
245
|
+
}
|
246
|
+
*/
|
247
|
+
}
|
248
|
+
|
249
|
+
/* called within ruby_curl_multi_perform */
|
250
|
+
static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running) {
|
251
|
+
CURLMcode mcode;
|
252
|
+
|
253
|
+
do {
|
254
|
+
mcode = curl_multi_perform(multi_handle, still_running);
|
255
|
+
} while (mcode == CURLM_CALL_MULTI_PERFORM);
|
256
|
+
|
257
|
+
if (mcode != CURLM_OK) {
|
258
|
+
raise_curl_multi_error_exception(mcode);
|
259
|
+
}
|
260
|
+
|
261
|
+
rb_curl_multi_read_info( self, multi_handle );
|
262
|
+
}
|
263
|
+
|
264
|
+
/*
|
265
|
+
* call-seq:
|
266
|
+
* multi = Curl::Multi.new
|
267
|
+
* easy1 = Curl::Easy.new('url')
|
268
|
+
* easy2 = Curl::Easy.new('url')
|
269
|
+
*
|
270
|
+
* multi.add(easy1)
|
271
|
+
* multi.add(easy2)
|
272
|
+
*
|
273
|
+
* multi.perform do
|
274
|
+
* # while idle other code my execute here
|
275
|
+
* end
|
276
|
+
*
|
277
|
+
* Run multi handles, looping selecting when data can be transfered
|
278
|
+
*/
|
279
|
+
static VALUE ruby_curl_multi_perform(VALUE self) {
|
280
|
+
CURLMcode mcode;
|
281
|
+
ruby_curl_multi *rbcm;
|
282
|
+
int maxfd, rc;
|
283
|
+
fd_set fdread, fdwrite, fdexcep;
|
284
|
+
|
285
|
+
long timeout;
|
286
|
+
struct timeval tv = {0, 0};
|
287
|
+
|
288
|
+
Data_Get_Struct(self, ruby_curl_multi, rbcm);
|
289
|
+
//rb_gc_mark(self);
|
290
|
+
|
291
|
+
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
292
|
+
|
293
|
+
while(rbcm->running) {
|
294
|
+
FD_ZERO(&fdread);
|
295
|
+
FD_ZERO(&fdwrite);
|
296
|
+
FD_ZERO(&fdexcep);
|
297
|
+
|
298
|
+
/* load the fd sets from the multi handle */
|
299
|
+
mcode = curl_multi_fdset(rbcm->handle, &fdread, &fdwrite, &fdexcep, &maxfd);
|
300
|
+
if (mcode != CURLM_OK) {
|
301
|
+
raise_curl_multi_error_exception(mcode);
|
302
|
+
}
|
303
|
+
|
304
|
+
/* get the curl suggested time out */
|
305
|
+
mcode = curl_multi_timeout(rbcm->handle, &timeout);
|
306
|
+
if (mcode != CURLM_OK) {
|
307
|
+
raise_curl_multi_error_exception(mcode);
|
308
|
+
}
|
309
|
+
|
310
|
+
if (timeout == 0) { /* no delay */
|
311
|
+
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
312
|
+
continue;
|
313
|
+
}
|
314
|
+
else if (timeout == -1) {
|
315
|
+
timeout = 1; /* You must not wait too long
|
316
|
+
(more than a few seconds perhaps) before
|
317
|
+
you call curl_multi_perform() again */
|
318
|
+
}
|
319
|
+
|
320
|
+
if (rb_block_given_p()) {
|
321
|
+
rb_yield(self);
|
322
|
+
}
|
323
|
+
|
324
|
+
tv.tv_sec = timeout / 1000;
|
325
|
+
tv.tv_usec = (timeout * 1000) % 1000000;
|
326
|
+
|
327
|
+
rc = rb_thread_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
|
328
|
+
if (rc < 0) {
|
329
|
+
rb_raise(rb_eRuntimeError, "select(): %s", strerror(errno));
|
330
|
+
}
|
331
|
+
|
332
|
+
rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
|
333
|
+
|
334
|
+
}
|
335
|
+
|
336
|
+
return Qnil;
|
337
|
+
}
|
338
|
+
|
339
|
+
/* =================== INIT LIB =====================*/
|
340
|
+
void init_curb_multi() {
|
341
|
+
idCall = rb_intern("call");
|
342
|
+
|
343
|
+
cCurlMulti = rb_define_class_under(mCurl, "Multi", rb_cObject);
|
344
|
+
|
345
|
+
/* Class methods */
|
346
|
+
rb_define_singleton_method(cCurlMulti, "new", ruby_curl_multi_new, -1);
|
347
|
+
|
348
|
+
/* Instnace methods */
|
349
|
+
rb_define_method(cCurlMulti, "max_connects=", ruby_curl_multi_max_connects, 1);
|
350
|
+
rb_define_method(cCurlMulti, "add", ruby_curl_multi_add, 1);
|
351
|
+
rb_define_method(cCurlMulti, "remove", ruby_curl_multi_remove, 1);
|
352
|
+
rb_define_method(cCurlMulti, "perform", ruby_curl_multi_perform, 0);
|
353
|
+
}
|