curb 0.5.8.0-x86-linux

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/ext/curb_errors.h ADDED
@@ -0,0 +1,128 @@
1
+ /* curb_errors.h - Ruby exception types for curl errors
2
+ * Copyright (c)2006 Ross Bamford.
3
+ * Licensed under the Ruby License. See LICENSE for details.
4
+ *
5
+ * $Id: curb_errors.h 4 2006-11-17 18:35:31Z roscopeco $
6
+ */
7
+ #ifndef __CURB_ERRORS_H
8
+ #define __CURB_ERRORS_H
9
+
10
+ #include "curb.h"
11
+
12
+ /* base errors */
13
+ extern VALUE cCurlErr;
14
+
15
+ /* easy errors */
16
+ extern VALUE mCurlErr;
17
+ extern VALUE eCurlErrError;
18
+ extern VALUE eCurlErrFTPError;
19
+ extern VALUE eCurlErrHTTPError;
20
+ extern VALUE eCurlErrFileError;
21
+ extern VALUE eCurlErrLDAPError;
22
+ extern VALUE eCurlErrTelnetError;
23
+ extern VALUE eCurlErrTFTPError;
24
+
25
+ /* libcurl errors */
26
+ extern VALUE eCurlErrUnsupportedProtocol;
27
+ extern VALUE eCurlErrFailedInit;
28
+ extern VALUE eCurlErrMalformedURL;
29
+ extern VALUE eCurlErrMalformedURLUser;
30
+ extern VALUE eCurlErrProxyResolution;
31
+ extern VALUE eCurlErrHostResolution;
32
+ extern VALUE eCurlErrConnectFailed;
33
+ extern VALUE eCurlErrFTPWierdReply;
34
+ extern VALUE eCurlErrFTPAccessDenied;
35
+ extern VALUE eCurlErrFTPBadPassword;
36
+ extern VALUE eCurlErrFTPWierdPassReply;
37
+ extern VALUE eCurlErrFTPWierdUserReply;
38
+ extern VALUE eCurlErrFTPWierdPasvReply;
39
+ extern VALUE eCurlErrFTPWierd227Format;
40
+ extern VALUE eCurlErrFTPCantGetHost;
41
+ extern VALUE eCurlErrFTPCantReconnect;
42
+ extern VALUE eCurlErrFTPCouldntSetBinary;
43
+ extern VALUE eCurlErrPartialFile;
44
+ extern VALUE eCurlErrFTPCouldntRetrFile;
45
+ extern VALUE eCurlErrFTPWrite;
46
+ extern VALUE eCurlErrFTPQuote;
47
+ extern VALUE eCurlErrHTTPFailed;
48
+ extern VALUE eCurlErrWriteError;
49
+ extern VALUE eCurlErrMalformedUser;
50
+ extern VALUE eCurlErrFTPCouldntStorFile;
51
+ extern VALUE eCurlErrReadError;
52
+ extern VALUE eCurlErrOutOfMemory;
53
+ extern VALUE eCurlErrTimeout;
54
+ extern VALUE eCurlErrFTPCouldntSetASCII;
55
+ extern VALUE eCurlErrFTPPortFailed;
56
+ extern VALUE eCurlErrFTPCouldntUseRest;
57
+ extern VALUE eCurlErrFTPCouldntGetSize;
58
+ extern VALUE eCurlErrHTTPRange;
59
+ extern VALUE eCurlErrHTTPPost;
60
+ extern VALUE eCurlErrSSLConnectError;
61
+ extern VALUE eCurlErrBadResume;
62
+ extern VALUE eCurlErrFileCouldntRead;
63
+ extern VALUE eCurlErrLDAPCouldntBind;
64
+ extern VALUE eCurlErrLDAPSearchFailed;
65
+ extern VALUE eCurlErrLibraryNotFound;
66
+ extern VALUE eCurlErrFunctionNotFound;
67
+ extern VALUE eCurlErrAbortedByCallback;
68
+ extern VALUE eCurlErrBadFunctionArgument;
69
+ extern VALUE eCurlErrBadCallingOrder;
70
+ extern VALUE eCurlErrInterfaceFailed;
71
+ extern VALUE eCurlErrBadPasswordEntered;
72
+ extern VALUE eCurlErrTooManyRedirects;
73
+ extern VALUE eCurlErrTelnetUnknownOption;
74
+ extern VALUE eCurlErrTelnetBadOptionSyntax;
75
+ extern VALUE eCurlErrObsolete;
76
+ extern VALUE eCurlErrSSLPeerCertificate;
77
+ extern VALUE eCurlErrGotNothing;
78
+ extern VALUE eCurlErrSSLEngineNotFound;
79
+ extern VALUE eCurlErrSSLEngineSetFailed;
80
+ extern VALUE eCurlErrSendError;
81
+ extern VALUE eCurlErrRecvError;
82
+ extern VALUE eCurlErrShareInUse;
83
+ extern VALUE eCurlErrSSLCertificate;
84
+ extern VALUE eCurlErrSSLCipher;
85
+ extern VALUE eCurlErrSSLCACertificate;
86
+ extern VALUE eCurlErrBadContentEncoding;
87
+ extern VALUE eCurlErrLDAPInvalidURL;
88
+ extern VALUE eCurlErrFileSizeExceeded;
89
+ extern VALUE eCurlErrFTPSSLFailed;
90
+ extern VALUE eCurlErrSendFailedRewind;
91
+ extern VALUE eCurlErrSSLEngineInitFailed;
92
+ extern VALUE eCurlErrLoginDenied;
93
+ extern VALUE eCurlErrTFTPNotFound;
94
+ extern VALUE eCurlErrTFTPPermission;
95
+ extern VALUE eCurlErrTFTPDiskFull;
96
+ extern VALUE eCurlErrTFTPIllegalOperation;
97
+ extern VALUE eCurlErrTFTPUnknownID;
98
+ extern VALUE eCurlErrTFTPFileExists;
99
+ extern VALUE eCurlErrTFTPNoSuchUser;
100
+ extern VALUE eCurlErrConvFailed;
101
+ extern VALUE eCurlErrConvReqd;
102
+ extern VALUE eCurlErrSSLCacertBadfile;
103
+ extern VALUE eCurlErrRemoteFileNotFound;
104
+ extern VALUE eCurlErrSSH;
105
+ extern VALUE eCurlErrSSLShutdownFailed;
106
+ extern VALUE eCurlErrAgain;
107
+ extern VALUE eCurlErrSSLCRLBadfile;
108
+ extern VALUE eCurlErrSSLIssuerError;
109
+
110
+ /* multi errors */
111
+ extern VALUE mCurlErrCallMultiPerform;
112
+ extern VALUE mCurlErrBadHandle;
113
+ extern VALUE mCurlErrBadEasyHandle;
114
+ extern VALUE mCurlErrOutOfMemory;
115
+ extern VALUE mCurlErrInternalError;
116
+ extern VALUE mCurlErrBadSocket;
117
+ extern VALUE mCurlErrUnknownOption;
118
+
119
+ /* binding errors */
120
+ extern VALUE eCurlErrInvalidPostField;
121
+
122
+ void init_curb_errors();
123
+ void raise_curl_easy_error_exception(CURLcode code);
124
+ void raise_curl_multi_error_exception(CURLMcode code);
125
+ VALUE rb_curl_easy_error(CURLcode code);
126
+ VALUE rb_curl_multi_error(CURLMcode code);
127
+
128
+ #endif
data/ext/curb_errors.o ADDED
Binary file
data/ext/curb_macros.h ADDED
@@ -0,0 +1,114 @@
1
+ /* Curb - helper macros for ruby integration
2
+ * Copyright (c)2006 Ross Bamford.
3
+ * Licensed under the Ruby License. See LICENSE for details.
4
+ *
5
+ * $Id: curb_macros.h 13 2006-11-23 23:54:25Z roscopeco $
6
+ */
7
+
8
+ #ifndef __CURB_MACROS_H
9
+ #define __CURB_MACROS_H
10
+
11
+ /* getter/setter macros for various things */
12
+ /* setter for anything that stores a ruby VALUE in the struct */
13
+ #define CURB_OBJECT_SETTER(type, attr) \
14
+ type *ptr; \
15
+ \
16
+ Data_Get_Struct(self, type, ptr); \
17
+ ptr->attr = attr; \
18
+ \
19
+ return attr;
20
+
21
+ /* getter for anything that stores a ruby VALUE */
22
+ #define CURB_OBJECT_GETTER(type, attr) \
23
+ type *ptr; \
24
+ \
25
+ Data_Get_Struct(self, type, ptr); \
26
+ return ptr->attr;
27
+
28
+ /* setter for bool flags */
29
+ #define CURB_BOOLEAN_SETTER(type, attr) \
30
+ type *ptr; \
31
+ Data_Get_Struct(self, type, ptr); \
32
+ \
33
+ if (attr == Qnil || attr == Qfalse) { \
34
+ ptr->attr = 0; \
35
+ } else { \
36
+ ptr->attr = 1; \
37
+ } \
38
+ \
39
+ return attr;
40
+
41
+ /* getter for bool flags */
42
+ #define CURB_BOOLEAN_GETTER(type, attr) \
43
+ type *ptr; \
44
+ Data_Get_Struct(self, type, ptr); \
45
+ \
46
+ return((ptr->attr) ? Qtrue : Qfalse);
47
+
48
+ /* special setter for on_event handlers that take a block */
49
+ #define CURB_HANDLER_PROC_SETTER(type, handler) \
50
+ type *ptr; \
51
+ VALUE oldproc; \
52
+ \
53
+ Data_Get_Struct(self, type, ptr); \
54
+ \
55
+ oldproc = ptr->handler; \
56
+ rb_scan_args(argc, argv, "0&", &ptr->handler); \
57
+ \
58
+ return oldproc; \
59
+
60
+ /* setter for numerics that are kept in c ints */
61
+ #define CURB_IMMED_SETTER(type, attr, nilval) \
62
+ type *ptr; \
63
+ \
64
+ Data_Get_Struct(self, type, ptr); \
65
+ if (attr == Qnil) { \
66
+ ptr->attr = nilval; \
67
+ } else { \
68
+ ptr->attr = NUM2INT(attr); \
69
+ } \
70
+ \
71
+ return attr; \
72
+
73
+ /* setter for numerics that are kept in c ints */
74
+ #define CURB_IMMED_GETTER(type, attr, nilval) \
75
+ type *ptr; \
76
+ \
77
+ Data_Get_Struct(self, type, ptr); \
78
+ if (ptr->attr == nilval) { \
79
+ return Qnil; \
80
+ } else { \
81
+ return INT2NUM(ptr->attr); \
82
+ }
83
+
84
+ /* special setter for port / port ranges */
85
+ #define CURB_IMMED_PORT_SETTER(type, attr, msg) \
86
+ type *ptr; \
87
+ \
88
+ Data_Get_Struct(self, type, ptr); \
89
+ if (attr == Qnil) { \
90
+ ptr->attr = 0; \
91
+ } else { \
92
+ int port = FIX2INT(attr); \
93
+ \
94
+ if ((port) && ((port & 0xFFFF) == port)) { \
95
+ ptr->attr = port; \
96
+ } else { \
97
+ rb_raise(rb_eArgError, "Invalid " msg " %d (expected between 1 and 65535)", port); \
98
+ } \
99
+ } \
100
+ \
101
+ return attr; \
102
+
103
+ /* special getter for port / port ranges */
104
+ #define CURB_IMMED_PORT_GETTER(type, attr) \
105
+ type *ptr; \
106
+ \
107
+ Data_Get_Struct(self, type, ptr); \
108
+ if (ptr->attr == 0) { \
109
+ return Qnil; \
110
+ } else { \
111
+ return INT2FIX(ptr->attr); \
112
+ }
113
+
114
+ #endif
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 => #&lt;Curl::Easy...&gt;
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 => [#&lt;Curl::Easy...&gt;, ...]
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
+ }