dnclabs-httparty 0.6.1.2010090201
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/.gitignore +7 -0
- data/History +216 -0
- data/MIT-LICENSE +20 -0
- data/Manifest +47 -0
- data/README.rdoc +54 -0
- data/Rakefile +89 -0
- data/VERSION +1 -0
- data/bin/httparty +108 -0
- data/cucumber.yml +1 -0
- data/examples/aaws.rb +32 -0
- data/examples/basic.rb +11 -0
- data/examples/custom_parsers.rb +67 -0
- data/examples/delicious.rb +37 -0
- data/examples/google.rb +16 -0
- data/examples/rubyurl.rb +14 -0
- data/examples/twitter.rb +31 -0
- data/examples/whoismyrep.rb +10 -0
- data/features/basic_authentication.feature +20 -0
- data/features/command_line.feature +7 -0
- data/features/deals_with_http_error_codes.feature +26 -0
- data/features/digest_authentication.feature +20 -0
- data/features/handles_compressed_responses.feature +19 -0
- data/features/handles_multiple_formats.feature +34 -0
- data/features/steps/env.rb +23 -0
- data/features/steps/httparty_response_steps.rb +26 -0
- data/features/steps/httparty_steps.rb +27 -0
- data/features/steps/mongrel_helper.rb +94 -0
- data/features/steps/remote_service_steps.rb +69 -0
- data/features/supports_redirection.feature +22 -0
- data/features/supports_timeout_option.feature +13 -0
- data/httparty.gemspec +146 -0
- data/lib/httparty.rb +365 -0
- data/lib/httparty/cookie_hash.rb +22 -0
- data/lib/httparty/core_extensions.rb +31 -0
- data/lib/httparty/exceptions.rb +26 -0
- data/lib/httparty/module_inheritable_attributes.rb +34 -0
- data/lib/httparty/net_digest_auth.rb +35 -0
- data/lib/httparty/parser.rb +141 -0
- data/lib/httparty/request.rb +231 -0
- data/lib/httparty/response.rb +79 -0
- data/spec/fixtures/delicious.xml +23 -0
- data/spec/fixtures/empty.xml +0 -0
- data/spec/fixtures/google.html +3 -0
- data/spec/fixtures/ssl/generate.sh +29 -0
- data/spec/fixtures/ssl/generated/1fe462c2.0 +15 -0
- data/spec/fixtures/ssl/generated/bogushost.crt +13 -0
- data/spec/fixtures/ssl/generated/ca.crt +15 -0
- data/spec/fixtures/ssl/generated/ca.key +15 -0
- data/spec/fixtures/ssl/generated/selfsigned.crt +14 -0
- data/spec/fixtures/ssl/generated/server.crt +13 -0
- data/spec/fixtures/ssl/generated/server.key +15 -0
- data/spec/fixtures/ssl/openssl-exts.cnf +9 -0
- data/spec/fixtures/twitter.json +1 -0
- data/spec/fixtures/twitter.xml +403 -0
- data/spec/fixtures/undefined_method_add_node_for_nil.xml +2 -0
- data/spec/httparty/cookie_hash_spec.rb +71 -0
- data/spec/httparty/parser_spec.rb +155 -0
- data/spec/httparty/request_spec.rb +430 -0
- data/spec/httparty/response_spec.rb +188 -0
- data/spec/httparty/ssl_spec.rb +54 -0
- data/spec/httparty_spec.rb +570 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/ssl_test_helper.rb +25 -0
- data/spec/support/ssl_test_server.rb +69 -0
- data/spec/support/stub_response.rb +30 -0
- data/website/css/common.css +47 -0
- data/website/index.html +73 -0
- metadata +245 -0
@@ -0,0 +1,71 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../spec_helper'))
|
2
|
+
|
3
|
+
describe HTTParty::CookieHash do
|
4
|
+
before(:each) do
|
5
|
+
@cookie_hash = HTTParty::CookieHash.new
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "#add_cookies" do
|
9
|
+
|
10
|
+
describe "with a hash" do
|
11
|
+
it "should add new key/value pairs to the hash" do
|
12
|
+
@cookie_hash.add_cookies(:foo => "bar")
|
13
|
+
@cookie_hash.add_cookies(:rofl => "copter")
|
14
|
+
@cookie_hash.length.should eql(2)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should overwrite any existing key" do
|
18
|
+
@cookie_hash.add_cookies(:foo => "bar")
|
19
|
+
@cookie_hash.add_cookies(:foo => "copter")
|
20
|
+
@cookie_hash.length.should eql(1)
|
21
|
+
@cookie_hash[:foo].should eql("copter")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "with a string" do
|
26
|
+
it "should add new key/value pairs to the hash" do
|
27
|
+
@cookie_hash.add_cookies("first=one; second=two; third")
|
28
|
+
@cookie_hash[:first].should == 'one'
|
29
|
+
@cookie_hash[:second].should == 'two'
|
30
|
+
@cookie_hash[:third].should == nil
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should overwrite any existing key" do
|
34
|
+
@cookie_hash[:foo] = 'bar'
|
35
|
+
@cookie_hash.add_cookies("foo=tar")
|
36
|
+
@cookie_hash.length.should eql(1)
|
37
|
+
@cookie_hash[:foo].should eql("tar")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'with other class' do
|
42
|
+
it "should error" do
|
43
|
+
lambda {
|
44
|
+
@cookie_hash.add_cookies(Array.new)
|
45
|
+
}.should raise_error
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# The regexen are required because Hashes aren't ordered, so a test against
|
51
|
+
# a hardcoded string was randomly failing.
|
52
|
+
describe "#to_cookie_string" do
|
53
|
+
before(:each) do
|
54
|
+
@cookie_hash.add_cookies(:foo => "bar")
|
55
|
+
@cookie_hash.add_cookies(:rofl => "copter")
|
56
|
+
@s = @cookie_hash.to_cookie_string
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should format the key/value pairs, delimited by semi-colons" do
|
60
|
+
@s.should match(/foo=bar/)
|
61
|
+
@s.should match(/rofl=copter/)
|
62
|
+
@s.should match(/^\w+=\w+; \w+=\w+$/)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should not include client side only cookies" do
|
66
|
+
@cookie_hash.add_cookies(:path => "/")
|
67
|
+
@s = @cookie_hash.to_cookie_string
|
68
|
+
@s.should_not match(/path=\//)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
describe HTTParty::Parser do
|
4
|
+
describe ".SupportedFormats" do
|
5
|
+
it "returns a hash" do
|
6
|
+
HTTParty::Parser::SupportedFormats.should be_instance_of(Hash)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe ".call" do
|
11
|
+
it "generates an HTTParty::Parser instance with the given body and format" do
|
12
|
+
HTTParty::Parser.should_receive(:new).with('body', :plain).and_return(stub(:parse => nil))
|
13
|
+
HTTParty::Parser.call('body', :plain)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "calls #parse on the parser" do
|
17
|
+
parser = mock('Parser')
|
18
|
+
parser.should_receive(:parse)
|
19
|
+
HTTParty::Parser.stub(:new => parser)
|
20
|
+
parser = HTTParty::Parser.call('body', :plain)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe ".formats" do
|
25
|
+
it "returns the SupportedFormats constant" do
|
26
|
+
HTTParty::Parser.formats.should == HTTParty::Parser::SupportedFormats
|
27
|
+
end
|
28
|
+
|
29
|
+
it "returns the SupportedFormats constant for subclasses" do
|
30
|
+
class MyParser < HTTParty::Parser
|
31
|
+
SupportedFormats = {"application/atom+xml" => :atom}
|
32
|
+
end
|
33
|
+
MyParser.formats.should == {"application/atom+xml" => :atom}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe ".format_from_mimetype" do
|
38
|
+
it "returns a symbol representing the format mimetype" do
|
39
|
+
HTTParty::Parser.format_from_mimetype("text/plain").should == :plain
|
40
|
+
end
|
41
|
+
|
42
|
+
it "returns nil when the mimetype is not supported" do
|
43
|
+
HTTParty::Parser.format_from_mimetype("application/atom+xml").should be_nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe ".supported_formats" do
|
48
|
+
it "returns a unique set of supported formats represented by symbols" do
|
49
|
+
HTTParty::Parser.supported_formats.should == HTTParty::Parser::SupportedFormats.values.uniq
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe ".supports_format?" do
|
54
|
+
it "returns true for a supported format" do
|
55
|
+
HTTParty::Parser.stub(:supported_formats => [:json])
|
56
|
+
HTTParty::Parser.supports_format?(:json).should be_true
|
57
|
+
end
|
58
|
+
|
59
|
+
it "returns false for an unsupported format" do
|
60
|
+
HTTParty::Parser.stub(:supported_formats => [])
|
61
|
+
HTTParty::Parser.supports_format?(:json).should be_false
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#parse" do
|
66
|
+
before do
|
67
|
+
@parser = HTTParty::Parser.new('body', :json)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "attempts to parse supported formats" do
|
71
|
+
@parser.stub(:supports_format? => true)
|
72
|
+
@parser.should_receive(:parse_supported_format)
|
73
|
+
@parser.parse
|
74
|
+
end
|
75
|
+
|
76
|
+
it "returns the unparsed body when the format is unsupported" do
|
77
|
+
@parser.stub(:supports_format? => false)
|
78
|
+
@parser.parse.should == @parser.body
|
79
|
+
end
|
80
|
+
|
81
|
+
it "returns nil for an empty body" do
|
82
|
+
@parser.stub(:body => '')
|
83
|
+
@parser.parse.should be_nil
|
84
|
+
end
|
85
|
+
|
86
|
+
it "returns nil for a nil body" do
|
87
|
+
@parser.stub(:body => nil)
|
88
|
+
@parser.parse.should be_nil
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "#supports_format?" do
|
93
|
+
it "utilizes the class method to determine if the format is supported" do
|
94
|
+
HTTParty::Parser.should_receive(:supports_format?).with(:json)
|
95
|
+
parser = HTTParty::Parser.new('body', :json)
|
96
|
+
parser.send(:supports_format?)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "#parse_supported_format" do
|
101
|
+
it "calls the parser for the given format" do
|
102
|
+
parser = HTTParty::Parser.new('body', :json)
|
103
|
+
parser.should_receive(:json)
|
104
|
+
parser.send(:parse_supported_format)
|
105
|
+
end
|
106
|
+
|
107
|
+
context "when a parsing method does not exist for the given format" do
|
108
|
+
it "raises an exception" do
|
109
|
+
parser = HTTParty::Parser.new('body', :atom)
|
110
|
+
expect do
|
111
|
+
parser.send(:parse_supported_format)
|
112
|
+
end.to raise_error(NotImplementedError, "HTTParty::Parser has not implemented a parsing method for the :atom format.")
|
113
|
+
end
|
114
|
+
|
115
|
+
it "raises a useful exception message for subclasses" do
|
116
|
+
atom_parser = Class.new(HTTParty::Parser) do
|
117
|
+
def self.name; 'AtomParser'; end
|
118
|
+
end
|
119
|
+
parser = atom_parser.new 'body', :atom
|
120
|
+
expect do
|
121
|
+
parser.send(:parse_supported_format)
|
122
|
+
end.to raise_error(NotImplementedError, "AtomParser has not implemented a parsing method for the :atom format.")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context "parsers" do
|
128
|
+
subject do
|
129
|
+
HTTParty::Parser.new('body', nil)
|
130
|
+
end
|
131
|
+
|
132
|
+
it "parses xml with Crack" do
|
133
|
+
Crack::XML.should_receive(:parse).with('body')
|
134
|
+
subject.send(:xml)
|
135
|
+
end
|
136
|
+
|
137
|
+
it "parses json with Crack" do
|
138
|
+
Crack::JSON.should_receive(:parse).with('body')
|
139
|
+
subject.send(:json)
|
140
|
+
end
|
141
|
+
|
142
|
+
it "parses yaml" do
|
143
|
+
YAML.should_receive(:load).with('body')
|
144
|
+
subject.send(:yaml)
|
145
|
+
end
|
146
|
+
|
147
|
+
it "parses html by simply returning the body" do
|
148
|
+
subject.send(:html).should == 'body'
|
149
|
+
end
|
150
|
+
|
151
|
+
it "parses plain text by simply returning the body" do
|
152
|
+
subject.send(:plain).should == 'body'
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,430 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
describe HTTParty::Request do
|
4
|
+
before do
|
5
|
+
@request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', :format => :xml)
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "initialization" do
|
9
|
+
it "sets parser to HTTParty::Parser" do
|
10
|
+
request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com')
|
11
|
+
request.parser.should == HTTParty::Parser
|
12
|
+
end
|
13
|
+
|
14
|
+
it "sets parser to the optional parser" do
|
15
|
+
my_parser = lambda {}
|
16
|
+
request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com', :parser => my_parser)
|
17
|
+
request.parser.should == my_parser
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#format" do
|
22
|
+
context "request yet to be made" do
|
23
|
+
it "returns format option" do
|
24
|
+
request = HTTParty::Request.new 'get', '/', :format => :xml
|
25
|
+
request.format.should == :xml
|
26
|
+
end
|
27
|
+
|
28
|
+
it "returns nil format" do
|
29
|
+
request = HTTParty::Request.new 'get', '/'
|
30
|
+
request.format.should be_nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "request has been made" do
|
35
|
+
it "returns format option" do
|
36
|
+
request = HTTParty::Request.new 'get', '/', :format => :xml
|
37
|
+
request.last_response = stub
|
38
|
+
request.format.should == :xml
|
39
|
+
end
|
40
|
+
|
41
|
+
it "returns the content-type from the last response when the option is not set" do
|
42
|
+
request = HTTParty::Request.new 'get', '/'
|
43
|
+
response = stub
|
44
|
+
response.should_receive(:[]).with('content-type').and_return('text/json')
|
45
|
+
request.last_response = response
|
46
|
+
request.format.should == :json
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
context "options" do
|
53
|
+
it "should use basic auth when configured" do
|
54
|
+
@request.options[:basic_auth] = {:username => 'foobar', :password => 'secret'}
|
55
|
+
@request.send(:setup_raw_request)
|
56
|
+
@request.instance_variable_get(:@raw_request)['authorization'].should_not be_nil
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should use digest auth when configured" do
|
60
|
+
FakeWeb.register_uri(:head, "http://api.foo.com/v1",
|
61
|
+
:www_authenticate => 'Digest realm="Log Viewer", qop="auth", nonce="2CA0EC6B0E126C4800E56BA0C0003D3C", opaque="5ccc069c403ebaf9f0171e9517f40e41", stale=false')
|
62
|
+
|
63
|
+
@request.options[:digest_auth] = {:username => 'foobar', :password => 'secret'}
|
64
|
+
@request.send(:setup_raw_request)
|
65
|
+
|
66
|
+
raw_request = @request.instance_variable_get(:@raw_request)
|
67
|
+
raw_request.instance_variable_get(:@header)['Authorization'].should_not be_nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#uri" do
|
72
|
+
context "query strings" do
|
73
|
+
it "does not add an empty query string when default_params are blank" do
|
74
|
+
@request.options[:default_params] = {}
|
75
|
+
@request.uri.query.should be_nil
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe 'http' do
|
81
|
+
it "should use ssl for port 443" do
|
82
|
+
request = HTTParty::Request.new(Net::HTTP::Get, 'https://api.foo.com/v1:443')
|
83
|
+
request.send(:http).use_ssl?.should == true
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'should not use ssl for port 80' do
|
87
|
+
request = HTTParty::Request.new(Net::HTTP::Get, 'http://foobar.com')
|
88
|
+
request.send(:http).use_ssl?.should == false
|
89
|
+
end
|
90
|
+
|
91
|
+
it "uses ssl for https scheme with default port" do
|
92
|
+
request = HTTParty::Request.new(Net::HTTP::Get, 'https://foobar.com')
|
93
|
+
request.send(:http).use_ssl?.should == true
|
94
|
+
end
|
95
|
+
|
96
|
+
it "uses ssl for https scheme regardless of port" do
|
97
|
+
request = HTTParty::Request.new(Net::HTTP::Get, 'https://foobar.com:123456')
|
98
|
+
request.send(:http).use_ssl?.should == true
|
99
|
+
end
|
100
|
+
|
101
|
+
context "PEM certificates" do
|
102
|
+
before do
|
103
|
+
OpenSSL::X509::Certificate.stub(:new)
|
104
|
+
OpenSSL::PKey::RSA.stub(:new)
|
105
|
+
end
|
106
|
+
|
107
|
+
context "when scheme is https" do
|
108
|
+
before do
|
109
|
+
@request.stub!(:uri).and_return(URI.parse("https://google.com"))
|
110
|
+
pem = :pem_contents
|
111
|
+
@cert = mock("OpenSSL::X509::Certificate")
|
112
|
+
@key = mock("OpenSSL::PKey::RSA")
|
113
|
+
OpenSSL::X509::Certificate.should_receive(:new).with(pem).and_return(@cert)
|
114
|
+
OpenSSL::PKey::RSA.should_receive(:new).with(pem).and_return(@key)
|
115
|
+
|
116
|
+
@request.options[:pem] = pem
|
117
|
+
@pem_http = @request.send(:http)
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should use a PEM certificate when provided" do
|
121
|
+
@pem_http.cert.should == @cert
|
122
|
+
@pem_http.key.should == @key
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should verify the certificate when provided" do
|
126
|
+
@pem_http = @request.send(:http)
|
127
|
+
@pem_http.verify_mode.should == OpenSSL::SSL::VERIFY_PEER
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context "when scheme is not https" do
|
132
|
+
it "does not assign a PEM" do
|
133
|
+
http = Net::HTTP.new('google.com')
|
134
|
+
http.should_not_receive(:cert=)
|
135
|
+
http.should_not_receive(:key=)
|
136
|
+
Net::HTTP.stub(:new => http)
|
137
|
+
|
138
|
+
request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com')
|
139
|
+
request.options[:pem] = :pem_contents
|
140
|
+
request.send(:http)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context "debugging" do
|
145
|
+
before do
|
146
|
+
@http = Net::HTTP.new('google.com')
|
147
|
+
Net::HTTP.stub(:new => @http)
|
148
|
+
@request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com')
|
149
|
+
end
|
150
|
+
|
151
|
+
it "calls #set_debug_output when the option is provided" do
|
152
|
+
@request.options[:debug_output] = $stderr
|
153
|
+
@http.should_receive(:set_debug_output).with($stderr)
|
154
|
+
@request.send(:http)
|
155
|
+
end
|
156
|
+
|
157
|
+
it "does not set_debug_output when the option is not provided" do
|
158
|
+
@http.should_not_receive(:set_debug_output)
|
159
|
+
@request.send(:http)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
context "when setting timeout" do
|
165
|
+
it "does nothing if the timeout option is a string" do
|
166
|
+
http = mock("http", :null_object => true)
|
167
|
+
http.should_not_receive(:open_timeout=)
|
168
|
+
http.should_not_receive(:read_timeout=)
|
169
|
+
Net::HTTP.stub(:new => http)
|
170
|
+
|
171
|
+
request = HTTParty::Request.new(Net::HTTP::Get, 'https://foobar.com', {:timeout => "five seconds"})
|
172
|
+
request.send(:http)
|
173
|
+
end
|
174
|
+
|
175
|
+
it "sets the timeout to 5 seconds" do
|
176
|
+
@request.options[:timeout] = 5
|
177
|
+
@request.send(:http).open_timeout.should == 5
|
178
|
+
@request.send(:http).read_timeout.should == 5
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe '#format_from_mimetype' do
|
184
|
+
it 'should handle text/xml' do
|
185
|
+
["text/xml", "text/xml; charset=iso8859-1"].each do |ct|
|
186
|
+
@request.send(:format_from_mimetype, ct).should == :xml
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'should handle application/xml' do
|
191
|
+
["application/xml", "application/xml; charset=iso8859-1"].each do |ct|
|
192
|
+
@request.send(:format_from_mimetype, ct).should == :xml
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'should handle text/json' do
|
197
|
+
["text/json", "text/json; charset=iso8859-1"].each do |ct|
|
198
|
+
@request.send(:format_from_mimetype, ct).should == :json
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'should handle application/json' do
|
203
|
+
["application/json", "application/json; charset=iso8859-1"].each do |ct|
|
204
|
+
@request.send(:format_from_mimetype, ct).should == :json
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
it 'should handle text/javascript' do
|
209
|
+
["text/javascript", "text/javascript; charset=iso8859-1"].each do |ct|
|
210
|
+
@request.send(:format_from_mimetype, ct).should == :json
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'should handle application/javascript' do
|
215
|
+
["application/javascript", "application/javascript; charset=iso8859-1"].each do |ct|
|
216
|
+
@request.send(:format_from_mimetype, ct).should == :json
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
it "returns nil for an unrecognized mimetype" do
|
221
|
+
@request.send(:format_from_mimetype, "application/atom+xml").should be_nil
|
222
|
+
end
|
223
|
+
|
224
|
+
it "returns nil when using a default parser" do
|
225
|
+
@request.options[:parser] = lambda {}
|
226
|
+
@request.send(:format_from_mimetype, "text/json").should be_nil
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
describe 'parsing responses' do
|
231
|
+
it 'should handle xml automatically' do
|
232
|
+
xml = %q[<books><book><id>1234</id><name>Foo Bar!</name></book></books>]
|
233
|
+
@request.options[:format] = :xml
|
234
|
+
@request.send(:parse_response, xml).should == {'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'should handle json automatically' do
|
238
|
+
json = %q[{"books": {"book": {"name": "Foo Bar!", "id": "1234"}}}]
|
239
|
+
@request.options[:format] = :json
|
240
|
+
@request.send(:parse_response, json).should == {'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'should handle yaml automatically' do
|
244
|
+
yaml = "books: \n book: \n name: Foo Bar!\n id: \"1234\"\n"
|
245
|
+
@request.options[:format] = :yaml
|
246
|
+
@request.send(:parse_response, yaml).should == {'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}
|
247
|
+
end
|
248
|
+
|
249
|
+
it "should include any HTTP headers in the returned response" do
|
250
|
+
@request.options[:format] = :html
|
251
|
+
response = stub_response "Content"
|
252
|
+
response.initialize_http_header("key" => "value")
|
253
|
+
|
254
|
+
@request.perform.headers.should == { "key" => ["value"] }
|
255
|
+
end
|
256
|
+
|
257
|
+
describe 'with non-200 responses' do
|
258
|
+
context "3xx responses" do
|
259
|
+
it 'returns a valid object for 304 not modified' do
|
260
|
+
stub_response '', 304
|
261
|
+
resp = @request.perform
|
262
|
+
resp.code.should == 304
|
263
|
+
resp.body.should == ''
|
264
|
+
resp.should be_nil
|
265
|
+
end
|
266
|
+
|
267
|
+
it "redirects if a 300 contains a location header" do
|
268
|
+
redirect = stub_response '', 300
|
269
|
+
redirect['location'] = 'http://foo.com/foo'
|
270
|
+
ok = stub_response('<hash><foo>bar</foo></hash>', 200)
|
271
|
+
@http.stub!(:request).and_return(redirect, ok)
|
272
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
273
|
+
end
|
274
|
+
|
275
|
+
it "returns the Net::HTTP response if the 300 does not contain a location header" do
|
276
|
+
net_response = stub_response '', 300
|
277
|
+
@request.perform.should be_kind_of(Net::HTTPMultipleChoice)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
it 'should return a valid object for 4xx response' do
|
282
|
+
stub_response '<foo><bar>yes</bar></foo>', 401
|
283
|
+
resp = @request.perform
|
284
|
+
resp.code.should == 401
|
285
|
+
resp.body.should == "<foo><bar>yes</bar></foo>"
|
286
|
+
resp['foo']['bar'].should == "yes"
|
287
|
+
end
|
288
|
+
|
289
|
+
it 'should return a valid object for 5xx response' do
|
290
|
+
stub_response '<foo><bar>error</bar></foo>', 500
|
291
|
+
resp = @request.perform
|
292
|
+
resp.code.should == 500
|
293
|
+
resp.body.should == "<foo><bar>error</bar></foo>"
|
294
|
+
resp['foo']['bar'].should == "error"
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
it "should not attempt to parse empty responses" do
|
300
|
+
[204, 304].each do |code|
|
301
|
+
stub_response "", code
|
302
|
+
|
303
|
+
@request.options[:format] = :xml
|
304
|
+
@request.perform.should be_nil
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
it "should not fail for missing mime type" do
|
309
|
+
stub_response "Content for you"
|
310
|
+
@request.options[:format] = :html
|
311
|
+
@request.perform.should == 'Content for you'
|
312
|
+
end
|
313
|
+
|
314
|
+
describe "a request that redirects" do
|
315
|
+
before(:each) do
|
316
|
+
@redirect = stub_response("", 302)
|
317
|
+
@redirect['location'] = '/foo'
|
318
|
+
|
319
|
+
@ok = stub_response('<hash><foo>bar</foo></hash>', 200)
|
320
|
+
end
|
321
|
+
|
322
|
+
describe "once" do
|
323
|
+
before(:each) do
|
324
|
+
@http.stub!(:request).and_return(@redirect, @ok)
|
325
|
+
end
|
326
|
+
|
327
|
+
it "should be handled by GET transparently" do
|
328
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
329
|
+
end
|
330
|
+
|
331
|
+
it "should be handled by POST transparently" do
|
332
|
+
@request.http_method = Net::HTTP::Post
|
333
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
334
|
+
end
|
335
|
+
|
336
|
+
it "should be handled by DELETE transparently" do
|
337
|
+
@request.http_method = Net::HTTP::Delete
|
338
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
339
|
+
end
|
340
|
+
|
341
|
+
it "should be handled by PUT transparently" do
|
342
|
+
@request.http_method = Net::HTTP::Put
|
343
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
344
|
+
end
|
345
|
+
|
346
|
+
it "should be handled by HEAD transparently" do
|
347
|
+
@request.http_method = Net::HTTP::Head
|
348
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
349
|
+
end
|
350
|
+
|
351
|
+
it "should be handled by OPTIONS transparently" do
|
352
|
+
@request.http_method = Net::HTTP::Options
|
353
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
354
|
+
end
|
355
|
+
|
356
|
+
it "should keep track of cookies between redirects" do
|
357
|
+
@redirect['Set-Cookie'] = 'foo=bar; name=value; HTTPOnly'
|
358
|
+
@request.perform
|
359
|
+
@request.options[:headers]['Cookie'].should match(/foo=bar/)
|
360
|
+
@request.options[:headers]['Cookie'].should match(/name=value/)
|
361
|
+
end
|
362
|
+
|
363
|
+
it 'should update cookies with rediects' do
|
364
|
+
@request.options[:headers] = {'Cookie'=> 'foo=bar;'}
|
365
|
+
@redirect['Set-Cookie'] = 'foo=tar;'
|
366
|
+
@request.perform
|
367
|
+
@request.options[:headers]['Cookie'].should match(/foo=tar/)
|
368
|
+
end
|
369
|
+
|
370
|
+
it 'should keep cookies between rediects' do
|
371
|
+
@request.options[:headers] = {'Cookie'=> 'keep=me'}
|
372
|
+
@redirect['Set-Cookie'] = 'foo=tar;'
|
373
|
+
@request.perform
|
374
|
+
@request.options[:headers]['Cookie'].should match(/keep=me/)
|
375
|
+
end
|
376
|
+
|
377
|
+
it 'should make resulting request a get request if it not already' do
|
378
|
+
@request.http_method = Net::HTTP::Delete
|
379
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
380
|
+
@request.http_method.should == Net::HTTP::Get
|
381
|
+
end
|
382
|
+
|
383
|
+
it 'should not make resulting request a get request if options[:maintain_method_across_redirects] is true' do
|
384
|
+
@request.options[:maintain_method_across_redirects] = true
|
385
|
+
@request.http_method = Net::HTTP::Delete
|
386
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
387
|
+
@request.http_method.should == Net::HTTP::Delete
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
describe "infinitely" do
|
392
|
+
before(:each) do
|
393
|
+
@http.stub!(:request).and_return(@redirect)
|
394
|
+
end
|
395
|
+
|
396
|
+
it "should raise an exception" do
|
397
|
+
lambda { @request.perform }.should raise_error(HTTParty::RedirectionTooDeep)
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
context "with POST http method" do
|
403
|
+
it "should raise argument error if query is not a hash" do
|
404
|
+
lambda {
|
405
|
+
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', :format => :xml, :query => 'astring').perform
|
406
|
+
}.should raise_error(ArgumentError)
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
describe "argument validation" do
|
411
|
+
it "should raise argument error if basic_auth and digest_auth are both present" do
|
412
|
+
lambda {
|
413
|
+
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', :basic_auth => {}, :digest_auth => {}).perform
|
414
|
+
}.should raise_error(ArgumentError, "only one authentication method, :basic_auth or :digest_auth may be used at a time")
|
415
|
+
end
|
416
|
+
|
417
|
+
it "should raise argument error if basic_auth is not a hash" do
|
418
|
+
lambda {
|
419
|
+
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', :basic_auth => ["foo", "bar"]).perform
|
420
|
+
}.should raise_error(ArgumentError, ":basic_auth must be a hash")
|
421
|
+
end
|
422
|
+
|
423
|
+
it "should raise argument error if digest_auth is not a hash" do
|
424
|
+
lambda {
|
425
|
+
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', :digest_auth => ["foo", "bar"]).perform
|
426
|
+
}.should raise_error(ArgumentError, ":digest_auth must be a hash")
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|