curb 0.8.4 → 0.9.11

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
@@ -4,13 +4,17 @@
4
4
  *
5
5
  */
6
6
  #include "curb_config.h"
7
- #ifdef HAVE_RUBY19_ST_H
8
- #include <ruby.h>
7
+ #include <ruby.h>
8
+ #ifdef HAVE_RUBY_ST_H
9
9
  #include <ruby/st.h>
10
10
  #else
11
- #include <ruby.h>
12
11
  #include <st.h>
13
12
  #endif
13
+
14
+ #ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
15
+ #include <ruby/thread.h>
16
+ #endif
17
+
14
18
  #include "curb_easy.h"
15
19
  #include "curb_errors.h"
16
20
  #include "curb_postfield.h"
@@ -33,6 +37,7 @@ static VALUE idCall;
33
37
  VALUE cCurlMulti;
34
38
 
35
39
  static long cCurlMutiDefaulttimeout = 100; /* milliseconds */
40
+ static char cCurlMutiAutoClose = 0;
36
41
 
37
42
  static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy);
38
43
  static void rb_curl_multi_read_info(VALUE self, CURLM *mptr);
@@ -40,39 +45,21 @@ static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_runnin
40
45
 
41
46
  static VALUE callback_exception(VALUE unused) {
42
47
  return Qfalse;
43
- }
44
-
45
- static void curl_multi_mark(ruby_curl_multi *rbcm) {
46
- rb_gc_mark(rbcm->requests);
47
48
  }
48
49
 
49
- static void curl_multi_flush_easy(VALUE key, VALUE easy, ruby_curl_multi *rbcm) {
50
- CURLMcode result;
51
- ruby_curl_easy *rbce;
52
-
53
- Data_Get_Struct(easy, ruby_curl_easy, rbce);
54
- result = curl_multi_remove_handle(rbcm->handle, rbce->curl);
55
- if (result != 0) {
56
- raise_curl_multi_error_exception(result);
57
- }
58
- }
59
-
60
- static int
61
- rb_hash_clear_i(VALUE key, VALUE value, VALUE dummy) {
62
- return ST_DELETE;
50
+ void curl_multi_free(ruby_curl_multi *rbcm) {
51
+ curl_multi_cleanup(rbcm->handle);
52
+ free(rbcm);
63
53
  }
64
54
 
65
- static void curl_multi_free(ruby_curl_multi *rbcm) {
66
-
67
- if (rbcm && !rbcm->requests == Qnil && rb_type(rbcm->requests) == T_HASH && RHASH_LEN(rbcm->requests) > 0) {
68
-
69
- rb_hash_foreach( rbcm->requests, (int (*)())curl_multi_flush_easy, (VALUE)rbcm );
70
-
71
- rb_hash_foreach(rbcm->requests, rb_hash_clear_i, 0); //rb_hash_clear(rbcm->requests);
72
- 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");
73
59
  }
74
- curl_multi_cleanup(rbcm->handle);
75
- free(rbcm);
60
+
61
+ rbcm->active = 0;
62
+ rbcm->running = 0;
76
63
  }
77
64
 
78
65
  /*
@@ -82,23 +69,17 @@ static void curl_multi_free(ruby_curl_multi *rbcm) {
82
69
  * Create a new Curl::Multi instance
83
70
  */
84
71
  VALUE ruby_curl_multi_new(VALUE klass) {
85
- VALUE new_curlm;
86
-
87
72
  ruby_curl_multi *rbcm = ALLOC(ruby_curl_multi);
88
73
 
89
- rbcm->handle = curl_multi_init();
90
- if (!rbcm->handle) {
91
- rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
92
- }
93
-
94
- rbcm->requests = rb_hash_new();
95
-
96
- rbcm->active = 0;
97
- rbcm->running = 0;
74
+ ruby_curl_multi_init(rbcm);
98
75
 
99
- new_curlm = Data_Wrap_Struct(klass, curl_multi_mark, curl_multi_free, rbcm);
100
-
101
- 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);
102
83
  }
103
84
 
104
85
  /*
@@ -110,7 +91,7 @@ VALUE ruby_curl_multi_new(VALUE klass) {
110
91
  *
111
92
  */
112
93
  VALUE ruby_curl_multi_set_default_timeout(VALUE klass, VALUE timeout) {
113
- cCurlMutiDefaulttimeout = FIX2LONG(timeout);
94
+ cCurlMutiDefaulttimeout = NUM2LONG(timeout);
114
95
  return timeout;
115
96
  }
