curb 0.9.3 → 1.0.1

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_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,71 +45,41 @@ 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
- if (rbcm->handle) {
83
- curl_multi_cleanup(rbcm->handle);
84
- }
85
- free(rbcm);
61
+ rbcm->active = 0;
62
+ rbcm->running = 0;
86
63
  }
87
64
 
88
65
  /*
89
66
  * call-seq:
90
- * Curl::Multi.new => #<Curl::Easy...>
67
+ * Curl::Multi.new => #<Curl::Easy...>
91
68
  *
92
69
  * Create a new Curl::Multi instance
93
70
  */
94
71
  VALUE ruby_curl_multi_new(VALUE klass) {
95
- VALUE new_curlm;
96
-
97
72
  ruby_curl_multi *rbcm = ALLOC(ruby_curl_multi);
98
73
 
99
- rbcm->handle = curl_multi_init();
100
- if (!rbcm->handle) {
101
- rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
102
- }
103
-
104
- rbcm->requests = rb_hash_new();
105
-
106
- rbcm->active = 0;
107
- rbcm->running = 0;
108
-
109
- new_curlm = Data_Wrap_Struct(klass, curl_multi_mark, curl_multi_free, rbcm);
74
+ ruby_curl_multi_init(rbcm);
110
75
 
111
- 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);
112
83
  }
113
84
 
114
85
  /*
@@ -135,52 +106,36 @@ VALUE ruby_curl_multi_get_default_timeout(VALUE klass) {
135
106
  return LONG2NUM(cCurlMutiDefaulttimeout);
136
107
  }
137
108
 
138
- /* Hash#foreach callback for ruby_curl_multi_requests */
139
- static int ruby_curl_multi_requests_callback(VALUE key, VALUE value, VALUE result_array) {
140
- rb_ary_push(result_array, value);
141
-
142
- 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;
143
120
  }
144
121
 
145
122
  /*
146
123
  * call-seq:
147
- * multi.requests => [#&lt;Curl::Easy...&gt;, ...]
148
- *
149
- * 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
+ *
150
128
  */
