typhoeus 0.2.0 → 0.2.4

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.
Files changed (50) hide show
  1. data/CHANGELOG.markdown +26 -1
  2. data/Gemfile +2 -0
  3. data/Gemfile.lock +2 -0
  4. data/LICENSE +20 -0
  5. data/README.textile +44 -5
  6. data/Rakefile +8 -5
  7. data/VERSION +1 -1
  8. data/examples/file.rb +12 -0
  9. data/examples/times.rb +40 -0
  10. data/ext/typhoeus/.gitignore +2 -1
  11. data/ext/typhoeus/native.c +1 -0
  12. data/ext/typhoeus/native.h +1 -0
  13. data/ext/typhoeus/typhoeus_easy.c +32 -7
  14. data/ext/typhoeus/typhoeus_easy.h +1 -0
  15. data/ext/typhoeus/typhoeus_form.c +59 -0
  16. data/ext/typhoeus/typhoeus_form.h +13 -0
  17. data/ext/typhoeus/typhoeus_multi.c +15 -29
  18. data/lib/typhoeus/easy.rb +74 -48
  19. data/lib/typhoeus/form.rb +32 -0
  20. data/lib/typhoeus/hydra/connect_options.rb +19 -3
  21. data/lib/typhoeus/hydra.rb +40 -7
  22. data/lib/typhoeus/multi.rb +7 -5
  23. data/lib/typhoeus/remote.rb +1 -1
  24. data/lib/typhoeus/remote_proxy_object.rb +2 -0
  25. data/lib/typhoeus/request.rb +17 -14
  26. data/lib/typhoeus/response.rb +16 -1
  27. data/lib/typhoeus/utils.rb +38 -0
  28. data/lib/typhoeus.rb +1 -0
  29. data/spec/fixtures/placeholder.gif +0 -0
  30. data/spec/fixtures/placeholder.txt +1 -0
  31. data/spec/fixtures/placeholder.ukn +0 -0
  32. data/spec/servers/app.rb +10 -0
  33. data/spec/spec.opts +1 -0
  34. data/spec/spec_helper.rb +3 -0
  35. data/spec/typhoeus/easy_spec.rb +74 -7
  36. data/spec/typhoeus/filter_spec.rb +2 -2
  37. data/spec/typhoeus/form_spec.rb +107 -0
  38. data/spec/typhoeus/hydra_mock_spec.rb +1 -1
  39. data/spec/typhoeus/hydra_spec.rb +108 -38
  40. data/spec/typhoeus/multi_spec.rb +1 -1
  41. data/spec/typhoeus/normalized_header_hash_spec.rb +1 -1
  42. data/spec/typhoeus/remote_method_spec.rb +2 -2
  43. data/spec/typhoeus/remote_proxy_object_spec.rb +1 -1
  44. data/spec/typhoeus/remote_spec.rb +1 -1
  45. data/spec/typhoeus/request_spec.rb +50 -5
  46. data/spec/typhoeus/response_spec.rb +13 -1
  47. data/spec/typhoeus/utils_spec.rb +1 -1
  48. data/typhoeus.gemspec +93 -75
  49. metadata +70 -28
  50. data/.gitignore +0 -3
data/CHANGELOG.markdown CHANGED
@@ -1,5 +1,30 @@
1
+ 0.2.4
2
+ -----
3
+ * Fix form POSTs to only use multipart for file uploads, otherwise use application/x-www-form-urlencoded [dbalatero]
4
+
5
+ 0.2.3
6
+ -----
7
+ * Code duplication in Typhoeus::Form led to nested URL param errors on POST only. Fixed [dbalatero]
8
+
9
+ 0.2.2
10
+ -----
11
+ * Fixed a problem with nested URL params encoding incorrectly [dbalatero]
12
+
13
+ 0.2.1
14
+ -----
15
+ * Added extended proxy support [Zapotek, GH-46]
16
+ * eliminated compile time warnings by using proper type declarations [skaes, GH-54]
17
+ * fixed broken calls to rb_raise [skaes, GH-54]
18
+ * prevent leaking of curl easy handles when exceptions are raised (either from typhoeus itself or user callbacks) [skaes, GH-54]
19
+ * fixed Easy#timed_out? using curl return codes [skaes, GH-54]
20
+ * provide curl return codes and corresponding curl error messages on classes Easy and Request [skaes, GH-54]
21
+ * allow VCR to whitelist hosts in Typhoeus stubbing/mocking [myronmarston, GH-57]
22
+ * added timed_out? documentation, method to Response [dbalatero, GH-34]
23
+ * added abort to Hydra to prematurely stop a hydra.run [Zapotek]
24
+ * added file upload support for POST requests [jtarchie, GH-59]
25
+
1
26
  0.2.0
