httparty 0.8.2 → 0.8.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of httparty might be problematic. Click here for more details.
- data/.gitignore +1 -0
- data/.travis.yml +9 -0
- data/Gemfile +8 -1
- data/Guardfile +16 -0
- data/History +7 -0
- data/README.rdoc +8 -16
- data/cucumber.yml +1 -1
- data/httparty.gemspec +1 -1
- data/lib/httparty.rb +10 -1
- data/lib/httparty/cookie_hash.rb +2 -2
- data/lib/httparty/core_extensions.rb +23 -0
- data/lib/httparty/hash_conversions.rb +1 -1
- data/lib/httparty/module_inheritable_attributes.rb +1 -1
- data/lib/httparty/parser.rb +6 -2
- data/lib/httparty/request.rb +8 -5
- data/lib/httparty/response.rb +17 -40
- data/lib/httparty/response/headers.rb +31 -0
- data/lib/httparty/version.rb +1 -1
- data/spec/httparty/cookie_hash_spec.rb +3 -4
- data/spec/httparty/net_digest_auth_spec.rb +8 -11
- data/spec/httparty/parser_spec.rb +6 -0
- data/spec/httparty/request_spec.rb +15 -0
- data/spec/httparty/response_spec.rb +34 -20
- data/spec/httparty/ssl_spec.rb +13 -5
- data/spec/httparty_spec.rb +15 -2
- data/spec/spec.opts +0 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/support/ssl_test_helper.rb +28 -6
- data/spec/support/ssl_test_server.rb +19 -8
- metadata +65 -38
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
@@ -1,8 +1,15 @@
|
|
1
1
|
source :rubygems
|
2
2
|
gemspec
|
3
3
|
|
4
|
-
gem 'rake'
|
4
|
+
gem 'rake'
|
5
5
|
gem 'cucumber', '~> 0.7'
|
6
6
|
gem 'fakeweb', '~> 1.2'
|
7
7
|
gem 'rspec', '~> 1.3'
|
8
8
|
gem 'mongrel', '1.2.0.pre2'
|
9
|
+
gem 'multi_json', '~> 1.3'
|
10
|
+
|
11
|
+
group :development do
|
12
|
+
gem 'guard'
|
13
|
+
gem 'guard-rspec'
|
14
|
+
gem 'guard-bundler'
|
15
|
+
end
|
data/Guardfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
rspec_options = {
|
2
|
+
:version => 1,
|
3
|
+
:all_after_pass => false,
|
4
|
+
:all_on_start => false,
|
5
|
+
}
|
6
|
+
|
7
|
+
guard 'rspec', rspec_options do
|
8
|
+
watch(%r{^spec/.+_spec\.rb$})
|
9
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
10
|
+
watch('spec/spec_helper.rb') { "spec" }
|
11
|
+
end
|
12
|
+
|
13
|
+
guard 'bundler' do
|
14
|
+
watch('Gemfile')
|
15
|
+
watch(/^.+\.gemspec/)
|
16
|
+
end
|
data/History
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
== 0.8.3 2012-04-21
|
2
|
+
* new
|
3
|
+
* [lazy parsing of responses](https://github.com/jnunemaker/httparty/commit/9fd5259c8dab00e426082b66af44ede2c9068f45)
|
4
|
+
* [add support for PATCH requests](https://github.com/jnunemaker/httparty/commit/7ab6641e37a9e31517e46f6124f38c615395d38a)
|
5
|
+
* bug fixes
|
6
|
+
* [subclasses no longer override superclass options](https://github.com/jnunemaker/httparty/commit/682af8fbf672e7b3009e650da776c85cdfe78d39)
|
7
|
+
|
1
8
|
== 0.8.2 2012-04-12
|
2
9
|
* new
|
3
10
|
* add -r to make CLI return failure code if status >= 400
|
data/README.rdoc
CHANGED
@@ -2,22 +2,6 @@
|
|
2
2
|
|
3
3
|
Makes http fun again!
|
4
4
|
|
5
|
-
== Note on Releases
|
6
|
-
|
7
|
-
Releases are tagged on github and also released as gems on github and rubyforge. Master is pushed to whenever I add a patch or a new feature. To build from master, you can clone the code, generate the updated gemspec, build the gem and install.
|
8
|
-
|
9
|
-
* rake gemspec
|
10
|
-
* gem build httparty.gemspec
|
11
|
-
* gem install the gem that was built
|
12
|
-
|
13
|
-
== Note on Patches/Pull Requests
|
14
|
-
|
15
|
-
* Fork the project.
|
16
|
-
* Make your feature addition or bug fix.
|
17
|
-
* Add tests for it. This is important so I don't break it in a future version unintentionally.
|
18
|
-
* Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself in another branch so I can ignore when I pull)
|
19
|
-
* Send me a pull request. Bonus points for topic branches.
|
20
|
-
|
21
5
|
== Features:
|
22
6
|
|
23
7
|
* Easy get, post requests
|
@@ -29,6 +13,14 @@ Releases are tagged on github and also released as gems on github and rubyforge.
|
|
29
13
|
|
30
14
|
See http://github.com/jnunemaker/httparty/tree/master/examples
|
31
15
|
|
16
|
+
== Note on Patches/Pull Requests
|
17
|
+
|
18
|
+
* Fork the project.
|
19
|
+
* Make your feature addition or bug fix.
|
20
|
+
* Add tests for it. This is important so I don't break it in a future version unintentionally.
|
21
|
+
* Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself in another branch so I can ignore when I pull)
|
22
|
+
* Send me a pull request. Bonus points for topic branches.
|
23
|
+
|
32
24
|
== Command Line Interface
|
33
25
|
|
34
26
|
httparty also includes the executable <tt>httparty</tt> which can be
|
data/cucumber.yml
CHANGED
@@ -1 +1 @@
|
|
1
|
-
default: features
|
1
|
+
default: features --format progress
|
data/httparty.gemspec
CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.summary = %q{Makes http fun! Also, makes consuming restful web services dead easy.}
|
13
13
|
s.description = %q{Makes http fun! Also, makes consuming restful web services dead easy.}
|
14
14
|
|
15
|
-
s.add_dependency 'multi_json'
|
15
|
+
s.add_dependency 'multi_json', "~> 1.0"
|
16
16
|
s.add_dependency 'multi_xml'
|
17
17
|
|
18
18
|
s.post_install_message = "When you HTTParty, you must party hard!"
|
data/lib/httparty.rb
CHANGED
@@ -36,7 +36,7 @@ module HTTParty
|
|
36
36
|
end
|
37
37
|
|
38
38
|
# == Common Request Options
|
39
|
-
# Request methods (get, post, put, delete, head, options) all take a common set of options. These are:
|
39
|
+
# Request methods (get, post, patch, put, delete, head, options) all take a common set of options. These are:
|
40
40
|
#
|
41
41
|
# [:+body+:] Body of the request. If passed a Hash, will try to normalize it first, by default passing it to ActiveSupport::to_params. Any other kind of object will get used as-is.
|
42
42
|
# [:+http_proxyaddr+:] Address of proxy server to use.
|
@@ -363,6 +363,11 @@ module HTTParty
|
|
363
363
|
perform_request Net::HTTP::Post, path, options, &block
|
364
364
|
end
|
365
365
|
|
366
|
+
# Perform a PATCH request to a path
|
367
|
+
def patch(path, options={}, &block)
|
368
|
+
perform_request Net::HTTP::Patch, path, options, &block
|
369
|
+
end
|
370
|
+
|
366
371
|
# Perform a PUT request to a path
|
367
372
|
def put(path, options={}, &block)
|
368
373
|
perform_request Net::HTTP::Put, path, options, &block
|
@@ -431,6 +436,10 @@ module HTTParty
|
|
431
436
|
Basement.post(*args, &block)
|
432
437
|
end
|
433
438
|
|
439
|
+
def self.patch(*args, &block)
|
440
|
+
Basement.patch(*args, &block)
|
441
|
+
end
|
442
|
+
|
434
443
|
def self.put(*args, &block)
|
435
444
|
Basement.put(*args, &block)
|
436
445
|
end
|
data/lib/httparty/cookie_hash.rb
CHANGED
@@ -6,4 +6,27 @@ module HTTParty
|
|
6
6
|
instance_methods.each { |m| undef_method m unless m =~ /^__|instance_eval/ }
|
7
7
|
end
|
8
8
|
end
|
9
|
+
|
10
|
+
unless defined?(Net::HTTP::Patch)
|
11
|
+
class Net::HTTP
|
12
|
+
def patch(path, data, initheader = nil, dest = nil, &block) #:nodoc:
|
13
|
+
res = nil
|
14
|
+
request(Patch.new(path, initheader), data) {|r|
|
15
|
+
r.read_body dest, &block
|
16
|
+
res = r
|
17
|
+
}
|
18
|
+
unless @newimpl
|
19
|
+
res.value
|
20
|
+
return res, res.body
|
21
|
+
end
|
22
|
+
res
|
23
|
+
end
|
24
|
+
|
25
|
+
class Patch < Net::HTTPRequest
|
26
|
+
METHOD = 'PATCH'
|
27
|
+
REQUEST_HAS_BODY = true
|
28
|
+
RESPONSE_HAS_BODY = true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
9
32
|
end
|
@@ -22,7 +22,7 @@ module HTTParty
|
|
22
22
|
if instance_variable_get(ivar).respond_to?(:merge)
|
23
23
|
method = <<-EOM
|
24
24
|
def self.#{inheritable_attribute}
|
25
|
-
#{ivar} = superclass.#{inheritable_attribute}.merge #{ivar}
|
25
|
+
#{ivar} = superclass.#{inheritable_attribute}.merge Marshal.load(Marshal.dump(#{ivar}))
|
26
26
|
end
|
27
27
|
EOM
|
28
28
|
subclass.class_eval method
|
data/lib/httparty/parser.rb
CHANGED
@@ -93,7 +93,6 @@ module HTTParty
|
|
93
93
|
@body = body
|
94
94
|
@format = format
|
95
95
|
end
|
96
|
-
private_class_method :new
|
97
96
|
|
98
97
|
# @return [Object] the parsed body
|
99
98
|
# @return [nil] when the response body is nil, an empty string, spaces only or "null"
|
@@ -113,7 +112,12 @@ module HTTParty
|
|
113
112
|
end
|
114
113
|
|
115
114
|
def json
|
116
|
-
|
115
|
+
# https://github.com/sferik/rails/commit/5e62670131dfa1718eaf21ff8dd3371395a5f1cc
|
116
|
+
if MultiJson.respond_to?(:adapter)
|
117
|
+
MultiJson.load(body)
|
118
|
+
else
|
119
|
+
MultiJson.decode(body)
|
120
|
+
end
|
117
121
|
end
|
118
122
|
|
119
123
|
def yaml
|
data/lib/httparty/request.rb
CHANGED
@@ -3,6 +3,7 @@ module HTTParty
|
|
3
3
|
SupportedHTTPMethods = [
|
4
4
|
Net::HTTP::Get,
|
5
5
|
Net::HTTP::Post,
|
6
|
+
Net::HTTP::Patch,
|
6
7
|
Net::HTTP::Put,
|
7
8
|
Net::HTTP::Delete,
|
8
9
|
Net::HTTP::Head,
|
@@ -70,6 +71,7 @@ module HTTParty
|
|
70
71
|
def perform(&block)
|
71
72
|
validate
|
72
73
|
setup_raw_request
|
74
|
+
chunked_body = nil
|
73
75
|
|
74
76
|
self.last_response = http.request(@raw_request) do |http_response|
|
75
77
|
if block
|
@@ -80,12 +82,12 @@ module HTTParty
|
|
80
82
|
block.call(fragment)
|
81
83
|
end
|
82
84
|
|
83
|
-
|
85
|
+
chunked_body = chunks.join
|
84
86
|
end
|
85
87
|
end
|
86
88
|
|
87
89
|
handle_deflation
|
88
|
-
handle_response
|
90
|
+
handle_response(chunked_body)
|
89
91
|
end
|
90
92
|
|
91
93
|
private
|
@@ -196,7 +198,7 @@ module HTTParty
|
|
196
198
|
query_string_parts.size > 0 ? query_string_parts.join('&') : nil
|
197
199
|
end
|
198
200
|
|
199
|
-
def handle_response
|
201
|
+
def handle_response(body)
|
200
202
|
if response_redirects?
|
201
203
|
options[:limit] -= 1
|
202
204
|
self.path = last_response['location']
|
@@ -205,7 +207,8 @@ module HTTParty
|
|
205
207
|
capture_cookies(last_response)
|
206
208
|
perform
|
207
209
|
else
|
208
|
-
|
210
|
+
body = body || last_response.body
|
211
|
+
Response.new(self, last_response, lambda { parse_response(body) }, :body => body)
|
209
212
|
end
|
210
213
|
end
|
211
214
|
|
@@ -258,7 +261,7 @@ module HTTParty
|
|
258
261
|
|
259
262
|
def validate
|
260
263
|
raise HTTParty::RedirectionTooDeep.new(last_response), 'HTTP redirects too deep' if options[:limit].to_i <= 0
|
261
|
-
raise ArgumentError, 'only get, post, put, delete, head, and options methods are supported' unless SupportedHTTPMethods.include?(http_method)
|
264
|
+
raise ArgumentError, 'only get, post, patch, put, delete, head, and options methods are supported' unless SupportedHTTPMethods.include?(http_method)
|
262
265
|
raise ArgumentError, ':headers must be a hash' if options[:headers] && !options[:headers].is_a?(Hash)
|
263
266
|
raise ArgumentError, 'only one authentication method, :basic_auth or :digest_auth may be used at a time' if options[:basic_auth] && options[:digest_auth]
|
264
267
|
raise ArgumentError, ':basic_auth must be a hash' if options[:basic_auth] && !options[:basic_auth].is_a?(Hash)
|
data/lib/httparty/response.rb
CHANGED
@@ -1,46 +1,21 @@
|
|
1
1
|
module HTTParty
|
2
2
|
class Response < HTTParty::BasicObject #:nodoc:
|
3
|
-
class Headers
|
4
|
-
include ::Net::HTTPHeader
|
5
|
-
|
6
|
-
def initialize(header = {})
|
7
|
-
@header = header
|
8
|
-
end
|
9
|
-
|
10
|
-
def ==(other)
|
11
|
-
@header == other
|
12
|
-
end
|
13
|
-
|
14
|
-
def inspect
|
15
|
-
@header.inspect
|
16
|
-
end
|
17
|
-
|
18
|
-
def method_missing(name, *args, &block)
|
19
|
-
if @header.respond_to?(name)
|
20
|
-
@header.send(name, *args, &block)
|
21
|
-
else
|
22
|
-
super
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def respond_to?(method)
|
27
|
-
super || @header.respond_to?(method)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
|
32
3
|
def self.underscore(string)
|
33
4
|
string.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z])([A-Z])/,'\1_\2').downcase
|
34
5
|
end
|
35
6
|
|
36
|
-
attr_reader :request, :response, :
|
7
|
+
attr_reader :request, :response, :body, :headers
|
37
8
|
|
38
|
-
def initialize(request, response,
|
39
|
-
@request
|
40
|
-
@response
|
41
|
-
@body
|
42
|
-
@
|
43
|
-
@headers
|
9
|
+
def initialize(request, response, parsed_block, options={})
|
10
|
+
@request = request
|
11
|
+
@response = response
|
12
|
+
@body = response.body || options[:body]
|
13
|
+
@parsed_block = parsed_block
|
14
|
+
@headers = Headers.new(response.to_hash)
|
15
|
+
end
|
16
|
+
|
17
|
+
def parsed_response
|
18
|
+
@parsed_response ||= @parsed_block.call
|
44
19
|
end
|
45
20
|
|
46
21
|
def class
|
@@ -53,7 +28,7 @@ module HTTParty
|
|
53
28
|
|
54
29
|
def inspect
|
55
30
|
inspect_id = "%x" % (object_id * 2)
|
56
|
-
%(#<#{self.class}:0x#{inspect_id}
|
31
|
+
%(#<#{self.class}:0x#{inspect_id} parsed_response=#{parsed_response.inspect}, @response=#{response.inspect}, @headers=#{headers.inspect}>)
|
57
32
|
end
|
58
33
|
|
59
34
|
CODES_TO_OBJ = ::Net::HTTPResponse::CODE_CLASS_TO_OBJ.merge ::Net::HTTPResponse::CODE_TO_OBJ
|
@@ -64,10 +39,10 @@ module HTTParty
|
|
64
39
|
klass === response
|
65
40
|
end
|
66
41
|
end
|
67
|
-
|
42
|
+
|
68
43
|
def respond_to?(name)
|
69
|
-
return true if [:request
|
70
|
-
parsed_response.respond_to?(name)
|
44
|
+
return true if [:request, :response, :parsed_response, :body, :headers].include?(name)
|
45
|
+
parsed_response.respond_to?(name) || response.respond_to?(name)
|
71
46
|
end
|
72
47
|
|
73
48
|
protected
|
@@ -83,3 +58,5 @@ module HTTParty
|
|
83
58
|
end
|
84
59
|
end
|
85
60
|
end
|
61
|
+
|
62
|
+
require 'httparty/response/headers'
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module HTTParty
|
2
|
+
class Response #:nodoc:
|
3
|
+
class Headers
|
4
|
+
include ::Net::HTTPHeader
|
5
|
+
|
6
|
+
def initialize(header = {})
|
7
|
+
@header = header
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(other)
|
11
|
+
@header == other
|
12
|
+
end
|
13
|
+
|
14
|
+
def inspect
|
15
|
+
@header.inspect
|
16
|
+
end
|
17
|
+
|
18
|
+
def method_missing(name, *args, &block)
|
19
|
+
if @header.respond_to?(name)
|
20
|
+
@header.send(name, *args, &block)
|
21
|
+
else
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def respond_to?(method)
|
27
|
+
super || @header.respond_to?(method)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/httparty/version.rb
CHANGED
@@ -6,7 +6,6 @@ describe HTTParty::CookieHash do
|
|
6
6
|
end
|
7
7
|
|
8
8
|
describe "#add_cookies" do
|
9
|
-
|
10
9
|
describe "with a hash" do
|
11
10
|
it "should add new key/value pairs to the hash" do
|
12
11
|
@cookie_hash.add_cookies(:foo => "bar")
|
@@ -29,7 +28,7 @@ describe HTTParty::CookieHash do
|
|
29
28
|
@cookie_hash[:second].should == 'two'
|
30
29
|
@cookie_hash[:third].should == nil
|
31
30
|
end
|
32
|
-
|
31
|
+
|
33
32
|
it "should overwrite any existing key" do
|
34
33
|
@cookie_hash[:foo] = 'bar'
|
35
34
|
@cookie_hash.add_cookies("foo=tar")
|
@@ -37,7 +36,7 @@ describe HTTParty::CookieHash do
|
|
37
36
|
@cookie_hash[:foo].should eql("tar")
|
38
37
|
end
|
39
38
|
end
|
40
|
-
|
39
|
+
|
41
40
|
describe 'with other class' do
|
42
41
|
it "should error" do
|
43
42
|
lambda {
|
@@ -61,7 +60,7 @@ describe HTTParty::CookieHash do
|
|
61
60
|
@s.should match(/rofl=copter/)
|
62
61
|
@s.should match(/^\w+=\w+; \w+=\w+$/)
|
63
62
|
end
|
64
|
-
|
63
|
+
|
65
64
|
it "should not include client side only cookies" do
|
66
65
|
@cookie_hash.add_cookies(:path => "/")
|
67
66
|
@s = @cookie_hash.to_cookie_string
|
@@ -13,11 +13,11 @@ describe Net::HTTPHeader::DigestAuthenticator do
|
|
13
13
|
@digest.authorization_header.join(", ")
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
16
|
context "with specified quality of protection (qop)" do
|
18
17
|
before do
|
19
|
-
@digest = setup_digest({
|
20
|
-
'Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth"'
|
18
|
+
@digest = setup_digest({
|
19
|
+
'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth"',
|
20
|
+
})
|
21
21
|
end
|
22
22
|
|
23
23
|
it "should set prefix" do
|
@@ -45,9 +45,7 @@ describe Net::HTTPHeader::DigestAuthenticator do
|
|
45
45
|
end
|
46
46
|
|
47
47
|
it "should set response" do
|
48
|
-
request_digest =
|
49
|
-
"md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life)" +
|
50
|
-
":NONCE:0:md5(deadbeef):auth:md5(GET:/dir/index.html))"
|
48
|
+
request_digest = "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:0:md5(deadbeef):auth:md5(GET:/dir/index.html))"
|
51
49
|
authorization_header.should include(%Q(response="#{request_digest}"))
|
52
50
|
end
|
53
51
|
end
|
@@ -55,8 +53,9 @@ describe Net::HTTPHeader::DigestAuthenticator do
|
|
55
53
|
|
56
54
|
context "with unspecified quality of protection (qop)" do
|
57
55
|
before do
|
58
|
-
@digest = setup_digest({
|
59
|
-
'Digest realm="myhost@testrealm.com", nonce="NONCE"'
|
56
|
+
@digest = setup_digest({
|
57
|
+
'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE"',
|
58
|
+
})
|
60
59
|
end
|
61
60
|
|
62
61
|
it "should set prefix" do
|
@@ -84,9 +83,7 @@ describe Net::HTTPHeader::DigestAuthenticator do
|
|
84
83
|
end
|
85
84
|
|
86
85
|
it "should set response" do
|
87
|
-
request_digest =
|
88
|
-
"md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life)" +
|
89
|
-
":NONCE:md5(GET:/dir/index.html))"
|
86
|
+
request_digest = "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:md5(GET:/dir/index.html))"
|
90
87
|
authorization_header.should include(%Q(response="#{request_digest}"))
|
91
88
|
end
|
92
89
|
end
|
@@ -145,6 +145,12 @@ describe HTTParty::Parser do
|
|
145
145
|
end
|
146
146
|
|
147
147
|
it "parses json with MultiJson" do
|
148
|
+
MultiJson.should_receive(:load).with('body')
|
149
|
+
subject.send(:json)
|
150
|
+
end
|
151
|
+
|
152
|
+
it "uses MultiJson.decode if MultiJson does not respond to adapter" do
|
153
|
+
MultiJson.should_receive(:respond_to?).with(:adapter).and_return(false)
|
148
154
|
MultiJson.should_receive(:decode).with('body')
|
149
155
|
subject.send(:json)
|
150
156
|
end
|
@@ -411,6 +411,16 @@ describe HTTParty::Request do
|
|
411
411
|
resp.body.should == "<foo><bar>error</bar></foo>"
|
412
412
|
resp['foo']['bar'].should == "error"
|
413
413
|
end
|
414
|
+
|
415
|
+
it "parses response lazily so codes can be checked prior" do
|
416
|
+
stub_response 'not xml', 500
|
417
|
+
@request.options[:format] = :xml
|
418
|
+
lambda {
|
419
|
+
response = @request.perform
|
420
|
+
response.code.should == 500
|
421
|
+
response.body.should == 'not xml'
|
422
|
+
}.should_not raise_error
|
423
|
+
end
|
414
424
|
end
|
415
425
|
end
|
416
426
|
|
@@ -456,6 +466,11 @@ describe HTTParty::Request do
|
|
456
466
|
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
457
467
|
end
|
458
468
|
|
469
|
+
it "should be handled by PATCH transparently" do
|
470
|
+
@request.http_method = Net::HTTP::Patch
|
471
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
472
|
+
end
|
473
|
+
|
459
474
|
it "should be handled by PUT transparently" do
|
460
475
|
@request.http_method = Net::HTTP::Put
|
461
476
|
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
@@ -9,7 +9,7 @@ describe HTTParty::Response do
|
|
9
9
|
@response_object.stub(:body => "{foo:'bar'}")
|
10
10
|
@response_object['last-modified'] = @last_modified
|
11
11
|
@response_object['content-length'] = @content_length
|
12
|
-
@parsed_response = {"foo" => "bar"}
|
12
|
+
@parsed_response = lambda { {"foo" => "bar"} }
|
13
13
|
@response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
|
14
14
|
end
|
15
15
|
|
@@ -51,17 +51,42 @@ describe HTTParty::Response do
|
|
51
51
|
end
|
52
52
|
|
53
53
|
it "should send missing methods to delegate" do
|
54
|
-
response = HTTParty::Response.new(@request_object, @response_object,
|
54
|
+
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
|
55
55
|
response['foo'].should == 'bar'
|
56
56
|
end
|
57
|
-
|
58
|
-
it "
|
59
|
-
response = HTTParty::Response.new(@request_object, @response_object,
|
57
|
+
|
58
|
+
it "response to request" do
|
59
|
+
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
|
60
|
+
response.respond_to?(:request).should be_true
|
61
|
+
end
|
62
|
+
|
63
|
+
it "responds to response" do
|
64
|
+
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
|
65
|
+
response.respond_to?(:response).should be_true
|
66
|
+
end
|
67
|
+
|
68
|
+
it "responds to body" do
|
69
|
+
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
|
70
|
+
response.respond_to?(:body).should be_true
|
71
|
+
end
|
72
|
+
|
73
|
+
it "responds to headers" do
|
74
|
+
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
|
75
|
+
response.respond_to?(:headers).should be_true
|
76
|
+
end
|
77
|
+
|
78
|
+
it "responds to parsed_response" do
|
79
|
+
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
|
60
80
|
response.respond_to?(:parsed_response).should be_true
|
61
81
|
end
|
62
82
|
|
83
|
+
it "responds to anything parsed_response responds to" do
|
84
|
+
response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
|
85
|
+
response.respond_to?(:[]).should be_true
|
86
|
+
end
|
87
|
+
|
63
88
|
it "should be able to iterate if it is array" do
|
64
|
-
response = HTTParty::Response.new(@request_object, @response_object, [{'foo' => 'bar'}, {'foo' => 'baz'}])
|
89
|
+
response = HTTParty::Response.new(@request_object, @response_object, lambda { [{'foo' => 'bar'}, {'foo' => 'baz'}] })
|
65
90
|
response.size.should == 2
|
66
91
|
expect {
|
67
92
|
response.each { |item| }
|
@@ -89,22 +114,11 @@ describe HTTParty::Response do
|
|
89
114
|
end
|
90
115
|
end
|
91
116
|
|
92
|
-
xit "should allow hashes to be accessed with dot notation" do
|
93
|
-
response = HTTParty::Response.new(@request_object, {'foo' => 'bar'}, "{foo:'bar'}", 200, 'OK')
|
94
|
-
response.foo.should == 'bar'
|
95
|
-
end
|
96
|
-
|
97
|
-
xit "should allow nested hashes to be accessed with dot notation" do
|
98
|
-
response = HTTParty::Response.new(@request_object, {'foo' => {'bar' => 'baz'}}, "{foo: {bar:'baz'}}", 200, 'OK')
|
99
|
-
response.foo.should == {'bar' => 'baz'}
|
100
|
-
response.foo.bar.should == 'baz'
|
101
|
-
end
|
102
|
-
|
103
117
|
describe "semantic methods for response codes" do
|
104
118
|
def response_mock(klass)
|
105
|
-
|
106
|
-
|
107
|
-
|
119
|
+
response = klass.new('', '', '')
|
120
|
+
response.stub(:body)
|
121
|
+
response
|
108
122
|
end
|
109
123
|
|
110
124
|
context "major codes" do
|
data/spec/httparty/ssl_spec.rb
CHANGED
@@ -3,18 +3,18 @@ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
|
3
3
|
describe HTTParty::Request do
|
4
4
|
context "SSL certificate verification" do
|
5
5
|
before do
|
6
|
-
FakeWeb.allow_net_connect = true
|
6
|
+
FakeWeb.allow_net_connect = true
|
7
7
|
end
|
8
8
|
|
9
9
|
after do
|
10
|
-
FakeWeb.allow_net_connect = false
|
10
|
+
FakeWeb.allow_net_connect = false
|
11
11
|
end
|
12
12
|
|
13
|
-
it "should work
|
13
|
+
it "should work when no trusted CA list is specified" do
|
14
14
|
ssl_verify_test(nil, nil, "selfsigned.crt").should == {'success' => true}
|
15
15
|
end
|
16
16
|
|
17
|
-
it "should work
|
17
|
+
it "should work when no trusted CA list is specified, even with a bogus hostname" do
|
18
18
|
ssl_verify_test(nil, nil, "bogushost.crt").should == {'success' => true}
|
19
19
|
end
|
20
20
|
|
@@ -25,8 +25,14 @@ describe HTTParty::Request do
|
|
25
25
|
it "should work when using ssl_ca_file with a certificate authority" do
|
26
26
|
ssl_verify_test(:ssl_ca_file, "ca.crt", "server.crt").should == {'success' => true}
|
27
27
|
end
|
28
|
+
|
28
29
|
it "should work when using ssl_ca_path with a certificate authority" do
|
29
|
-
|
30
|
+
http = Net::HTTP.new('www.google.com', 443, nil, nil, nil, nil)
|
31
|
+
response = stub(Net::HTTPResponse, :[] => '', :body => '', :to_hash => {})
|
32
|
+
http.stub(:request).and_return(response)
|
33
|
+
Net::HTTP.should_receive(:new).with('www.google.com', 443, nil, nil, nil, nil).and_return(http)
|
34
|
+
http.should_receive(:ca_path=).with('/foo/bar')
|
35
|
+
HTTParty.get('https://www.google.com', :ssl_ca_path => '/foo/bar')
|
30
36
|
end
|
31
37
|
|
32
38
|
it "should fail when using ssl_ca_file and the server uses an unrecognized certificate authority" do
|
@@ -34,6 +40,7 @@ describe HTTParty::Request do
|
|
34
40
|
ssl_verify_test(:ssl_ca_file, "ca.crt", "selfsigned.crt")
|
35
41
|
end.should raise_error(OpenSSL::SSL::SSLError)
|
36
42
|
end
|
43
|
+
|
37
44
|
it "should fail when using ssl_ca_path and the server uses an unrecognized certificate authority" do
|
38
45
|
lambda do
|
39
46
|
ssl_verify_test(:ssl_ca_path, ".", "selfsigned.crt")
|
@@ -45,6 +52,7 @@ describe HTTParty::Request do
|
|
45
52
|
ssl_verify_test(:ssl_ca_file, "ca.crt", "bogushost.crt")
|
46
53
|
end.should raise_error(OpenSSL::SSL::SSLError)
|
47
54
|
end
|
55
|
+
|
48
56
|
it "should fail when using ssl_ca_path and the server uses a bogus hostname" do
|
49
57
|
lambda do
|
50
58
|
ssl_verify_test(:ssl_ca_path, ".", "bogushost.crt")
|
data/spec/httparty_spec.rb
CHANGED
@@ -10,6 +10,7 @@ describe HTTParty do
|
|
10
10
|
before do
|
11
11
|
Kernel.stub(:warn)
|
12
12
|
end
|
13
|
+
|
13
14
|
it "warns with a deprecation message" do
|
14
15
|
Kernel.should_receive(:warn).with("Deprecated: Use HTTParty::Parser::SupportedFormats")
|
15
16
|
HTTParty::AllowedFormats
|
@@ -21,7 +22,6 @@ describe HTTParty do
|
|
21
22
|
end
|
22
23
|
|
23
24
|
describe "pem" do
|
24
|
-
|
25
25
|
it 'should set the pem content' do
|
26
26
|
@klass.pem 'PEM-CONTENT'
|
27
27
|
@klass.default_options[:pem].should == 'PEM-CONTENT'
|
@@ -36,7 +36,6 @@ describe HTTParty do
|
|
36
36
|
@klass.pem 'PEM-CONTENT', 'PASSWORD'
|
37
37
|
@klass.default_options[:pem_password].should == 'PASSWORD'
|
38
38
|
end
|
39
|
-
|
40
39
|
end
|
41
40
|
|
42
41
|
describe 'http_proxy' do
|
@@ -433,6 +432,12 @@ describe HTTParty do
|
|
433
432
|
end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
|
434
433
|
end
|
435
434
|
|
435
|
+
it "should fail with redirected PATCH" do
|
436
|
+
lambda do
|
437
|
+
@klass.patch('/foo', :no_follow => true)
|
438
|
+
end.should raise_error(HTTParty::RedirectionTooDeep) {|e| e.response.body.should == 'first redirect'}
|
439
|
+
end
|
440
|
+
|
436
441
|
it "should fail with redirected DELETE" do
|
437
442
|
lambda do
|
438
443
|
@klass.delete('/foo', :no_follow => true)
|
@@ -521,6 +526,14 @@ describe HTTParty do
|
|
521
526
|
@parent.default_options.should == {:basic_auth => {:username => 'user', :password => 'password'}}
|
522
527
|
end
|
523
528
|
|
529
|
+
it "doesn't modify hashes in the parent's default options" do
|
530
|
+
@parent.headers 'Accept' => 'application/json'
|
531
|
+
@child1.headers 'Accept' => 'application/xml'
|
532
|
+
|
533
|
+
@parent.default_options[:headers].should == {'Accept' => 'application/json'}
|
534
|
+
@child1.default_options[:headers].should == {'Accept' => 'application/xml'}
|
535
|
+
end
|
536
|
+
|
524
537
|
it "inherits default_cookies from the parent class" do
|
525
538
|
@parent.cookies 'type' => 'chocolate_chip'
|
526
539
|
@child1.default_cookies.should == {"type" => "chocolate_chip"}
|
data/spec/spec.opts
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -13,9 +13,11 @@ Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].e
|
|
13
13
|
Spec::Runner.configure do |config|
|
14
14
|
config.include HTTParty::StubResponse
|
15
15
|
config.include HTTParty::SSLTestHelper
|
16
|
+
|
16
17
|
config.before(:suite) do
|
17
18
|
FakeWeb.allow_net_connect = false
|
18
19
|
end
|
20
|
+
|
19
21
|
config.after(:suite) do
|
20
22
|
FakeWeb.allow_net_connect = true
|
21
23
|
end
|
@@ -1,25 +1,47 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
1
3
|
module HTTParty
|
2
4
|
module SSLTestHelper
|
3
5
|
def ssl_verify_test(mode, ca_basename, server_cert_filename)
|
4
|
-
|
6
|
+
options = {
|
7
|
+
:format => :json,
|
8
|
+
:timeout => 30,
|
9
|
+
}
|
10
|
+
|
11
|
+
if mode
|
12
|
+
ca_path = File.expand_path("../../fixtures/ssl/generated/#{ca_basename}", __FILE__)
|
13
|
+
raise ArgumentError.new("#{ca_path} does not exist") unless File.exist?(ca_path)
|
14
|
+
options[mode] = ca_path
|
15
|
+
end
|
16
|
+
|
5
17
|
begin
|
6
|
-
# Start an HTTPS server
|
7
18
|
test_server = SSLTestServer.new(
|
8
19
|
:rsa_key => File.read(File.expand_path("../../fixtures/ssl/generated/server.key", __FILE__)),
|
9
|
-
:cert
|
20
|
+
:cert => File.read(File.expand_path("../../fixtures/ssl/generated/#{server_cert_filename}", __FILE__)))
|
21
|
+
|
10
22
|
test_server.start
|
11
23
|
|
12
|
-
# Build a request
|
13
24
|
if mode
|
14
25
|
ca_path = File.expand_path("../../fixtures/ssl/generated/#{ca_basename}", __FILE__)
|
15
26
|
raise ArgumentError.new("#{ca_path} does not exist") unless File.exist?(ca_path)
|
16
|
-
return HTTParty.get("https://localhost:#{test_server.port}/", :format => :json, :timeout=>30, mode => ca_path)
|
27
|
+
return HTTParty.get("https://localhost:#{test_server.port}/", :format => :json, :timeout => 30, mode => ca_path)
|
17
28
|
else
|
18
|
-
return HTTParty.get("https://localhost:#{test_server.port}/", :format => :json, :timeout=>30)
|
29
|
+
return HTTParty.get("https://localhost:#{test_server.port}/", :format => :json, :timeout => 30)
|
19
30
|
end
|
20
31
|
ensure
|
21
32
|
test_server.stop if test_server
|
22
33
|
end
|
34
|
+
|
35
|
+
test_server = SSLTestServer.new({
|
36
|
+
:rsa_key => path.join('server.key').read,
|
37
|
+
:cert => path.join(server_cert_filename).read,
|
38
|
+
})
|
39
|
+
|
40
|
+
test_server.start
|
41
|
+
|
42
|
+
HTTParty.get("https://localhost:#{test_server.port}/", options)
|
43
|
+
ensure
|
44
|
+
test_server.stop if test_server
|
23
45
|
end
|
24
46
|
end
|
25
47
|
end
|
@@ -10,26 +10,30 @@ class SSLTestServer
|
|
10
10
|
attr_reader :port
|
11
11
|
|
12
12
|
def initialize(options={})
|
13
|
-
@ctx
|
14
|
-
@ctx.cert
|
15
|
-
@ctx.key
|
13
|
+
@ctx = OpenSSL::SSL::SSLContext.new
|
14
|
+
@ctx.cert = OpenSSL::X509::Certificate.new(options[:cert])
|
15
|
+
@ctx.key = OpenSSL::PKey::RSA.new(options[:rsa_key])
|
16
16
|
@ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE # Don't verify client certificate
|
17
|
-
@port
|
18
|
-
@thread
|
19
|
-
@stopping_mutex
|
20
|
-
@stopping
|
17
|
+
@port = options[:port] || 0
|
18
|
+
@thread = nil
|
19
|
+
@stopping_mutex = Mutex.new
|
20
|
+
@stopping = false
|
21
21
|
end
|
22
22
|
|
23
23
|
def start
|
24
24
|
@raw_server = TCPServer.new(@port)
|
25
|
+
|
25
26
|
if @port == 0
|
26
27
|
@port = Socket::getnameinfo(@raw_server.getsockname, Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV)[1].to_i
|
27
28
|
end
|
29
|
+
|
28
30
|
@ssl_server = OpenSSL::SSL::SSLServer.new(@raw_server, @ctx)
|
31
|
+
|
29
32
|
@stopping_mutex.synchronize{
|
30
33
|
return if @stopping
|
31
34
|
@thread = Thread.new{ thread_main }
|
32
35
|
}
|
36
|
+
|
33
37
|
nil
|
34
38
|
end
|
35
39
|
|
@@ -46,24 +50,31 @@ class SSLTestServer
|
|
46
50
|
def thread_main
|
47
51
|
until @stopping_mutex.synchronize{ @stopping }
|
48
52
|
(rr,ww,ee) = select([@ssl_server.to_io], nil, nil, 0.1)
|
53
|
+
|
49
54
|
next unless rr && rr.include?(@ssl_server.to_io)
|
55
|
+
|
50
56
|
socket = @ssl_server.accept
|
57
|
+
|
51
58
|
Thread.new{
|
52
59
|
header = []
|
60
|
+
|
53
61
|
until (line = socket.readline).rstrip.empty?
|
54
62
|
header << line
|
55
63
|
end
|
56
64
|
|
57
|
-
|
65
|
+
response =<<EOF
|
58
66
|
HTTP/1.1 200 OK
|
59
67
|
Connection: close
|
60
68
|
Content-Type: application/json; charset=UTF-8
|
61
69
|
|
62
70
|
{"success":true}
|
63
71
|
EOF
|
72
|
+
|
73
|
+
socket.write(response.gsub(/\r\n/n, "\n").gsub(/\n/n, "\r\n"))
|
64
74
|
socket.close
|
65
75
|
}
|
66
76
|
end
|
77
|
+
|
67
78
|
@ssl_server.close
|
68
79
|
end
|
69
80
|
end
|
metadata
CHANGED
@@ -1,49 +1,66 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: httparty
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 57
|
5
5
|
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 8
|
9
|
+
- 3
|
10
|
+
version: 0.8.3
|
6
11
|
platform: ruby
|
7
|
-
authors:
|
12
|
+
authors:
|
8
13
|
- John Nunemaker
|
9
14
|
- Sandro Turriate
|
10
15
|
autorequire:
|
11
16
|
bindir: bin
|
12
17
|
cert_chain: []
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
requirement: &70278647636880 !ruby/object:Gem::Requirement
|
18
|
-
none: false
|
19
|
-
requirements:
|
20
|
-
- - ! '>='
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: '0'
|
18
|
+
|
19
|
+
date: 2012-04-22 00:00:00 Z
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
23
22
|
type: :runtime
|
24
23
|
prerelease: false
|
25
|
-
|
26
|
-
- !ruby/object:Gem::Dependency
|
27
|
-
name: multi_xml
|
28
|
-
requirement: &70278647636320 !ruby/object:Gem::Requirement
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
29
25
|
none: false
|
30
|
-
requirements:
|
31
|
-
- -
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 15
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 0
|
33
|
+
version: "1.0"
|
34
|
+
version_requirements: *id001
|
35
|
+
name: multi_json
|
36
|
+
- !ruby/object:Gem::Dependency
|
34
37
|
type: :runtime
|
35
38
|
prerelease: false
|
36
|
-
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 3
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
version: "0"
|
48
|
+
version_requirements: *id002
|
49
|
+
name: multi_xml
|
37
50
|
description: Makes http fun! Also, makes consuming restful web services dead easy.
|
38
|
-
email:
|
51
|
+
email:
|
39
52
|
- nunemaker@gmail.com
|
40
|
-
executables:
|
53
|
+
executables:
|
41
54
|
- httparty
|
42
55
|
extensions: []
|
56
|
+
|
43
57
|
extra_rdoc_files: []
|
44
|
-
|
58
|
+
|
59
|
+
files:
|
45
60
|
- .gitignore
|
61
|
+
- .travis.yml
|
46
62
|
- Gemfile
|
63
|
+
- Guardfile
|
47
64
|
- History
|
48
65
|
- MIT-LICENSE
|
49
66
|
- README.rdoc
|
@@ -86,6 +103,7 @@ files:
|
|
86
103
|
- lib/httparty/parser.rb
|
87
104
|
- lib/httparty/request.rb
|
88
105
|
- lib/httparty/response.rb
|
106
|
+
- lib/httparty/response/headers.rb
|
89
107
|
- lib/httparty/version.rb
|
90
108
|
- spec/fixtures/delicious.xml
|
91
109
|
- spec/fixtures/empty.xml
|
@@ -118,29 +136,38 @@ files:
|
|
118
136
|
- website/index.html
|
119
137
|
homepage: http://httparty.rubyforge.org/
|
120
138
|
licenses: []
|
139
|
+
|
121
140
|
post_install_message: When you HTTParty, you must party hard!
|
122
141
|
rdoc_options: []
|
123
|
-
|
142
|
+
|
143
|
+
require_paths:
|
124
144
|
- lib
|
125
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
145
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
126
146
|
none: false
|
127
|
-
requirements:
|
128
|
-
- -
|
129
|
-
- !ruby/object:Gem::Version
|
130
|
-
|
131
|
-
|
147
|
+
requirements:
|
148
|
+
- - ">="
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
hash: 3
|
151
|
+
segments:
|
152
|
+
- 0
|
153
|
+
version: "0"
|
154
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
155
|
none: false
|
133
|
-
requirements:
|
134
|
-
- -
|
135
|
-
- !ruby/object:Gem::Version
|
136
|
-
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
hash: 3
|
160
|
+
segments:
|
161
|
+
- 0
|
162
|
+
version: "0"
|
137
163
|
requirements: []
|
164
|
+
|
138
165
|
rubyforge_project:
|
139
166
|
rubygems_version: 1.8.10
|
140
167
|
signing_key:
|
141
168
|
specification_version: 3
|
142
169
|
summary: Makes http fun! Also, makes consuming restful web services dead easy.
|
143
|
-
test_files:
|
170
|
+
test_files:
|
144
171
|
- features/basic_authentication.feature
|
145
172
|
- features/command_line.feature
|
146
173
|
- features/deals_with_http_error_codes.feature
|