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.
- data/CHANGELOG.markdown +26 -1
- data/Gemfile +2 -0
- data/Gemfile.lock +2 -0
- data/LICENSE +20 -0
- data/README.textile +44 -5
- data/Rakefile +8 -5
- data/VERSION +1 -1
- data/examples/file.rb +12 -0
- data/examples/times.rb +40 -0
- data/ext/typhoeus/.gitignore +2 -1
- data/ext/typhoeus/native.c +1 -0
- data/ext/typhoeus/native.h +1 -0
- data/ext/typhoeus/typhoeus_easy.c +32 -7
- data/ext/typhoeus/typhoeus_easy.h +1 -0
- data/ext/typhoeus/typhoeus_form.c +59 -0
- data/ext/typhoeus/typhoeus_form.h +13 -0
- data/ext/typhoeus/typhoeus_multi.c +15 -29
- data/lib/typhoeus/easy.rb +74 -48
- data/lib/typhoeus/form.rb +32 -0
- data/lib/typhoeus/hydra/connect_options.rb +19 -3
- data/lib/typhoeus/hydra.rb +40 -7
- data/lib/typhoeus/multi.rb +7 -5
- data/lib/typhoeus/remote.rb +1 -1
- data/lib/typhoeus/remote_proxy_object.rb +2 -0
- data/lib/typhoeus/request.rb +17 -14
- data/lib/typhoeus/response.rb +16 -1
- data/lib/typhoeus/utils.rb +38 -0
- data/lib/typhoeus.rb +1 -0
- data/spec/fixtures/placeholder.gif +0 -0
- data/spec/fixtures/placeholder.txt +1 -0
- data/spec/fixtures/placeholder.ukn +0 -0
- data/spec/servers/app.rb +10 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/typhoeus/easy_spec.rb +74 -7
- data/spec/typhoeus/filter_spec.rb +2 -2
- data/spec/typhoeus/form_spec.rb +107 -0
- data/spec/typhoeus/hydra_mock_spec.rb +1 -1
- data/spec/typhoeus/hydra_spec.rb +108 -38
- data/spec/typhoeus/multi_spec.rb +1 -1
- data/spec/typhoeus/normalized_header_hash_spec.rb +1 -1
- data/spec/typhoeus/remote_method_spec.rb +2 -2
- data/spec/typhoeus/remote_proxy_object_spec.rb +1 -1
- data/spec/typhoeus/remote_spec.rb +1 -1
- data/spec/typhoeus/request_spec.rb +50 -5
- data/spec/typhoeus/response_spec.rb +13 -1
- data/spec/typhoeus/utils_spec.rb +1 -1
- data/typhoeus.gemspec +93 -75
- metadata +70 -28
- 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
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
|
|
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
|
-
|
|
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
|
-
:
|
|
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
|
|
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 = "
|
|
12
|
-
gemspec.homepage = "http://github.com/
|
|
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 `
|
|
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.
|
|
1
|
+
0.2.4
|
data/examples/file.rb
ADDED
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
|
data/ext/typhoeus/.gitignore
CHANGED
data/ext/typhoeus/native.c
CHANGED
data/ext/typhoeus/native.h
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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);
|
|
@@ -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
|
+
}
|
|
@@ -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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
174
|
+
return INT2FIX(curl_multi->active);
|
|
189
175
|
}
|
|
190
176
|
|
|
191
177
|
static VALUE multi_cleanup(VALUE self) {
|