curb 0.9.4 → 0.9.10

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
@@ -55,6 +55,8 @@ typedef struct {
55
55
  unsigned long ftp_response_timeout;
56
56
  long low_speed_limit;
57
57
  long low_speed_time;
58
+ long max_send_speed_large;
59
+ long max_recv_speed_large;
58
60
  long ssl_version;
59
61
  long use_ssl;
60
62
  long ftp_filemethod;
@@ -76,7 +78,9 @@ typedef struct {
76
78
  char callback_active;
77
79
 
78
80
  struct curl_slist *curl_headers;
81
+ struct curl_slist *curl_proxy_headers;
79
82
  struct curl_slist *curl_ftp_commands;
83
+ struct curl_slist *curl_resolve;
80
84
 
81
85
  int last_result; /* last result code from multi loop */
82
86
 
data/ext/curb_errors.c CHANGED
@@ -21,6 +21,7 @@ VALUE eCurlErrFileError;
21
21
  VALUE eCurlErrLDAPError;
22
22
  VALUE eCurlErrTelnetError;
23
23
  VALUE eCurlErrTFTPError;
24
+ VALUE eCurlErrRTSPError;
24
25
 
25
26
  /* Specific libcurl errors */
26
27
  VALUE eCurlErrOK; /* not really an error but a return code */
@@ -127,6 +128,18 @@ VALUE mCurlErrUnknownOption;
127
128
  VALUE eCurlErrInvalidPostField;
128
129
 
129
130
 
131
+ /* new errors */
132
+ VALUE eCurlErrFTPPRETFailed;
133
+ VALUE eCurlErrRTSPCseqError;
134
+ VALUE eCurlErrRTSPSessionError;
135
+ VALUE eCurlErrFTPBadFileList;
136
+ VALUE eCurlErrChunkFailed;
137
+ VALUE eCurlErrNoConnectionAvailable;
138
+ VALUE eCurlErrSSLPinnedPubKeyNotMatch;
139
+ VALUE eCurlErrSSLInvalidCertStatus;
140
+ VALUE eCurlErrHTTP2Stream;
141
+
142
+
130
143
  VALUE rb_curl_easy_error(CURLcode code) {
131
144
  VALUE exclz;
132
145
  const char *exmsg = NULL;
@@ -294,9 +307,11 @@ VALUE rb_curl_easy_error(CURLcode code) {
294
307
  exclz = eCurlErrObsolete;
295
308
  break;
296
309
  #endif
310
+ #if LIBCURL_VERSION_NUM < 0x073e00
297
311
  case CURLE_SSL_PEER_CERTIFICATE: /* 51 - peer's certificate wasn't ok */
298
312
  exclz = eCurlErrSSLPeerCertificate;
299
313
  break;
314
+ #endif
300
315
  case CURLE_GOT_NOTHING: /* 52 - when this is a specific error */
301
316
  exclz = eCurlErrGotNothing;
302
317
  break;
@@ -321,8 +336,13 @@ VALUE rb_curl_easy_error(CURLcode code) {
321
336
  case CURLE_SSL_CIPHER: /* 59 - couldn't use specified cipher */
322
337
  exclz = eCurlErrSSLCipher;
323
338
  break;
339
+ #if LIBCURL_VERSION_NUM >= 0x073e00
340
+ case CURLE_PEER_FAILED_VERIFICATION: /* 60 - problem with the CA cert (path?) */
341
+ exclz = eCurlErrSSLPeerCertificate;
342
+ #else
324
343
  case CURLE_SSL_CACERT: /* 60 - problem with the CA cert (path?) */
325
344
  exclz = eCurlErrSSLCACertificate;
345
+ #endif
326
346
  break;
327
347
  case CURLE_BAD_CONTENT_ENCODING: /* 61 - Unrecognized transfer encoding */
328
348
  exclz = eCurlErrBadContentEncoding;
@@ -445,6 +465,61 @@ VALUE rb_curl_easy_error(CURLcode code) {
445
465
  exclz = eCurlErrSSLIssuerError;
446
466
  break;
447
467
  #endif
468
+
469
+ #ifdef HAVE_CURLE_FTP_PRET_FAILED
470
+ case CURLE_FTP_PRET_FAILED: /* 84 */
471
+ exclz = eCurlErrFTPPRETFailed;
472
+ break;
473
+ #endif
474
+
475
+ #ifdef HAVE_CURLE_RTSP_CSEQ_ERROR
476
+ case CURLE_RTSP_CSEQ_ERROR: /* 85 */
477
+ exclz = eCurlErrRTSPCseqError;
478
+ break;
479
+ #endif
480
+
481
+ #ifdef HAVE_CURLE_RTSP_SESSION_ERROR
482
+ case CURLE_RTSP_SESSION_ERROR: /* 86 */
483
+ exclz = eCurlErrRTSPSessionError;
484
+ break;
485
+ #endif
486
+
487
+ #ifdef HAVE_CURLE_FTP_BAD_FILE_LIST
488
+ case CURLE_FTP_BAD_FILE_LIST: /* 87 */
489
+ exclz = eCurlErrFTPBadFileList;
490
+ break;
491
+ #endif
492
+
493
+ #ifdef HAVE_CURLE_CHUNK_FAILED
494
+ case CURLE_CHUNK_FAILED: /* 88 */
495
+ exclz = eCurlErrChunkFailed;
496
+ break;
497
+ #endif
498
+
499
+ #ifdef HAVE_CURLE_NO_CONNECTION_AVAILABLE
500
+ case CURLE_NO_CONNECTION_AVAILABLE: /* 89 */
501
+ exclz = eCurlErrNoConnectionAvailable;
502
+ break;
503
+ #endif
504
+
505
+ #ifdef HAVE_CURLE_SSL_PINNEDPUBKEYNOTMATCH
506
+ case CURLE_SSL_PINNEDPUBKEYNOTMATCH: /* 90 */
507
+ exclz = eCurlErrSSLPinnedPubKeyNotMatch;
508
+ break;
509
+ #endif
510
+
511
+ #ifdef HAVE_CURLE_SSL_INVALIDCERTSTATUS
512
+ case CURLE_SSL_INVALIDCERTSTATUS: /* 91 */
513
+ exclz = eCurlErrSSLInvalidCertStatus;
514
+ break;
515
+ #endif
516
+
517
+ #ifdef HAVE_CURLE_HTTP2_STREAM
518
+ case CURLE_HTTP2_STREAM: /* 92 */
519
+ exclz = eCurlErrHTTP2Stream;
520
+ break;
521
+ #endif
522
+
448
523
  default:
449
524
  exclz = eCurlErrError;
450
525
  exmsg = "Unknown error result from libcurl";
@@ -532,6 +607,7 @@ void init_curb_errors() {
532
607
  eCurlErrLDAPError = rb_define_class_under(mCurlErr, "LDAPError", eCurlErrError);
533
608
  eCurlErrTelnetError = rb_define_class_under(mCurlErr, "TelnetError", eCurlErrError);
534
609
  eCurlErrTFTPError = rb_define_class_under(mCurlErr, "TFTPError", eCurlErrError);
610
+ eCurlErrRTSPError = rb_define_class_under(mCurlErr, "RTSPError", eCurlErrError);
535
611
 
536
612
  eCurlErrOK = rb_define_class_under(mCurlErr, "CurlOK", eCurlErrError);
537
613
  eCurlErrUnsupportedProtocol = rb_define_class_under(mCurlErr, "UnsupportedProtocolError", eCurlErrError);
@@ -657,4 +733,14 @@ void init_curb_errors() {
657
733
  eCurlErrTFTPNoSuchUser = rb_define_class_under(mCurlErr, "NoSuchUserError", eCurlErrTFTPError);
658
734
 
659
735
  eCurlErrInvalidPostField = rb_define_class_under(mCurlErr, "InvalidPostFieldError", eCurlErrError);
736
+
737
+ eCurlErrFTPPRETFailed = rb_define_class_under(mCurlErr, "PPRETFailedError", eCurlErrFTPError);
738
+ eCurlErrRTSPCseqError = rb_define_class_under(mCurlErr, "CseqError", eCurlErrRTSPError);
739
+ eCurlErrRTSPSessionError = rb_define_class_under(mCurlErr, "SessionError", eCurlErrRTSPError);
740
+ eCurlErrFTPBadFileList = rb_define_class_under(mCurlErr, "BadFileListError", eCurlErrFTPError);
741
+ eCurlErrChunkFailed = rb_define_class_under(mCurlErr, "ChunkFailedError", eCurlErrError);
742
+ eCurlErrNoConnectionAvailable = rb_define_class_under(mCurlErr, "NoConnectionAvailableError", eCurlErrError);
743
+ eCurlErrSSLPinnedPubKeyNotMatch = rb_define_class_under(mCurlErr, "SSLPinnedPubKeyNotMatchError", eCurlErrError);
744
+ eCurlErrSSLInvalidCertStatus = rb_define_class_under(mCurlErr, "SSLInvalidCertStatusError", eCurlErrError);
745
+ eCurlErrHTTP2Stream = rb_define_class_under(mCurlErr, "HTTP2StreamError", eCurlErrHTTPError);
660
746
  }
data/ext/curb_multi.c CHANGED
@@ -37,6 +37,7 @@ static VALUE idCall;
37
37
  VALUE cCurlMulti;
38
38
 
39
39
  static long cCurlMutiDefaulttimeout = 100; /* milliseconds */
40
+ static char cCurlMutiAutoClose = 0;
40
41
 
41
42
  static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy);
42
43
  static void rb_curl_multi_read_info(VALUE self, CURLM *mptr);
@@ -44,43 +45,21 @@ static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_runnin
44
45
 
45
46
  static VALUE callback_exception(VALUE unused) {
46
47
  return Qfalse;
47
- }
48
-
49
- static void curl_multi_mark(ruby_curl_multi *rbcm) {
50
- if (!NIL_P(rbcm->requests)) rb_gc_mark(rbcm->requests);
51
- }
52
-
53
- /* Hash#foreach callback for curl_multi_free */
54
- static int curl_multi_flush_easy(VALUE key, VALUE easy, ruby_curl_multi *rbcm) {
55
- CURLMcode result;
56
- ruby_curl_easy *rbce;
57
-
58
- // sometimes the type is T_ZOMBIE, e.g. after Ruby has received the SIGTERM signal
59
- if (rb_type(easy) == T_DATA) {
60
- Data_Get_Struct(easy, ruby_curl_easy, rbce);
61
-
62
- result = curl_multi_remove_handle(rbcm->handle, rbce->curl);
63
- if (result != 0) {
64
- raise_curl_multi_error_exception(result);
65
- }
66
- }
67
-
68
- return ST_DELETE;
69
48
  }
70
49
 
71
50
  void curl_multi_free(ruby_curl_multi *rbcm) {
72
- VALUE hash = rbcm->requests;
73
-
74
- if (!NIL_P(hash) && rb_type(hash) == T_HASH && RHASH_SIZE(hash) > 0) {
75
-
76
- rb_hash_foreach(hash, curl_multi_flush_easy, (VALUE)rbcm);
77
- /* rb_hash_clear(rbcm->requests); */
51
+ curl_multi_cleanup(rbcm->handle);
52
+ free(rbcm);
53
+ }
78
54
 
79
- rbcm->requests = Qnil;
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");
80
59
  }
81
60
 
82
- curl_multi_cleanup(rbcm->handle);
83
- free(rbcm);
61
+ rbcm->active = 0;
62
+ rbcm->running = 0;
84
63
  }
85
64
 
86
65
  /*
@@ -90,23 +69,17 @@ void curl_multi_free(ruby_curl_multi *rbcm) {
90
69
  * Create a new Curl::Multi instance
91
70
  */
92
71
  VALUE ruby_curl_multi_new(VALUE klass) {
93
- VALUE new_curlm;
94
-
95
72
  ruby_curl_multi *rbcm = ALLOC(ruby_curl_multi);
96
73
 
97
- rbcm->handle = curl_multi_init();
98
- if (!rbcm->handle) {
99
- rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
100
- }
74
+ ruby_curl_multi_init(rbcm);
101
75
 
102
- rbcm->requests = rb_hash_new();
103
-
104
- rbcm->active = 0;
105
- rbcm->running = 0;
106
-
107
- new_curlm = Data_Wrap_Struct(klass, curl_multi_mark, curl_multi_free, rbcm);
108
-
109
- return new_curlm;
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);
110
83
  }
111
84
 
112
85
  /*
@@ -133,52 +106,36 @@ VALUE ruby_curl_multi_get_default_timeout(VALUE klass) {
133
106
  return LONG2NUM(cCurlMutiDefaulttimeout);
134
107
  }
135
108
 
136
- /* Hash#foreach callback for ruby_curl_multi_requests */
137
- static int ruby_curl_multi_requests_callback(VALUE key, VALUE value, VALUE result_array) {
138
- rb_ary_push(result_array, value);
139
-
140
- return ST_CONTINUE;
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;
141
120
  }
142
121
 
143
122
  /*
144
123
  * call-seq:
145
- * multi.requests => [#&lt;Curl::Easy...&gt;, ...]
146
- *
147
- * Returns an array containing all the active requests on this Curl::Multi object.
124
+ * Curl::Multi.autoclose => true|false
125
+ *
126
+ * Get the global default autoclose setting for all Curl::Multi Handles.
127
+ *
148
128
  */
149
- static VALUE ruby_curl_multi_requests(VALUE self) {
150
- ruby_curl_multi *rbcm;
151
- VALUE result_array;
152
-
153
- Data_Get_Struct(self, ruby_curl_multi, rbcm);
154
-
155
- result_array = rb_ary_new();
156
-
157
- /* iterate over the requests hash, and stuff references into the array. */
158
- rb_hash_foreach(rbcm->requests, ruby_curl_multi_requests_callback, result_array);
159
-
160
- return result_array;
129
+ VALUE ruby_curl_multi_get_autoclose(VALUE klass) {
130
+ return cCurlMutiAutoClose == 1 ? Qtrue : Qfalse;
161
131
  }
162
132
 
163
133
  /*
164
134
  * call-seq:
165
- * multi.idle? => true or false
135
+ * multi.requests => [#&lt;Curl::Easy...&gt;, ...]
166
136
  *
167
- * Returns whether or not this Curl::Multi handle is processing any requests. E.g. this returns
168
- * true when multi.requests.length == 0.
137
+ * Returns an array containing all the active requests on this Curl::Multi object.
169
138
  */
170
- static VALUE ruby_curl_multi_idle(VALUE self) {
171
- ruby_curl_multi *rbcm;
172
-
173
- Data_Get_Struct(self, ruby_curl_multi, rbcm);
174
-
175
- if (RHASH_SIZE(rbcm->requests) == 0) {
176
- return Qtrue;
177
- } else {
178
- return Qfalse;
179
- }
180
- }
181
-
182
139
  /*
183
140
  * call-seq:
184
141
  * multi = Curl::Multi.new
@@ -241,19 +198,12 @@ static VALUE ruby_curl_multi_pipeline(VALUE self, VALUE method) {
241
198
  */
242
199
  VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
243
200
  CURLMcode mcode;
244
- VALUE r;
245
201
  ruby_curl_easy *rbce;
246
202
  ruby_curl_multi *rbcm;
247
203
 
248
204
  Data_Get_Struct(self, ruby_curl_multi, rbcm);
249
205
  Data_Get_Struct(easy, ruby_curl_easy, rbce);
250
206
 
251
- // check if this curl handle has been added before adding again
252
- r = rb_hash_aref(rbcm->requests, LONG2NUM((long)rbce->curl));
253
- if ( r != Qnil ) {
254
- return Qnil;
255
- }
256
-
257
207
  /* setup the easy handle */
258
208
  ruby_curl_easy_setup( rbce );
259
209
 
@@ -271,8 +221,6 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
271
221
  /* track a reference to associated multi handle */
272
222
  rbce->multi = self;
273
223
 
274
- rb_hash_aset( rbcm->requests, LONG2NUM((long)rbce->curl), easy );
275
-
276
224
  return self;
277
225
  }
278
226
 
@@ -290,28 +238,20 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
290
238
  *
291
239
  * Will raise an exception if the easy handle is not found
292
240
  */
293
- VALUE ruby_curl_multi_remove(VALUE self, VALUE easy) {
241
+ VALUE ruby_curl_multi_remove(VALUE self, VALUE rb_easy_handle) {
294
242
  ruby_curl_multi *rbcm;
295
243
 
296
244
  Data_Get_Struct(self, ruby_curl_multi, rbcm);
297
245
 
298
- rb_curl_multi_remove(rbcm,easy);
246
+ rb_curl_multi_remove(rbcm, rb_easy_handle);
299
247
 
300
248
  return self;
301
249
  }
302
250
  static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
303
251
  CURLMcode result;
304
252
  ruby_curl_easy *rbce;
305
- VALUE r;
306
253
 
307
254
  Data_Get_Struct(easy, ruby_curl_easy, rbce);
308
-
309
- // check if this curl handle has been added before removing
310
- r = rb_hash_aref(rbcm->requests, LONG2NUM((long)rbce->curl));
311
- if ( r == Qnil ) {
312
- return;
313
- }
314
-
315
255
  result = curl_multi_remove_handle(rbcm->handle, rbce->curl);
316
256
  if (result != 0) {
317
257
  raise_curl_multi_error_exception(result);
@@ -320,36 +260,6 @@ static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
320
260
  rbcm->active--;
321
261
 
322
262
  ruby_curl_easy_cleanup( easy, rbce );
323
-
324
- // active should equal LONG2NUM(RHASH(rbcm->requests)->tbl->num_entries)
325
- r = rb_hash_delete( rbcm->requests, LONG2NUM((long)rbce->curl) );
326
- if( r != easy || r == Qnil ) {
327
- rb_warn("Possibly lost track of Curl::Easy VALUE, it may not be reclaimed by GC");
328
- }
329
- }
330
-
331
- /* Hash#foreach callback for ruby_curl_multi_cancel */
332
- static int ruby_curl_multi_cancel_callback(VALUE key, VALUE value, ruby_curl_multi *rbcm) {
333
- rb_curl_multi_remove(rbcm, value);
334
-
335
- return ST_CONTINUE;
336
- }
337
-
338
- /*
339
- * call-seq:
340
- * multi.cancel!
341
- *
342
- * Cancels all requests currently being made on this Curl::Multi handle.
343
- */
344
- static VALUE ruby_curl_multi_cancel(VALUE self) {
345
- ruby_curl_multi *rbcm;
346
-
347
- Data_Get_Struct(self, ruby_curl_multi, rbcm);
348
-
349
- rb_hash_foreach( rbcm->requests, ruby_curl_multi_cancel_callback, (VALUE)rbcm );
350
-
351
- /* for chaining */
352
- return self;
353
263
  }
354
264
 
355
265
  // on_success, on_failure, on_complete
@@ -361,7 +271,6 @@ static VALUE call_status_handler2(VALUE ary) {
361
271
  }
362
272
 
363
273
  static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int result) {
364
-
365
274
  long response_code = -1;
366
275
  VALUE easy;
367
276
  ruby_curl_easy *rbce = NULL;
@@ -373,7 +282,8 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
373
282
 
374
283
  rbce->last_result = result; /* save the last easy result code */
375
284
 
376
- ruby_curl_multi_remove( self, easy );
285
+ // remove the easy handle from multi on completion so it can be reused again
286
+ rb_funcall(self, rb_intern("remove"), 1, easy);
377
287
 
378
288
  /* after running a request cleanup the headers, these are set before each request */
379
289
  if (rbce->curl_headers) {
@@ -436,26 +346,30 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
436
346
  //rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
437
347
  }
438
348
 
439
- if (val == Qfalse) {
440
- rb_warn("uncaught exception from callback");
441
- }
442
-
443
349
  }
444
350
 
445
351
  static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
446
- int msgs_left, result;
447
- CURLMsg *msg;
448
- CURL *easy_handle;
449
-
450
- /* check for finished easy handles and remove from the multi handle */
451
- while ((msg = curl_multi_info_read(multi_handle, &msgs_left))) {
452
- if (msg->msg == CURLMSG_DONE) {
453
- easy_handle = msg->easy_handle;
454
- result = msg->data.result;
455
- if (easy_handle) {
456
- rb_curl_mutli_handle_complete(self, easy_handle, result);
457
- }
458
- }
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);
459
373
  }
460
374
  }
461
375
 
@@ -463,14 +377,32 @@ static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
463
377
  static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running) {
464
378
  CURLMcode mcode;
465
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
+ */
466
387
  do {
467
388
  mcode = curl_multi_perform(multi_handle, still_running);
468
389
  } while (mcode == CURLM_CALL_MULTI_PERFORM);
469
390
 
391
+ /*
392
+ * Nothing more to do, check if an error occured in the loop above and raise an exception if necessary.
393
+ */
394
+
470
395
  if (mcode != CURLM_OK) {
471
396
  raise_curl_multi_error_exception(mcode);
472
397
  }
473
-
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
+ */
474
406
  }
475
407
 
476
408
  #ifdef _WIN32
@@ -541,6 +473,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
541
473
  #endif
542
474
  long timeout_milliseconds;
543
475
  struct timeval tv = {0, 0};
476
+ struct timeval tv_100ms = {0, 100000};
544
477
  VALUE block = Qnil;
545
478
  #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
546
479
  struct _select_set fdset_args;
@@ -552,10 +485,23 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
552
485
 
553
486
  timeout_milliseconds = cCurlMutiDefaulttimeout;
554
487
 
488
+ // Run curl_multi_perform for the first time to get the ball rolling
555
489
  rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
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.
556
496
  rb_curl_multi_read_info( self, rbcm->handle );
557
- if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
558
-
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
+
559
505
  do {
560
506
  while (rbcm->running) {
561
507
 
@@ -597,7 +543,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
597
543
 
598
544
  if (maxfd == -1) {
599
545
  /* libcurl recommends sleeping for 100ms */
600
- rb_thread_wait_for(rb_time_timeval(DBL2NUM(0.1)));
546
+ rb_thread_wait_for(tv_100ms);
601
547
  rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
602
548
  rb_curl_multi_read_info( self, rbcm->handle );
603
549
  if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
@@ -610,12 +556,15 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
610
556
  create_crt_fd(&fdexcep, &crt_fdexcep);
611
557
  #endif
612
558
 
613
- #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
559
+
560
+ #if (defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL))
614
561
  fdset_args.maxfd = maxfd+1;
615
562
  fdset_args.fdread = &fdread;
616
563
  fdset_args.fdwrite = &fdwrite;
617
564
  fdset_args.fdexcep = &fdexcep;
618
565
  fdset_args.tv = &tv;
566
+ #endif
567
+
619
568
  #ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
620
569
  rc = (int)(VALUE) rb_thread_call_without_gvl((void *(*)(void *))curb_select, &fdset_args, RUBY_UBF_IO, 0);
621
570
  #elif HAVE_RB_THREAD_BLOCKING_REGION
@@ -626,8 +575,6 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
626
575
  rc = rb_thread_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
627
576
  #endif
628
577
 
629
- #endif
630
-
631
578
  #ifdef _WIN32
632
579
  cleanup_crt_fd(&fdread, &crt_fdread);
633
580
  cleanup_crt_fd(&fdwrite, &crt_fdwrite);
@@ -653,30 +600,44 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
653
600
 
654
601
  rb_curl_multi_read_info( self, rbcm->handle );
655
602
  if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
656
-
603
+ if (cCurlMutiAutoClose == 1) {
604
+ rb_funcall(self, rb_intern("close"), 0);
605
+ }
657
606
  return Qtrue;
658
607
  }
659
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
+
660
625
  /* =================== INIT LIB =====================*/
661
626
  void init_curb_multi() {
662
627
  idCall = rb_intern("call");
663
-
664
628
  cCurlMulti = rb_define_class_under(mCurl, "Multi", rb_cObject);
665
629
 
666
630
  /* Class methods */
667
631
  rb_define_singleton_method(cCurlMulti, "new", ruby_curl_multi_new, 0);
668
632
  rb_define_singleton_method(cCurlMulti, "default_timeout=", ruby_curl_multi_set_default_timeout, 1);
669
633
  rb_define_singleton_method(cCurlMulti, "default_timeout", ruby_curl_multi_get_default_timeout, 0);
670
-
671
- /* "Attributes" */
672
- rb_define_method(cCurlMulti, "requests", ruby_curl_multi_requests, 0);
673
- rb_define_method(cCurlMulti, "idle?", ruby_curl_multi_idle, 0);
674
-
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);
675
636
  /* Instance methods */
676
637
  rb_define_method(cCurlMulti, "max_connects=", ruby_curl_multi_max_connects, 1);
677
638
  rb_define_method(cCurlMulti, "pipeline=", ruby_curl_multi_pipeline, 1);
678
- rb_define_method(cCurlMulti, "add", ruby_curl_multi_add, 1);
679
- rb_define_method(cCurlMulti, "remove", ruby_curl_multi_remove, 1);
680
- 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);
681
641
  rb_define_method(cCurlMulti, "perform", ruby_curl_multi_perform, -1);
642
+ rb_define_method(cCurlMulti, "_close", ruby_curl_multi_close, 0);
682
643
  }