httpserious 0.13.5.lstoll1
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.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rubocop.yml +92 -0
- data/.rubocop_todo.yml +124 -0
- data/.simplecov +1 -0
- data/.travis.yml +7 -0
- data/CONTRIBUTING.md +23 -0
- data/Gemfile +19 -0
- data/Guardfile +16 -0
- data/History +370 -0
- data/MIT-LICENSE +20 -0
- data/README.md +78 -0
- data/Rakefile +10 -0
- data/bin/httparty +116 -0
- data/cucumber.yml +1 -0
- data/examples/README.md +67 -0
- data/examples/aaws.rb +32 -0
- data/examples/basic.rb +28 -0
- data/examples/crack.rb +19 -0
- data/examples/custom_parsers.rb +64 -0
- data/examples/delicious.rb +37 -0
- data/examples/google.rb +16 -0
- data/examples/headers_and_user_agents.rb +6 -0
- data/examples/logging.rb +36 -0
- data/examples/nokogiri_html_parser.rb +19 -0
- data/examples/rescue_json.rb +17 -0
- data/examples/rubyurl.rb +14 -0
- data/examples/stackexchange.rb +24 -0
- data/examples/tripit_sign_in.rb +33 -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 +90 -0
- data/features/deals_with_http_error_codes.feature +26 -0
- data/features/digest_authentication.feature +20 -0
- data/features/handles_compressed_responses.feature +27 -0
- data/features/handles_multiple_formats.feature +57 -0
- data/features/steps/env.rb +27 -0
- data/features/steps/httparty_response_steps.rb +52 -0
- data/features/steps/httparty_steps.rb +43 -0
- data/features/steps/mongrel_helper.rb +94 -0
- data/features/steps/remote_service_steps.rb +86 -0
- data/features/supports_read_timeout_option.feature +13 -0
- data/features/supports_redirection.feature +22 -0
- data/features/supports_timeout_option.feature +13 -0
- data/httparty.gemspec +28 -0
- data/httpserious.gemspec +25 -0
- data/lib/httparty.rb +612 -0
- data/lib/httparty/connection_adapter.rb +190 -0
- data/lib/httparty/cookie_hash.rb +21 -0
- data/lib/httparty/exceptions.rb +29 -0
- data/lib/httparty/hash_conversions.rb +49 -0
- data/lib/httparty/logger/apache_formatter.rb +22 -0
- data/lib/httparty/logger/curl_formatter.rb +48 -0
- data/lib/httparty/logger/logger.rb +26 -0
- data/lib/httparty/module_inheritable_attributes.rb +56 -0
- data/lib/httparty/net_digest_auth.rb +117 -0
- data/lib/httparty/parser.rb +141 -0
- data/lib/httparty/request.rb +361 -0
- data/lib/httparty/response.rb +77 -0
- data/lib/httparty/response/headers.rb +31 -0
- data/lib/httparty/version.rb +3 -0
- data/lib/httpserious.rb +1 -0
- data/script/release +42 -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 +16 -0
- data/spec/fixtures/ssl/generated/bogushost.crt +13 -0
- data/spec/fixtures/ssl/generated/ca.crt +16 -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.csv +2 -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/connection_adapter_spec.rb +468 -0
- data/spec/httparty/cookie_hash_spec.rb +83 -0
- data/spec/httparty/exception_spec.rb +38 -0
- data/spec/httparty/hash_conversions_spec.rb +41 -0
- data/spec/httparty/logger/apache_formatter_spec.rb +41 -0
- data/spec/httparty/logger/curl_formatter_spec.rb +18 -0
- data/spec/httparty/logger/logger_spec.rb +38 -0
- data/spec/httparty/net_digest_auth_spec.rb +191 -0
- data/spec/httparty/parser_spec.rb +167 -0
- data/spec/httparty/request_spec.rb +872 -0
- data/spec/httparty/response_spec.rb +241 -0
- data/spec/httparty/ssl_spec.rb +74 -0
- data/spec/httparty_spec.rb +823 -0
- data/spec/spec_helper.rb +59 -0
- data/spec/support/ssl_test_helper.rb +47 -0
- data/spec/support/ssl_test_server.rb +80 -0
- data/spec/support/stub_response.rb +43 -0
- data/website/css/common.css +47 -0
- data/website/index.html +73 -0
- metadata +219 -0
@@ -0,0 +1,167 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
RSpec.describe HTTParty::Parser do
|
4
|
+
describe ".SupportedFormats" do
|
5
|
+
it "returns a hash" do
|
6
|
+
expect(HTTParty::Parser::SupportedFormats).to 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
|
+
expect(HTTParty::Parser).to receive(:new).with('body', :plain).and_return(double(parse: nil))
|
13
|
+
HTTParty::Parser.call('body', :plain)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "calls #parse on the parser" do
|
17
|
+
parser = double('Parser')
|
18
|
+
expect(parser).to receive(:parse)
|
19
|
+
allow(HTTParty::Parser).to receive_messages(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
|
+
expect(HTTParty::Parser.formats).to eq(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
|
+
expect(MyParser.formats).to eq({"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
|
+
expect(HTTParty::Parser.format_from_mimetype("text/plain")).to eq(:plain)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "returns nil when the mimetype is not supported" do
|
43
|
+
expect(HTTParty::Parser.format_from_mimetype("application/atom+xml")).to 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
|
+
expect(HTTParty::Parser.supported_formats).to eq(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
|
+
allow(HTTParty::Parser).to receive_messages(supported_formats: [:json])
|
56
|
+
expect(HTTParty::Parser.supports_format?(:json)).to be_truthy
|
57
|
+
end
|
58
|
+
|
59
|
+
it "returns false for an unsupported format" do
|
60
|
+
allow(HTTParty::Parser).to receive_messages(supported_formats: [])
|
61
|
+
expect(HTTParty::Parser.supports_format?(:json)).to be_falsey
|
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
|
+
allow(@parser).to receive_messages(supports_format?: true)
|
72
|
+
expect(@parser).to receive(:parse_supported_format)
|
73
|
+
@parser.parse
|
74
|
+
end
|
75
|
+
|
76
|
+
it "returns the unparsed body when the format is unsupported" do
|
77
|
+
allow(@parser).to receive_messages(supports_format?: false)
|
78
|
+
expect(@parser.parse).to eq(@parser.body)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "returns nil for an empty body" do
|
82
|
+
allow(@parser).to receive_messages(body: '')
|
83
|
+
expect(@parser.parse).to be_nil
|
84
|
+
end
|
85
|
+
|
86
|
+
it "returns nil for a nil body" do
|
87
|
+
allow(@parser).to receive_messages(body: nil)
|
88
|
+
expect(@parser.parse).to be_nil
|
89
|
+
end
|
90
|
+
|
91
|
+
it "returns nil for a 'null' body" do
|
92
|
+
allow(@parser).to receive_messages(body: "null")
|
93
|
+
expect(@parser.parse).to be_nil
|
94
|
+
end
|
95
|
+
|
96
|
+
it "returns nil for a body with spaces only" do
|
97
|
+
allow(@parser).to receive_messages(body: " ")
|
98
|
+
expect(@parser.parse).to be_nil
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "#supports_format?" do
|
103
|
+
it "utilizes the class method to determine if the format is supported" do
|
104
|
+
expect(HTTParty::Parser).to receive(:supports_format?).with(:json)
|
105
|
+
parser = HTTParty::Parser.new('body', :json)
|
106
|
+
parser.send(:supports_format?)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "#parse_supported_format" do
|
111
|
+
it "calls the parser for the given format" do
|
112
|
+
parser = HTTParty::Parser.new('body', :json)
|
113
|
+
expect(parser).to receive(:json)
|
114
|
+
parser.send(:parse_supported_format)
|
115
|
+
end
|
116
|
+
|
117
|
+
context "when a parsing method does not exist for the given format" do
|
118
|
+
it "raises an exception" do
|
119
|
+
parser = HTTParty::Parser.new('body', :atom)
|
120
|
+
expect do
|
121
|
+
parser.send(:parse_supported_format)
|
122
|
+
end.to raise_error(NotImplementedError, "HTTParty::Parser has not implemented a parsing method for the :atom format.")
|
123
|
+
end
|
124
|
+
|
125
|
+
it "raises a useful exception message for subclasses" do
|
126
|
+
atom_parser = Class.new(HTTParty::Parser) do
|
127
|
+
def self.name
|
128
|
+
'AtomParser'
|
129
|
+
end
|
130
|
+
end
|
131
|
+
parser = atom_parser.new 'body', :atom
|
132
|
+
expect do
|
133
|
+
parser.send(:parse_supported_format)
|
134
|
+
end.to raise_error(NotImplementedError, "AtomParser has not implemented a parsing method for the :atom format.")
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context "parsers" do
|
140
|
+
subject do
|
141
|
+
HTTParty::Parser.new('body', nil)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "parses xml with MultiXml" do
|
145
|
+
expect(MultiXml).to receive(:parse).with('body')
|
146
|
+
subject.send(:xml)
|
147
|
+
end
|
148
|
+
|
149
|
+
it "parses json with JSON" do
|
150
|
+
expect(JSON).to receive(:parse).with('body', :quirks_mode => true, :allow_nan => true)
|
151
|
+
subject.send(:json)
|
152
|
+
end
|
153
|
+
|
154
|
+
it "parses html by simply returning the body" do
|
155
|
+
expect(subject.send(:html)).to eq('body')
|
156
|
+
end
|
157
|
+
|
158
|
+
it "parses plain text by simply returning the body" do
|
159
|
+
expect(subject.send(:plain)).to eq('body')
|
160
|
+
end
|
161
|
+
|
162
|
+
it "parses csv with CSV" do
|
163
|
+
expect(CSV).to receive(:parse).with('body')
|
164
|
+
subject.send(:csv)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,872 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
RSpec.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 "::NON_RAILS_QUERY_STRING_NORMALIZER" do
|
9
|
+
let(:normalizer) { HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER }
|
10
|
+
|
11
|
+
it "doesn't modify strings" do
|
12
|
+
query_string = normalizer["foo=bar&foo=baz"]
|
13
|
+
expect(CGI.unescape(query_string)).to eq("foo=bar&foo=baz")
|
14
|
+
end
|
15
|
+
|
16
|
+
context "when the query is an array" do
|
17
|
+
it "doesn't include brackets" do
|
18
|
+
query_string = normalizer[{page: 1, foo: %w(bar baz)}]
|
19
|
+
expect(CGI.unescape(query_string)).to eq("foo=bar&foo=baz&page=1")
|
20
|
+
end
|
21
|
+
|
22
|
+
it "URI encodes array values" do
|
23
|
+
query_string = normalizer[{people: ["Otis Redding", "Bob Marley", "Tim & Jon"], page: 1, xyzzy: 3}]
|
24
|
+
expect(query_string).to eq("page=1&people=Otis%20Redding&people=Bob%20Marley&people=Tim%20%26%20Jon&xyzzy=3")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "when the query is a hash" do
|
29
|
+
it "correctly handles nil values" do
|
30
|
+
query_string = normalizer[{page: 1, per_page: nil}]
|
31
|
+
expect(query_string).to eq("page=1&per_page")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "initialization" do
|
37
|
+
it "sets parser to HTTParty::Parser" do
|
38
|
+
request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com')
|
39
|
+
expect(request.parser).to eq(HTTParty::Parser)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "sets parser to the optional parser" do
|
43
|
+
my_parser = lambda {}
|
44
|
+
request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com', parser: my_parser)
|
45
|
+
expect(request.parser).to eq(my_parser)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "sets connection_adapter to HTTPParty::ConnectionAdapter" do
|
49
|
+
request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com')
|
50
|
+
expect(request.connection_adapter).to eq(HTTParty::ConnectionAdapter)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "sets connection_adapter to the optional connection_adapter" do
|
54
|
+
my_adapter = lambda {}
|
55
|
+
request = HTTParty::Request.new(Net::HTTP::Get, 'http://google.com', connection_adapter: my_adapter)
|
56
|
+
expect(request.connection_adapter).to eq(my_adapter)
|
57
|
+
end
|
58
|
+
|
59
|
+
context "when basic authentication credentials provided in uri" do
|
60
|
+
context "when basic auth options wasn't set explicitly" do
|
61
|
+
it "sets basic auth from uri" do
|
62
|
+
request = HTTParty::Request.new(Net::HTTP::Get, 'http://user1:pass1@example.com')
|
63
|
+
expect(request.options[:basic_auth]).to eq({username: 'user1', password: 'pass1'})
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "when basic auth options was set explicitly" do
|
68
|
+
it "uses basic auth from url anyway" do
|
69
|
+
basic_auth = {username: 'user2', password: 'pass2'}
|
70
|
+
request = HTTParty::Request.new(Net::HTTP::Get, 'http://user1:pass1@example.com', basic_auth: basic_auth)
|
71
|
+
expect(request.options[:basic_auth]).to eq({username: 'user1', password: 'pass1'})
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#format" do
|
78
|
+
context "request yet to be made" do
|
79
|
+
it "returns format option" do
|
80
|
+
request = HTTParty::Request.new 'get', '/', format: :xml
|
81
|
+
expect(request.format).to eq(:xml)
|
82
|
+
end
|
83
|
+
|
84
|
+
it "returns nil format" do
|
85
|
+
request = HTTParty::Request.new 'get', '/'
|
86
|
+
expect(request.format).to be_nil
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "request has been made" do
|
91
|
+
it "returns format option" do
|
92
|
+
request = HTTParty::Request.new 'get', '/', format: :xml
|
93
|
+
request.last_response = double
|
94
|
+
expect(request.format).to eq(:xml)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "returns the content-type from the last response when the option is not set" do
|
98
|
+
request = HTTParty::Request.new 'get', '/'
|
99
|
+
response = double
|
100
|
+
expect(response).to receive(:[]).with('content-type').and_return('text/json')
|
101
|
+
request.last_response = response
|
102
|
+
expect(request.format).to eq(:json)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "options" do
|
108
|
+
it "should use basic auth when configured" do
|
109
|
+
@request.options[:basic_auth] = {username: 'foobar', password: 'secret'}
|
110
|
+
@request.send(:setup_raw_request)
|
111
|
+
expect(@request.instance_variable_get(:@raw_request)['authorization']).not_to be_nil
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should use digest auth when configured" do
|
115
|
+
FakeWeb.register_uri(:get, "http://api.foo.com/v1",
|
116
|
+
www_authenticate: 'Digest realm="Log Viewer", qop="auth", nonce="2CA0EC6B0E126C4800E56BA0C0003D3C", opaque="5ccc069c403ebaf9f0171e9517f40e41", stale=false')
|
117
|
+
|
118
|
+
@request.options[:digest_auth] = {username: 'foobar', password: 'secret'}
|
119
|
+
@request.send(:setup_raw_request)
|
120
|
+
|
121
|
+
raw_request = @request.instance_variable_get(:@raw_request)
|
122
|
+
expect(raw_request.instance_variable_get(:@header)['Authorization']).not_to be_nil
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should use the right http method for digest authentication" do
|
126
|
+
@post_request = HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', format: :xml)
|
127
|
+
FakeWeb.register_uri(:post, "http://api.foo.com/v1", {})
|
128
|
+
|
129
|
+
http = @post_request.send(:http)
|
130
|
+
expect(@post_request).to receive(:http).and_return(http)
|
131
|
+
expect(http).not_to receive(:head).with({'www-authenticate' => nil})
|
132
|
+
@post_request.options[:digest_auth] = {username: 'foobar', password: 'secret'}
|
133
|
+
@post_request.send(:setup_raw_request)
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'should maintain cookies returned from setup_digest_auth' do
|
137
|
+
FakeWeb.register_uri(
|
138
|
+
:get, "http://api.foo.com/v1",
|
139
|
+
set_cookie: 'custom-cookie=1234567',
|
140
|
+
www_authenticate: 'Digest realm="Log Viewer", qop="auth", nonce="2CA0EC6B0E126C4800E56BA0C0003D3C", opaque="5ccc069c403ebaf9f0171e9517f40e41", stale=false'
|
141
|
+
)
|
142
|
+
|
143
|
+
@request.options[:digest_auth] = {username: 'foobar', password: 'secret'}
|
144
|
+
@request.send(:setup_raw_request)
|
145
|
+
|
146
|
+
raw_request = @request.instance_variable_get(:@raw_request)
|
147
|
+
expect(raw_request.instance_variable_get(:@header)['cookie']).to eql ["custom-cookie=1234567"]
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'should merge cookies from setup_digest_auth and request' do
|
151
|
+
FakeWeb.register_uri(
|
152
|
+
:get, "http://api.foo.com/v1",
|
153
|
+
set_cookie: 'custom-cookie=1234567',
|
154
|
+
www_authenticate: 'Digest realm="Log Viewer", qop="auth", nonce="2CA0EC6B0E126C4800E56BA0C0003D3C", opaque="5ccc069c403ebaf9f0171e9517f40e41", stale=false'
|
155
|
+
)
|
156
|
+
|
157
|
+
@request.options[:digest_auth] = {username: 'foobar', password: 'secret'}
|
158
|
+
@request.options[:headers] = {'cookie' => 'request-cookie=test'}
|
159
|
+
@request.send(:setup_raw_request)
|
160
|
+
|
161
|
+
raw_request = @request.instance_variable_get(:@raw_request)
|
162
|
+
expect(raw_request.instance_variable_get(:@header)['cookie']).to eql ['request-cookie=test', 'custom-cookie=1234567']
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'should use body_stream when configured' do
|
166
|
+
stream = StringIO.new('foo')
|
167
|
+
request = HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', body_stream: stream)
|
168
|
+
request.send(:setup_raw_request)
|
169
|
+
expect(request.instance_variable_get(:@raw_request).body_stream).to eq(stream)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
describe "#uri" do
|
174
|
+
context "redirects" do
|
175
|
+
it "returns correct path when the server sets the location header to a filename" do
|
176
|
+
@request.last_uri = URI.parse("http://example.com/foo/bar")
|
177
|
+
@request.path = URI.parse("bar?foo=bar")
|
178
|
+
@request.redirect = true
|
179
|
+
|
180
|
+
expect(@request.uri).to eq(URI.parse("http://example.com/foo/bar?foo=bar"))
|
181
|
+
end
|
182
|
+
|
183
|
+
context "location header is an absolute path" do
|
184
|
+
it "returns correct path when location has leading slash" do
|
185
|
+
@request.last_uri = URI.parse("http://example.com/foo/bar")
|
186
|
+
@request.path = URI.parse("/bar?foo=bar")
|
187
|
+
@request.redirect = true
|
188
|
+
|
189
|
+
expect(@request.uri).to eq(URI.parse("http://example.com/bar?foo=bar"))
|
190
|
+
end
|
191
|
+
|
192
|
+
it "returns the correct path when location has no leading slash" do
|
193
|
+
@request.last_uri = URI.parse("http://example.com")
|
194
|
+
@request.path = URI.parse("bar/")
|
195
|
+
@request.redirect = true
|
196
|
+
|
197
|
+
expect(@request.uri).to eq(URI.parse("http://example.com/bar/"))
|
198
|
+
end
|
199
|
+
end
|
200
|
+
it "returns correct path when the server sets the location header to a full uri" do
|
201
|
+
@request.last_uri = URI.parse("http://example.com/foo/bar")
|
202
|
+
@request.path = URI.parse("http://example.com/bar?foo=bar")
|
203
|
+
@request.redirect = true
|
204
|
+
|
205
|
+
expect(@request.uri).to eq(URI.parse("http://example.com/bar?foo=bar"))
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
context "query strings" do
|
210
|
+
it "does not add an empty query string when default_params are blank" do
|
211
|
+
@request.options[:default_params] = {}
|
212
|
+
expect(@request.uri.query).to be_nil
|
213
|
+
end
|
214
|
+
|
215
|
+
it "respects the query string normalization proc" do
|
216
|
+
empty_proc = lambda {|qs| ""}
|
217
|
+
@request.options[:query_string_normalizer] = empty_proc
|
218
|
+
@request.options[:query] = {foo: :bar}
|
219
|
+
expect(CGI.unescape(@request.uri.query)).to eq("")
|
220
|
+
end
|
221
|
+
|
222
|
+
it "does not append an ampersand when queries are embedded in paths" do
|
223
|
+
@request.path = "/path?a=1"
|
224
|
+
@request.options[:query] = {}
|
225
|
+
expect(@request.uri.query).to eq("a=1")
|
226
|
+
end
|
227
|
+
|
228
|
+
it "does not duplicate query string parameters when uri is called twice" do
|
229
|
+
@request.options[:query] = {foo: :bar}
|
230
|
+
@request.uri
|
231
|
+
expect(@request.uri.query).to eq("foo=bar")
|
232
|
+
end
|
233
|
+
|
234
|
+
context "when representing an array" do
|
235
|
+
it "returns a Rails style query string" do
|
236
|
+
@request.options[:query] = {foo: %w(bar baz)}
|
237
|
+
expect(CGI.unescape(@request.uri.query)).to eq("foo[]=bar&foo[]=baz")
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
describe "#setup_raw_request" do
|
244
|
+
context "when query_string_normalizer is set" do
|
245
|
+
it "sets the body to the return value of the proc" do
|
246
|
+
@request.options[:query_string_normalizer] = HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER
|
247
|
+
@request.options[:body] = {page: 1, foo: %w(bar baz)}
|
248
|
+
@request.send(:setup_raw_request)
|
249
|
+
body = @request.instance_variable_get(:@raw_request).body
|
250
|
+
expect(CGI.unescape(body)).to eq("foo=bar&foo=baz&page=1")
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
describe 'http' do
|
256
|
+
it "should get a connection from the connection_adapter" do
|
257
|
+
http = Net::HTTP.new('google.com')
|
258
|
+
adapter = double('adapter')
|
259
|
+
request = HTTParty::Request.new(Net::HTTP::Get, 'https://api.foo.com/v1:443', connection_adapter: adapter)
|
260
|
+
expect(adapter).to receive(:call).with(request.uri, request.options).and_return(http)
|
261
|
+
expect(request.send(:http)).to be http
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
describe '#format_from_mimetype' do
|
266
|
+
it 'should handle text/xml' do
|
267
|
+
["text/xml", "text/xml; charset=iso8859-1"].each do |ct|
|
268
|
+
expect(@request.send(:format_from_mimetype, ct)).to eq(:xml)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
it 'should handle application/xml' do
|
273
|
+
["application/xml", "application/xml; charset=iso8859-1"].each do |ct|
|
274
|
+
expect(@request.send(:format_from_mimetype, ct)).to eq(:xml)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
it 'should handle text/json' do
|
279
|
+
["text/json", "text/json; charset=iso8859-1"].each do |ct|
|
280
|
+
expect(@request.send(:format_from_mimetype, ct)).to eq(:json)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
it 'should handle application/json' do
|
285
|
+
["application/json", "application/json; charset=iso8859-1"].each do |ct|
|
286
|
+
expect(@request.send(:format_from_mimetype, ct)).to eq(:json)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
it 'should handle text/csv' do
|
291
|
+
["text/csv", "text/csv; charset=iso8859-1"].each do |ct|
|
292
|
+
expect(@request.send(:format_from_mimetype, ct)).to eq(:csv)
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
it 'should handle application/csv' do
|
297
|
+
["application/csv", "application/csv; charset=iso8859-1"].each do |ct|
|
298
|
+
expect(@request.send(:format_from_mimetype, ct)).to eq(:csv)
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
it 'should handle text/comma-separated-values' do
|
303
|
+
["text/comma-separated-values", "text/comma-separated-values; charset=iso8859-1"].each do |ct|
|
304
|
+
expect(@request.send(:format_from_mimetype, ct)).to eq(:csv)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
it 'should handle text/javascript' do
|
309
|
+
["text/javascript", "text/javascript; charset=iso8859-1"].each do |ct|
|
310
|
+
expect(@request.send(:format_from_mimetype, ct)).to eq(:plain)
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
it 'should handle application/javascript' do
|
315
|
+
["application/javascript", "application/javascript; charset=iso8859-1"].each do |ct|
|
316
|
+
expect(@request.send(:format_from_mimetype, ct)).to eq(:plain)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
it "returns nil for an unrecognized mimetype" do
|
321
|
+
expect(@request.send(:format_from_mimetype, "application/atom+xml")).to be_nil
|
322
|
+
end
|
323
|
+
|
324
|
+
it "returns nil when using a default parser" do
|
325
|
+
@request.options[:parser] = lambda {}
|
326
|
+
expect(@request.send(:format_from_mimetype, "text/json")).to be_nil
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
describe 'parsing responses' do
|
331
|
+
it 'should handle xml automatically' do
|
332
|
+
xml = '<books><book><id>1234</id><name>Foo Bar!</name></book></books>'
|
333
|
+
@request.options[:format] = :xml
|
334
|
+
expect(@request.send(:parse_response, xml)).to eq({'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}})
|
335
|
+
end
|
336
|
+
|
337
|
+
it 'should handle csv automatically' do
|
338
|
+
csv = ['"id","Name"', '"1234","Foo Bar!"'].join("\n")
|
339
|
+
@request.options[:format] = :csv
|
340
|
+
expect(@request.send(:parse_response, csv)).to eq([%w(id Name), ["1234", "Foo Bar!"]])
|
341
|
+
end
|
342
|
+
|
343
|
+
it 'should handle json automatically' do
|
344
|
+
json = '{"books": {"book": {"name": "Foo Bar!", "id": "1234"}}}'
|
345
|
+
@request.options[:format] = :json
|
346
|
+
expect(@request.send(:parse_response, json)).to eq({'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}})
|
347
|
+
end
|
348
|
+
|
349
|
+
it "should include any HTTP headers in the returned response" do
|
350
|
+
@request.options[:format] = :html
|
351
|
+
response = stub_response "Content"
|
352
|
+
response.initialize_http_header("key" => "value")
|
353
|
+
|
354
|
+
expect(@request.perform.headers).to eq({ "key" => ["value"] })
|
355
|
+
end
|
356
|
+
|
357
|
+
if "".respond_to?(:encoding)
|
358
|
+
|
359
|
+
it "should process charset in content type properly" do
|
360
|
+
response = stub_response "Content"
|
361
|
+
response.initialize_http_header("Content-Type" => "text/plain;charset = utf-8")
|
362
|
+
resp = @request.perform
|
363
|
+
expect(resp.body.encoding).to eq(Encoding.find("UTF-8"))
|
364
|
+
end
|
365
|
+
|
366
|
+
it "should process charset in content type properly if it has a different case" do
|
367
|
+
response = stub_response "Content"
|
368
|
+
response.initialize_http_header("Content-Type" => "text/plain;CHARSET = utf-8")
|
369
|
+
resp = @request.perform
|
370
|
+
expect(resp.body.encoding).to eq(Encoding.find("UTF-8"))
|
371
|
+
end
|
372
|
+
|
373
|
+
it "should process quoted charset in content type properly" do
|
374
|
+
response = stub_response "Content"
|
375
|
+
response.initialize_http_header("Content-Type" => "text/plain;charset = \"utf-8\"")
|
376
|
+
resp = @request.perform
|
377
|
+
expect(resp.body.encoding).to eq(Encoding.find("UTF-8"))
|
378
|
+
end
|
379
|
+
|
380
|
+
it "should process utf-16 charset with little endian bom correctly" do
|
381
|
+
@request.options[:assume_utf16_is_big_endian] = true
|
382
|
+
|
383
|
+
response = stub_response "\xFF\xFEC\x00o\x00n\x00t\x00e\x00n\x00t\x00"
|
384
|
+
response.initialize_http_header("Content-Type" => "text/plain;charset = utf-16")
|
385
|
+
resp = @request.perform
|
386
|
+
expect(resp.body.encoding).to eq(Encoding.find("UTF-16LE"))
|
387
|
+
end
|
388
|
+
|
389
|
+
it "should process utf-16 charset with big endian bom correctly" do
|
390
|
+
@request.options[:assume_utf16_is_big_endian] = false
|
391
|
+
|
392
|
+
response = stub_response "\xFE\xFF\x00C\x00o\x00n\x00t\x00e\x00n\x00t"
|
393
|
+
response.initialize_http_header("Content-Type" => "text/plain;charset = utf-16")
|
394
|
+
resp = @request.perform
|
395
|
+
expect(resp.body.encoding).to eq(Encoding.find("UTF-16BE"))
|
396
|
+
end
|
397
|
+
|
398
|
+
it "should assume utf-16 little endian if options has been chosen" do
|
399
|
+
@request.options[:assume_utf16_is_big_endian] = false
|
400
|
+
|
401
|
+
response = stub_response "C\x00o\x00n\x00t\x00e\x00n\x00t\x00"
|
402
|
+
response.initialize_http_header("Content-Type" => "text/plain;charset = utf-16")
|
403
|
+
resp = @request.perform
|
404
|
+
expect(resp.body.encoding).to eq(Encoding.find("UTF-16LE"))
|
405
|
+
end
|
406
|
+
|
407
|
+
it "should perform no encoding if the charset is not available" do
|
408
|
+
response = stub_response "Content"
|
409
|
+
response.initialize_http_header("Content-Type" => "text/plain;charset = utf-lols")
|
410
|
+
resp = @request.perform
|
411
|
+
expect(resp.body).to eq("Content")
|
412
|
+
expect(resp.body.encoding).to eq("Content".encoding)
|
413
|
+
end
|
414
|
+
|
415
|
+
it "should perform no encoding if the content type is specified but no charset is specified" do
|
416
|
+
response = stub_response "Content"
|
417
|
+
response.initialize_http_header("Content-Type" => "text/plain")
|
418
|
+
resp = @request.perform
|
419
|
+
expect(resp.body).to eq("Content")
|
420
|
+
expect(resp.body.encoding).to eq("Content".encoding)
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
describe 'with non-200 responses' do
|
425
|
+
context "3xx responses" do
|
426
|
+
it 'returns a valid object for 304 not modified' do
|
427
|
+
stub_response '', 304
|
428
|
+
resp = @request.perform
|
429
|
+
expect(resp.code).to eq(304)
|
430
|
+
expect(resp.body).to eq('')
|
431
|
+
expect(resp).to be_nil
|
432
|
+
end
|
433
|
+
|
434
|
+
it "redirects if a 300 contains a location header" do
|
435
|
+
redirect = stub_response '', 300
|
436
|
+
redirect['location'] = 'http://foo.com/foo'
|
437
|
+
ok = stub_response('<hash><foo>bar</foo></hash>', 200)
|
438
|
+
allow(@http).to receive(:request).and_return(redirect, ok)
|
439
|
+
response = @request.perform
|
440
|
+
expect(response.request.base_uri.to_s).to eq("http://foo.com")
|
441
|
+
expect(response.request.path.to_s).to eq("http://foo.com/foo")
|
442
|
+
expect(response.request.uri.request_uri).to eq("/foo")
|
443
|
+
expect(response.request.uri.to_s).to eq("http://foo.com/foo")
|
444
|
+
expect(response.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
445
|
+
end
|
446
|
+
|
447
|
+
it "calls block given to perform with each redirect" do
|
448
|
+
@request = HTTParty::Request.new(Net::HTTP::Get, 'http://test.com/redirect', format: :xml)
|
449
|
+
FakeWeb.register_uri(:get, "http://test.com/redirect", status: [300, "REDIRECT"], location: "http://api.foo.com/v2")
|
450
|
+
FakeWeb.register_uri(:get, "http://api.foo.com/v2", body: "<hash><foo>bar</foo></hash>")
|
451
|
+
body = ""
|
452
|
+
response = @request.perform { |chunk| body += chunk }
|
453
|
+
expect(body.length).to eq(27)
|
454
|
+
end
|
455
|
+
|
456
|
+
it "redirects if a 300 contains a relative location header" do
|
457
|
+
redirect = stub_response '', 300
|
458
|
+
redirect['location'] = '/foo/bar'
|
459
|
+
ok = stub_response('<hash><foo>bar</foo></hash>', 200)
|
460
|
+
allow(@http).to receive(:request).and_return(redirect, ok)
|
461
|
+
response = @request.perform
|
462
|
+
expect(response.request.base_uri.to_s).to eq("http://api.foo.com")
|
463
|
+
expect(response.request.path.to_s).to eq("/foo/bar")
|
464
|
+
expect(response.request.uri.request_uri).to eq("/foo/bar")
|
465
|
+
expect(response.request.uri.to_s).to eq("http://api.foo.com/foo/bar")
|
466
|
+
expect(response.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
467
|
+
end
|
468
|
+
|
469
|
+
it "handles multiple redirects and relative location headers on different hosts" do
|
470
|
+
@request = HTTParty::Request.new(Net::HTTP::Get, 'http://test.com/redirect', format: :xml)
|
471
|
+
FakeWeb.register_uri(:get, "http://test.com/redirect", status: [300, "REDIRECT"], location: "http://api.foo.com/v2")
|
472
|
+
FakeWeb.register_uri(:get, "http://api.foo.com/v2", status: [300, "REDIRECT"], location: "/v3")
|
473
|
+
FakeWeb.register_uri(:get, "http://api.foo.com/v3", body: "<hash><foo>bar</foo></hash>")
|
474
|
+
response = @request.perform
|
475
|
+
expect(response.request.base_uri.to_s).to eq("http://api.foo.com")
|
476
|
+
expect(response.request.path.to_s).to eq("/v3")
|
477
|
+
expect(response.request.uri.request_uri).to eq("/v3")
|
478
|
+
expect(response.request.uri.to_s).to eq("http://api.foo.com/v3")
|
479
|
+
expect(response.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
480
|
+
end
|
481
|
+
|
482
|
+
it "returns the HTTParty::Response when the 300 does not contain a location header" do
|
483
|
+
stub_response '', 300
|
484
|
+
expect(HTTParty::Response).to be === @request.perform
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
488
|
+
it 'should return a valid object for 4xx response' do
|
489
|
+
stub_response '<foo><bar>yes</bar></foo>', 401
|
490
|
+
resp = @request.perform
|
491
|
+
expect(resp.code).to eq(401)
|
492
|
+
expect(resp.body).to eq("<foo><bar>yes</bar></foo>")
|
493
|
+
expect(resp['foo']['bar']).to eq("yes")
|
494
|
+
end
|
495
|
+
|
496
|
+
it 'should return a valid object for 5xx response' do
|
497
|
+
stub_response '<foo><bar>error</bar></foo>', 500
|
498
|
+
resp = @request.perform
|
499
|
+
expect(resp.code).to eq(500)
|
500
|
+
expect(resp.body).to eq("<foo><bar>error</bar></foo>")
|
501
|
+
expect(resp['foo']['bar']).to eq("error")
|
502
|
+
end
|
503
|
+
|
504
|
+
it "parses response lazily so codes can be checked prior" do
|
505
|
+
stub_response 'not xml', 500
|
506
|
+
@request.options[:format] = :xml
|
507
|
+
expect {
|
508
|
+
response = @request.perform
|
509
|
+
expect(response.code).to eq(500)
|
510
|
+
expect(response.body).to eq('not xml')
|
511
|
+
}.not_to raise_error
|
512
|
+
end
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
it "should not attempt to parse empty responses" do
|
517
|
+
[204, 304].each do |code|
|
518
|
+
stub_response "", code
|
519
|
+
|
520
|
+
@request.options[:format] = :xml
|
521
|
+
expect(@request.perform).to be_nil
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
it "should not fail for missing mime type" do
|
526
|
+
stub_response "Content for you"
|
527
|
+
@request.options[:format] = :html
|
528
|
+
expect(@request.perform.parsed_response).to eq('Content for you')
|
529
|
+
end
|
530
|
+
|
531
|
+
describe "a request that 302 redirects" do
|
532
|
+
before(:each) do
|
533
|
+
@redirect = stub_response("", 302)
|
534
|
+
@redirect['location'] = '/foo'
|
535
|
+
|
536
|
+
@ok = stub_response('<hash><foo>bar</foo></hash>', 200)
|
537
|
+
end
|
538
|
+
|
539
|
+
describe "once" do
|
540
|
+
before(:each) do
|
541
|
+
allow(@http).to receive(:request).and_return(@redirect, @ok)
|
542
|
+
end
|
543
|
+
|
544
|
+
it "should be handled by GET transparently" do
|
545
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
546
|
+
end
|
547
|
+
|
548
|
+
it "should be handled by POST transparently" do
|
549
|
+
@request.http_method = Net::HTTP::Post
|
550
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
551
|
+
end
|
552
|
+
|
553
|
+
it "should be handled by DELETE transparently" do
|
554
|
+
@request.http_method = Net::HTTP::Delete
|
555
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
556
|
+
end
|
557
|
+
|
558
|
+
it "should be handled by MOVE transparently" do
|
559
|
+
@request.http_method = Net::HTTP::Move
|
560
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
561
|
+
end
|
562
|
+
|
563
|
+
it "should be handled by COPY transparently" do
|
564
|
+
@request.http_method = Net::HTTP::Copy
|
565
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
566
|
+
end
|
567
|
+
|
568
|
+
it "should be handled by PATCH transparently" do
|
569
|
+
@request.http_method = Net::HTTP::Patch
|
570
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
571
|
+
end
|
572
|
+
|
573
|
+
it "should be handled by PUT transparently" do
|
574
|
+
@request.http_method = Net::HTTP::Put
|
575
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
576
|
+
end
|
577
|
+
|
578
|
+
it "should be handled by HEAD transparently" do
|
579
|
+
@request.http_method = Net::HTTP::Head
|
580
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
581
|
+
end
|
582
|
+
|
583
|
+
it "should be handled by OPTIONS transparently" do
|
584
|
+
@request.http_method = Net::HTTP::Options
|
585
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
586
|
+
end
|
587
|
+
|
588
|
+
it "should keep track of cookies between redirects" do
|
589
|
+
@redirect['Set-Cookie'] = 'foo=bar; name=value; HTTPOnly'
|
590
|
+
@request.perform
|
591
|
+
expect(@request.options[:headers]['Cookie']).to match(/foo=bar/)
|
592
|
+
expect(@request.options[:headers]['Cookie']).to match(/name=value/)
|
593
|
+
end
|
594
|
+
|
595
|
+
it 'should update cookies with rediects' do
|
596
|
+
@request.options[:headers] = {'Cookie' => 'foo=bar;'}
|
597
|
+
@redirect['Set-Cookie'] = 'foo=tar;'
|
598
|
+
@request.perform
|
599
|
+
expect(@request.options[:headers]['Cookie']).to match(/foo=tar/)
|
600
|
+
end
|
601
|
+
|
602
|
+
it 'should keep cookies between rediects' do
|
603
|
+
@request.options[:headers] = {'Cookie' => 'keep=me'}
|
604
|
+
@redirect['Set-Cookie'] = 'foo=tar;'
|
605
|
+
@request.perform
|
606
|
+
expect(@request.options[:headers]['Cookie']).to match(/keep=me/)
|
607
|
+
end
|
608
|
+
|
609
|
+
it "should handle multiple Set-Cookie headers between redirects" do
|
610
|
+
@redirect.add_field 'set-cookie', 'foo=bar; name=value; HTTPOnly'
|
611
|
+
@redirect.add_field 'set-cookie', 'one=1; two=2; HTTPOnly'
|
612
|
+
@request.perform
|
613
|
+
expect(@request.options[:headers]['Cookie']).to match(/foo=bar/)
|
614
|
+
expect(@request.options[:headers]['Cookie']).to match(/name=value/)
|
615
|
+
expect(@request.options[:headers]['Cookie']).to match(/one=1/)
|
616
|
+
expect(@request.options[:headers]['Cookie']).to match(/two=2/)
|
617
|
+
end
|
618
|
+
|
619
|
+
it 'should make resulting request a get request if it not already' do
|
620
|
+
@request.http_method = Net::HTTP::Delete
|
621
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
622
|
+
expect(@request.http_method).to eq(Net::HTTP::Get)
|
623
|
+
end
|
624
|
+
|
625
|
+
it 'should not make resulting request a get request if options[:maintain_method_across_redirects] is true' do
|
626
|
+
@request.options[:maintain_method_across_redirects] = true
|
627
|
+
@request.http_method = Net::HTTP::Delete
|
628
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
629
|
+
expect(@request.http_method).to eq(Net::HTTP::Delete)
|
630
|
+
end
|
631
|
+
|
632
|
+
it 'should log the redirection' do
|
633
|
+
logger_double = double
|
634
|
+
expect(logger_double).to receive(:info).twice
|
635
|
+
@request.options[:logger] = logger_double
|
636
|
+
@request.perform
|
637
|
+
end
|
638
|
+
end
|
639
|
+
|
640
|
+
describe "infinitely" do
|
641
|
+
before(:each) do
|
642
|
+
allow(@http).to receive(:request).and_return(@redirect)
|
643
|
+
end
|
644
|
+
|
645
|
+
it "should raise an exception" do
|
646
|
+
expect { @request.perform }.to raise_error(HTTParty::RedirectionTooDeep)
|
647
|
+
end
|
648
|
+
end
|
649
|
+
end
|
650
|
+
|
651
|
+
describe "a request that 303 redirects" do
|
652
|
+
before(:each) do
|
653
|
+
@redirect = stub_response("", 303)
|
654
|
+
@redirect['location'] = '/foo'
|
655
|
+
|
656
|
+
@ok = stub_response('<hash><foo>bar</foo></hash>', 200)
|
657
|
+
end
|
658
|
+
|
659
|
+
describe "once" do
|
660
|
+
before(:each) do
|
661
|
+
allow(@http).to receive(:request).and_return(@redirect, @ok)
|
662
|
+
end
|
663
|
+
|
664
|
+
it "should be handled by GET transparently" do
|
665
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
666
|
+
end
|
667
|
+
|
668
|
+
it "should be handled by POST transparently" do
|
669
|
+
@request.http_method = Net::HTTP::Post
|
670
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
671
|
+
end
|
672
|
+
|
673
|
+
it "should be handled by DELETE transparently" do
|
674
|
+
@request.http_method = Net::HTTP::Delete
|
675
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
676
|
+
end
|
677
|
+
|
678
|
+
it "should be handled by MOVE transparently" do
|
679
|
+
@request.http_method = Net::HTTP::Move
|
680
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
681
|
+
end
|
682
|
+
|
683
|
+
it "should be handled by COPY transparently" do
|
684
|
+
@request.http_method = Net::HTTP::Copy
|
685
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
686
|
+
end
|
687
|
+
|
688
|
+
it "should be handled by PATCH transparently" do
|
689
|
+
@request.http_method = Net::HTTP::Patch
|
690
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
691
|
+
end
|
692
|
+
|
693
|
+
it "should be handled by PUT transparently" do
|
694
|
+
@request.http_method = Net::HTTP::Put
|
695
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
696
|
+
end
|
697
|
+
|
698
|
+
it "should be handled by HEAD transparently" do
|
699
|
+
@request.http_method = Net::HTTP::Head
|
700
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
701
|
+
end
|
702
|
+
|
703
|
+
it "should be handled by OPTIONS transparently" do
|
704
|
+
@request.http_method = Net::HTTP::Options
|
705
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
706
|
+
end
|
707
|
+
|
708
|
+
it "should keep track of cookies between redirects" do
|
709
|
+
@redirect['Set-Cookie'] = 'foo=bar; name=value; HTTPOnly'
|
710
|
+
@request.perform
|
711
|
+
expect(@request.options[:headers]['Cookie']).to match(/foo=bar/)
|
712
|
+
expect(@request.options[:headers]['Cookie']).to match(/name=value/)
|
713
|
+
end
|
714
|
+
|
715
|
+
it 'should update cookies with rediects' do
|
716
|
+
@request.options[:headers] = {'Cookie' => 'foo=bar;'}
|
717
|
+
@redirect['Set-Cookie'] = 'foo=tar;'
|
718
|
+
@request.perform
|
719
|
+
expect(@request.options[:headers]['Cookie']).to match(/foo=tar/)
|
720
|
+
end
|
721
|
+
|
722
|
+
it 'should keep cookies between rediects' do
|
723
|
+
@request.options[:headers] = {'Cookie' => 'keep=me'}
|
724
|
+
@redirect['Set-Cookie'] = 'foo=tar;'
|
725
|
+
@request.perform
|
726
|
+
expect(@request.options[:headers]['Cookie']).to match(/keep=me/)
|
727
|
+
end
|
728
|
+
|
729
|
+
it "should handle multiple Set-Cookie headers between redirects" do
|
730
|
+
@redirect.add_field 'set-cookie', 'foo=bar; name=value; HTTPOnly'
|
731
|
+
@redirect.add_field 'set-cookie', 'one=1; two=2; HTTPOnly'
|
732
|
+
@request.perform
|
733
|
+
expect(@request.options[:headers]['Cookie']).to match(/foo=bar/)
|
734
|
+
expect(@request.options[:headers]['Cookie']).to match(/name=value/)
|
735
|
+
expect(@request.options[:headers]['Cookie']).to match(/one=1/)
|
736
|
+
expect(@request.options[:headers]['Cookie']).to match(/two=2/)
|
737
|
+
end
|
738
|
+
|
739
|
+
it 'should make resulting request a get request if it not already' do
|
740
|
+
@request.http_method = Net::HTTP::Delete
|
741
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
742
|
+
expect(@request.http_method).to eq(Net::HTTP::Get)
|
743
|
+
end
|
744
|
+
|
745
|
+
it 'should make resulting request a get request if options[:maintain_method_across_redirects] is false' do
|
746
|
+
@request.options[:maintain_method_across_redirects] = false
|
747
|
+
@request.http_method = Net::HTTP::Delete
|
748
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
749
|
+
expect(@request.http_method).to eq(Net::HTTP::Get)
|
750
|
+
end
|
751
|
+
|
752
|
+
it 'should make resulting request a get request if options[:maintain_method_across_redirects] is true but options[:resend_on_redirect] is false' do
|
753
|
+
@request.options[:maintain_method_across_redirects] = true
|
754
|
+
@request.options[:resend_on_redirect] = false
|
755
|
+
@request.http_method = Net::HTTP::Delete
|
756
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
757
|
+
expect(@request.http_method).to eq(Net::HTTP::Get)
|
758
|
+
end
|
759
|
+
|
760
|
+
it 'should not make resulting request a get request if options[:maintain_method_across_redirects] and options[:resend_on_redirect] is true' do
|
761
|
+
@request.options[:maintain_method_across_redirects] = true
|
762
|
+
@request.options[:resend_on_redirect] = true
|
763
|
+
@request.http_method = Net::HTTP::Delete
|
764
|
+
expect(@request.perform.parsed_response).to eq({"hash" => {"foo" => "bar"}})
|
765
|
+
expect(@request.http_method).to eq(Net::HTTP::Delete)
|
766
|
+
end
|
767
|
+
|
768
|
+
it 'should log the redirection' do
|
769
|
+
logger_double = double
|
770
|
+
expect(logger_double).to receive(:info).twice
|
771
|
+
@request.options[:logger] = logger_double
|
772
|
+
@request.perform
|
773
|
+
end
|
774
|
+
end
|
775
|
+
|
776
|
+
describe "infinitely" do
|
777
|
+
before(:each) do
|
778
|
+
allow(@http).to receive(:request).and_return(@redirect)
|
779
|
+
end
|
780
|
+
|
781
|
+
it "should raise an exception" do
|
782
|
+
expect { @request.perform }.to raise_error(HTTParty::RedirectionTooDeep)
|
783
|
+
end
|
784
|
+
end
|
785
|
+
end
|
786
|
+
|
787
|
+
describe "#handle_deflation" do
|
788
|
+
context "context-encoding" do
|
789
|
+
before do
|
790
|
+
@request.options[:format] = :html
|
791
|
+
@last_response = double
|
792
|
+
allow(@last_response).to receive(:body).and_return('')
|
793
|
+
end
|
794
|
+
|
795
|
+
it "should inflate the gzipped body with content-encoding: gzip" do
|
796
|
+
allow(@last_response).to receive(:[]).with("content-encoding").and_return("gzip")
|
797
|
+
allow(@request).to receive(:last_response).and_return(@last_response)
|
798
|
+
expect(Zlib::GzipReader).to receive(:new).and_return(StringIO.new(''))
|
799
|
+
expect(@request.last_response).to receive(:delete).with('content-encoding')
|
800
|
+
@request.send(:handle_deflation)
|
801
|
+
end
|
802
|
+
|
803
|
+
it "should inflate the gzipped body with content-encoding: x-gzip" do
|
804
|
+
allow(@last_response).to receive(:[]).with("content-encoding").and_return("x-gzip")
|
805
|
+
allow(@request).to receive(:last_response).and_return(@last_response)
|
806
|
+
expect(Zlib::GzipReader).to receive(:new).and_return(StringIO.new(''))
|
807
|
+
expect(@request.last_response).to receive(:delete).with('content-encoding')
|
808
|
+
@request.send(:handle_deflation)
|
809
|
+
end
|
810
|
+
|
811
|
+
it "should inflate the deflated body" do
|
812
|
+
allow(@last_response).to receive(:[]).with("content-encoding").and_return("deflate")
|
813
|
+
allow(@request).to receive(:last_response).and_return(@last_response)
|
814
|
+
expect(Zlib::Inflate).to receive(:inflate).and_return('')
|
815
|
+
expect(@request.last_response).to receive(:delete).with('content-encoding')
|
816
|
+
@request.send(:handle_deflation)
|
817
|
+
end
|
818
|
+
end
|
819
|
+
end
|
820
|
+
|
821
|
+
context "with POST http method" do
|
822
|
+
it "should raise argument error if query is not a hash" do
|
823
|
+
expect {
|
824
|
+
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', format: :xml, query: 'astring').perform
|
825
|
+
}.to raise_error(ArgumentError)
|
826
|
+
end
|
827
|
+
end
|
828
|
+
|
829
|
+
describe "argument validation" do
|
830
|
+
it "should raise argument error if basic_auth and digest_auth are both present" do
|
831
|
+
expect {
|
832
|
+
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', basic_auth: {}, digest_auth: {}).perform
|
833
|
+
}.to raise_error(ArgumentError, "only one authentication method, :basic_auth or :digest_auth may be used at a time")
|
834
|
+
end
|
835
|
+
|
836
|
+
it "should raise argument error if basic_auth is not a hash" do
|
837
|
+
expect {
|
838
|
+
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', basic_auth: %w(foo bar)).perform
|
839
|
+
}.to raise_error(ArgumentError, ":basic_auth must be a hash")
|
840
|
+
end
|
841
|
+
|
842
|
+
it "should raise argument error if digest_auth is not a hash" do
|
843
|
+
expect {
|
844
|
+
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', digest_auth: %w(foo bar)).perform
|
845
|
+
}.to raise_error(ArgumentError, ":digest_auth must be a hash")
|
846
|
+
end
|
847
|
+
|
848
|
+
it "should raise argument error if headers is not a hash" do
|
849
|
+
expect {
|
850
|
+
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', headers: %w(foo bar)).perform
|
851
|
+
}.to raise_error(ArgumentError, ":headers must be a hash")
|
852
|
+
end
|
853
|
+
|
854
|
+
it "should raise argument error if options method is not http accepted method" do
|
855
|
+
expect {
|
856
|
+
HTTParty::Request.new('SuperPost', 'http://api.foo.com/v1').perform
|
857
|
+
}.to raise_error(ArgumentError, "only get, post, patch, put, delete, head, and options methods are supported")
|
858
|
+
end
|
859
|
+
|
860
|
+
it "should raise argument error if http method is post and query is not hash" do
|
861
|
+
expect {
|
862
|
+
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', query: "message: hello").perform
|
863
|
+
}.to raise_error(ArgumentError, ":query must be hash if using HTTP Post")
|
864
|
+
end
|
865
|
+
|
866
|
+
it "should raise RedirectionTooDeep error if limit is negative" do
|
867
|
+
expect {
|
868
|
+
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', limit: -1).perform
|
869
|
+
}.to raise_error(HTTParty::RedirectionTooDeep, 'HTTP redirects too deep')
|
870
|
+
end
|
871
|
+
end
|
872
|
+
end
|