2
- ------------
27
+ ------
3
28
  * Fix warning in Request#headers from attr_accessor
4
29
  * Params with array values were not parsing into the format that rack expects
5
30
  [GH-39, smartocci]
data/Gemfile CHANGED
@@ -1,5 +1,7 @@
1
1
  source :rubygems
2
2
 
3
+ gem "mime-types"
4
+
3
5
  group :test do
4
6
  gem 'rspec', '1.3.1'
5
7
  gem 'jeweler'
data/Gemfile.lock CHANGED
@@ -10,6 +10,7 @@ GEM
10
10
  rubyforge (>= 2.0.0)
11
11
  json (1.4.6)
12
12
  json_pure (1.4.6)
13
+ mime-types (1.16)
13
14
  rack (1.2.1)
14
15
  rspec (1.3.1)
15
16
  rubyforge (2.0.4)
@@ -26,5 +27,6 @@ DEPENDENCIES
26
27
  diff-lcs
27
28
  jeweler
28
29
  json
30
+ mime-types
29
31
  rspec (= 1.3.1)
30
32
  sinatra
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 David Balatero
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.textile CHANGED
@@ -18,7 +18,7 @@ h2. Installation
18
18
 
19
19
  Typhoeus requires you to have a current version of libcurl installed. I've tested this with 7.19.4 and higher.
20
20
  <pre>
21
- gem install typhoeus --source http://gemcutter.org
21
+ gem install typhoeus
22
22
  </pre>
23
23
  If you're on Debian or Ubuntu and getting errors while trying to install, it could be because you don't have the latest version of libcurl installed. Do this to fix:
24
24
  <pre>
@@ -29,7 +29,7 @@ There's also something built in so that if you have a super old version of curl
29
29
  gem install typhoeus --source http://gemcutter.org -- --with-curl=/usr/local/curl/7.19.7/
30
30
  </pre>
31
31
 
32
- Another problem could be if you are running Mac Ports and you have libcurl installed through there. You need to uninstall it for Typhoeus to work! The version in Mac Ports is old and doesn't play nice. You should "download curl":http://curl.haxx.se/download.html and build from source. Then you'll have to install the gem again.
32
+ -Another problem could be if you are running Mac Ports and you have libcurl installed through there. You need to uninstall it for Typhoeus to work! The version in Mac Ports is old and doesn't play nice. You should "download curl":http://curl.haxx.se/download.html and build from source. Then you'll have to install the gem again.- The current version of Mac Ports (7.21.2) works just fine.
33
33
 
34
34
  If you're still having issues, please let me know on "the mailing list":http://groups.google.com/group/typhoeus.
35
35
 
