httparty 0.13.7 → 0.14.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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1e38f46dd22e2a48a187998d68305ae356a1a9c8
4
- data.tar.gz: 257d70e92f1a15ee99c017c30f05b93b95b2410a
3
+ metadata.gz: f66396072d869ad76f661789a0e9b2d66495faa2
4
+ data.tar.gz: ae8e43661459ee5bd5c9018645812ecd9500cd6b
5
5
  SHA512:
6
- metadata.gz: 106f0de2d1a32408da7442ccedc324dda618fa233e4e29389b632f3be6b0c39ab36280b9fe33247a7878fc8a8a6323eee8ed6566ff5ea24ea341e04ffc0d5da1
7
- data.tar.gz: f4f2e8725cf55e63480e54b84e6f4c386815debd6033dd396c4a12217cddc7f8154c22ccde4999a9b048f4b0e53192e4a5f975793a57114270afb8d867ce985c
6
+ metadata.gz: ac6a080a19674df0435a6715406b97645ed9ee2ed803b92eb3e90ade39e75ac311b3e11960d2c44d00a561720be53aa1d229f216fb40e67f4452659701005695
7
+ data.tar.gz: 7529cf1586833fef74e6e6840a8992ab6b8c8b9ff83db89c5179bf166ce21abc0868c63bd800b5f110a85f65c90472e24cc89632d9cedf0f3bae39c70c1baa6c
@@ -2,6 +2,8 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
4
  - 2.0.0
5
- notifications:
6
- email: false
5
+ - 2.1.8
6
+ - 2.2.4
7
+ - 2.3.0
7
8
  bundler_args: --without development
9
+ before_install: gem install bundler
data/Gemfile CHANGED
@@ -12,8 +12,8 @@ group :development do
12
12
  end
13
13
 
14
14
  group :test do
15
- gem 'rspec', '~> 3.1'
15
+ gem 'rspec', '~> 3.4'
16
16
  gem 'simplecov', require: false
17
17
  gem 'aruba'
18
- gem 'cucumber', '~> 1.3.17'
18
+ gem 'cucumber', '~> 2.3'
19
19
  end
