curb 0.9.7 → 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_easy.h CHANGED
@@ -36,6 +36,9 @@ typedef struct {
36
36
  /* The handler */
37
37
  CURL *curl;
38
38
 
39
+ /* Buffer for error details from CURLOPT_ERRORBUFFER */
40
+ char err_buf[CURL_ERROR_SIZE];
41
+
39
42
  VALUE opts; /* rather then allocate everything we might need to store, allocate a Hash and only store objects we actually use... */
40
43
  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 */
41
44
 
@@ -55,6 +58,8 @@ typedef struct {
55
58
  unsigned long ftp_response_timeout;
56
59
  long low_speed_limit;
57
60
  long low_speed_time;
61
+ long max_send_speed_large;
62
+ long max_recv_speed_large;
58
63
  long ssl_version;
59
64
  long use_ssl;
60
65
  long ftp_filemethod;
@@ -76,6 +81,7 @@ typedef struct {
76
81
  char callback_active;
77
82
 
78
83
  struct curl_slist *curl_headers;
84
+ struct curl_slist *curl_proxy_headers;
79
85
  struct curl_slist *curl_ftp_commands;
80
86
  struct curl_slist *curl_resolve;
81
87
 
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,69 +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
- curl_multi_cleanup(rbcm->handle);
83
- free(rbcm);
61
+ rbcm->active = 0;
62
+ rbcm->running = 0;
84
63
  }
85
64
 
86
65
  /*
87
66
  * call-seq:
88
- * Curl::Multi.new => #<Curl::Easy...>
67
+ * Curl::Multi.new => #<Curl::Easy...>
89
68
  *
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
- }
101
-
102
- rbcm->requests = rb_hash_new();
103
-
104
- rbcm->active = 0;
105
- rbcm->running = 0;
74
+ ruby_curl_multi_init(rbcm);
106
75
 
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 => [#<Curl::Easy...>, ...]
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) {
@@ -390,10 +300,14 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
390
300
  rbce->callback_active = 1;
391
301
  val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
392
302
  rbce->callback_active = 0;
393
- //rb_funcall( rb_easy_get("complete_proc"), idCall, 1, easy );
394
303
  }
395
304
 
305
+ #ifdef HAVE_CURLINFO_RESPONSE_CODE
396
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
397
311
 
398
312
  if (result != 0) {
399
313
  if (!rb_easy_nil("failure_proc")) {
@@ -403,8 +317,7 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
403
317
  rbce->callback_active = 0;
404
318
  //rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
405
319
  }
406
- }
407
- else if (!rb_easy_nil("success_proc") &&
320
+ } else if (!rb_easy_nil("success_proc") &&
408
321
  ((response_code >= 200 && response_code < 300) || response_code == 0)) {
409
322
  /* NOTE: we allow response_code == 0, in the case of non http requests e.g. reading from disk */
410
323
  callargs = rb_ary_new3(2, rb_easy_get("success_proc"), easy);
@@ -412,22 +325,19 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
412
325
  val = rb_rescue(call_status_handler1, callargs, callback_exception, Qnil);
413
326
  rbce->callback_active = 0;
414
327
  //rb_funcall( rb_easy_get("success_proc"), idCall, 1, easy );
415
- }
416
- else if (!rb_easy_nil("redirect_proc") &&
328
+ } else if (!rb_easy_nil("redirect_proc") &&
417
329
  (response_code >= 300 && response_code < 400)) {
418
330
  rbce->callback_active = 1;
419
331
  callargs = rb_ary_new3(3, rb_easy_get("redirect_proc"), easy, rb_curl_easy_error(result));
420
332
  rbce->callback_active = 0;
421
333
  val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
422
- }
423
- else if (!rb_easy_nil("missing_proc") &&
334
+ } else if (!rb_easy_nil("missing_proc") &&
424
335
  (response_code >= 400 && response_code < 500)) {
425
336
  rbce->callback_active = 1;
426
337
  callargs = rb_ary_new3(3, rb_easy_get("missing_proc"), easy, rb_curl_easy_error(result));
427
338
  rbce->callback_active = 0;
428
339
  val = rb_rescue(call_status_handler2, callargs, callback_exception, Qnil);
429
- }
430
- else if (!rb_easy_nil("failure_proc") &&
340
+ } else if (!rb_easy_nil("failure_proc") &&
431
341
  (response_code >= 500 && response_code <= 999)) {
432
342
  callargs = rb_ary_new3(3, rb_easy_get("failure_proc"), easy, rb_curl_easy_error(result));
433
343
  rbce->callback_active = 1;
@@ -439,19 +349,27 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
439
349
  }
440
350
 
441
351
  static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
442
- int msgs_left, result;
443
- CURLMsg *msg;
444
- CURL *easy_handle;
445
-
446
- /* check for finished easy handles and remove from the multi handle */
447
- while ((msg = curl_multi_info_read(multi_handle, &msgs_left))) {
448
- if (msg->msg == CURLMSG_DONE) {
449
- easy_handle = msg->easy_handle;
450
- result = msg->data.result;
451
- if (easy_handle) {
452
- rb_curl_mutli_handle_complete(self, easy_handle, result);
453
- }
454
- }
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);
455
373
  }
