curb 0.7.1 → 0.7.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of curb might be problematic. Click here for more details.

data/ext/curb.h CHANGED
@@ -20,11 +20,11 @@
20
20
  #include "curb_macros.h"
21
21
 
22
22
  // These should be managed from the Rake 'release' task.
23
- #define CURB_VERSION "0.7.1"
24
- #define CURB_VER_NUM 701
23
+ #define CURB_VERSION "0.7.3"
24
+ #define CURB_VER_NUM 703
25
25
  #define CURB_VER_MAJ 0
26
26
  #define CURB_VER_MIN 7
27
- #define CURB_VER_MIC 1
27
+ #define CURB_VER_MIC 3
28
28
  #define CURB_VER_PATCH 0
29
29
 
30
30
 
@@ -37,11 +37,13 @@
37
37
  #define RSTRING_PTR(x) RSTRING(x)->ptr
38
38
  #endif
39
39
 
40
+ #ifndef RHASH_LEN
40
41
  #ifdef HAVE_RUBY19_HASH
41
42
  #define RHASH_LEN(hash) RHASH(hash)->ntbl->num_entries
42
43
  #else
43
44
  #define RHASH_LEN(hash) RHASH(hash)->tbl->num_entries
44
45
  #endif
46
+ #endif
45
47
 
46
48
  extern VALUE mCurl;
47
49
 