data/History CHANGED
@@ -1,3 +1,11 @@
1
+ == 0.14.0
2
+ * [added status predicate methods to Response#respond_to?](https://github.com/jnunemaker/httparty/pull/482)
3
+ * [allow empty array to be used as param](https://github.com/jnunemaker/httparty/pull/477)
4
+ * [add support for MKCOL method](https://github.com/jnunemaker/httparty/pull/465)
5
+ * [remove json gem from gemspec](https://github.com/jnunemaker/httparty/pull/464)
6
+ * [stop mutating cookie hash](https://github.com/jnunemaker/httparty/pull/460)
7
+ * [optional raising exception on certain status codes](https://github.com/jnunemaker/httparty/pull/455)
8
+
1
9
  == 0.13.7 aka "party not as hard"
2
10
  * remove post install emoji as it caused installation issues for some people
3
11
 
data/README.md CHANGED
@@ -62,6 +62,7 @@ httparty "https://api.stackexchange.com/2.2/questions?site=stackoverflow"
62
62
 
63
63
  ## Help and Docs
64
64
 
65
+ * [Docs](docs/)
65
66
  * https://groups.google.com/forum/#!forum/httparty-gem
66
67
  * http://rdoc.info/projects/jnunemaker/httparty
67
68
  * http://stackoverflow.com/questions/tagged/httparty
@@ -0,0 +1,100 @@
1
+ # httparty
2
+
3
+ Makes http fun again!
4
+
5
+ ## Table of contents
6
+ - [Working with SSL](#working-with-ssl)
7
+
8
+ ## Working with SSL
9
+
10
+ You can use this guide to work with SSL certificates.
11
+
12
+ #### Using `pem` option
13
+
14
+ ```ruby
15
+ # Use this example if you are using a pem file
16
+
17
+ class Client
18
+ include HTTParty
19
+
20
+ base_uri "https://example.com"
21
+ pem File.read("#{File.expand_path('.')}/path/to/certs/cert.pem"), "123456"
22
+
23
+ end
24
+ ```
25
+
26
+ #### Using `pkcs12` option
27
+
28
+ ```ruby
29
+ # Use this example if you are using a pkcs12 file
30
+
31
+ class Client
32
+ include HTTParty
33
+
34
+ base_uri "https://example.com"
35
+ pkcs12 File.read("#{File.expand_path('.')}/path/to/certs/cert.p12"), "123456"
36
+
37
+ end
38
+ ```
39
+
40
+ #### Using `ssl_ca_file` option
41
+
42
+ ```ruby
43
+ # Use this example if you are using a pkcs12 file
44
+
45
+ class Client
46
+ include HTTParty
47
+
48
+ base_uri "https://example.com"
49
+ ssl_ca_file "#{File.expand_path('.')}/path/to/certs/cert.pem"
50
+
51
+ end
52
+ ```
53
+
54
+ #### Using `ssl_ca_path` option
55
+
56
+ ```ruby
57
+ # Use this example if you are using a pkcs12 file
58
+
59
+ class Client
60
+ include HTTParty
61
+
62
+ base_uri "https://example.com"
63
+ ssl_ca_path '/path/to/certs'
64
+ end
65
+ ```
66
+
67
+ You can also include this options with the call:
68
+
69
+ ```ruby
70
+ class Client
71
+ include HTTParty
72
+
73
+ base_uri "https://example.com"
74
+
75
+ def self.fetch
76
+ get("/resources", pem: (File.read("#{File.expand_path('.')}/path/to/certs/cert.pem"), "123456")
77
+ end
78
+ end
79
+ ```
80
+
81
+ ### Avoid SSL verification
82
+
83
+ In some cases you may want to skip SSL verification, because the entity that issue the certificate is not a valid one, but you still want to work with it. You can achieve this through:
84
+
85
+ ```ruby
86
+ #Skips SSL certificate verification
87
+
88
+ class Client
89
+ include HTTParty
90
+
91
+ base_uri "https://example.com"
92
+ pem File.read("#{File.expand_path('.')}/path/to/certs/cert.pem"), "123456"
93
+
94
+ def self.fetch
95
+ get("/resources", verify: false)
96
+ # You can also use something like:
97
+ # get("resources", verify_peer: false)
98
+ end
99
+ end
100
+ ```
@@ -3,30 +3,41 @@ require File.join(dir, 'httparty')
3
3
 
4
4
  class TripIt
5
5
  include HTTParty
6
- base_uri 'http://www.tripit.com'
6
+ base_uri 'https://www.tripit.com'
7
7
  debug_output
8
8
 
9
9
  def initialize(email, password)
10
10
  @email = email
11
- response = self.class.get('/account/login')
12
- response = self.class.post(
11
+ get_response = self.class.get('/account/login')
12
+ get_response_cookie = parse_cookie(get_response.headers['Set-Cookie'])
13
+
14
+ post_response = self.class.post(
13
15
  '/account/login',
14
16
  body: {
15
17
  login_email_address: email,
16
18
  login_password: password
17
19
  },
18
- headers: {'Cookie' => response.headers['Set-Cookie']}
20
+ headers: {'Cookie' => get_response_cookie.to_cookie_string }
19
21
  )
20
- @cookie = response.request.options[:headers]['Cookie']
22
+
23
+ @cookie = parse_cookie(post_response.headers['Set-Cookie'])
21
24
  end
22
25
 
23
26
  def account_settings
24
- self.class.get('/account/edit', headers: {'Cookie' => @cookie})
27
+ self.class.get('/account/edit', headers: {'Cookie' => @cookie.to_cookie_string})
25
28
  end
26
29
 
27
30
  def logged_in?
28
31
  account_settings.include? "You're logged in as #{@email}"
29
32
  end
33
+
34
+ private
35
+
36
+ def parse_cookie(resp)
37
+ cookie_hash = CookieHash.new
38
+ resp.get_fields('Set-Cookie').each { |c| cookie_hash.add_cookies(c) }
39
+ cookie_hash
40
+ end
30
41
  end
31
42
 
32
43
  tripit = TripIt.new('email', 'password')
@@ -11,8 +11,8 @@ def constantize(camel_cased_word)
11
11
  constant
12
12
  end
13
13
 
14
- Then /it should return an? (\w+)$/ do |class_string|
15
- expect(@response_from_httparty).to be_a(class_string.class)
14
+ Then /it should return an? ([\w\:]+)$/ do |class_string|
15
+ expect(@response_from_httparty.parsed_response).to be_a(Object.const_get(class_string))
16
16
  end
17
17
 
18
18
  Then /the return value should match '(.*)'/ do |expected_text|
@@ -20,7 +20,7 @@ Then /the return value should match '(.*)'/ do |expected_text|
20
20
  end
21
21
 
22
22
  Then /it should return a Hash equaling:/ do |hash_table|
23
- expect(@response_from_httparty).to be_a(Hash)
23
+ expect(@response_from_httparty.parsed_response).to be_a(Hash)
24
24
  expect(@response_from_httparty.keys.length).to eq(hash_table.rows.length)
25
25
  hash_table.hashes.each do |pair|
26
26
  key, value = pair["key"], pair["value"]
@@ -30,7 +30,7 @@ Then /it should return a Hash equaling:/ do |hash_table|
30
30
  end
31
31
 
32
32
  Then /it should return an Array equaling:/ do |array|
33
- expect(@response_from_httparty).to be_a(Array)
33
+ expect(@response_from_httparty.parsed_response).to be_a(Array)
34
34
  expect(@response_from_httparty.parsed_response).to eq(array.raw)
35
35
  end
36
36
 
@@ -50,3 +50,7 @@ Then /it should raise (?:an|a) ([\w:]+) exception/ do |exception|
50
50
  expect(@exception_from_httparty).to_not be_nil
51
51
  expect(@exception_from_httparty).to be_a constantize(exception)
52
52
  end
53
+
54
+ Then /it should not raise (?:an|a) ([\w:]+) exception/ do |exception|
55
+ expect(@exception_from_httparty).to be_nil
56
+ end
@@ -17,9 +17,11 @@ Given /that service is accessed at the path '(.*)'/ do |path|
17
17
  @server.register(path, @handler)
18
18
  end
19
19
 
20
- Given /^that service takes (\d+) seconds to generate a response$/ do |time|
21
- @server_response_time = time.to_i
22
- @handler.preprocessor = proc { sleep time.to_i }
20
+ Given /^that service takes (\d+) (.*) to generate a response$/ do |time, unit|
21
+ time = time.to_i
22
+ time *= 60 if unit =~ /minute/
23
+ @server_response_time = time
24
+ @handler.preprocessor = proc { sleep time }
23
25
  end
24
26
 
25
27
  Given /^a remote deflate service$/ do
@@ -66,7 +68,7 @@ end
66
68
 
67
69
  # customize aruba cucumber step
68
70
  Then /^the output should contain '(.*)'$/ do |expected|
69
- assert_partial_output(expected, all_output)
71
+ expect(all_commands.map(&:output).join("\n")).to match_output_string(expected)
70
72
  end
71
73
 
72
74
  Given /a restricted page at '(.*)'/ do |url|
@@ -15,7 +15,6 @@ Gem::Specification.new do |s|
15
15
 
16
16
  s.required_ruby_version = '>= 1.9.3'
17
17
 
18
- s.add_dependency 'json', "~> 1.8"
19
18
  s.add_dependency 'multi_xml', ">= 0.5.2"
20
19
 
21
20
  # If this line is removed, all hard partying will cease.
@@ -72,6 +72,16 @@ module HTTParty
72
72
  default_options[:log_format] = format
73
73
  end
74
74
 
75
+ # Raises HTTParty::ResponseError if response's code matches this statuses
76
+ #
77
+ # class Foo
78
+ # include HTTParty
79
+ # raise_on [404, 500]
80
+ # end
81
+ def raise_on(codes = [])
82
+ default_options[:raise_on] = *codes
83
+ end
84
+
75
85
  # Allows setting http proxy information to be used
76
86
  #
77
87
  # class Foo
@@ -147,7 +157,7 @@ module HTTParty
147
157
  # default_params api_key: 'secret', another: 'foo'
148
158
  # end
149
159
  def default_params(h = {})
150
- raise ArgumentError, 'Default params must an object which respond to #to_hash' unless h.respond_to?(:to_hash)
160
+ raise ArgumentError, 'Default params must be an object which responds to #to_hash' unless h.respond_to?(:to_hash)
151
161
  default_options[:default_params] ||= {}
152
162
  default_options[:default_params].merge!(h)
153
163
  end
@@ -204,13 +214,13 @@ module HTTParty
204
214
  # headers 'Accept' => 'text/html'
205
215
  # end
206
216
  def headers(h = {})
207
- raise ArgumentError, 'Headers must an object which responds to #to_hash' unless h.respond_to?(:to_hash)
217
+ raise ArgumentError, 'Headers must be an object which responds to #to_hash' unless h.respond_to?(:to_hash)
208
218
  default_options[:headers] ||= {}
209
219
  default_options[:headers].merge!(h.to_hash)
210
220
  end
211
221
 
212
222
  def cookies(h = {})
213
- raise ArgumentError, 'Cookies must an object which respond to #to_hash' unless h.respond_to?(:to_hash)
223
+ raise ArgumentError, 'Cookies must be an object which responds to #to_hash' unless h.respond_to?(:to_hash)
214
224
  default_cookies.add_cookies(h)
215
225
  end
216
226
 
@@ -486,7 +496,7 @@ module HTTParty
486
496
  # Foo.post('http://foo.com/resources', body: {bar: 'baz'})
487
497
  #
488
498
  # # Simple post with full url using :query option,
489
- # # which gets set as form data on the request.
499
+ # # which appends the parameters to the URI.
490
500
  # Foo.post('http://foo.com/resources', query: {bar: 'baz'})
491
501
  def post(path, options = {}, &block)
492
502
  perform_request Net::HTTP::Post, path, options, &block
@@ -528,12 +538,17 @@ module HTTParty
528
538
  perform_request Net::HTTP::Options, path, options, &block
529
539
  end
530
540
 
541
+ # Perform a MKCOL request to a path
542
+ def mkcol(path, options = {}, &block)
543
+ perform_request Net::HTTP::Mkcol, path, options, &block
544
+ end
545
+
531
546
  attr_reader :default_options
532
547
 
533
548
  private
534
549
 
535
550
  def ensure_method_maintained_across_redirects(options)
536
- unless options.has_key? :maintain_method_across_redirects
551
+ unless options.key?(:maintain_method_across_redirects)
537
552
  options[:maintain_method_across_redirects] = true
538
553
  end
539
554
  end
@@ -53,6 +53,11 @@ module HTTParty
53
53
  # Private: Regex used to strip brackets from IPv6 URIs.
54
54
  StripIpv6BracketsRegex = /\A\[(.*)\]\z/
55
55
 
56
+ OPTION_DEFAULTS = {
57
+ verify: true,
58
+ verify_peer: true
59
+ }
60
+
56
61
  # Public
57
62
  def self.call(uri, options)
58
63
  new(uri, options).connection
@@ -65,13 +70,13 @@ module HTTParty
65
70
  raise ArgumentError, "uri must be a #{uri_adapter}, not a #{uri.class}" unless uri.is_a? uri_adapter
66
71
 
67
72
  @uri = uri
68
- @options = options
73
+ @options = OPTION_DEFAULTS.merge(options)
69
74
  end
70
75
 
71
76
  def connection
72
77
  host = clean_host(uri.host)
73
78
  port = uri.port || (uri.scheme == 'https' ? 443 : 80)
74
- if options[:http_proxyaddr]
79
+ if options.key?(:http_proxyaddr)
75
80
  http = Net::HTTP.new(host, port, options[:http_proxyaddr], options[:http_proxyport], options[:http_proxyuser], options[:http_proxypass])
76
81
  else
77
82
  http = Net::HTTP.new(host, port)
@@ -138,6 +143,10 @@ module HTTParty
138
143
  uri.port == 443 || uri.scheme == 'https'
139
144
  end
140
145
 
146
+ def verify_ssl_certificate?
147
+ !(options[:verify] == false || options[:verify_peer] == false)
148
+ end
149
+
141
150
  def attach_ssl_certificates(http, options)
142
151
  if http.use_ssl?
143
152
  if options.fetch(:verify, true)
@@ -158,7 +167,7 @@ module HTTParty
158
167
  if options[:pem]
159
168
  http.cert = OpenSSL::X509::Certificate.new(options[:pem])
160
169
  http.key = OpenSSL::PKey::RSA.new(options[:pem], options[:pem_password])
161
- http.verify_mode = options[:verify_peer] == false ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER
170
+ http.verify_mode = verify_ssl_certificate? ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
162
171
  end
163
172
 
164
173
  # PKCS12 client certificate authentication
@@ -166,7 +175,7 @@ module HTTParty
166
175
  p12 = OpenSSL::PKCS12.new(options[:p12], options[:p12_password])
167
176
  http.cert = p12.certificate
168
177
  http.key = p12.key
169
- http.verify_mode = options[:verify_peer] == false ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER
178
+ http.verify_mode = verify_ssl_certificate? ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
170
179
  end
171
180
 
172
181
  # SSL certificate authority file and/or directory
@@ -16,6 +16,6 @@ class HTTParty::CookieHash < Hash #:nodoc:
16
16
  end
17
17
 
18
18
  def to_cookie_string
19
- delete_if { |k, v| CLIENT_COOKIES.include?(k.to_s.downcase) }.collect { |k, v| "#{k}=#{v}" }.join("; ")
19
+ reject { |k, v| CLIENT_COOKIES.include?(k.to_s.downcase) }.collect { |k, v| "#{k}=#{v}" }.join("; ")
20
20
  end
21
21
  end
@@ -2,7 +2,7 @@ module HTTParty
2
2
  # @abstact Exceptions raised by HTTParty inherit from Error
3
3
  class Error < StandardError; end
4
4
 
5
- # Exception raised when you attempt to set a non-existant format
5
+ # Exception raised when you attempt to set a non-existent format
6
6
  class UnsupportedFormat < Error; end
7
7
 
8
8
  # Exception raised when using a URI scheme other than HTTP or HTTPS
@@ -26,4 +26,7 @@ module HTTParty
26
26
  # Exception that is raised when request has redirected too many times.
27
27
  # Calling {#response} returns the Net:HTTP response object.
28
28
  class RedirectionTooDeep < ResponseError; end
29
+
30
+ # Exception that is raised when request redirects and location header is present more than once
31
+ class DuplicateLocationHeader < ResponseError; end
29
32
  end
@@ -26,7 +26,11 @@ module HTTParty
26
26
  stack = []
27
27
 
28
28
  if value.respond_to?(:to_ary)
29
- param << value.to_ary.map { |element| normalize_param("#{key}[]", element) }.join
29
+ param << if value.empty?
30
+ "#{key}[]=&"
31
+ else
32
+ value.to_ary.map { |element| normalize_param("#{key}[]", element) }.join
33
+ end
30
34
  elsif value.respond_to?(:to_hash)
31
35
  stack << [key, value.to_hash]
32
36
  else
@@ -2,46 +2,89 @@ module HTTParty
2
2
  module Logger
3
3
  class CurlFormatter #:nodoc:
4
4
  TAG_NAME = HTTParty.name
5
- OUT = ">"
6
- IN = "<"
5
+ OUT = '>'.freeze
6
+ IN = '<'.freeze
7
7
 
8
- attr_accessor :level, :logger, :current_time
8
+ attr_accessor :level, :logger
9
9
 
10
10
  def initialize(logger, level)
11
- @logger = logger
12
- @level = level.to_sym
11
+ @logger = logger
12
+ @level = level.to_sym
13
+ @messages = []
13
14
  end
14
15
 
15
16
  def format(request, response)
16
- messages = []
17
- time = Time.now.strftime("%Y-%m-%d %H:%M:%S %z")
18
- http_method = request.http_method.name.split("::").last.upcase
19
- path = request.path.to_s
17
+ @request = request
18
+ @response = response
20
19
 
21
- messages << print(time, OUT, "#{http_method} #{path}")
20
+ log_request
21
+ log_response
22
22
 
23
- if request.options[:headers] && request.options[:headers].size > 0
24
- request.options[:headers].each do |k, v|
25
- messages << print(time, OUT, "#{k}: #{v}")
26
- end
27
- end
23
+ logger.send level, messages.join("\n")
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :request, :response
29
+ attr_accessor :messages
30
+
31
+ def log_request
32
+ log_url
33
+ log_headers
34
+ log_query
35
+ log OUT, request.raw_body if request.raw_body
36
+ log OUT
37
+ end
38
+
39
+ def log_response
40
+ log IN, "HTTP/#{response.http_version} #{response.code}"
41
+ log_response_headers
42
+ log IN, "\n#{response.body}"
43
+ log IN
44
+ end
45
+
46
+ def log_url
47
+ http_method = request.http_method.name.split("::").last.upcase
48
+ uri = if request.options[:base_uri]
49
+ request.options[:base_uri] + request.path.path
50
+ else
51
+ request.path.to_s
52
+ end
28
53
 
29
- messages << print(time, OUT, request.raw_body)
30
- messages << print(time, OUT, "")
31
- messages << print(time, IN, "HTTP/#{response.http_version} #{response.code}")
54
+ log OUT, "#{http_method} #{uri}"
55
+ end
56
+
57
+ def log_headers
58
+ return unless request.options[:headers] && request.options[:headers].size > 0
59
+
60
+ log OUT, 'Headers: '
61
+ log_hash request.options[:headers]
62
+ end
63
+
64
+ def log_query
65
+ return unless request.options[:query]
32
66
 
67
+ log OUT, 'Query: '
68
+ log_hash request.options[:query]
69
+ end
70
+
71
+ def log_response_headers
33
72
  headers = response.respond_to?(:headers) ? response.headers : response
34
73
  response.each_header do |response_header|
35
- messages << print(time, IN, "#{response_header.capitalize}: #{headers[response_header]}")
74
+ log IN, "#{response_header.capitalize}: #{headers[response_header]}"
36
75
  end
76
+ end
37
77
 
38
- messages << print(time, IN, "\n#{response.body}")
78
+ def log_hash(hash)
79
+ hash.each { |k, v| log(OUT, "#{k}: #{v}") }
80
+ end
39
81
 
40
- @logger.send @level, messages.join("\n")
82
+ def log(direction, line = '')
83
+ messages << "[#{TAG_NAME}] [#{time}] #{direction} #{line}"
41
84
  end
42
85
 
43
- def print(time, direction, line)
44
- "[#{TAG_NAME}] [#{time}] #{direction} #{line}"
86
+ def time
87
+ @time ||= Time.now.strftime("%Y-%m-%d %H:%M:%S %z")
45
88
  end
46
89
  end
47
90
  end