httparty 0.6.1 → 0.7.0

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.

@@ -4,13 +4,27 @@ describe HTTParty::Response do
4
4
  before do
5
5
  @last_modified = Date.new(2010, 1, 15).to_s
6
6
  @content_length = '1024'
7
- @response_object = {'foo' => 'bar'}
7
+ @request_object = HTTParty::Request.new Net::HTTP::Get, '/'
8
8
  @response_object = Net::HTTPOK.new('1.1', 200, 'OK')
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
12
  @parsed_response = {"foo" => "bar"}
13
- @response = HTTParty::Response.new(@response_object, @parsed_response)
13
+ @response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
14
+ end
15
+
16
+ describe ".underscore" do
17
+ it "works with one capitalized word" do
18
+ HTTParty::Response.underscore("Accepted").should == "accepted"
19
+ end
20
+
21
+ it "works with titlecase" do
22
+ HTTParty::Response.underscore("BadGateway").should == "bad_gateway"
23
+ end
24
+
25
+ it "works with all caps" do
26
+ HTTParty::Response.underscore("OK").should == "ok"
27
+ end
14
28
  end
15
29
 
16
30
  describe "initialization" do
@@ -32,17 +46,17 @@ describe HTTParty::Response do
32
46
  end
33
47
 
34
48
  it "returns response headers" do
35
- response = HTTParty::Response.new(@response_object, @parsed_response)
49
+ response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
36
50
  response.headers.should == {'last-modified' => [@last_modified], 'content-length' => [@content_length]}
37
51
  end
38
52
 
39
53
  it "should send missing methods to delegate" do
40
- response = HTTParty::Response.new(@response_object, {'foo' => 'bar'})
54
+ response = HTTParty::Response.new(@request_object, @response_object, {'foo' => 'bar'})
41
55
  response['foo'].should == 'bar'
42
56
  end
43
57
 
44
58
  it "should be able to iterate if it is array" do
45
- response = HTTParty::Response.new(@response_object, [{'foo' => 'bar'}, {'foo' => 'baz'}])
59
+ response = HTTParty::Response.new(@request_object, @response_object, [{'foo' => 'bar'}, {'foo' => 'baz'}])
46
60
  response.size.should == 2
