curb 0.6.5.0 → 0.8.4

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/ext/curb_easy.h CHANGED
@@ -11,6 +11,24 @@
11
11
 
12
12
  #include <curl/easy.h>
13
13
 
14
+ #ifdef CURL_VERSION_SSL
15
+ #if LIBCURL_VERSION_NUM >= 0x070b00
16
+ # if LIBCURL_VERSION_NUM <= 0x071004
17
+ # define CURB_FTPSSL CURLOPT_FTP_SSL
18
+ # define CURB_FTPSSL_ALL CURLFTPSSL_ALL
19
+ # define CURB_FTPSSL_TRY CURLFTPSSL_TRY
20
+ # define CURB_FTPSSL_CONTROL CURLFTPSSL_CONTROL
21
+ # define CURB_FTPSSL_NONE CURLFTPSSL_NONE
22
+ # else
23
+ # define CURB_FTPSSL CURLOPT_USE_SSL
24
+ # define CURB_FTPSSL_ALL CURLUSESSL_ALL
25
+ # define CURB_FTPSSL_TRY CURLUSESSL_TRY
26
+ # define CURB_FTPSSL_CONTROL CURLUSESSL_CONTROL
27
+ # define CURB_FTPSSL_NONE CURLUSESSL_NONE
28
+ # endif
29
+ #endif
30
+ #endif
31
+
14
32
  /* a lot of this *could* be kept in the handler itself,
15
33
  * but then we lose the ability to query it's status.
16
34
  */