151
- static VALUE ruby_curl_multi_requests(VALUE self) {
152
- ruby_curl_multi *rbcm;
153
- VALUE result_array;
154
-
155
- Data_Get_Struct(self, ruby_curl_multi, rbcm);
156
-
157
- result_array = rb_ary_new();
158
-
159
- /* iterate over the requests hash, and stuff references into the array. */
160
- rb_hash_foreach(rbcm->requests, ruby_curl_multi_requests_callback, result_array);
161
-
162
- return result_array;
129
+ VALUE ruby_curl_multi_get_autoclose(VALUE klass) {
130
+ return cCurlMutiAutoClose == 1 ? Qtrue : Qfalse;
163
131
  }
164
132
 
165
133
  /*
166
134
  * call-seq:
167
- * multi.idle? => true or false
135
+ * multi.requests => [#<Curl::Easy...>, ...]
168
136
  *
169
- * Returns whether or not this Curl::Multi handle is processing any requests. E.g. this returns
170
- * true when multi.requests.length == 0.
137
+ * Returns an array containing all the active requests on this Curl::Multi object.
171
138
  */
172
- static VALUE ruby_curl_multi_idle(VALUE self) {
173
- ruby_curl_multi *rbcm;
174
-
175
- Data_Get_Struct(self, ruby_curl_multi, rbcm);
176
-
177
- if (RHASH_SIZE(rbcm->requests) == 0) {
178
- return Qtrue;
179
- } else {
180
- return Qfalse;
181
- }
182
- }
183
-
184
139
  /*
185
140
  * call-seq:
186
141
  * multi = Curl::Multi.new
@@ -194,13 +149,6 @@ static VALUE ruby_curl_multi_max_connects(VALUE self, VALUE count) {
194
149
 
195
150
  Data_Get_Struct(self, ruby_curl_multi, rbcm);
196
151
 
197
- if (!rbcm->handle) {
198
- rbcm->handle = curl_multi_init();
199
- if (!rbcm->handle) {
200
- rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
201
- }
202
- }
203
-
204
152
  curl_multi_setopt(rbcm->handle, CURLMOPT_MAXCONNECTS, NUM2LONG(count));
205
153
  #endif
206
154
 
@@ -234,14 +182,6 @@ static VALUE ruby_curl_multi_pipeline(VALUE self, VALUE method) {
234
182
  }
235
183
 
236
184
  Data_Get_Struct(self, ruby_curl_multi, rbcm);
237
-
238
- if (!rbcm->handle) {
239
- rbcm->handle = curl_multi_init();
240
- if (!rbcm->handle) {
241
- rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
242
- }
243
- }
244
-
245
185
  curl_multi_setopt(rbcm->handle, CURLMOPT_PIPELINING, value);
246
186
  #endif
247
187
  return method == Qtrue ? 1 : 0;
@@ -258,29 +198,15 @@ static VALUE ruby_curl_multi_pipeline(VALUE self, VALUE method) {
258
198
  */
259
199
  VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
260
200
  CURLMcode mcode;
261
- VALUE r;
262
201
  ruby_curl_easy *rbce;
263
202
  ruby_curl_multi *rbcm;
264
203
 
265
204
  Data_Get_Struct(self, ruby_curl_multi, rbcm);
266
205
  Data_Get_Struct(easy, ruby_curl_easy, rbce);
267
206
 
268
- // check if this curl handle has been added before adding again
269
- r = rb_hash_aref(rbcm->requests, LONG2NUM((long)rbce->curl));
270
- if ( r != Qnil ) {
271
- return Qnil;
272
- }
273
-
274
207
  /* setup the easy handle */
275
208
  ruby_curl_easy_setup( rbce );
276
209
 
277
- if (!rbcm->handle) {
278
- rbcm->handle = curl_multi_init();
279
- if (!rbcm->handle) {
280
- rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
281
- }
282
- }
283
-
284
210
  mcode = curl_multi_add_handle(rbcm->handle, rbce->curl);
285
211
  if (mcode != CURLM_CALL_MULTI_PERFORM && mcode != CURLM_OK) {
286
212
  raise_curl_multi_error_exception(mcode);
@@ -295,8 +221,6 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
295
221
  /* track a reference to associated multi handle */
296
222
  rbce->multi = self;
297
223
 
298
- rb_hash_aset( rbcm->requests, LONG2NUM((long)rbce->curl), easy );
299
-
300
224
  return self;
301
225
  }
302
226
 
@@ -314,35 +238,20 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
314
238
  *
315
239
  * Will raise an exception if the easy handle is not found
316
240
  */
317
- VALUE ruby_curl_multi_remove(VALUE self, VALUE easy) {
241
+ VALUE ruby_curl_multi_remove(VALUE self, VALUE rb_easy_handle) {
318
242
  ruby_curl_multi *rbcm;
319
243
 
320
244
  Data_Get_Struct(self, ruby_curl_multi, rbcm);
321
245
 
322
- if (!rbcm->handle) {
323
- rbcm->handle = curl_multi_init();
324
- if (!rbcm->handle) {
325
- rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
326
- }
327
- }
328
-
329
- rb_curl_multi_remove(rbcm,easy);
246
+ rb_curl_multi_remove(rbcm, rb_easy_handle);
330
247
 
331
248
  return self;
332
249
  }
333
250
  static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
334
251
  CURLMcode result;
335
252
  ruby_curl_easy *rbce;
336
- VALUE r;
337
253
 
338
254
  Data_Get_Struct(easy, ruby_curl_easy, rbce);
339
-
340
- // check if this curl handle has been added before removing
341
- r = rb_hash_aref(rbcm->requests, LONG2NUM((long)rbce->curl));
342
- if ( r == Qnil ) {
343
- return;
344
- }
345
-
346
255
  result = curl_multi_remove_handle(rbcm->handle, rbce->curl);
347
256
  if (result != 0) {
348
257
  raise_curl_multi_error_exception(result);
@@ -351,43 +260,6 @@ static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
351
260
  rbcm->active--;
352
261
 
353
262
  ruby_curl_easy_cleanup( easy, rbce );
354
-
355
- // active should equal LONG2NUM(RHASH(rbcm->requests)->tbl->num_entries)
356
- r = rb_hash_delete( rbcm->requests, LONG2NUM((long)rbce->curl) );
357
- if( r != easy || r == Qnil ) {
358
- rb_warn("Possibly lost track of Curl::Easy VALUE, it may not be reclaimed by GC");
359
- }
360
- }
361
-
362
- /* Hash#foreach callback for ruby_curl_multi_cancel */
363
- static int ruby_curl_multi_cancel_callback(VALUE key, VALUE value, ruby_curl_multi *rbcm) {
364
- rb_curl_multi_remove(rbcm, value);
365
-
366
- return ST_CONTINUE;
367
- }
368
-
369
- /*
370
- * call-seq:
371
- * multi.cancel!
372
- *
373
- * Cancels all requests currently being made on this Curl::Multi handle.
374
- */
375
- static VALUE ruby_curl_multi_cancel(VALUE self) {
376
- ruby_curl_multi *rbcm;
377
-
378
- Data_Get_Struct(self, ruby_curl_multi, rbcm);
379
-
380
- if (!rbcm->handle) {
381
- rbcm->handle = curl_multi_init();
382
- if (!rbcm->handle) {
383
- rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
384
- }
385
- }
386
-
387
- rb_hash_foreach( rbcm->requests, ruby_curl_multi_cancel_callback, (VALUE)rbcm );
388
-
389
- /* for chaining */
390
- return self;
391
263
  }
392
264
 
393
265
  // on_success, on_failure, on_complete
@@ -399,7 +271,6 @@ static VALUE call_status_handler2(VALUE ary) {
399
271
  }
400
272
 
401
273
  static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int result) {
402
-
403
274
  long response_code = -1;
404
275
  VALUE easy;
405
276
  ruby_curl_easy *rbce = NULL;
@@ -411,7 +282,8 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
411
282
 
412
283
  rbce->last_result = result; /* save the last easy result code */
413
284
 
414
- 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);
415
287
 
416
288
  /* after running a request cleanup the headers, these are set before each request */
417
289
  if (rbce->curl_headers) {
@@ -428,10 +300,14 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
428
300
  rbce->callback_active = 1;
429
301
  val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
430
302
  rbce->callback_active = 0;
431
- //rb_funcall( rb_easy_get("complete_proc"), idCall, 1, easy );
432
303
  }
433
304
 
305
+ #ifdef HAVE_CURLINFO_RESPONSE_CODE
434
306
  curl_easy_getinfo(rbce->curl, CURLINFO_RESPONSE_CODE, &response_code);
307
+ #else
308
+ // old libcurl
309
+ curl_easy_getinfo(rbce->curl, CURLINFO_HTTP_CODE, &response_code);
310
+ #endif
435
311
 
436
312
  if (result != 0) {
437
313
  if (!rb_easy_nil("failure_proc")) {
@@ -441,8 +317,7 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
441
317
  rbce->callback_active = 0;
442
318
  //rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
443
319
  }
444
- }
445
- else if (!rb_easy_nil("success_proc") &&
320
+ } else if (!rb_easy_nil("success_proc") &&
446
321
  ((response_code >= 200 && response_code < 300) || response_code == 0)) {
447
322
  /* NOTE: we allow response_code == 0, in the case of non http requests e.g. reading from disk */
448
323
  callargs = rb_ary_new3(2, rb_easy_get("success_proc"), easy);
@@ -450,22 +325,19 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
450
325
  val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
451
326
  rbce->callback_active = 0;
452
327
  //rb_funcall( rb_easy_get("success_proc"), idCall, 1, easy );
453
- }
454
- else if (!rb_easy_nil("redirect_proc") &&
328
+ } else if (!rb_easy_nil("redirect_proc") &&
455
329
  (response_code >= 300 && response_code < 400)) {
456
330
  rbce->callback_active = 1;
457
331
  callargs = rb_ary_new3(3, rb_easy_get("redirect_proc"), easy, rb_curl_easy_error(result));
458
332
  rbce->callback_active = 0;
459
333
  val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
460
- }
461
- else if (!rb_easy_nil("missing_proc") &&
334
+ } else if (!rb_easy_nil("missing_proc") &&
462
335
  (response_code >= 400 && response_code < 500)) {
463
336
  rbce->callback_active = 1;
464
337
  callargs = rb_ary_new3(3, rb_easy_get("missing_proc"), easy, rb_curl_easy_error(result));
465
338
  rbce->callback_active = 0;
466
339
  val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
467
- }
468
- else if (!rb_easy_nil("failure_proc") &&
340
+ } else if (!rb_easy_nil("failure_proc") &&
469
341
  (response_code >= 500 && response_code <= 999)) {
470
342
  callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
471
343
  rbce->callback_active = 1;
@@ -474,26 +346,30 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
474
346
  //rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
475
347
  }
476
348
 
477
- if (val == Qfalse) {
478
- rb_warn("uncaught exception from callback");
479
- }
480
-
481
349
  }