47
61
  expect {
48
62
  response.each { |item| }
@@ -50,20 +64,20 @@ describe HTTParty::Response do
50
64
  end
51
65
 
52
66
  it "allows headers to be accessed by mixed-case names in hash notation" do
53
- response = HTTParty::Response.new(@response_object, @parsed_response)
67
+ response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
54
68
  response.headers['Content-LENGTH'].should == @content_length
55
69
  end
56
70
 
57
71
  it "returns a comma-delimited value when multiple values exist" do
58
72
  @response_object.add_field 'set-cookie', 'csrf_id=12345; path=/'
59
73
  @response_object.add_field 'set-cookie', '_github_ses=A123CdE; path=/'
60
- response = HTTParty::Response.new(@response_object, @parsed_response)
74
+ response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
61
75
  response.headers['set-cookie'].should == "csrf_id=12345; path=/, _github_ses=A123CdE; path=/"
62
76
  end
63
77
 
64
78
  # Backwards-compatibility - previously, #headers returned a Hash
65
79
  it "responds to hash methods" do
66
- response = HTTParty::Response.new(@response_object, @parsed_response)
80
+ response = HTTParty::Response.new(@request_object, @response_object, @parsed_response)
67
81
  hash_methods = {}.methods - response.headers.methods
68
82
  hash_methods.each do |method_name|
69
83
  response.headers.respond_to?(method_name).should be_true
@@ -71,13 +85,104 @@ describe HTTParty::Response do
71
85
  end
72
86
 
73
87
  xit "should allow hashes to be accessed with dot notation" do
74
- response = HTTParty::Response.new({'foo' => 'bar'}, "{foo:'bar'}", 200, 'OK')
88
+ response = HTTParty::Response.new(@request_object, {'foo' => 'bar'}, "{foo:'bar'}", 200, 'OK')
75
89
  response.foo.should == 'bar'
76
90
  end
77
91
 
78
92
  xit "should allow nested hashes to be accessed with dot notation" do
79
- response = HTTParty::Response.new({'foo' => {'bar' => 'baz'}}, "{foo: {bar:'baz'}}", 200, 'OK')
93
+ response = HTTParty::Response.new(@request_object, {'foo' => {'bar' => 'baz'}}, "{foo: {bar:'baz'}}", 200, 'OK')
80
94
  response.foo.should == {'bar' => 'baz'}
81
95
  response.foo.bar.should == 'baz'
82
96
  end
97
+
98
+ describe "semantic methods for response codes" do
99
+ def response_mock(klass)
100
+ r = klass.new('', '', '')
101
+ r.stub(:body)
102
+ r
103
+ end
104
+
105
+ context "major codes" do
106
+ it "is information" do
107
+ net_response = response_mock(Net::HTTPInformation)
108
+ response = HTTParty::Response.new(@request_object, net_response, '')
109
+ response.information?.should be_true
110
+ end
111
+
112
+ it "is success" do
113
+ net_response = response_mock(Net::HTTPSuccess)
114
+ response = HTTParty::Response.new(@request_object, net_response, '')
115
+ response.success?.should be_true
116
+ end
117
+
118
+ it "is redirection" do
119
+ net_response = response_mock(Net::HTTPRedirection)
120
+ response = HTTParty::Response.new(@request_object, net_response, '')
121
+ response.redirection?.should be_true
122
+ end
123
+
124
+ it "is client error" do
125
+ net_response = response_mock(Net::HTTPClientError)
126
+ response = HTTParty::Response.new(@request_object, net_response, '')
127
+ response.client_error?.should be_true
128
+ end
129
+
130
+ it "is server error" do
131
+ net_response = response_mock(Net::HTTPServerError)
132
+ response = HTTParty::Response.new(@request_object, net_response, '')
133
+ response.server_error?.should be_true
134
+ end
135
+ end
136
+
137
+ context "for specific codes" do
138
+ SPECIFIC_CODES = {
139
+ :accepted? => Net::HTTPAccepted,
140
+ :bad_gateway? => Net::HTTPBadGateway,
141
+ :bad_request? => Net::HTTPBadRequest,
142
+ :conflict? => Net::HTTPConflict,
143
+ :continue? => Net::HTTPContinue,
144
+ :created? => Net::HTTPCreated,
145
+ :expectation_failed? => Net::HTTPExpectationFailed,
146
+ :forbidden? => Net::HTTPForbidden,
147
+ :found? => Net::HTTPFound,
148
+ :gateway_time_out? => Net::HTTPGatewayTimeOut,
149
+ :gone? => Net::HTTPGone,
150
+ :internal_server_error? => Net::HTTPInternalServerError,
151
+ :length_required? => Net::HTTPLengthRequired,
152
+ :method_not_allowed? => Net::HTTPMethodNotAllowed,
153
+ :moved_permanently? => Net::HTTPMovedPermanently,
154
+ :multiple_choice? => Net::HTTPMultipleChoice,
155
+ :no_content? => Net::HTTPNoContent,
156
+ :non_authoritative_information? => Net::HTTPNonAuthoritativeInformation,
157
+ :not_acceptable? => Net::HTTPNotAcceptable,
158
+ :not_found? => Net::HTTPNotFound,
159
+ :not_implemented? => Net::HTTPNotImplemented,
160
+ :not_modified? => Net::HTTPNotModified,
161
+ :ok? => Net::HTTPOK,
162
+ :partial_content? => Net::HTTPPartialContent,
163
+ :payment_required? => Net::HTTPPaymentRequired,
164
+ :precondition_failed? => Net::HTTPPreconditionFailed,
165
+ :proxy_authentication_required? => Net::HTTPProxyAuthenticationRequired,
166
+ :request_entity_too_large? => Net::HTTPRequestEntityTooLarge,
167
+ :request_time_out? => Net::HTTPRequestTimeOut,
168
+ :request_uri_too_long? => Net::HTTPRequestURITooLong,
169
+ :requested_range_not_satisfiable? => Net::HTTPRequestedRangeNotSatisfiable,
170
+ :reset_content? => Net::HTTPResetContent,
171
+ :see_other? => Net::HTTPSeeOther,
172
+ :service_unavailable? => Net::HTTPServiceUnavailable,
173
+ :switch_protocol? => Net::HTTPSwitchProtocol,
174
+ :temporary_redirect? => Net::HTTPTemporaryRedirect,
175
+ :unauthorized? => Net::HTTPUnauthorized,
176
+ :unsupported_media_type? => Net::HTTPUnsupportedMediaType,
177
+ :use_proxy? => Net::HTTPUseProxy,
178
+ :version_not_supported? => Net::HTTPVersionNotSupported
179
+ }.each do |method, klass|
180
+ it "responds to #{method}" do
181
+ net_response = response_mock(klass)
182
+ response = HTTParty::Response.new(@request_object, net_response, '')
183
+ response.__send__(method).should be_true
184
+ end
185
+ end
186
+ end
187
+ end
83
188
  end
@@ -0,0 +1,54 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ describe HTTParty::Request do
4
+ context "SSL certificate verification" do
5
+ before do
6
+ FakeWeb.allow_net_connect = true # enable network connections just for this test
7
+ end
8
+
9
+ after do
10
+ FakeWeb.allow_net_connect = false # Restore allow_net_connect value for testing
11
+ end
12
+
13
+ it "should work with when no trusted CA list is specified" do
14
+ ssl_verify_test(nil, nil, "selfsigned.crt").should == {'success' => true}
15
+ end
16
+
17
+ it "should work with when no trusted CA list is specified, even with a bogus hostname" do
18
+ ssl_verify_test(nil, nil, "bogushost.crt").should == {'success' => true}
19
+ end
20
+
21
+ it "should work when using ssl_ca_file with a self-signed CA" do
22
+ ssl_verify_test(:ssl_ca_file, "selfsigned.crt", "selfsigned.crt").should == {'success' => true}
23
+ end
24
+
25
+ it "should work when using ssl_ca_file with a certificate authority" do
26
+ ssl_verify_test(:ssl_ca_file, "ca.crt", "server.crt").should == {'success' => true}
27
+ end
28
+ it "should work when using ssl_ca_path with a certificate authority" do
29
+ ssl_verify_test(:ssl_ca_path, ".", "server.crt").should == {'success' => true}
30
+ end
31
+
32
+ it "should fail when using ssl_ca_file and the server uses an unrecognized certificate authority" do
33
+ lambda do
34
+ ssl_verify_test(:ssl_ca_file, "ca.crt", "selfsigned.crt")
35
+ end.should raise_error(OpenSSL::SSL::SSLError)
36
+ end
37
+ it "should fail when using ssl_ca_path and the server uses an unrecognized certificate authority" do
38
+ lambda do
39
+ ssl_verify_test(:ssl_ca_path, ".", "selfsigned.crt")
40
+ end.should raise_error(OpenSSL::SSL::SSLError)
41
+ end
42
+
43
+ it "should fail when using ssl_ca_file and the server uses a bogus hostname" do
44
+ lambda do
45
+ ssl_verify_test(:ssl_ca_file, "ca.crt", "bogushost.crt")
46
+ end.should raise_error(OpenSSL::SSL::SSLError)
47
+ end
48
+ it "should fail when using ssl_ca_path and the server uses a bogus hostname" do
49
+ lambda do
50
+ ssl_verify_test(:ssl_ca_path, ".", "bogushost.crt")
51
+ end.should raise_error(OpenSSL::SSL::SSLError)
52
+ end
53
+ end
54
+ end
@@ -1,11 +1,5 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
2
 
3
- class CustomParser
4
- def self.parse(body)
5
- return {:sexy => true}
6
- end
7
- end
8
-
9
3
  describe HTTParty do
10
4
  before(:each) do
11
5
  @klass = Class.new
@@ -25,6 +19,25 @@ describe HTTParty do
25
19
  HTTParty::AllowedFormats.should == HTTParty::Parser::SupportedFormats
26
20
  end
27
21
  end
22
+
23
+ describe "pem" do
24
+
25
+ it 'should set the pem content' do
26
+ @klass.pem 'PEM-CONTENT'
27
+ @klass.default_options[:pem].should == 'PEM-CONTENT'
28
+ end
29
+
30
+ it "should set the password to nil if it's not provided" do
31
+ @klass.pem 'PEM-CONTENT'
32
+ @klass.default_options[:pem_password].should be_nil
33
+ end
34
+
35
+ it 'should set the password' do
36
+ @klass.pem 'PEM-CONTENT', 'PASSWORD'
37
+ @klass.default_options[:pem_password].should == 'PASSWORD'
38
+ end
39
+
40
+ end
28
41
 
29
42
  describe "base uri" do
30
43
  before(:each) do
@@ -47,7 +60,14 @@ describe HTTParty do
47
60
  end
48
61
  end
49
62
 
50
- describe "#normalize_base_uri" do
63
+ describe ".disable_rails_query_string_format" do
64
+ it "sets the query string normalizer to HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER" do
65
+ @klass.disable_rails_query_string_format
66
+ @klass.default_options[:query_string_normalizer].should == HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER
67
+ end
68
+ end
69
+
70
+ describe ".normalize_base_uri" do
51
71
  it "should add http if not present for non ssl requests" do
52
72
  uri = HTTParty.normalize_base_uri('api.foobar.com')
53
73
  uri.should == 'http://api.foobar.com'
@@ -241,6 +261,12 @@ describe HTTParty do
241
261
  end
242
262
 
243
263
  describe "parser" do
264
+ class CustomParser
265
+ def self.parse(body)
266
+ return {:sexy => true}
267
+ end
268
+ end
269
+
244
270
  let(:parser) do
245
271
  Proc.new{ |data, format| CustomParser.parse(data) }
246
272
  end
@@ -346,6 +372,26 @@ describe HTTParty do
346
372
  end
347
373
  end
348
374
 
375
+ describe ".follow_redirects" do
376
+ it "sets follow redirects to true by default" do
377
+ @klass.follow_redirects
378
+ @klass.default_options[:follow_redirects].should be_true
379
+ end
380
+
381
+ it "sets the follow_redirects option to false" do
382
+ @klass.follow_redirects false
383
+ @klass.default_options[:follow_redirects].should be_false
384
+ end
385
+ end
386
+
387
+ describe ".query_string_normalizer" do
388
+ it "sets the query_string_normalizer option" do
389
+ normalizer = proc {}
390
+ @klass.query_string_normalizer normalizer
391
+ @klass.default_options[:query_string_normalizer].should == normalizer
392
+ end
393
+ end
394
+
349
395
  describe "with explicit override of automatic redirect handling" do
350
396
  before do
351
397
  @request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', :format => :xml, :no_follow => true)
@@ -10,6 +10,7 @@ Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].e
10
10
 