456
374
  }
457
375
 
@@ -459,14 +377,32 @@ static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
459
377
  static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running) {
460
378
  CURLMcode mcode;
461
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
+ */
462
387
  do {
463
388
  mcode = curl_multi_perform(multi_handle, still_running);
464
389
  } while (mcode == CURLM_CALL_MULTI_PERFORM);
465
390
 
391
+ /*
392
+ * Nothing more to do, check if an error occured in the loop above and raise an exception if necessary.
393
+ */
394
+
466
395
  if (mcode != CURLM_OK) {
467
396
  raise_curl_multi_error_exception(mcode);
468
397
  }
469
-
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
+ */
470
406
  }
471
407
 
472
408
  #ifdef _WIN32
@@ -537,6 +473,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
537
473
  #endif
538
474
  long timeout_milliseconds;
539
475
  struct timeval tv = {0, 0};
476
+ struct timeval tv_100ms = {0, 100000};
540
477
  VALUE block = Qnil;
541
478
  #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
542
479
  struct _select_set fdset_args;
@@ -548,10 +485,23 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
548
485
 
549
486
  timeout_milliseconds = cCurlMutiDefaulttimeout;
550
487
 
488
+ // Run curl_multi_perform for the first time to get the ball rolling
551
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.
552
496
  rb_curl_multi_read_info( self, rbcm->handle );
553
- if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
554
-
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
+
555
505
  do {
556
506
  while (rbcm->running) {
557
507
 
@@ -593,7 +543,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
593
543
 
594
544
  if (maxfd == -1) {
595
545
  /* libcurl recommends sleeping for 100ms */
596
- rb_thread_wait_for(rb_time_timeval(DBL2NUM(0.1)));
546
+ rb_thread_wait_for(tv_100ms);
597
547
  rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
598
548
  rb_curl_multi_read_info( self, rbcm->handle );
599
549
  if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
@@ -606,12 +556,15 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
606
556
  create_crt_fd(&fdexcep, &crt_fdexcep);
607
557
  #endif
608
558
 
609
- #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))
610
561
  fdset_args.maxfd = maxfd+1;
611
562
  fdset_args.fdread = &fdread;
612
563
  fdset_args.fdwrite = &fdwrite;
613
564
  fdset_args.fdexcep = &fdexcep;
614
565
  fdset_args.tv = &tv;
566
+ #endif
567
+
615
568
  #ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
616
569
  rc = (int)(VALUE) rb_thread_call_without_gvl((void *(*)(void *))curb_select, &fdset_args, RUBY_UBF_IO, 0);
617
570
  #elif HAVE_RB_THREAD_BLOCKING_REGION
@@ -622,8 +575,6 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
622
575
  rc = rb_thread_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
623
576
  #endif
624
577
 
625
- #endif
626
-
627
578
  #ifdef _WIN32
628
579
  cleanup_crt_fd(&fdread, &crt_fdread);
629
580
  cleanup_crt_fd(&fdwrite, &crt_fdwrite);
@@ -649,30 +600,44 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
649
600
 
650
601
  rb_curl_multi_read_info( self, rbcm->handle );
651
602
  if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
652
-
603
+ if (cCurlMutiAutoClose == 1) {
604
+ rb_funcall(self, rb_intern("close"), 0);
605
+ }
653
606
  return Qtrue;
654
607
  }
655
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
+
656
625
  /* =================== INIT LIB =====================*/
657
626
  void init_curb_multi() {
658
627
  idCall = rb_intern("call");
659
-
660
628
  cCurlMulti = rb_define_class_under(mCurl, "Multi", rb_cObject);
661
629
 
662
630
  /* Class methods */
663
631
  rb_define_singleton_method(cCurlMulti, "new", ruby_curl_multi_new, 0);
664
632
  rb_define_singleton_method(cCurlMulti, "default_timeout=", ruby_curl_multi_set_default_timeout, 1);
665
633
  rb_define_singleton_method(cCurlMulti, "default_timeout", ruby_curl_multi_get_default_timeout, 0);
666
-
667
- /* "Attributes" */
668
- rb_define_method(cCurlMulti, "requests", ruby_curl_multi_requests, 0);
669
- rb_define_method(cCurlMulti, "idle?", ruby_curl_multi_idle, 0);
670
-
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);
671
636
  /* Instance methods */
672
637
  rb_define_method(cCurlMulti, "max_connects=", ruby_curl_multi_max_connects, 1);
673
638
  rb_define_method(cCurlMulti, "pipeline=", ruby_curl_multi_pipeline, 1);
674
- rb_define_method(cCurlMulti, "add", ruby_curl_multi_add, 1);
675
- rb_define_method(cCurlMulti, "remove", ruby_curl_multi_remove, 1);
676
- 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);
677
641
  rb_define_method(cCurlMulti, "perform", ruby_curl_multi_perform, -1);
642
+ rb_define_method(cCurlMulti, "_close", ruby_curl_multi_close, 0);
678
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