482
350
 
483
351
  static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
484
- int msgs_left, result;
485
- CURLMsg *msg;
486
- CURL *easy_handle;
487
-
488
- /* check for finished easy handles and remove from the multi handle */
489
- while ((msg = curl_multi_info_read(multi_handle, &msgs_left))) {
490
- if (msg->msg == CURLMSG_DONE) {
491
- easy_handle = msg->easy_handle;
492
- result = msg->data.result;
493
- if (easy_handle) {
494
- rb_curl_mutli_handle_complete(self, easy_handle, result);
495
- }
496
- }
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);
497
373
  }
498
374
  }
499
375
 
@@ -501,15 +377,32 @@ static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
501
377
  static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running) {
502
378
  CURLMcode mcode;
503
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
+ */
504
387
  do {
505
388
  mcode = curl_multi_perform(multi_handle, still_running);
506
389
  } while (mcode == CURLM_CALL_MULTI_PERFORM);
507
390
 
391
+ /*
392
+ * Nothing more to do, check if an error occured in the loop above and raise an exception if necessary.
393
+ */
508
394
 
509
395
  if (mcode != CURLM_OK) {
510
396
  raise_curl_multi_error_exception(mcode);
511
397
  }
512
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
+ */
513
406
  }
514
407
 
515
408
  #ifdef _WIN32