11
11
  Spec::Runner.configure do |config|
12
12
  config.include HTTParty::StubResponse
13
+ config.include HTTParty::SSLTestHelper
13
14
  config.before(:suite) do
14
15
  FakeWeb.allow_net_connect = false
15
16
  end
@@ -0,0 +1,25 @@
1
+ module HTTParty
2
+ module SSLTestHelper
3
+ def ssl_verify_test(mode, ca_basename, server_cert_filename)
4
+ test_server = nil
5
+ begin
6
+ # Start an HTTPS server
7
+ test_server = SSLTestServer.new(
8
+ :rsa_key => File.read(File.expand_path("../../fixtures/ssl/generated/server.key", __FILE__)),
9
+ :cert => File.read(File.expand_path("../../fixtures/ssl/generated/#{server_cert_filename}", __FILE__)))
10
+ test_server.start
11
+
12
+ # Build a request
13
+ if mode
14
+ ca_path = File.expand_path("../../fixtures/ssl/generated/#{ca_basename}", __FILE__)
15
+ 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)
17
+ else
18
+ return HTTParty.get("https://localhost:#{test_server.port}/", :format => :json, :timeout=>30)
19
+ end
20
+ ensure
21
+ test_server.stop if test_server
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,69 @@
1
+ require 'openssl'
2
+ require 'socket'
3
+ require 'thread'
4
+
5
+ # NOTE: This code is garbage. It probably has deadlocks, it might leak
6
+ # threads, and otherwise cause problems in a real system. It's really only
7
+ # intended for testing HTTParty.
8
+ class SSLTestServer
9
+ attr_accessor :ctx # SSLContext object
10
+ attr_reader :port
11
+
12
+ def initialize(options={})
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
+ @ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE # Don't verify client certificate
17
+ @port = options[:port] || 0
18
+ @thread = nil
19
+ @stopping_mutex = Mutex.new
20
+ @stopping = false
21
+ end
22
+
23
+ def start
24
+ @raw_server = TCPServer.new(@port)
25
+ if @port == 0
26
+ @port = Socket::getnameinfo(@raw_server.getsockname, Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV)[1].to_i
27
+ end
28
+ @ssl_server = OpenSSL::SSL::SSLServer.new(@raw_server, @ctx)
29
+ @stopping_mutex.synchronize{
30
+ return if @stopping
31
+ @thread = Thread.new{ thread_main }
32
+ }
33
+ nil
34
+ end
35
+
36
+ def stop
37
+ @stopping_mutex.synchronize{
38
+ return if @stopping
39
+ @stopping = true
40
+ }
41
+ @thread.join
42
+ end
43
+
44
+ private
45
+
46
+ def thread_main
47
+ until @stopping_mutex.synchronize{ @stopping }
48
+ (rr,ww,ee) = select([@ssl_server.to_io], nil, nil, 0.1)
49
+ next unless rr && rr.include?(@ssl_server.to_io)
50
+ socket = @ssl_server.accept
51
+ Thread.new{
52
+ header = []
53
+ until (line = socket.readline).rstrip.empty?
54
+ header << line
55
+ end
56
+
57
+ socket.write <<'EOF'.gsub(/\r\n/n, "\n").gsub(/\n/n, "\r\n")
58
+ HTTP/1.1 200 OK
59
+ Connection: close
60
+ Content-Type: application/json; charset=UTF-8
61
+
62
+ {"success":true}
63
+ EOF
64
+ socket.close
65
+ }
66
+ end
67
+ @ssl_server.close
68
+ end
69
+ end
@@ -8,16 +8,16 @@ module HTTParty
8
8
  response.stub!(:body).and_return(data)
9
9
 
10
10
  http_request = HTTParty::Request.new(Net::HTTP::Get, 'http://localhost', :format => format)
11
- http_request.stub!(:perform_actual_request).and_return(response)
11
+ http_request.stub_chain(:http, :request).and_return(response)
12
12
 
13
13
  HTTParty::Request.should_receive(:new).and_return(http_request)
14
14
  end
15
15
 
16
16
  def stub_response(body, code = 200)
17
+ @request.options[:base_uri] ||= 'http://localhost'
17
18
  unless defined?(@http) && @http
18
19
  @http = Net::HTTP.new('localhost', 80)
19
20
  @request.stub!(:http).and_return(@http)
20
- @request.stub!(:uri).and_return(URI.parse("http://foo.com/foobar"))
21
21
  end
22
22
 
23
23
  response = Net::HTTPResponse::CODE_TO_OBJ[code.to_s].new("1.1", code, body)