@@ -19,6 +37,7 @@ typedef struct {
19
37
  CURL *curl;
20
38
 
21
39
  VALUE opts; /* rather then allocate everything we might need to store, allocate a Hash and only store objects we actually use... */
40
+ VALUE multi; /* keep a multi handle alive for each easy handle not being used by a multi handle. This improves easy performance when not within a multi context */
22
41
 
23
42
  /* Other opts */
24
43
  unsigned short local_port; // 0 is no port
@@ -32,6 +51,12 @@ typedef struct {
32
51
  unsigned long connect_timeout;
33
52
  long dns_cache_timeout;
34
53
  unsigned long ftp_response_timeout;
54
+ long low_speed_limit;
55
+ long low_speed_time;
56
+ long ssl_version;
57
+ long use_ssl;
58
+ long ftp_filemethod;
59
+ unsigned short resolve_mode;
35
60
 
36
61
  /* bool flags */
37
62
  char proxy_tunnel;
@@ -45,7 +70,11 @@ typedef struct {
45
70
  char verbose;
46
71
  char multipart_form_post;
47
72
  char enable_cookies;
73
+ char ignore_content_length;
74
+ char callback_active;
75
+
48
76
  struct curl_slist *curl_headers;
77
+ struct curl_slist *curl_ftp_commands;
49
78
 
50
79
  int last_result; /* last result code from multi loop */
51
80
 
@@ -53,8 +82,8 @@ typedef struct {
53
82
 
54
83
  extern VALUE cCurlEasy;
55
84
 
56
- VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce, struct curl_slist **headers);
57
- VALUE ruby_curl_easy_cleanup(VALUE self, ruby_curl_easy *rbce, struct curl_slist *headers);
85
+ VALUE ruby_curl_easy_setup(ruby_curl_easy *rbce);
86
+ VALUE ruby_curl_easy_cleanup(VALUE self, ruby_curl_easy *rbce);
58
87
 
59
88
  void init_curb_easy();
60
89
 
data/ext/curb_errors.c CHANGED
@@ -28,6 +28,7 @@ VALUE eCurlErrUnsupportedProtocol;
28
28
  VALUE eCurlErrFailedInit;
29
29
  VALUE eCurlErrMalformedURL;
30
30
  VALUE eCurlErrMalformedURLUser;
31
+ VALUE eCurlErrNotBuiltIn;
31
32
  VALUE eCurlErrProxyResolution;
32
33
  VALUE eCurlErrHostResolution;
33
34
  VALUE eCurlErrConnectFailed;
@@ -110,6 +111,7 @@ VALUE eCurlErrSSLCRLBadfile;
110
111
  VALUE eCurlErrSSLIssuerError;
111
112
 
112
113
  /* multi errors */
114
+ VALUE mCurlErrFailedInit;
113
115
  VALUE mCurlErrCallMultiPerform;
114
116
  VALUE mCurlErrBadHandle;
115
117
  VALUE mCurlErrBadEasyHandle;
@@ -140,9 +142,15 @@ VALUE rb_curl_easy_error(CURLcode code) {
140
142
  case CURLE_URL_MALFORMAT: /* 3 */
141
143
  exclz = eCurlErrMalformedURL;
142
144
  break;
145
+ #ifdef HAVE_CURLE_NOT_BUILT_IN
146
+ case CURLE_NOT_BUILT_IN: /* 4 - [was obsoleted in August 2007 for 7.17.0, reused in April 2011 for 7.21.5] */
147
+ exclz = eCurlErrNotBuiltIn;
148
+ break;
149
+ #else
143
150
  case CURLE_URL_MALFORMAT_USER: /* 4 (NOT USED) */
144
151
  exclz = eCurlErrMalformedURLUser;
145
152
  break;
153
+ #endif
146
154
  case CURLE_COULDNT_RESOLVE_PROXY: /* 5 */
147
155
  exclz = eCurlErrProxyResolution;
148
156
  break;
@@ -278,9 +286,11 @@ VALUE rb_curl_easy_error(CURLcode code) {
278
286
  case CURLE_TELNET_OPTION_SYNTAX: /* 49 - Malformed telnet option */
279
287
  exclz = eCurlErrTelnetBadOptionSyntax;
280
288
  break;
289
+ #ifdef HAVE_CURLE_OBSOLETE
281
290
  case CURLE_OBSOLETE: /* 50 - NOT USED */
282
291
  exclz = eCurlErrObsolete;
283
292
  break;
293
+ #endif
284
294
  case CURLE_SSL_PEER_CERTIFICATE: /* 51 - peer's certificate wasn't ok */
285
295
  exclz = eCurlErrSSLPeerCertificate;
286
296
  break;
@@ -449,7 +459,8 @@ VALUE rb_curl_easy_error(CURLcode code) {
449
459
  /* rb_raise an approriate exception for the supplied CURLcode */
450
460
  void raise_curl_easy_error_exception(CURLcode code) {
451
461
  VALUE obj = rb_curl_easy_error(code);
452
- rb_raise(rb_ary_entry(obj,0), RSTRING_PTR(rb_ary_entry(obj,1)));
462
+ VALUE exmsg = rb_ary_entry(obj,1);
463
+ rb_raise(rb_ary_entry(obj,0), "CURLError: %s", StringValueCStr(exmsg));
453
464
  }
454
465
  VALUE rb_curl_multi_error(CURLMcode code) {
455
466
  VALUE exclz;
@@ -499,7 +510,8 @@ VALUE rb_curl_multi_error(CURLMcode code) {
499
510
  }
500
511
  void raise_curl_multi_error_exception(CURLMcode code) {
501
512
  VALUE obj = rb_curl_multi_error(code);
502
- rb_raise(rb_ary_entry(obj,0), RSTRING_PTR(rb_ary_entry(obj,1)));
513
+ VALUE exmsg = rb_ary_entry(obj,1);
514
+ rb_raise(rb_ary_entry(obj,0), "CURLError: %s", StringValueCStr(exmsg));
503
515
  }
504
516
 
505
517
  void init_curb_errors() {
@@ -517,6 +529,7 @@ void init_curb_errors() {
517
529
  eCurlErrUnsupportedProtocol = rb_define_class_under(mCurlErr, "UnsupportedProtocolError", eCurlErrError);
518
530
  eCurlErrFailedInit = rb_define_class_under(mCurlErr, "FailedInitError", eCurlErrError);
519
531
  eCurlErrMalformedURL = rb_define_class_under(mCurlErr, "MalformedURLError", eCurlErrError);
532
+ eCurlErrNotBuiltIn = rb_define_class_under(mCurlErr, "NotBuiltInError", eCurlErrError);
520
533
  eCurlErrMalformedURLUser = rb_define_class_under(mCurlErr, "MalformedURLUserError", eCurlErrError);
521
534
  eCurlErrProxyResolution = rb_define_class_under(mCurlErr, "ProxyResolutionError", eCurlErrError);
522
535
  eCurlErrHostResolution = rb_define_class_under(mCurlErr, "HostResolutionError", eCurlErrError);
@@ -605,6 +618,7 @@ void init_curb_errors() {
605
618
  eCurlErrSSLShutdownFailed = rb_define_class_under(mCurlErr, "SSLShutdownFailed", eCurlErrError);
606
619
  eCurlErrSSH = rb_define_class_under(mCurlErr, "SSH", eCurlErrError);
607
620
 
621
+ mCurlErrFailedInit = rb_define_class_under(mCurlErr, "MultiInitError", eCurlErrError);
608
622
  mCurlErrCallMultiPerform = rb_define_class_under(mCurlErr, "MultiPerform", eCurlErrError);
609
623
  mCurlErrBadHandle = rb_define_class_under(mCurlErr, "MultiBadHandle", eCurlErrError);
610
624
  mCurlErrBadEasyHandle = rb_define_class_under(mCurlErr, "MultiBadEasyHandle", eCurlErrError);
data/ext/curb_errors.h CHANGED
@@ -108,6 +108,7 @@ extern VALUE eCurlErrSSLCRLBadfile;
108
108
  extern VALUE eCurlErrSSLIssuerError;
109
109
 
110
110
  /* multi errors */
111
+ extern VALUE mCurlErrFailedInit;
111
112
  extern VALUE mCurlErrCallMultiPerform;
112
113
  extern VALUE mCurlErrBadHandle;
113
114
  extern VALUE mCurlErrBadEasyHandle;
data/ext/curb_macros.h CHANGED
@@ -8,6 +8,7 @@
8
8
  #ifndef __CURB_MACROS_H
9
9
  #define __CURB_MACROS_H
10
10
 
11
+ #define rb_easy_sym(sym) ID2SYM(rb_intern(sym))
11
12
  #define rb_easy_hkey(key) ID2SYM(rb_intern(key))
12
13
  #define rb_easy_set(key,val) rb_hash_aset(rbce->opts, rb_easy_hkey(key) , val)
13
14
  #define rb_easy_get(key) rb_hash_aref(rbce->opts, rb_easy_hkey(key))
@@ -152,4 +153,7 @@
152
153
  return INT2FIX(ptr->attr); \
153
154
  }
154
155
 
156
+ #define CURB_DEFINE(name) \
157
+ rb_define_const(mCurl, #name, INT2FIX(name))
158
+
155
159
  #endif
data/ext/curb_multi.c CHANGED
@@ -3,7 +3,6 @@
3
3
  * Licensed under the Ruby License. See LICENSE for details.
4
4
  *
5
5
  */
6
-
7
6
  #include "curb_config.h"
8
7
  #ifdef HAVE_RUBY19_ST_H
9
8
  #include <ruby.h>
@@ -19,6 +18,11 @@
19
18
 
20
19
  #include <errno.h>
21
20
 
21
+ #ifdef _WIN32
22
+ // for O_RDWR and O_BINARY
23
+ #include <fcntl.h>
24
+ #endif
25
+
22
26
  extern VALUE mCurl;
23
27
  static VALUE idCall;
24
28
 
@@ -28,16 +32,18 @@ static VALUE idCall;
28
32
 
29
33
  VALUE cCurlMulti;
30
34
 
35
+ static long cCurlMutiDefaulttimeout = 100; /* milliseconds */
36
+
31
37
  static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy);
32
38
  static void rb_curl_multi_read_info(VALUE self, CURLM *mptr);
39
+ static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running);
33
40
 
34
- static void rb_curl_multi_mark_all_easy(VALUE key, VALUE rbeasy, ruby_curl_multi *rbcm) {
35
- rb_gc_mark(rbeasy);
36
- }
41
+ static VALUE callback_exception(VALUE unused) {
42
+ return Qfalse;
43
+ }
37
44
 
38
45
  static void curl_multi_mark(ruby_curl_multi *rbcm) {
39
46
  rb_gc_mark(rbcm->requests);
40
- rb_hash_foreach( rbcm->requests, (int (*)())rb_curl_multi_mark_all_easy, (VALUE)rbcm );
41
47
  }
42
48
 
43
49
  static void curl_multi_flush_easy(VALUE key, VALUE easy, ruby_curl_multi *rbcm) {
@@ -81,6 +87,9 @@ VALUE ruby_curl_multi_new(VALUE klass) {
81
87
  ruby_curl_multi *rbcm = ALLOC(ruby_curl_multi);
82
88
 
83
89
  rbcm->handle = curl_multi_init();
90
+ if (!rbcm->handle) {
91
+ rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
92
+ }
84
93
 
85
94
  rbcm->requests = rb_hash_new();
86
95
 
@@ -92,6 +101,30 @@ VALUE ruby_curl_multi_new(VALUE klass) {
92
101
  return new_curlm;
93
102
  }
94
103
 
104
+ /*
105
+ * call-seq:
106
+ * Curl::Multi.default_timeout = 4 => 4
107
+ *
108
+ * Set the global default time out for all Curl::Multi Handles. This value is used
109
+ * when libcurl cannot determine a timeout value when calling curl_multi_timeout.
110
+ *
111
+ */
112
+ VALUE ruby_curl_multi_set_default_timeout(VALUE klass, VALUE timeout) {
113
+ cCurlMutiDefaulttimeout = FIX2LONG(timeout);
114
+ return timeout;
115
+ }
116
+
117
+ /*
118
+ * call-seq:
119
+ * Curl::Multi.default_timeout = 4 => 4
120
+ *
121
+ * Get the global default time out for all Curl::Multi Handles.
122
+ *
123
+ */
124
+ VALUE ruby_curl_multi_get_default_timeout(VALUE klass) {
125
+ return INT2FIX(cCurlMutiDefaulttimeout);
126
+ }
127
+
95
128
  /* Hash#foreach callback for ruby_curl_multi_requests */
96
129
  static int ruby_curl_multi_requests_callback(VALUE key, VALUE value, VALUE result_array) {
97
130
  rb_ary_push(result_array, value);
@@ -194,14 +227,14 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
194
227
  Data_Get_Struct(self, ruby_curl_multi, rbcm);
195
228
  Data_Get_Struct(easy, ruby_curl_easy, rbce);
196
229
 
230
+ /* setup the easy handle */
231
+ ruby_curl_easy_setup( rbce );
232
+
197
233
  mcode = curl_multi_add_handle(rbcm->handle, rbce->curl);
198
234
  if (mcode != CURLM_CALL_MULTI_PERFORM && mcode != CURLM_OK) {
199
235
  raise_curl_multi_error_exception(mcode);
200
236
  }
201
237
 
202
- /* setup the easy handle */
203
- ruby_curl_easy_setup( rbce, &(rbce->curl_headers) );
204
-
205
238
  rbcm->active++;
206
239
 
207
240
  /* Increase the running count, so that the perform loop keeps running.
@@ -253,7 +286,7 @@ static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
253
286
 
254
287
  rbcm->active--;
255
288
 
256
- ruby_curl_easy_cleanup( easy, rbce, rbce->curl_headers );
289
+ ruby_curl_easy_cleanup( easy, rbce );
257
290
 
258
291
  // active should equal INT2FIX(RHASH(rbcm->requests)->tbl->num_entries)
259
292
  r = rb_hash_delete( rbcm->requests, easy );
@@ -286,44 +319,96 @@ static VALUE ruby_curl_multi_cancel(VALUE self) {
286
319
  return self;
287
320
  }
288
321
 
322
+ // on_success, on_failure, on_complete
323
+ static VALUE call_status_handler1(VALUE ary) {
324
+ return rb_funcall(rb_ary_entry(ary, 0), idCall, 1, rb_ary_entry(ary, 1));
325
+ }
326
+ static VALUE call_status_handler2(VALUE ary) {
327
+ return rb_funcall(rb_ary_entry(ary, 0), idCall, 2, rb_ary_entry(ary, 1), rb_ary_entry(ary, 2));
328
+ }
329
+
289
330
  static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int result) {
290
331
 
291
332
  long response_code = -1;
292
333
  VALUE easy;
293
334
  ruby_curl_easy *rbce = NULL;
335
+ VALUE callargs, val = Qtrue;
294
336
 
295
337
  CURLcode ecode = curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, (char**)&easy);
296
338
 
297
339
  Data_Get_Struct(easy, ruby_curl_easy, rbce);
298
340
 
299
- if (ecode != 0) {
300
- raise_curl_easy_error_exception(ecode);
301
- }
302
-
303
341
  rbce->last_result = result; /* save the last easy result code */
304
342
 
305
343
  ruby_curl_multi_remove( self, easy );
306
344
 
345
+ /* after running a request cleanup the headers, these are set before each request */
346
+ if (rbce->curl_headers) {
347
+ curl_slist_free_all(rbce->curl_headers);
348
+ rbce->curl_headers = NULL;
349
+ }
350
+
351
+ if (ecode != 0) {
352
+ raise_curl_easy_error_exception(ecode);
353
+ }
354
+
307
355
  if (!rb_easy_nil("complete_proc")) {
308
- rb_funcall( rb_easy_get("complete_proc"), idCall, 1, easy );
356
+ callargs = rb_ary_new3(2, rb_easy_get("complete_proc"), easy);
357
+ rbce->callback_active = 1;
358
+ val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
359
+ rbce->callback_active = 0;
360
+ //rb_funcall( rb_easy_get("complete_proc"), idCall, 1, easy );
309
361
  }
310
362
 
311
363
  curl_easy_getinfo(rbce->curl, CURLINFO_RESPONSE_CODE, &response_code);
312
364
 
313
365
  if (result != 0) {
314
366
  if (!rb_easy_nil("failure_proc")) {
315
- rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
367
+ callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
368
+ rbce->callback_active = 1;
369
+ val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
370
+ rbce->callback_active = 0;
371
+ //rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
316
372
  }
317
373
  }
318
374
  else if (!rb_easy_nil("success_proc") &&
319
375
  ((response_code >= 200 && response_code < 300) || response_code == 0)) {
320
376
  /* NOTE: we allow response_code == 0, in the case of non http requests e.g. reading from disk */
321
- rb_funcall( rb_easy_get("success_proc"), idCall, 1, easy );
377
+ callargs = rb_ary_new3(2, rb_easy_get("success_proc"), easy);
378
+ rbce->callback_active = 1;
379
+ val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
380
+ rbce->callback_active = 0;
381
+ //rb_funcall( rb_easy_get("success_proc"), idCall, 1, easy );
382
+ }
383
+ else if (!rb_easy_nil("redirect_proc") &&
384
+ (response_code >= 300 && response_code < 400)) {
385
+ rbce->callback_active = 1;
386
+ callargs = rb_ary_new3(3, rb_easy_get("redirect_proc"), easy, rb_curl_easy_error(result));
387
+ rbce->callback_active = 0;
388
+ val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
389
+ }
390
+ else if (!rb_easy_nil("missing_proc") &&
391
+ (response_code >= 400 && response_code < 500)) {
392
+ rbce->callback_active = 1;
393
+ callargs = rb_ary_new3(3, rb_easy_get("missing_proc"), easy, rb_curl_easy_error(result));
394
+ rbce->callback_active = 0;
395
+ val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
322
396
  }
323
397
  else if (!rb_easy_nil("failure_proc") &&
324
- (response_code >= 300 && response_code <= 999)) {
325
- rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
398
+ (response_code >= 500 && response_code <= 999)) {
399
+ callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
400
+ rbce->callback_active = 1;
401
+ val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
402
+ rbce->callback_active = 0;
403
+ //rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
326
404
  }
405
+
406
+ if (val == Qfalse) {
407
+ rb_warn("uncaught exception from callback");
408
+ // exception was raised?
409
+ //fprintf(stderr, "exception raised from callback\n");
410
+ }
411
+
327
412
  }
328
413
 
329
414
  static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
@@ -354,9 +439,51 @@ static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_runnin
354
439
  if (mcode != CURLM_OK) {
355
440
  raise_curl_multi_error_exception(mcode);
356
441
  }
442
+
443
+ }
444
+
445
+ #ifdef _WIN32
446
+ void create_crt_fd(fd_set *os_set, fd_set *crt_set)
447
+ {
448
+ int i;
449
+ crt_set->fd_count = os_set->fd_count;
450
+ for (i = 0; i < os_set->fd_count; i++) {
451
+ WSAPROTOCOL_INFO wsa_pi;
452
+ // dupicate the SOCKET
453
+ int r = WSADuplicateSocket(os_set->fd_array[i], GetCurrentProcessId(), &wsa_pi);
454
+ SOCKET s = WSASocket(wsa_pi.iAddressFamily, wsa_pi.iSocketType, wsa_pi.iProtocol, &wsa_pi, 0, 0);
455
+ // create the CRT fd so ruby can get back to the SOCKET
456
+ int fd = _open_osfhandle(s, O_RDWR|O_BINARY);
457
+ os_set->fd_array[i] = s;
458
+ crt_set->fd_array[i] = fd;
459
+ }
460
+ }
357
461
 
358
- rb_curl_multi_read_info( self, multi_handle );
462
+ void cleanup_crt_fd(fd_set *os_set, fd_set *crt_set)
463
+ {
464
+ int i;
465
+ for (i = 0; i < os_set->fd_count; i++) {
466
+ // cleanup the CRT fd
467
+ _close(crt_set->fd_array[i]);
468
+ // cleanup the duplicated SOCKET
469
+ closesocket(os_set->fd_array[i]);
470
+ }
359
471
  }
472
+ #endif
473
+
474
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
475
+ struct _select_set {
476
+ int maxfd;
477
+ fd_set *fdread, *fdwrite, *fdexcep;
478
+ struct timeval *tv;
479
+ };
480
+
481
+ static VALUE curb_select(void *args) {
482
+ struct _select_set* set = args;
483
+ int rc = select(set->maxfd, set->fdread, set->fdwrite, set->fdexcep, set->tv);
484
+ return INT2FIX(rc);
485
+ }
486
+ #endif
360
487
 
361
488
  /*
362
489
  * call-seq:
@@ -378,75 +505,108 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
378
505
  ruby_curl_multi *rbcm;
379
506
  int maxfd, rc;
380
507
  fd_set fdread, fdwrite, fdexcep;
381
-
508
+ #ifdef _WIN32
509
+ fd_set crt_fdread, crt_fdwrite, crt_fdexcep;
510
+ #endif
382
511
  long timeout_milliseconds;
383
512
  struct timeval tv = {0, 0};
384
513
  VALUE block = Qnil;
514
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
515
+ struct _select_set fdset_args;
516
+ #endif
385
517
 
386
518
  rb_scan_args(argc, argv, "0&", &block);
387
519
 
388
520
  Data_Get_Struct(self, ruby_curl_multi, rbcm);
389
521
 
522
+ timeout_milliseconds = cCurlMutiDefaulttimeout;
523
+
390
524
  rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
525
+ rb_curl_multi_read_info( self, rbcm->handle );
526
+ if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
527
+
528
+ do {
529
+ while (rbcm->running) {
530
+
531
+ #ifdef HAVE_CURL_MULTI_TIMEOUT
532
+ /* get the curl suggested time out */
533
+ mcode = curl_multi_timeout(rbcm->handle, &timeout_milliseconds);
534
+ if (mcode != CURLM_OK) {
535
+ raise_curl_multi_error_exception(mcode);
536
+ }
537
+ #else
538
+ /* libcurl doesn't have a timeout method defined, initialize to -1 we'll pick up the default later */
539
+ timeout_milliseconds = -1;
540
+ #endif
541
+
542
+ if (timeout_milliseconds == 0) { /* no delay */
543
+ rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
544
+ rb_curl_multi_read_info( self, rbcm->handle );
545
+ if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
546
+ continue;
547
+ }
391
548
 
392
- while(rbcm->running) {
393
- FD_ZERO(&fdread);
394
- FD_ZERO(&fdwrite);
395
- FD_ZERO(&fdexcep);
549
+ if (timeout_milliseconds < 0 || timeout_milliseconds > cCurlMutiDefaulttimeout) {
550
+ timeout_milliseconds = cCurlMutiDefaulttimeout; /* libcurl doesn't know how long to wait, use a default timeout */
551
+ /* or buggy versions libcurl sometimes reports huge timeouts... let's cap it */
552
+ }
396
553
 
397
- /* load the fd sets from the multi handle */
398
- mcode = curl_multi_fdset(rbcm->handle, &fdread, &fdwrite, &fdexcep, &maxfd);
399
- if (mcode != CURLM_OK) {
400
- raise_curl_multi_error_exception(mcode);
401
- }
554
+ tv.tv_sec = 0; /* never wait longer than 1 second */
555
+ tv.tv_usec = (int)(timeout_milliseconds * 1000); /* XXX: int is the right type for OSX, what about linux? */
402
556
 
403
- #ifdef HAVE_CURL_MULTI_TIMEOUT
404
- /* get the curl suggested time out */
405
- mcode = curl_multi_timeout(rbcm->handle, &timeout_milliseconds);
406
- if (mcode != CURLM_OK) {
407
- raise_curl_multi_error_exception(mcode);
408
- }
557
+ FD_ZERO(&fdread);
558
+ FD_ZERO(&fdwrite);
559
+ FD_ZERO(&fdexcep);
560
+
561
+ /* load the fd sets from the multi handle */
562
+ mcode = curl_multi_fdset(rbcm->handle, &fdread, &fdwrite, &fdexcep, &maxfd);
563
+ if (mcode != CURLM_OK) {
564
+ raise_curl_multi_error_exception(mcode);
565
+ }
566
+
567
+ #ifdef _WIN32
568
+ create_crt_fd(&fdread, &crt_fdread);
569
+ create_crt_fd(&fdwrite, &crt_fdwrite);
570
+ create_crt_fd(&fdexcep, &crt_fdexcep);
571
+ #endif
572
+
573
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
574
+ fdset_args.maxfd = maxfd+1;
575
+ fdset_args.fdread = &fdread;
576
+ fdset_args.fdwrite = &fdwrite;
577
+ fdset_args.fdexcep = &fdexcep;
578
+ fdset_args.tv = &tv;
579
+ rc = rb_thread_blocking_region(curb_select, &fdset_args, RUBY_UBF_IO, 0);
409
580
  #else
410
- /* libcurl doesn't have a timeout method defined... make a wild guess */
411
- timeout_milliseconds = -1;
581
+ rc = rb_thread_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
412
582
  #endif
413
- //printf("libcurl says wait: %ld ms or %ld s\n", timeout_milliseconds, timeout_milliseconds/1000);
414
583
 
415
- if (timeout_milliseconds == 0) { /* no delay */
416
- rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
417
- continue;
418
- }
419
- else if(timeout_milliseconds < 0) {
420
- timeout_milliseconds = 500; /* wait half a second, libcurl doesn't know how long to wait */
421
- }
422
- #ifdef __APPLE_CC__
423
- if(timeout_milliseconds > 1000) {
424
- timeout_milliseconds = 1000; /* apple libcurl sometimes reports huge timeouts... let's cap it */
425
- }
584
+ #ifdef _WIN32
585
+ cleanup_crt_fd(&fdread, &crt_fdread);
586
+ cleanup_crt_fd(&fdwrite, &crt_fdwrite);
587
+ cleanup_crt_fd(&fdexcep, &crt_fdexcep);
426
588
  #endif
427
589
 
428
- tv.tv_sec = timeout_milliseconds / 1000; // convert milliseconds to seconds
429
- tv.tv_usec = (timeout_milliseconds % 1000) * 1000; // get the remainder of milliseconds and convert to micro seconds
430
-
431
- rc = rb_thread_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
432
- switch(rc) {
433
- case -1:
434
- rb_raise(rb_eRuntimeError, "select(): %s", strerror(errno));
435
- break;
436
- case 0:
437
- if (block != Qnil) {
438
- rb_funcall(block, rb_intern("call"), 1, self);
590
+ switch(rc) {
591
+ case -1:
592
+ if(errno != EINTR) {
593
+ rb_raise(rb_eRuntimeError, "select(): %s", strerror(errno));
594
+ break;
595
+ }
596
+ case 0: /* timeout */
597
+ default: /* action */
598
+ rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
599
+ rb_curl_multi_read_info( self, rbcm->handle );
600
+ if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
601
+ break;
439
602
  }
440
- // if (rb_block_given_p()) {
441
- // rb_yield(self);
442
- // }
443
- default:
444
- rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
445
- break;
446
603
  }
447
604
 
448
- }
605
+ } while( rbcm->running );
449
606
 
607
+ rb_curl_multi_read_info( self, rbcm->handle );
608
+ if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
609
+
450
610
  return Qtrue;
451
611
  }
452
612
 
@@ -458,6 +618,8 @@ void init_curb_multi() {
458
618
 
459
619
  /* Class methods */
460
620
  rb_define_singleton_method(cCurlMulti, "new", ruby_curl_multi_new, 0);
621
+ rb_define_singleton_method(cCurlMulti, "default_timeout=", ruby_curl_multi_set_default_timeout, 1);
622
+ rb_define_singleton_method(cCurlMulti, "default_timeout", ruby_curl_multi_get_default_timeout, 0);
461
623
 
462
624
  /* "Attributes" */
463
625
  rb_define_method(cCurlMulti, "requests", ruby_curl_multi_requests, 0);