data/ext/curb_easy.c CHANGED
@@ -135,9 +135,10 @@ static int proc_debug_handler(CURL *curl,
135
135
  /* ================== MARK/FREE FUNC ==================*/
136
136
  void curl_easy_mark(ruby_curl_easy *rbce) {
137
137
  rb_gc_mark(rbce->opts);
138
+ if (!NIL_P(rbce->multi)) { rb_gc_mark(rbce->multi); }
138
139
  }
139
140
 
140
- void curl_easy_free(ruby_curl_easy *rbce) {
141
+ static void ruby_curl_easy_free(ruby_curl_easy *rbce) {
141
142
  if (rbce->curl_headers) {
142
143
  curl_slist_free_all(rbce->curl_headers);
143
144
  }
@@ -146,40 +147,22 @@ void curl_easy_free(ruby_curl_easy *rbce) {
146
147
  curl_slist_free_all(rbce->curl_ftp_commands);
147
148
  }
148
149
 
149
- curl_easy_cleanup(rbce->curl);
150
+ if (rbce->curl) {
151
+ curl_easy_cleanup(rbce->curl);
152
+ }
153
+ }
154
+
155
+ void curl_easy_free(ruby_curl_easy *rbce) {
156
+ ruby_curl_easy_free(rbce);
150
157
  free(rbce);
151
158
  }
152
159
 
153
160
 
154
161
  /* ================= ALLOC METHODS ====================*/
155
162
 
156
- /*
157
- * call-seq:
158
- * Curl::Easy.new => #<Curl::Easy...>
159
- * Curl::Easy.new(url = nil) => #<Curl::Easy...>
160
- * Curl::Easy.new(url = nil) { |self| ... } => #<Curl::Easy...>
161
- *
162
- * Create a new Curl::Easy instance, optionally supplying the URL.
163
- * The block form allows further configuration to be supplied before
164
- * the instance is returned.
165
- */
166
- static VALUE ruby_curl_easy_new(int argc, VALUE *argv, VALUE klass) {
167
- CURLcode ecode;
168
- VALUE url, blk;
169
- VALUE new_curl;
170
- ruby_curl_easy *rbce;
171
-
172
- rb_scan_args(argc, argv, "01&", &url, &blk);
173
-
174
- rbce = ALLOC(ruby_curl_easy);
175
-
176
- /* handler */
177
- rbce->curl = curl_easy_init();
178
-
163
+ static void ruby_curl_easy_zero(ruby_curl_easy *rbce) {
179
164
  rbce->opts = rb_hash_new();
180
165
 
181
- rb_easy_set("url", url);
182
-
183
166
  rbce->curl_headers = NULL;
184
167
  rbce->curl_ftp_commands = NULL;
185
168
 
@@ -211,6 +194,39 @@ static VALUE ruby_curl_easy_new(int argc, VALUE *argv, VALUE klass) {
211
194
  rbce->verbose = 0;
212
195
  rbce->multipart_form_post = 0;
213
196
  rbce->enable_cookies = 0;
197
+ }
198
+
199
+ /*
200
+ * call-seq:
201
+ * Curl::Easy.new => #<Curl::Easy...>
202
+ * Curl::Easy.new(url = nil) => #<Curl::Easy...>
203
+ * Curl::Easy.new(url = nil) { |self| ... } => #<Curl::Easy...>
204
+ *
205
+ * Create a new Curl::Easy instance, optionally supplying the URL.
206
+ * The block form allows further configuration to be supplied before
207
+ * the instance is returned.
208
+ */
209
+ static VALUE ruby_curl_easy_new(int argc, VALUE *argv, VALUE klass) {
210
+ CURLcode ecode;
211
+ VALUE url, blk;
212
+ VALUE new_curl;
213
+ ruby_curl_easy *rbce;
214
+
215
+ rb_scan_args(argc, argv, "01&", &url, &blk);
216
+
217
+ rbce = ALLOC(ruby_curl_easy);
218
+
219
+ /* handler */
220
+ rbce->curl = curl_easy_init();
221
+ if (!rbce->curl) {
222
+ rb_raise(eCurlErrFailedInit, "Failed to initialize easy handle");
223
+ }
224
+
225
+ rbce->multi = Qnil;
226
+
227
+ ruby_curl_easy_zero(rbce);
228
+
229
+ rb_easy_set("url", url);
214
230
 
215
231
  new_curl = Data_Wrap_Struct(klass, curl_easy_mark, curl_easy_free, rbce);
216
232
 
@@ -249,6 +265,72 @@ static VALUE ruby_curl_easy_clone(VALUE self) {
249
265
  return Data_Wrap_Struct(cCurlEasy, curl_easy_mark, curl_easy_free, newrbce);
250
266
  }
251
267
 
268
+ /*
269
+ * call-seq:
270
+ * easy.close => nil
271
+ *
272
+ * Close the Curl::Easy instance. Any open connections are closed
273
+ * The easy handle is reinitialized. If a previous multi handle was
274
+ * open it is set to nil and will be cleared after a GC.
275
+ */
276
+ static VALUE ruby_curl_easy_close(VALUE self) {
277
+ CURLcode ecode;
278
+ ruby_curl_easy *rbce;
279
+
280
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
281
+
282
+ ruby_curl_easy_free(rbce);
283
+
284
+ /* reinit the handle */
285
+ rbce->curl = curl_easy_init();
286
+ if (!rbce->curl) {
287
+ rb_raise(eCurlErrFailedInit, "Failed to initialize easy handle");
288
+ }
289
+
290
+ rbce->multi = Qnil;
291
+
292
+ ruby_curl_easy_zero(rbce);
293
+
294
+ /* give the new curl handle a reference back to the ruby object */
295
+ ecode = curl_easy_setopt(rbce->curl, CURLOPT_PRIVATE, (void*)self);
296
+ if (ecode != CURLE_OK) {
297
+ raise_curl_easy_error_exception(ecode);
298
+ }
299
+
300
+ return Qnil;
301
+ }
302
+
303
+ /*
304
+ * call-seq:
305
+ * easy.reset => Hash
306
+ *
307
+ * Reset the Curl::Easy instance, clears out all settings.
308
+ *
309
+ * from http://curl.haxx.se/libcurl/c/curl_easy_reset.html
310
+ * Re-initializes all options previously set on a specified CURL handle to the default values. This puts back the handle to the same state as it was in when it was just created with curl_easy_init(3).
311
+ * It does not change the following information kept in the handle: live connections, the Session ID cache, the DNS cache, the cookies and shares.
312
+ *
313
+ * The return value contains all settings stored.
314
+ */
315
+ static VALUE ruby_curl_easy_reset(VALUE self) {
316
+ CURLcode ecode;
317
+ ruby_curl_easy *rbce;
318
+ VALUE opts_dup;
319
+ Data_Get_Struct(self, ruby_curl_easy, rbce);
320
+ opts_dup = rb_funcall(rbce->opts, rb_intern("dup"), 0);
321
+
322
+ curl_easy_reset(rbce->curl);
323
+ ruby_curl_easy_zero(rbce);
324
+
325
+ /* rest clobbers the private setting, so reset it to self */
326
+ ecode = curl_easy_setopt(rbce->curl, CURLOPT_PRIVATE, (void*)self);
327
+ if (ecode != CURLE_OK) {
328
+ raise_curl_easy_error_exception(ecode);
329
+ }
330
+
331
+ return opts_dup;
332
+ }
333
+
252
334
 
253
335
  /* ================ OBJ ATTRIBUTES ==================*/
254
336
 
@@ -1985,10 +2067,13 @@ VALUE ruby_curl_easy_cleanup( VALUE self, ruby_curl_easy *rbce ) {
1985
2067
  static VALUE handle_perform(VALUE self, ruby_curl_easy *rbce) {
1986
2068
 
1987
2069
  VALUE ret;
1988
- VALUE multi = ruby_curl_multi_new(cCurlMulti);
1989
2070
 
1990
- rb_funcall(multi, rb_intern("add"), 1, self );
1991
- ret = rb_funcall(multi, rb_intern("perform"), 0);
2071
+ /* reuse existing multi handle for this easy handle */
2072
+ if (NIL_P(rbce->multi)) {
2073
+ rbce->multi = ruby_curl_multi_new(cCurlMulti);
2074
+ }
2075
+ rb_funcall(rbce->multi, rb_intern("add"), 1, self );
2076
+ ret = rb_funcall(rbce->multi, rb_intern("perform"), 0);
1992
2077
 
1993
2078
  /* check for errors in the easy response and raise exceptions if anything went wrong and their is no on_failure handler */
1994
2079
  if (rbce->last_result != 0 && rb_easy_nil("failure_proc")) {
@@ -2134,6 +2219,7 @@ static VALUE ruby_curl_easy_perform_post(int argc, VALUE *argv, VALUE self) {
2134
2219
  return ret;
2135
2220
  } else {
2136
2221
  VALUE post_body = Qnil;
2222
+ /* TODO: check for PostField.file and raise error before to_s fails */
2137
2223
  if ((post_body = rb_funcall(args_ary, idJoin, 1, rbstrAmp)) == Qnil) {
2138
2224
  rb_raise(eCurlErrError, "Failed to join arguments");
2139
2225
  return Qnil;
@@ -3199,7 +3285,9 @@ void init_curb_easy() {
3199
3285
  rb_define_method(cCurlEasy, "os_errno", ruby_curl_easy_os_errno_get, 0);
3200
3286
  rb_define_method(cCurlEasy, "num_connects", ruby_curl_easy_num_connects_get, 0);
3201
3287
  rb_define_method(cCurlEasy, "ftp_entry_path", ruby_curl_easy_ftp_entry_path_get, 0);
3202
- rb_define_method(cCurlEasy, "inspect", ruby_curl_easy_inspect, 0);
3288
+
3289
+ rb_define_method(cCurlEasy, "close", ruby_curl_easy_close, 0);
3290
+ rb_define_method(cCurlEasy, "reset", ruby_curl_easy_reset, 0);
3203
3291
 
3204
3292
  /* Curl utils */
3205
3293
  rb_define_method(cCurlEasy, "escape", ruby_curl_easy_escape, 1);
@@ -3208,4 +3296,5 @@ void init_curb_easy() {
3208
3296
  /* Runtime support */
3209
3297
  rb_define_method(cCurlEasy, "clone", ruby_curl_easy_clone, 0);
3210
3298
  rb_define_alias(cCurlEasy, "dup", "clone");
3299
+ rb_define_method(cCurlEasy, "inspect", ruby_curl_easy_inspect, 0);
3211
3300
  }
data/ext/curb_easy.h CHANGED
@@ -37,6 +37,7 @@ typedef struct {
37
37
  CURL *curl;
38
38
 
39
39
  VALUE opts; /* rather then allocate everything we might need to store, allocate a Hash and only store objects we actually use... */
40
+ VALUE multi; /* keep a multi handle alive for each easy handle not being used by a multi handle. This improves easy performance when not within a multi context */
40
41
 
41
42
  /* Other opts */
42
43
  unsigned short local_port; // 0 is no port
data/ext/curb_errors.c CHANGED
@@ -110,6 +110,7 @@ VALUE eCurlErrSSLCRLBadfile;
110
110
  VALUE eCurlErrSSLIssuerError;
111
111
 
112
112
  /* multi errors */
113
+ VALUE mCurlErrFailedInit;
113
114
  VALUE mCurlErrCallMultiPerform;
114
115
  VALUE mCurlErrBadHandle;
115
116
  VALUE mCurlErrBadEasyHandle;
@@ -605,6 +606,7 @@ void init_curb_errors() {
605
606
  eCurlErrSSLShutdownFailed = rb_define_class_under(mCurlErr, "SSLShutdownFailed", eCurlErrError);
606
607
  eCurlErrSSH = rb_define_class_under(mCurlErr, "SSH", eCurlErrError);
607
608
 
609
+ mCurlErrFailedInit = rb_define_class_under(mCurlErr, "MultiInitError", eCurlErrError);
608
610
  mCurlErrCallMultiPerform = rb_define_class_under(mCurlErr, "MultiPerform", eCurlErrError);
609
611
  mCurlErrBadHandle = rb_define_class_under(mCurlErr, "MultiBadHandle", eCurlErrError);
610
612
  mCurlErrBadEasyHandle = rb_define_class_under(mCurlErr, "MultiBadEasyHandle", eCurlErrError);
data/ext/curb_errors.h CHANGED
@@ -108,6 +108,7 @@ extern VALUE eCurlErrSSLCRLBadfile;
108
108
  extern VALUE eCurlErrSSLIssuerError;
109
109
 
110
110
  /* multi errors */
111
+ extern VALUE mCurlErrFailedInit;
111
112
  extern VALUE mCurlErrCallMultiPerform;
112
113
  extern VALUE mCurlErrBadHandle;
113
114
  extern VALUE mCurlErrBadEasyHandle;
data/ext/curb_multi.c CHANGED
@@ -83,6 +83,9 @@ VALUE ruby_curl_multi_new(VALUE klass) {
83
83
  ruby_curl_multi *rbcm = ALLOC(ruby_curl_multi);
84
84
 
85
85
  rbcm->handle = curl_multi_init();
86
+ if (!rbcm->handle) {
87
+ rb_raise(mCurlErrFailedInit, "Failed to initialize multi handle");
88
+ }
86
89
 
87
90
  rbcm->requests = rb_hash_new();
88
91
 
@@ -415,16 +418,7 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
415
418
 
416
419
  rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
417
420
 
418
- while(rbcm->running) {
419
- FD_ZERO(&fdread);
420
- FD_ZERO(&fdwrite);
421
- FD_ZERO(&fdexcep);
422
-
423
- /* load the fd sets from the multi handle */
424
- mcode = curl_multi_fdset(rbcm->handle, &fdread, &fdwrite, &fdexcep, &maxfd);
425
- if (mcode != CURLM_OK) {
426
- raise_curl_multi_error_exception(mcode);
427
- }
421
+ while (rbcm->running) {
428
422
 
429
423
  #ifdef HAVE_CURL_MULTI_TIMEOUT
430
424
  /* get the curl suggested time out */
@@ -433,26 +427,34 @@ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) {
433
427
  raise_curl_multi_error_exception(mcode);
434
428
  }
435
429
  #else
436
- /* libcurl doesn't have a timeout method defined... make a wild guess */
430
+ /* libcurl doesn't have a timeout method defined, initialize to -1 we'll pick up the default later */
437
431
  timeout_milliseconds = -1;
438
432
  #endif
439
- //printf("libcurl says wait: %ld ms or %ld s\n", timeout_milliseconds, timeout_milliseconds/1000);
440
433
 
441
434
  if (timeout_milliseconds == 0) { /* no delay */
442
435
  rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) );
443
436
  continue;
444
437
  }
445
- else if(timeout_milliseconds < 0) {
446
- timeout_milliseconds = cCurlMutiDefaulttimeout; /* wait half a second, libcurl doesn't know how long to wait */
438
+ else if (timeout_milliseconds < 0) {
439
+ timeout_milliseconds = cCurlMutiDefaulttimeout; /* libcurl doesn't know how long to wait, use a default timeout */
447
440
  }
448
- #ifdef __APPLE_CC__
449
- if(timeout_milliseconds > 1000) {
450
- timeout_milliseconds = cCurlMutiDefaulttimeout; /* apple libcurl sometimes reports huge timeouts... let's cap it */
441
+
442
+ if (timeout_milliseconds > cCurlMutiDefaulttimeout) {
443
+ timeout_milliseconds = cCurlMutiDefaulttimeout; /* buggy versions libcurl sometimes reports huge timeouts... let's cap it */
451
444
  }
452
- #endif
453
445
 
454
- tv.tv_sec = timeout_milliseconds / 1000; // convert milliseconds to seconds
455
- tv.tv_usec = (timeout_milliseconds % 1000) * 1000; // get the remainder of milliseconds and convert to micro seconds
446
+ tv.tv_sec = 0; /* never wait longer than 1 second */
447
+ tv.tv_usec = timeout_milliseconds * 1000;
448
+
449
+ FD_ZERO(&fdread);
450
+ FD_ZERO(&fdwrite);
451
+ FD_ZERO(&fdexcep);
452
+
453
+ /* load the fd sets from the multi handle */
454
+ mcode = curl_multi_fdset(rbcm->handle, &fdread, &fdwrite, &fdexcep, &maxfd);
455
+ if (mcode != CURLM_OK) {
456
+ raise_curl_multi_error_exception(mcode);
457
+ }
456
458
 
457
459
  rc = rb_thread_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
458
460
  switch(rc) {
data/ext/curb_postfield.c CHANGED
@@ -281,7 +281,7 @@ static VALUE ruby_curl_postfield_new_file(int argc, VALUE *argv, VALUE klass) {
281
281
  rbcpf->content = Qnil;
282
282
  rbcpf->content_type = Qnil;
283
283
  rbcpf->buffer_str = Qnil;
284
-
284
+
285
285
  return Data_Wrap_Struct(cCurlPostField, curl_postfield_mark, curl_postfield_free, rbcpf);
286
286
  }
287
287
 
@@ -465,14 +465,14 @@ static VALUE ruby_curl_postfield_to_str(VALUE self) {
465
465
  result = escd_name;
466
466
  rb_str_cat(result, "=", 1);
467
467
  rb_str_concat(result, escd_content);
468
- }
468
+ }
469
469
  }
470
470
  } else {
471
471
  rb_raise(eCurlErrInvalidPostField, "Cannot convert unnamed field to string %s:%d", __FILE__, __LINE__);
472
- }
472
+ }
473
473
  } else {
474
474
  rb_raise(eCurlErrInvalidPostField, "Cannot convert non-content field to string %s:%d", __FILE__, __LINE__);
475
- }
475
+ }
476
476
 
477
477
  return result;
478
478
  }
data/lib/curb.rb CHANGED
@@ -191,7 +191,7 @@ module Curl
191
191
 
192
192
  # call-seq:
193
193
  #
194
- # Curl::Multi.download(['http://example.com/p/a/t/h/file1.txt','http://example.com/p/a/t/h/file2.txt'])
194
+ # Curl::Multi.download(['http://example.com/p/a/t/h/file1.txt','http://example.com/p/a/t/h/file2.txt']){|c|}
195
195
  #
196
196
  # will create 2 new files file1.txt and file2.txt
197
197
  #
@@ -232,7 +232,11 @@ module Curl
232
232
  url_to_download_paths[url] = download_path # store for later
233
233
  end
234
234
 
235
- Curl::Multi.http(urls_with_config, multi_options) {|c,code,method| blk.call(c,url_to_download_paths[c.url]) }
235
+ if blk
236
+ Curl::Multi.http(urls_with_config, multi_options) {|c,code,method| blk.call(c,url_to_download_paths[c.url]) }
237
+ else
238
+ Curl::Multi.http(urls_with_config, multi_options)
239
+ end
236
240
 
237
241
  ensure
238
242
  errors = []
data/tests/helper.rb CHANGED
@@ -73,14 +73,18 @@ class TestServlet < WEBrick::HTTPServlet::AbstractServlet
73
73
  end
74
74
 
75
75
  def do_POST(req,res)
76
- if req.body
77
- params = {}
78
- req.body.split('&').map{|s| k,v=s.split('='); params[k] = v }
79
- end
80
- if params and params['s'] == '500'
81
- res.status = 500
76
+ if !req.query['filename'].nil?
77
+ respond_with(req.query['filename'],req,res)
82
78
  else
83
- respond_with("POST\n#{req.body}",req,res)
79
+ if req.body
80
+ params = {}
81
+ req.body.split('&').map{|s| k,v=s.split('='); params[k] = v }
82
+ end
83
+ if params and params['s'] == '500'
84
+ res.status = 500
85
+ else
86
+ respond_with("POST\n#{req.body}",req,res)
87
+ end
84
88
  end
85
89
  end
86
90
 
@@ -57,8 +57,7 @@ class TestCurbCurlEasy < Test::Unit::TestCase
57
57
  assert_equal nil, c.on_body
58
58
  end
59
59
 
60
- class Foo < Curl::Easy
61
- end
60
+ class Foo < Curl::Easy ; end
62
61
  def test_new_05
63
62
  # can use Curl::Easy as a base class
64
63
  c = Foo.new
@@ -698,6 +697,40 @@ class TestCurbCurlEasy < Test::Unit::TestCase
698
697
  end
699
698
  end
700
699
 
700
+ def test_post_streaming
701
+ readme = File.expand_path(File.join(File.dirname(__FILE__),'..','README'))
702
+
703
+ pf = Curl::PostField.file("filename", readme)
704
+
705
+ easy = Curl::Easy.new
706
+
707
+ easy.url = TestServlet.url
708
+ easy.multipart_form_post = true
709
+ easy.http_post(pf)
710
+
711
+ assert_not_equal(0,easy.body_str.size)
712
+ assert_equal(easy.body_str,File.read(readme))
713
+ end
714
+
715
+ def test_easy_close
716
+ easy = Curl::Easy.new
717
+ easy.close
718
+ easy.url = TestServlet.url
719
+ easy.http_get
720
+ end
721
+
722
+ def test_easy_reset
723
+ easy = Curl::Easy.new
724
+ easy.url = TestServlet.url + "?query=foo"
725
+ easy.http_get
726
+ settings = easy.reset
727
+ assert settings.key?(:url)
728
+ assert settings.key?(:body_data)
729
+ assert settings.key?(:header_data)
730
+ easy.url = TestServlet.url
731
+ easy.http_get
732
+ end
733
+
701
734
  include TestServerMethods
702
735
 
703
736
  def setup
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 7
8
- - 1
9
- version: 0.7.1
8
+ - 3
9
+ version: 0.7.3
10
10
  platform: ruby
11
11
  authors:
12
12
  - Ross Bamford
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-04-20 00:00:00 -04:00
18
+ date: 2010-05-18 00:00:00 -04:00
19
19
  default_executable:
20
20
  dependencies: []
21
21