@@ -580,6 +473,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
580
473
  #endif
581
474
  long timeout_milliseconds;
582
475
  struct timeval tv = {0, 0};
476
+ struct timeval tv_100ms = {0, 100000};
583
477
  VALUE block = Qnil;
584
478
  #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
585
479
  struct _select_set fdset_args;
@@ -591,10 +485,23 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
591
485
 
592
486
  timeout_milliseconds = cCurlMutiDefaulttimeout;
593
487
 
488
+ // Run curl_multi_perform for the first time to get the ball rolling
594
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.
595
496
  rb_curl_multi_read_info( self, rbcm->handle );
596
- if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
597
-
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
+
598
505
  do {
599
506
  while (rbcm->running) {
600
507
 
@@ -634,18 +541,30 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
634
541
  raise_curl_multi_error_exception(mcode);
635
542
  }
636
543
 
544
+ if (maxfd == -1) {
545
+ /* libcurl recommends sleeping for 100ms */
546
+ rb_thread_wait_for(tv_100ms);
547
+ rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
548
+ rb_curl_multi_read_info( self, rbcm->handle );
549
+ if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
550
+ continue;
551
+ }
552
+
637
553
  #ifdef _WIN32
638
554
  create_crt_fd(&fdread, &crt_fdread);
639
555
  create_crt_fd(&fdwrite, &crt_fdwrite);
640
556
  create_crt_fd(&fdexcep, &crt_fdexcep);
641
557
  #endif
642
558
 
643
- #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))
644
561
  fdset_args.maxfd = maxfd+1;
645
562
  fdset_args.fdread = &fdread;
646
563
  fdset_args.fdwrite = &fdwrite;
647
564
  fdset_args.fdexcep = &fdexcep;
648
565
  fdset_args.tv = &tv;
566
+ #endif
567
+
649
568
  #ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
650
569
  rc = (int)(VALUE) rb_thread_call_without_gvl((void *(*)(void *))curb_select, &fdset_args, RUBY_UBF_IO, 0);
651
570
  #elif HAVE_RB_THREAD_BLOCKING_REGION
@@ -656,8 +575,6 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
656
575
  rc = rb_thread_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
657
576
  #endif
658
577
 
659
- #endif
660
-
661
578
  #ifdef _WIN32
662
579
  cleanup_crt_fd(&fdread, &crt_fdread);
663
580
  cleanup_crt_fd(&fdwrite, &crt_fdwrite);
@@ -683,38 +600,44 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
683
600
 
684
601
  rb_curl_multi_read_info( self, rbcm->handle );
685
602
  if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
686
-
687
- /* do early cleanup */
688
- VALUE hash = rbcm->requests;
689
- if (!NIL_P(hash) && rb_type(hash) == T_HASH && RHASH_SIZE(hash) > 0) {
690
- rb_hash_foreach(hash, curl_multi_flush_easy, (VALUE)rbcm);
603
+ if (cCurlMutiAutoClose == 1) {
604
+ rb_funcall(self, rb_intern("close"), 0);
691
605
  }
692
- curl_multi_cleanup(rbcm->handle);
693
- rbcm->handle = NULL;
694
-
695
606
  return Qtrue;
696
607
  }
697
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
+
698
625
  /* =================== INIT LIB =====================*/
699
626
  void init_curb_multi() {
700
627
  idCall = rb_intern("call");
701
-
702
628
  cCurlMulti = rb_define_class_under(mCurl, "Multi", rb_cObject);
703
629
 
704
630
  /* Class methods */
705
631
  rb_define_singleton_method(cCurlMulti, "new", ruby_curl_multi_new, 0);
706
632
  rb_define_singleton_method(cCurlMulti, "default_timeout=", ruby_curl_multi_set_default_timeout, 1);
707
633
  rb_define_singleton_method(cCurlMulti, "default_timeout", ruby_curl_multi_get_default_timeout, 0);
708
-
709
- /* "Attributes" */
710
- rb_define_method(cCurlMulti, "requests", ruby_curl_multi_requests, 0);
711
- rb_define_method(cCurlMulti, "idle?", ruby_curl_multi_idle, 0);
712
-
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);
713
636
  /* Instance methods */
714
637
  rb_define_method(cCurlMulti, "max_connects=", ruby_curl_multi_max_connects, 1);
715
638
  rb_define_method(cCurlMulti, "pipeline=", ruby_curl_multi_pipeline, 1);
716
- rb_define_method(cCurlMulti, "add", ruby_curl_multi_add, 1);
717
- rb_define_method(cCurlMulti, "remove", ruby_curl_multi_remove, 1);
718
- 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);
719
641
  rb_define_method(cCurlMulti, "perform", ruby_curl_multi_perform, -1);