@@ -59,6 +59,11 @@ request = Typhoeus::Request.new("http://www.pauldix.net",
59
59
  # the options are all optional. The default for :method is :get. Timeout is measured in milliseconds.
60
60
  # cache_timeout is measured in seconds.
61
61
 
62
+ # Run the request via Hydra.
63
+ hydra = Typhoeus::Hydra.new
64
+ hydra.queue(request)
65
+ hydra.run
66
+
62
67
  # the response object will be set after the request is run
63
68
  response = request.response
64
69
  response.code # http status code
@@ -79,6 +84,18 @@ response = Typhoeus::Request.post("http://localhost:3000/posts", :params => {:ti
79
84
  response = Typhoeus::Request.delete("http://localhost:3000/posts/1")
80
85
  </pre>
81
86
 
87
+ *Handling file uploads*
88
+ A File object can be passed as a param for a POST request to handle uploading files to the server. Typhoeus will upload the file as the original file name and use Mime::Types to set the content type.
89
+
90
+ <pre>
91
+ response = Typhoeus::Request.post("http://localhost:3000/posts",
92
+ :params => {
93
+ :title => "test post", :content => "this is my test",
94
+ :file => File.open("thesis.txt","r")
95
+ }
96
+ )
97
+ </pre>
98
+
82
99
  *Making Parallel Requests*
83
100
 
84
101
  <pre>
@@ -149,7 +166,7 @@ hydra.cache_getter do |request|
149
166
  end
150
167
  </pre>
151
168
 
152
- *Stubbing*
169
+ *Direct Stubbing*
153
170
  Hydra allows you to stub out specific urls and patters to avoid hitting remote servers while testing.
154
171
 
155
172
  <pre>
@@ -180,6 +197,15 @@ hydra = Typhoeus::Hydra.hydra
180
197
  hydra.stub(:get, "http://localhost:3000/users")
181
198
  </pre>
182
199
 
200
+ *Timeouts*
201
+
202
+ No exceptions are raised on HTTP timeouts. You can check whether a request timed out with the following methods:
203
+
204
+ <pre>
205
+ easy.timed_out? # for a raw Easy handle
206
+ response.timed_out? # for a Response handle
207
+ </pre>
208
+
183
209
  *Basic Authentication*
184
210
 
185
211
  <pre>
@@ -203,7 +229,9 @@ Typhoeus::Request.get("https://mail.google.com/mail", :disable_ssl_peer_verifica
203
229
  *LibCurl*
204
230
  Typhoeus also has a more raw libcurl interface. These are the Easy and Multi objects. If you're into accessing just the raw libcurl style, those are your best bet.
205
231
 
206
- SSL Certs can be provided to the Easy interface :
232
+ However, by using this raw interface, you do not get access to Hydra-specific features, such as stubbing/mocking.
233
+
234
+ SSL Certs can be provided to the Easy interface:
207
235
 
208
236
  <pre>
209
237
  e = Typhoeus::Easy.new
@@ -262,7 +290,7 @@ There's also an easy way to perform any kind of authentication via the quick req
262
290
  e = Typhoeus::Request.get("http://example.com",
263
291
  :username => 'username',
264
292
  :password => 'password',
265
- :method => :ntlm)
293
+ :auth_method => :ntlm)
266
294
  </pre>
267
295
 
268
296
  All methods listed above is available in a shorter form - :basic, :digest, :gssnegotiate, :ntlm, :digest_ie, :auto.
@@ -300,6 +328,17 @@ I set up a benchmark to test how the parallel performance works vs Ruby's built
300
328
 
301
329
  We can see from this that NET::HTTP performs as expected, taking 10 seconds to run 20 500ms requests. Typhoeus only takes 500ms (the time of the response that took the longest.) One other thing to note is that Typhoeus keeps a pool of libcurl Easy handles to use. For this benchmark I warmed the pool first. So if you test this out it may be a bit slower until the Easy handle pool has enough in it to run all the simultaneous requests. For some reason the easy handles can take quite some time to allocate.
302
330
 
331
+ h2. Running the specs
332
+
333
+ Running the specs requires a couple of Sinatra servers to be booted. Do this:
334
+
335
+ <pre>
336
+ ruby spec/servers/app.rb -p 3000
337
+ ruby spec/servers/app.rb -p 3001
338
+ ruby spec/servers/app.rb -p 3002
339
+ rake spec
340
+ </pre>
341
+
303
342
  h2. Next Steps
304
343
 
305
344
  * Add in ability to keep-alive requests and reuse them within hydra.
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+
1
3
  require "spec"
2
4
  require "spec/rake/spectask"
3
- require 'lib/typhoeus.rb'
5
+ require 'lib/typhoeus'
4
6
 
5
7
  begin
6
8
  require 'jeweler'
@@ -8,9 +10,10 @@ begin
8
10
  gemspec.name = "typhoeus"
9
11
  gemspec.summary = "A library for interacting with web services (and building SOAs) at blinding speed."
10
12
  gemspec.description = "Like a modern code version of the mythical beast with 100 serpent heads, Typhoeus runs HTTP requests in parallel while cleanly encapsulating handling logic."
11
- gemspec.email = "paul@pauldix.net"
12
- gemspec.homepage = "http://github.com/pauldix/typhoeus"
13
- gemspec.authors = ["Paul Dix"]
13
+ gemspec.email = "dbalatero@gmail.com"
14
+ gemspec.homepage = "http://github.com/dbalatero/typhoeus"
15
+ gemspec.authors = ["Paul Dix", "David Balatero"]
16
+ gemspec.add_dependency "mime-types"
14
17
  gemspec.add_development_dependency "rspec"
15
18
  gemspec.add_development_dependency "jeweler"
16
19
  gemspec.add_development_dependency "diff-lcs"
@@ -31,7 +34,7 @@ end
31
34
  task :install do
32
35
  rm_rf "*.gem"
33
36
  puts `gem build typhoeus.gemspec`
34
- puts `sudo gem install typhoeus-#{Typhoeus::VERSION}.gem`
37
+ puts `gem install typhoeus-#{Typhoeus::VERSION}.gem`
35
38
  end
36
39
 
37
40
  desc "Run all the tests"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.2.4
data/examples/file.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require File.dirname(__FILE__) + '/../lib/typhoeus.rb'
3
+
4
+
5
+ response = Typhoeus::Request.post(
6
+ "http://video-feed.local",
7
+ :params => {
8
+ :file => File.new("file.rb")
9
+ }
10
+ )
11
+
12
+ puts response.inspect
data/examples/times.rb ADDED
@@ -0,0 +1,40 @@
1
+ require File.dirname(__FILE__) + '/../lib/typhoeus.rb'
2
+
3
+ hydra = Typhoeus::Hydra.new
4
+ hydra.disable_memoization
5
+
6
+ urls = [
7
+ 'http://google.com',
8
+ 'http://testphp.vulnweb.com',
9
+ 'http://demo.testfire.net',
10
+ 'http://example.net',
11
+ ]
12
+
13
+ 10.times {
14
+ |i|
15
+
16
+ req = Typhoeus::Request.new( urls[ i % urls.size] )
17
+ req.on_complete {
18
+ |res|
19
+ puts 'URL: ' + res.effective_url
20
+ puts 'Time: ' + res.time.to_s
21
+ puts 'Connect time: ' + res.connect_time.to_s
22
+ puts 'App connect time: ' + res.app_connect_time.to_s
23
+ puts 'Start transfer time: ' + res.start_transfer_time.to_s
24
+ puts 'Pre transfer time: ' + res.pretransfer_time.to_s
25
+ puts '-------------'
26
+ }
27
+
28
+ hydra.queue( req )
29
+ puts 'Queued: ' + req.url
30
+ }
31
+
32
+ puts
33
+ puts 'Harvesting responses...'
34
+ puts
35
+
36
+ hydra.run
37
+
38
+ puts
39
+ puts 'Done.'
40
+ puts
@@ -2,5 +2,6 @@ conftest.dSYM/*
2
2
  conftest.dSYM
3
3
  mkmf.log
4
4
  *.o
5
- native.bundleMakefile
5
+ native.bundle
6
+ Makefile
6
7
  *.bundle
@@ -8,4 +8,5 @@ void Init_native()
8
8
 
9
9
  init_typhoeus_easy();
10
10
  init_typhoeus_multi();
11
+ init_typhoeus_form();
11
12
  }
@@ -10,6 +10,7 @@ void Init_native();
10
10
  extern VALUE mTyphoeus;
11
11
  extern void init_typhoeus_easy();
12
12
  extern void init_typhoeus_multi();
13
+ extern void init_typhoeus_form();
13
14
 
14
15
  #endif
15
16
 
@@ -21,16 +21,27 @@ static VALUE easy_setopt_string(VALUE self, VALUE opt_name, VALUE parameter) {
21
21
  CurlEasy *curl_easy;
22
22
  Data_Get_Struct(self, CurlEasy, curl_easy);
23
23
 
24
- long opt = NUM2LONG(opt_name);
24
+ CURLoption opt = NUM2LONG(opt_name);
25
25
  curl_easy_setopt(curl_easy->curl, opt, StringValuePtr(parameter));
26
26
  return opt_name;
27
27
  }
28
28
 
29
+ static VALUE easy_setopt_form(VALUE self, VALUE opt_name, VALUE parameter) {
30
+ CurlEasy *curl_easy;
31
+ CurlForm *curl_form;
32
+ Data_Get_Struct(self, CurlEasy, curl_easy);
33
+ Data_Get_Struct(parameter, CurlForm, curl_form);
34
+
35
+ CURLoption opt = NUM2LONG(opt_name);
36
+ curl_easy_setopt(curl_easy->curl, opt, curl_form->first);
37
+ return opt_name;
38
+ }
39
+
29
40
  static VALUE easy_setopt_long(VALUE self, VALUE opt_name, VALUE parameter) {
30
41
  CurlEasy *curl_easy;
31
42
  Data_Get_Struct(self, CurlEasy, curl_easy);
32
43
 
33
- long opt = NUM2LONG(opt_name);
44
+ CURLoption opt = NUM2LONG(opt_name);
34
45
  curl_easy_setopt(curl_easy->curl, opt, NUM2LONG(parameter));
35
46
  return opt_name;
36
47
  }
@@ -40,7 +51,7 @@ static VALUE easy_getinfo_string(VALUE self, VALUE info) {
40
51
  CurlEasy *curl_easy;
41
52
  Data_Get_Struct(self, CurlEasy, curl_easy);
42
53
 
43
- long opt = NUM2LONG(info);
54
+ CURLoption opt = NUM2LONG(info);
44
55
  curl_easy_getinfo(curl_easy->curl, opt, &info_string);
45
56
 
46
57
  return rb_str_new2(info_string);
@@ -51,7 +62,7 @@ static VALUE easy_getinfo_long(VALUE self, VALUE info) {
51
62
  CurlEasy *curl_easy;
52
63
  Data_Get_Struct(self, CurlEasy, curl_easy);
53
64
 
54
- long opt = NUM2LONG(info);
65
+ CURLoption opt = NUM2LONG(info);
55
66
  curl_easy_getinfo(curl_easy->curl, opt, &info_long);
56
67
 
57
68
  return LONG2NUM(info_long);
@@ -62,7 +73,7 @@ static VALUE easy_getinfo_double(VALUE self, VALUE info) {
62
73
  CurlEasy *curl_easy;
63
74
  Data_Get_Struct(self, CurlEasy, curl_easy);
64
75
 
65
- long opt = NUM2LONG(info);
76
+ CURLoption opt = NUM2LONG(info);
66
77
  curl_easy_getinfo(curl_easy->curl, opt, &info_double);
67
78
 
68
79
  return rb_float_new(info_double);
@@ -70,8 +81,10 @@ static VALUE easy_getinfo_double(VALUE self, VALUE info) {
70
81
 
71
82
  static VALUE easy_perform(VALUE self) {
72
83
  CurlEasy *curl_easy;
84
+ CURLcode return_code;
73
85
  Data_Get_Struct(self, CurlEasy, curl_easy);
74
- curl_easy_perform(curl_easy->curl);
86
+ return_code = curl_easy_perform(curl_easy->curl);
87
+ rb_iv_set(self, "@curl_return_code", INT2FIX(return_code));
75
88
 
76
89
  return Qnil;
77
90
  }
@@ -166,7 +179,7 @@ static VALUE easy_escape(VALUE self, VALUE data, VALUE length) {
166
179
  CurlEasy *curl_easy;
167
180
  Data_Get_Struct(self, CurlEasy, curl_easy);
168
181
 
169
- return rb_str_new2(curl_easy_escape(curl_easy->curl, StringValuePtr(data), NUM2INT(length)));
182
+ return rb_str_new2(curl_easy_escape(curl_easy->curl, StringValuePtr(data), (int)NUM2INT(length)));
170
183
  }
171
184
 
172
185
  static VALUE version(VALUE self) {
@@ -188,11 +201,23 @@ static VALUE new(int argc, VALUE *argv, VALUE klass) {
188
201
  return easy;
189
202
  }
190
203
 
204
+ static VALUE curl_error_message(VALUE self) {
205
+ VALUE return_code = rb_iv_get(self, "@curl_return_code");
206
+ if (return_code == Qnil)
207
+ return Qnil;
208
+ else {
209
+ CURLcode rc = (CURLcode)FIX2INT(return_code);
210
+ return rb_str_new2(curl_easy_strerror(rc));
211
+ }
212
+ }
213
+
191
214
  void init_typhoeus_easy() {
192
215
  VALUE klass = cTyphoeusEasy = rb_define_class_under(mTyphoeus, "Easy", rb_cObject);
193
216
  idAppend = rb_intern("<<");
194
217
  rb_define_singleton_method(klass, "new", new, -1);
218
+ rb_define_method(klass, "curl_error_message", curl_error_message, 0);
195
219
  rb_define_private_method(klass, "easy_setopt_string", easy_setopt_string, 2);
220
+ rb_define_private_method(klass, "easy_setopt_form", easy_setopt_form, 2);
196
221
  rb_define_private_method(klass, "easy_setopt_long", easy_setopt_long, 2);
197
222
  rb_define_private_method(klass, "easy_getinfo_string", easy_getinfo_string, 1);
198
223
  rb_define_private_method(klass, "easy_getinfo_long", easy_getinfo_long, 1);
@@ -2,6 +2,7 @@
2
2
  #define TYPHOEUS_EASY
3
3
 
4
4
  #include <native.h>
5
+ #include <typhoeus_form.h>
5
6
 
6
7
  void init_typhoeus_easy();
7
8
  typedef struct {
@@ -0,0 +1,59 @@
1
+ #include <typhoeus_form.h>
2
+
3
+ VALUE cTyphoeusForm;
4
+
5
+ static void dealloc(CurlForm *curl_form) {
6
+ curl_formfree(curl_form->first);
7
+ free(curl_form);
8
+ }
9
+
10
+ static VALUE formadd_file(VALUE self,
11
+ VALUE name,
12
+ VALUE filename,
13
+ VALUE content_type,
14
+ VALUE path) {
15
+ CurlForm *curl_form;
16
+ Data_Get_Struct(self, CurlForm, curl_form);
17
+
18
+ return INT2NUM((long) curl_formadd(&curl_form->first, &curl_form->last,
19
+ CURLFORM_COPYNAME, RSTRING_PTR(name),
20
+ CURLFORM_NAMELENGTH, (long)RSTRING_LEN(name),
21
+ CURLFORM_FILENAME, RSTRING_PTR(filename),
22
+ CURLFORM_CONTENTTYPE, RSTRING_PTR(content_type),
23
+ CURLFORM_FILE, RSTRING_PTR(path),
24
+ CURLFORM_END
25
+ ));
26
+ }
27
+
28
+ static VALUE formadd_param(VALUE self, VALUE name, VALUE value) {
29
+ CurlForm *curl_form;
30
+ Data_Get_Struct(self, CurlForm, curl_form);
31
+
32
+ return INT2NUM((long)curl_formadd(&curl_form->first, &curl_form->last,
33
+ CURLFORM_COPYNAME, RSTRING_PTR(name),
34
+ CURLFORM_NAMELENGTH, (long)RSTRING_LEN(name),
35
+ CURLFORM_COPYCONTENTS, RSTRING_PTR(value),
36
+ CURLFORM_CONTENTSLENGTH, (long)RSTRING_LEN(value),
37
+ CURLFORM_END
38
+ ));
39
+ }
40
+
41
+ static VALUE new(int argc, VALUE *argv, VALUE klass) {
42
+ CurlForm *curl_form = ALLOC(CurlForm);
43
+ curl_form->first = NULL;
44
+ curl_form->last = NULL;
45
+
46
+ VALUE form = Data_Wrap_Struct(cTyphoeusForm, 0, dealloc, curl_form);
47
+
48
+ rb_obj_call_init(form, argc, argv);
49
+
50
+ return form;
51
+ }
52
+
53
+ void init_typhoeus_form() {
54
+ VALUE klass = cTyphoeusForm = rb_define_class_under(mTyphoeus, "Form", rb_cObject);
55
+
56
+ rb_define_singleton_method(klass, "new", new, -1);
57
+ rb_define_private_method(klass, "formadd_file", formadd_file, 4);
58
+ rb_define_private_method(klass, "formadd_param", formadd_param, 2);
59
+ }
@@ -0,0 +1,13 @@
1
+ #ifndef TYPHOEUS_FORM
2
+ #define TYPHOEUS_FORM
3
+
4
+ #include <native.h>
5
+
6
+ typedef struct {
7
+ struct curl_httppost *first;
8
+ struct curl_httppost *last;
9
+ } CurlForm;
10
+
11
+ void init_typhoeus_form();
12
+
13
+ #endif
@@ -16,12 +16,15 @@ static VALUE multi_add_handle(VALUE self, VALUE easy) {
16
16
 
17
17
  mcode = curl_multi_add_handle(curl_multi->multi, curl_easy->curl);
18
18
  if (mcode != CURLM_CALL_MULTI_PERFORM && mcode != CURLM_OK) {
19
- rb_raise((VALUE)mcode, "An error occured adding the handle");
19
+ rb_raise(rb_eRuntimeError, "An error occured adding the handle: %d: %s", mcode, curl_multi_strerror(mcode));
20
20
  }
21
21
 
22
22
  curl_easy_setopt(curl_easy->curl, CURLOPT_PRIVATE, easy);
23
23
  curl_multi->active++;
24
24
 
25
+ VALUE easy_handles = rb_iv_get(self, "@easy_handles");
26
+ rb_ary_push(easy_handles, easy);
27
+
25
28
  if (mcode == CURLM_CALL_MULTI_PERFORM) {
26
29
  curl_multi_perform(curl_multi->multi, &(curl_multi->running));
27
30
  }
@@ -43,6 +46,9 @@ static VALUE multi_remove_handle(VALUE self, VALUE easy) {
43
46
  curl_multi->active--;
44
47
  curl_multi_remove_handle(curl_multi->multi, curl_easy->curl);
45
48
 
49
+ VALUE easy_handles = rb_iv_get(self, "@easy_handles");
50
+ rb_ary_delete(easy_handles, easy);
51
+
46
52
  return easy;
47
53
  }
48
54
 
@@ -65,36 +71,14 @@ static void multi_read_info(VALUE self, CURLM *multi_handle) {
65
71
  if (easy_handle) {
66
72
  ecode = curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, &easy);
67
73
  if (ecode != 0) {
68
- rb_raise(ecode, "error getting easy object");
74
+ rb_raise(rb_eRuntimeError, "error getting easy object: %d: %s", ecode, curl_easy_strerror(ecode));
69
75
  }
70
76
 
71
77
  long response_code = -1;
72
78
  curl_easy_getinfo(easy_handle, CURLINFO_RESPONSE_CODE, &response_code);
73
79
 
74
- // TODO: find out what the real problem is here and fix it.
75
- // this next bit is a horrible hack. For some reason my tests against a local server on my laptop
76
- // fail intermittently and return this result number. However, it will succeed if you try it a few
77
- // more times. Also noteworthy is that this doens't happen when hitting an external server. WTF?!
78
-
79
- // Sandofsky says:
80
- // This is caused by OS X first attempting to resolve using IPV6.
81
- // Hack solution: connect to yourself with 127.0.0.1, not localhost
82
- // http://curl.haxx.se/mail/tracker-2009-09/0018.html
83
- if (result == 7) {
84
- VALUE max_retries = rb_funcall(easy, rb_intern("max_retries?"), 0);
85
- if (max_retries != Qtrue) {
86
- multi_remove_handle(self, easy);
87
- multi_add_handle(self, easy);
88
- CurlMulti *curl_multi;
89
- Data_Get_Struct(self, CurlMulti, curl_multi);
90
- curl_multi_perform(curl_multi->multi, &(curl_multi->running));
91
-
92
- rb_funcall(easy, rb_intern("increment_retries"), 0);
93
-
94
- continue;
95
- }
96
- }
97
80
  multi_remove_handle(self, easy);
81
+ rb_iv_set(easy, "@curl_return_code", INT2FIX(result));
98
82
 
99
83
  if (result != 0) {
100
84
  rb_funcall(easy, rb_intern("failure"), 0);
@@ -118,7 +102,7 @@ static void rb_curl_multi_run(VALUE self, CURLM *multi_handle, int *still_runnin
118
102
  } while (mcode == CURLM_CALL_MULTI_PERFORM);
119
103
 
120
104
  if (mcode != CURLM_OK) {
121
- rb_raise((VALUE)mcode, "an error occured while running perform");
105
+ rb_raise(rb_eRuntimeError, "an error occured while running perform: %d: %s", mcode, curl_multi_strerror(mcode));
122
106
  }
123
107
 
124
108
  multi_read_info( self, multi_handle );
@@ -128,6 +112,8 @@ static VALUE fire_and_forget(VALUE self) {
128
112
  CurlMulti *curl_multi;
129
113
  Data_Get_Struct(self, CurlMulti, curl_multi);
130
114
  rb_curl_multi_run( self, curl_multi->multi, &(curl_multi->running) );
115
+
116
+ return Qnil;
131
117
  }
132
118
 
133
119
  static VALUE multi_perform(VALUE self) {
@@ -150,7 +136,7 @@ static VALUE multi_perform(VALUE self) {
150
136
  /* get the curl suggested time out */
151
137
  mcode = curl_multi_timeout(curl_multi->multi, &timeout);
152
138
  if (mcode != CURLM_OK) {
153
- rb_raise((VALUE)mcode, "an error occured getting the timeout");
139
+ rb_raise(rb_eRuntimeError, "an error occured getting the timeout: %d: %s", mcode, curl_multi_strerror(mcode));
154
140
  }
155
141
 
156
142
  if (timeout == 0) { /* no delay */
@@ -167,7 +153,7 @@ static VALUE multi_perform(VALUE self) {
167
153
  /* load the fd sets from the multi handle */
168
154
  mcode = curl_multi_fdset(curl_multi->multi, &fdread, &fdwrite, &fdexcep, &maxfd);
169
155
  if (mcode != CURLM_OK) {
170
- rb_raise((VALUE)mcode, "an error occured getting the fdset");
156
+ rb_raise(rb_eRuntimeError, "an error occured getting the fdset: %d: %s", mcode, curl_multi_strerror(mcode));
171
157
  }
172
158
 
173
159
  rc = rb_thread_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv);
@@ -185,7 +171,7 @@ static VALUE active_handle_count(VALUE self) {
185
171
  CurlMulti *curl_multi;
186
172
  Data_Get_Struct(self, CurlMulti, curl_multi);
187
173
 
188
- return INT2NUM(curl_multi->active);
174
+ return INT2FIX(curl_multi->active);
189
175
  }
190
176
 
191
177
  static VALUE multi_cleanup(VALUE self) {