116
97
 
@@ -122,55 +103,39 @@ VALUE ruby_curl_multi_set_default_timeout(VALUE klass, VALUE timeout) {
122
103
  *
123
104
  */
124
105
  VALUE ruby_curl_multi_get_default_timeout(VALUE klass) {
125
- return INT2FIX(cCurlMutiDefaulttimeout);
106
+ return LONG2NUM(cCurlMutiDefaulttimeout);
126
107
  }
127
108
 
128
- /* Hash#foreach callback for ruby_curl_multi_requests */
129
- static int ruby_curl_multi_requests_callback(VALUE key, VALUE value, VALUE result_array) {
130
- rb_ary_push(result_array, value);
131
-
132
- 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;
133
120
  }
134
121
 
135
122
  /*
136
123
  * call-seq:
137
- * multi.requests => [#&lt;Curl::Easy...&gt;, ...]
138
- *
139
- * 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
+ *
140
128
  */
141
- static VALUE ruby_curl_multi_requests(VALUE self) {
142
- ruby_curl_multi *rbcm;
143
- VALUE result_array;
144
-
145
- Data_Get_Struct(self, ruby_curl_multi, rbcm);
146
-
147
- result_array = rb_ary_new();
148
-
149
- /* iterate over the requests hash, and stuff references into the array. */
150
- rb_hash_foreach(rbcm->requests, ruby_curl_multi_requests_callback, result_array);
151
-
152
- return result_array;
129
+ VALUE ruby_curl_multi_get_autoclose(VALUE klass) {
130
+ return cCurlMutiAutoClose == 1 ? Qtrue : Qfalse;
153
131
  }
154
132
 
155
133
  /*
156
134
  * call-seq:
157
- * multi.idle? => true or false
135
+ * multi.requests => [#&lt;Curl::Easy...&gt;, ...]
158
136
  *
159
- * Returns whether or not this Curl::Multi handle is processing any requests. E.g. this returns
160
- * true when multi.requests.length == 0.
137
+ * Returns an array containing all the active requests on this Curl::Multi object.
161
138
  */
162
- static VALUE ruby_curl_multi_idle(VALUE self) {
163
- ruby_curl_multi *rbcm;
164
-
165
- Data_Get_Struct(self, ruby_curl_multi, rbcm);
166
-
167
- if ( FIX2INT( rb_funcall(rbcm->requests, rb_intern("length"), 0) ) == 0 ) {
168
- return Qtrue;
169
- } else {
170
- return Qfalse;
171
- }
172
- }
173
-
174
139
  /*
175
140
  * call-seq:
176
141
  * multi = Curl::Multi.new
@@ -183,7 +148,8 @@ static VALUE ruby_curl_multi_max_connects(VALUE self, VALUE count) {
183
148
  ruby_curl_multi *rbcm;
184
149
 
185
150
  Data_Get_Struct(self, ruby_curl_multi, rbcm);
186
- curl_multi_setopt(rbcm->handle, CURLMOPT_MAXCONNECTS, NUM2INT(count));
151
+
152
+ curl_multi_setopt(rbcm->handle, CURLMOPT_MAXCONNECTS, NUM2LONG(count));
187
153
  #endif
188
154
 
189
155
  return count;
@@ -194,20 +160,31 @@ static VALUE ruby_curl_multi_max_connects(VALUE self, VALUE count) {
194
160
  * multi = Curl::Multi.new
195
161
  * multi.pipeline = true
196
162
  *
197
- * Pass a long set to 1 to enable or 0 to disable. Enabling pipelining on a multi handle will make it
198
- * attempt to perform HTTP Pipelining as far as possible for transfers using this handle. This means
199
- * that if you add a second request that can use an already existing connection, the second request will
200
- * be "piped" on the same connection rather than being executed in parallel. (Added in 7.16.0)
163
+ * Pass a long set to 1 for HTTP/1.1 pipelining, 2 for HTTP/2 multiplexing, or 0 to disable.
164
+ * Enabling pipelining on a multi handle will make it attempt to perform HTTP Pipelining as
165
+ * far as possible for transfers using this handle. This means that if you add a second request
166
+ * that can use an already existing connection, the second request will be "piped" on the same
167
+ * connection rather than being executed in parallel. (Added in 7.16.0, multiplex added in 7.43.0)
201
168
  *
202
169
  */
203
- static VALUE ruby_curl_multi_pipeline(VALUE self, VALUE onoff) {
170
+ static VALUE ruby_curl_multi_pipeline(VALUE self, VALUE method) {
204
171
  #ifdef HAVE_CURLMOPT_PIPELINING
205
172
  ruby_curl_multi *rbcm;
206
173
 
174
+ long value;
175
+
176
+ if (method == Qtrue) {
177
+ value = 1;
178
+ } else if (method == Qfalse) {
179
+ value = 0;
180
+ } else {
181
+ value = NUM2LONG(method);
182
+ }
183
+
207
184
  Data_Get_Struct(self, ruby_curl_multi, rbcm);
208
- curl_multi_setopt(rbcm->handle, CURLMOPT_PIPELINING, onoff == Qtrue ? 1 : 0);
185
+ curl_multi_setopt(rbcm->handle, CURLMOPT_PIPELINING, value);
209
186
  #endif
210
- return onoff;
187
+ return method == Qtrue ? 1 : 0;
211
188
  }
212
189
 
213
190
  /*
@@ -241,7 +218,8 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
241
218
  * If this number is not correct, the next call to curl_multi_perform will correct it. */
242
219
  rbcm->running++;
243
220
 
244
- rb_hash_aset( rbcm->requests, easy, easy );
221
+ /* track a reference to associated multi handle */
222
+ rbce->multi = self;
245
223
 
246
224
  return self;
247
225
  }
@@ -260,25 +238,20 @@ VALUE ruby_curl_multi_add(VALUE self, VALUE easy) {
260
238
  *
261
239
  * Will raise an exception if the easy handle is not found
262
240
  */
263
- VALUE ruby_curl_multi_remove(VALUE self, VALUE easy) {
241
+ VALUE ruby_curl_multi_remove(VALUE self, VALUE rb_easy_handle) {
264
242
  ruby_curl_multi *rbcm;
265
- ruby_curl_easy *rbce;
266
243
 
267
244
  Data_Get_Struct(self, ruby_curl_multi, rbcm);
268
245
 
269
- Data_Get_Struct(easy, ruby_curl_easy, rbce);
270
-
271
- rb_curl_multi_remove(rbcm,easy);
246
+ rb_curl_multi_remove(rbcm, rb_easy_handle);
272
247
 
273
248
  return self;
274
249
  }
275
250
  static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
276
251
  CURLMcode result;
277
252
  ruby_curl_easy *rbce;
278
- VALUE r;
279
253
 
280
254
  Data_Get_Struct(easy, ruby_curl_easy, rbce);
281
-
282
255
  result = curl_multi_remove_handle(rbcm->handle, rbce->curl);
283
256
  if (result != 0) {
284
257
  raise_curl_multi_error_exception(result);
@@ -287,36 +260,6 @@ static void rb_curl_multi_remove(ruby_curl_multi *rbcm, VALUE easy) {
287
260
  rbcm->active--;
288
261
 
289
262
  ruby_curl_easy_cleanup( easy, rbce );
290
-
291
- // active should equal INT2FIX(RHASH(rbcm->requests)->tbl->num_entries)
292
- r = rb_hash_delete( rbcm->requests, easy );
293
- if( r != easy || r == Qnil ) {
294
- rb_warn("Possibly lost track of Curl::Easy VALUE, it may not be reclaimed by GC");
295
- }
296
- }
297
-
298
- /* Hash#foreach callback for ruby_curl_multi_cancel */
299
- static int ruby_curl_multi_cancel_callback(VALUE key, VALUE value, ruby_curl_multi *rbcm) {
300
- rb_curl_multi_remove(rbcm, value);
301
-
302
- return ST_CONTINUE;
303
- }
304
-
305
- /*
306
- * call-seq:
307
- * multi.cancel!
308
- *
309
- * Cancels all requests currently being made on this Curl::Multi handle.
310
- */
311
- static VALUE ruby_curl_multi_cancel(VALUE self) {
312
- ruby_curl_multi *rbcm;
313
-
314
- Data_Get_Struct(self, ruby_curl_multi, rbcm);
315
-
316
- rb_hash_foreach( rbcm->requests, ruby_curl_multi_cancel_callback, (VALUE)rbcm );
317
-
318
- /* for chaining */
319
- return self;
320
263
  }
321
264
 
322
265
  // on_success, on_failure, on_complete
@@ -328,7 +271,6 @@ static VALUE call_status_handler2(VALUE ary) {
328
271
  }
329
272
 
330
273
  static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int result) {
331
-
332
274
  long response_code = -1;
333
275
  VALUE easy;
334
276
  ruby_curl_easy *rbce = NULL;
@@ -340,7 +282,8 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
340
282
 
341
283
  rbce->last_result = result; /* save the last easy result code */
342
284
 
343
- 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);
344
287
 
345
288
  /* after running a request cleanup the headers, these are set before each request */
346
289
  if (rbce->curl_headers) {
@@ -403,28 +346,30 @@ static void rb_curl_mutli_handle_complete(VALUE self, CURL *easy_handle, int res
403
346
  //rb_funcall( rb_easy_get("failure_proc"), idCall, 2, easy, rb_curl_easy_error(result) );
404
347
  }
405
348
 
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
-
412
349
  }
413
350
 
414
351
  static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
415
- int msgs_left, result;
416
- CURLMsg *msg;
417
- CURL *easy_handle;
418
-
419
- /* check for finished easy handles and remove from the multi handle */
420
- while ((msg = curl_multi_info_read(multi_handle, &msgs_left))) {
421
- if (msg->msg == CURLMSG_DONE) {
422
- easy_handle = msg->easy_handle;
423
- result = msg->data.result;
424
- if (easy_handle) {
425
- rb_curl_mutli_handle_complete(self, easy_handle, result);
426
- }
427
- }
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);
428
373
  }
429
374
  }
430
375
 
@@ -432,14 +377,32 @@ static void rb_curl_multi_read_info(VALUE self, CURLM *multi_handle) {
432
377
  static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_running) {
433
378
  CURLMcode mcode;
434
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
+ */
435
387
  do {
436
388
  mcode = curl_multi_perform(multi_handle, still_running);
437
389
  } while (mcode == CURLM_CALL_MULTI_PERFORM);
438
390
 
391
+ /*
392
+ * Nothing more to do, check if an error occured in the loop above and raise an exception if necessary.
393
+ */
394
+
439
395
  if (mcode != CURLM_OK) {
440
396
  raise_curl_multi_error_exception(mcode);
441
397
  }
442
-
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
+ */
443
406
  }
444
407
 
445
408
  #ifdef _WIN32
@@ -471,7 +434,7 @@ void cleanup_crt_fd(fd_set *os_set, fd_set *crt_set)
471
434
  }
472
435
  #endif
473
436
 
474
- #ifdef HAVE_RB_THREAD_BLOCKING_REGION
437
+ #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
475
438
  struct _select_set {
476
439
  int maxfd;
477
440
  fd_set *fdread, *fdwrite, *fdexcep;
@@ -503,15 +466,16 @@ static VALUE curb_select(void *args) {
503
466
  VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
504
467
  CURLMcode mcode;
505
468
  ruby_curl_multi *rbcm;
506
- int maxfd, rc;
469
+ int maxfd, rc = -1;
507
470
  fd_set fdread, fdwrite, fdexcep;
508
471
  #ifdef _WIN32
509
472
  fd_set crt_fdread, crt_fdwrite, crt_fdexcep;
510
473
  #endif
511
474
  long timeout_milliseconds;
512
475
  struct timeval tv = {0, 0};
476
+ struct timeval tv_100ms = {0, 100000};
513
477
  VALUE block = Qnil;
514
- #ifdef HAVE_RB_THREAD_BLOCKING_REGION
478
+ #if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
515
479
  struct _select_set fdset_args;
516
480
  #endif
517
481
 
@@ -521,10 +485,23 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
521
485
 
522
486
  timeout_milliseconds = cCurlMutiDefaulttimeout;
523
487
 
488
+ // Run curl_multi_perform for the first time to get the ball rolling
524
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.
525
496
  rb_curl_multi_read_info( self, rbcm->handle );
526
- if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
527
-
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
+
528
505
  do {
529
506
  while (rbcm->running) {
530
507
 
@@ -564,19 +541,36 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
564
541
  raise_curl_multi_error_exception(mcode);
565
542
  }
566
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
+
567
553
  #ifdef _WIN32
568
554
  create_crt_fd(&fdread, &crt_fdread);
569
555
  create_crt_fd(&fdwrite, &crt_fdwrite);
570
556
  create_crt_fd(&fdexcep, &crt_fdexcep);
571
557
  #endif
572
558
 
573
- #ifdef HAVE_RB_THREAD_BLOCKING_REGION
559
+
560
+ #if (defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL))
574
561
  fdset_args.maxfd = maxfd+1;
575
562
  fdset_args.fdread = &fdread;
576
563
  fdset_args.fdwrite = &fdwrite;
577
564
  fdset_args.fdexcep = &fdexcep;
578
565
  fdset_args.tv = &tv;
566
+ #endif
567
+
568
+ #ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
569
+ rc = (int)(VALUE) rb_thread_call_without_gvl((void *(*)(void *))curb_select, &fdset_args, RUBY_UBF_IO, 0);
570
+ #elif HAVE_RB_THREAD_BLOCKING_REGION
579
571
  rc = rb_thread_blocking_region(curb_select, &fdset_args, RUBY_UBF_IO, 0);
572
+ #elif HAVE_RB_THREAD_FD_SELECT
573
+ rc = rb_thread_fd_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
580
574
  #else
581
575
  rc = rb_thread_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
582
576
  #endif
@@ -606,30 +600,44 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
606
600
 
607
601
  rb_curl_multi_read_info( self, rbcm->handle );
608
602
  if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); }
609
-
603
+ if (cCurlMutiAutoClose == 1) {
604
+ rb_funcall(self, rb_intern("close"), 0);
605
+ }
610
606
  return Qtrue;
611
607
  }
612
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
+
613
625
  /* =================== INIT LIB =====================*/
614
626
  void init_curb_multi() {
615
627
  idCall = rb_intern("call");
616
-
617
628
  cCurlMulti = rb_define_class_under(mCurl, "Multi", rb_cObject);
618
629
 
619
630
  /* Class methods */
620
631
  rb_define_singleton_method(cCurlMulti, "new", ruby_curl_multi_new, 0);
621
632
  rb_define_singleton_method(cCurlMulti, "default_timeout=", ruby_curl_multi_set_default_timeout, 1);
622
633
  rb_define_singleton_method(cCurlMulti, "default_timeout", ruby_curl_multi_get_default_timeout, 0);
623
-
624
- /* "Attributes" */
625
- rb_define_method(cCurlMulti, "requests", ruby_curl_multi_requests, 0);
626
- rb_define_method(cCurlMulti, "idle?", ruby_curl_multi_idle, 0);
627
-
628
- /* Instnace methods */
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);
636
+ /* Instance methods */
629
637
  rb_define_method(cCurlMulti, "max_connects=", ruby_curl_multi_max_connects, 1);
630
638
  rb_define_method(cCurlMulti, "pipeline=", ruby_curl_multi_pipeline, 1);
631
- rb_define_method(cCurlMulti, "add", ruby_curl_multi_add, 1);
632
- rb_define_method(cCurlMulti, "remove", ruby_curl_multi_remove, 1);
633
- 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);
634
641
  rb_define_method(cCurlMulti, "perform", ruby_curl_multi_perform, -1);
642
+ rb_define_method(cCurlMulti, "_close", ruby_curl_multi_close, 0);
635
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_upload.c CHANGED
@@ -56,7 +56,7 @@ VALUE ruby_curl_upload_stream_get(VALUE self) {
56
56
  VALUE ruby_curl_upload_offset_set(VALUE self, VALUE offset) {
57
57
  ruby_curl_upload *rbcu;
58
58
  Data_Get_Struct(self, ruby_curl_upload, rbcu);
59
- rbcu->offset = FIX2LONG(offset);
59
+ rbcu->offset = NUM2LONG(offset);
60
60
  return offset;
61
61
  }
62
62
  /*
@@ -66,7 +66,7 @@ VALUE ruby_curl_upload_offset_set(VALUE self, VALUE offset) {
66
66
  VALUE ruby_curl_upload_offset_get(VALUE self) {
67
67
  ruby_curl_upload *rbcu;
68
68
  Data_Get_Struct(self, ruby_curl_upload, rbcu);
69
- return INT2FIX(rbcu->offset);
69
+ return LONG2NUM(rbcu->offset);
70
70
  }
71
71
 
72
72
  /* =================== INIT LIB =====================*/