642
+ rb_define_method(cCurlMulti, "_close", ruby_curl_multi_close, 0);
720
643
  }
data/ext/curb_multi.h CHANGED
@@ -14,7 +14,6 @@
14
14
  typedef struct {
15
15
  int active;
16
16
  int running;
17
- VALUE requests; /* hash of handles currently added */
18
17
  CURLM *handle;
19
18
  } ruby_curl_multi;
20
19
 
data/ext/curb_postfield.c CHANGED
@@ -195,9 +195,9 @@ void curl_postfield_free(ruby_curl_postfield *rbcpf) {
195
195
 
196
196
  /*
197
197
  * call-seq:
198
- * Curl::PostField.content(name, content) => #&lt;Curl::PostField...&gt;
199
- * Curl::PostField.content(name, content, content_type = nil) => #&lt;Curl::PostField...&gt;
200
- * Curl::PostField.content(name, content_type = nil) { |field| ... } => #&lt;Curl::PostField...&gt;
198
+ * Curl::PostField.content(name, content) => #<Curl::PostField...>
199
+ * Curl::PostField.content(name, content, content_type = nil) => #<Curl::PostField...>
200
+ * Curl::PostField.content(name, content_type = nil) { |field| ... } => #<Curl::PostField...>
201
201
  *
202
202
  * Create a new Curl::PostField, supplying the field name, content,
203
203
  * and, optionally, Content-type (curl will attempt to determine this if
@@ -241,9 +241,9 @@ static VALUE ruby_curl_postfield_new_content(int argc, VALUE *argv, VALUE klass)
241
241
 
242
242
  /*
243
243
  * call-seq:
244
- * Curl::PostField.file(name, local_file_name) => #&lt;Curl::PostField...&gt;
245
- * Curl::PostField.file(name, local_file_name, remote_file_name = local_file_name) => #&lt;Curl::PostField...&gt;
246
- * Curl::PostField.file(name, remote_file_name) { |field| ... } => #&lt;Curl::PostField...&gt;
244
+ * Curl::PostField.file(name, local_file_name) => #<Curl::PostField...>
245
+ * Curl::PostField.file(name, local_file_name, remote_file_name = local_file_name) => #<Curl::PostField...>
246
+ * Curl::PostField.file(name, remote_file_name) { |field| ... } => #<Curl::PostField...>
247
247
  *
248
248
  * Create a new Curl::PostField for a file upload field, supplying the local filename
249
249
  * to read from, and optionally the remote filename (defaults to the local name).
@@ -399,7 +399,7 @@ static VALUE ruby_curl_postfield_remote_file_get(VALUE self) {
399
399
 
400
400
  /*
401
401
  * call-seq:
402
- * field.set_content_proc { |field| ... } => &lt;old proc&gt;
402
+ * field.set_content_proc { |field| ... } => <old proc>
403
403
  *
404
404
  * Set a content proc for this field. This proc will be called during the
405
405
  * perform to supply the content for this field, overriding any setting