httparty 0.5.2 → 0.6.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.

data/History CHANGED
@@ -1,3 +1,21 @@
1
+ == 0.6.0 2010-06-13
2
+ * major enhancements
3
+ * Digest Auth (bartiaco, sbecker, gilles, and aaronrussell)
4
+ * Maintain HTTP method across redirects (bartiaco and sbecker)
5
+ * HTTParty::Response#response returns the Net::HTTPResponse object
6
+ * HTTParty::Response#headers returns a HTTParty::Response::Headers object
7
+ which quacks like a Hash + Net::HTTPHeader. The #headers method continues
8
+ to be backwards-compatible with the old Hash return value but may become
9
+ deprecated in the future.
10
+
11
+ * minor enhancements
12
+ * Update crack requirement to version 0.1.7
13
+ You may still get a warning because Crack's version constant is out of date
14
+ * Timeout option can be set for all requests using HTTParty.default_timeout (taazza)
15
+ * Closed #38 "headers hash should downcase keys so canonical header name can be used"
16
+ * Closed #40 "Gzip response" wherein gziped and deflated responses are
17
+ automatically inflated. (carsonmcdonald)
18
+
1
19
  == 0.5.2 2010-01-31
2
20
  * minor enhancements
3
21
  * Update crack requirement to version 0.1.6
data/Rakefile CHANGED
@@ -9,12 +9,12 @@ begin
9
9
  gem.email = "nunemaker@gmail.com"
10
10
  gem.homepage = "http://httparty.rubyforge.org"
11
11
  gem.authors = ["John Nunemaker", "Sandro Turriate"]
12
- gem.add_dependency 'crack', '0.1.6'
12
+ gem.add_dependency 'crack', '0.1.7'
13
13
  gem.add_development_dependency "activesupport", "~>2.3"
14
- gem.add_development_dependency "cucumber", "~>0.4"
14
+ gem.add_development_dependency "cucumber", "~>0.7"
15
15
  gem.add_development_dependency "fakeweb", "~>1.2"
16
16
  gem.add_development_dependency "mongrel", "~>1.1"
17
- gem.add_development_dependency "rspec", "1.2.9"
17
+ gem.add_development_dependency "rspec", "~>1.3"
18
18
  gem.post_install_message = "When you HTTParty, you must party hard!"
19
19
  gem.rubyforge_project = 'httparty'
20
20
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.2
1
+ 0.6.0
@@ -1,5 +1,5 @@
1
1
  require 'rubygems'
2
- require 'activesupport'
2
+ require 'active_support'
3
3
 
4
4
  dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
5
5
  require File.join(dir, 'httparty')
@@ -29,4 +29,4 @@ module AAWS
29
29
  end
30
30
 
31
31
  aaws = AAWS::Book.new(config[:access_key])
32
- pp aaws.search(:query => {:title => 'Ruby On Rails'})
32
+ pp aaws.search(:query => {:title => 'Ruby On Rails'})
@@ -0,0 +1,20 @@
1
+ Feature: Digest Authentication
2
+
3
+ As a developer
4
+ I want to be able to use a service that requires Digest Authentication
5
+ Because that is not an uncommon requirement
6
+
7
+ Scenario: Passing no credentials to a page requiring Digest Authentication
8
+ Given a restricted page at '/protected.html'
9
+ When I call HTTParty#get with '/protected.html'
10
+ Then it should return a response with a 401 response code
11
+
12
+ Scenario: Passing proper credentials to a page requiring Digest Authentication
13
+ Given a remote service that returns 'Digest Authenticated Page'
14
+ And that service is accessed at the path '/protected.html'
15
+ And that service is protected by Digest Authentication
16
+ And that service requires the username 'jcash' with the password 'maninblack'
17
+ When I call HTTParty#get with '/protected.html' and a digest_auth hash:
18
+ | username | password |
19
+ | jcash | maninblack |
20
+ Then the return value should match 'Digest Authenticated Page'
@@ -0,0 +1,19 @@
1
+ Feature: Handles Compressed Responses
2
+
3
+ In order to save bandwidth
4
+ As a developer
5
+ I want to uncompress compressed responses
6
+
7
+ Scenario: Supports deflate encoding
8
+ Given a remote deflate service
9
+ And the response from the service has a body of '<h1>Some HTML</h1>'
10
+ And that service is accessed at the path '/service.html'
11
+ When I call HTTParty#get with '/service.html'
12
+ Then the return value should match '<h1>Some HTML</h1>'
13
+
14
+ Scenario: Supports gzip encoding
15
+ Given a remote gzip service
16
+ And the response from the service has a body of '<h1>Some HTML</h1>'
17
+ And that service is accessed at the path '/service.html'
18
+ When I call HTTParty#get with '/service.html'
19
+ Then the return value should match '<h1>Some HTML</h1>'
@@ -1,10 +1,17 @@
1
1
  require 'mongrel'
2
- require 'activesupport'
2
+ require 'active_support'
3
3
  require 'lib/httparty'
4
4
  require 'spec/expectations'
5
5
 
6
6
  Before do
7
- port = ENV["HTTPARTY_PORT"] || 31981
7
+ def new_port
8
+ server = TCPServer.new('0.0.0.0', nil)
9
+ port = server.addr[1]
10
+ ensure
11
+ server.close
12
+ end
13
+
14
+ port = ENV["HTTPARTY_PORT"] || new_port
8
15
  @host_and_port = "0.0.0.0:#{port}"
9
16
  @server = Mongrel::HttpServer.new("0.0.0.0", port)
10
17
  @server.run
@@ -17,3 +17,11 @@ When /I call HTTParty#get with '(.*)' and a basic_auth hash:/ do |url, auth_tabl
17
17
  :basic_auth => { :username => h["username"], :password => h["password"] }
18
18
  )
19
19
  end
20
+
21
+ When /I call HTTParty#get with '(.*)' and a digest_auth hash:/ do |url, auth_table|
22
+ h = auth_table.hashes.first
23
+ @response_from_httparty = HTTParty.get(
24
+ "http://#{@host_and_port}#{url}",
25
+ :digest_auth => { :username => h["username"], :password => h["password"] }
26
+ )
27
+ end
@@ -1,53 +1,90 @@
1
- def basic_mongrel_handler
2
- Class.new(Mongrel::HttpHandler) do
3
- attr_writer :content_type, :response_body, :response_code, :preprocessor
4
-
5
- def initialize
6
- @content_type = "text/html"
7
- @response_body = ""
8
- @response_code = 200
9
- @custom_headers = {}
10
- end
1
+ class BasicMongrelHandler < Mongrel::HttpHandler
2
+ attr_accessor :content_type, :custom_headers, :response_body, :response_code, :preprocessor, :username, :password
3
+
4
+ def initialize
5
+ @content_type = "text/html"
6
+ @response_body = ""
7
+ @response_code = 200
8
+ @custom_headers = {}
9
+ end
11
10
 
12
- def process(request, response)
13
- instance_eval &@preprocessor if @preprocessor
14
- reply_with(response, @response_code, @response_body)
11
+ def process(request, response)
12
+ instance_eval &preprocessor if preprocessor
13
+ reply_with(response, response_code, response_body)
14
+ end
15
+
16
+ def reply_with(response, code, response_body)
17
+ response.start(code) do |head, body|
18
+ head["Content-Type"] = content_type
19
+ custom_headers.each { |k,v| head[k] = v }
20
+ body.write(response_body)
15
21
  end
22
+ end
23
+ end
16
24
 
17
- def reply_with(response, code, response_body)
18
- response.start(code) do |head, body|
19
- head["Content-Type"] = @content_type
20
- @custom_headers.each { |k,v| head[k] = v }
21
- body.write(response_body)
22
- end
25
+ class DeflateHandler < BasicMongrelHandler
26
+ def process(request, response)
27
+ response.start do |head, body|
28
+ head['Content-Encoding'] = 'deflate'
29
+ body.write Zlib::Deflate.deflate(response_body)
23
30
  end
24
31
  end
25
32
  end
26
33
 
27
- def new_mongrel_handler
28
- basic_mongrel_handler.new
34
+ class GzipHandler < BasicMongrelHandler
35
+ def process(request, response)
36
+ response.start do |head, body|
37
+ head['Content-Encoding'] = 'gzip'
38
+ body.write gzip(response_body)
39
+ end
40
+ end
41
+
42
+ protected
43
+
44
+ def gzip(string)
45
+ sio = StringIO.new('', 'r+')
46
+ gz = Zlib::GzipWriter.new sio
47
+ gz.write string
48
+ gz.finish
49
+ sio.rewind
50
+ sio.read
51
+ end
29
52
  end
30
53
 
31
- def add_basic_authentication_to(handler)
32
- m = Module.new do
33
- attr_writer :username, :password
54
+ module BasicAuthentication
55
+ def self.extended(base)
56
+ base.custom_headers["WWW-Authenticate"] = 'Basic Realm="Super Secret Page"'
57
+ end
34
58
 
35
- def self.extended(base)
36
- base.instance_eval { @custom_headers["WWW-Authenticate"] = 'Basic Realm="Super Secret Page"' }
37
- base.class_eval { alias_method_chain :process, :basic_authentication }
59
+ def process(request, response)
60
+ if authorized?(request)
61
+ super
62
+ else
63
+ reply_with(response, 401, "Incorrect. You have 20 seconds to comply.")
38
64
  end
65
+ end
39
66
 
40
- def process_with_basic_authentication(request, response)
41
- if authorized?(request) then process_without_basic_authentication(request, response)
42
- else reply_with(response, 401, "Incorrect. You have 20 seconds to comply.")
43
- end
44
- end
67
+ def authorized?(request)
68
+ request.params["HTTP_AUTHORIZATION"] == "Basic " + Base64.encode64("#{@username}:#{@password}").strip
69
+ end
70
+ end
71
+
72
+ module DigestAuthentication
73
+ def self.extended(base)
74
+ base.custom_headers["WWW-Authenticate"] = 'Digest realm="testrealm@host.com",qop="auth,auth-int",nonce="nonce",opaque="opaque"'
75
+ end
45
76
 
46
- def authorized?(request)
47
- request.params["HTTP_AUTHORIZATION"] == "Basic " + Base64.encode64("#{@username}:#{@password}").strip
77
+ def process(request, response)
78
+ if authorized?(request)
79
+ super
80
+ else
81
+ reply_with(response, 401, "Incorrect. You have 20 seconds to comply.")
48
82
  end
49
83
  end
50
- handler.extend(m)
84
+
85
+ def authorized?(request)
86
+ request.params["HTTP_AUTHORIZATION"] =~ /Digest.*uri=/
87
+ end
51
88
  end
52
89
 
53
90
  def new_mongrel_redirector(target_url, relative_path = false)
@@ -1,10 +1,10 @@
1
1
  Given /a remote service that returns '(.*)'/ do |response_body|
2
- @handler = new_mongrel_handler
2
+ @handler = BasicMongrelHandler.new
3
3
  Given "the response from the service has a body of '#{response_body}'"
4
4
  end
5
5
 
6
6
  Given /a remote service that returns a (\d+) status code/ do |code|
7
- @handler = new_mongrel_handler
7
+ @handler = BasicMongrelHandler.new
8
8
  @handler.response_code = code
9
9
  end
10
10
 
@@ -13,8 +13,16 @@ Given /that service is accessed at the path '(.*)'/ do |path|
13
13
  end
14
14
 
15
15
  Given /^that service takes (\d+) seconds to generate a response$/ do |time|
16
- preprocessor = lambda { sleep time.to_i }
17
- @handler.preprocessor = preprocessor
16
+ @server_response_time = time.to_i
17
+ @handler.preprocessor = lambda { sleep time.to_i }
18
+ end
19
+
20
+ Given /^a remote deflate service$/ do
21
+ @handler = DeflateHandler.new
22
+ end
23
+
24
+ Given /^a remote gzip service$/ do
25
+ @handler = GzipHandler.new
18
26
  end
19
27
 
20
28
  Given /the response from the service has a Content-Type of '(.*)'/ do |content_type|
@@ -30,7 +38,11 @@ Given /the url '(.*)' redirects to '(.*)'/ do |redirection_url, target_url|
30
38
  end
31
39
 
32
40
  Given /that service is protected by Basic Authentication/ do
33
- add_basic_authentication_to @handler
41
+ @handler.extend BasicAuthentication
42
+ end
43
+
44
+ Given /that service is protected by Digest Authentication/ do
45
+ @handler.extend DigestAuthentication
34
46
  end
35
47
 
36
48
  Given /that service requires the username '(.*)' with the password '(.*)'/ do |username, password|
@@ -50,3 +62,8 @@ end
50
62
  Given /I want to hit this in a browser/ do
51
63
  @server.acceptor.join
52
64
  end
65
+
66
+ Then /I wait for the server to recover/ do
67
+ timeout = @request_options[:timeout] || 0
68
+ sleep @server_response_time - timeout
69
+ end
@@ -10,3 +10,4 @@ Feature: Supports the timeout option
10
10
  When I set my HTTParty timeout option to 1
11
11
  And I call HTTParty#get with '/service.html'
12
12
  Then it should raise a Timeout::Error exception
13
+ And I wait for the server to recover
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{httparty}
8
- s.version = "0.5.2"
8
+ s.version = "0.6.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["John Nunemaker", "Sandro Turriate"]
12
- s.date = %q{2010-01-31}
12
+ s.date = %q{2010-06-13}
13
13
  s.default_executable = %q{httparty}
14
14
  s.description = %q{Makes http fun! Also, makes consuming restful web services dead easy.}
15
15
  s.email = %q{nunemaker@gmail.com}
@@ -38,6 +38,8 @@ Gem::Specification.new do |s|
38
38
  "features/basic_authentication.feature",
39
39
  "features/command_line.feature",
40
40
  "features/deals_with_http_error_codes.feature",
41
+ "features/digest_authentication.feature",
42
+ "features/handles_compressed_responses.feature",
41
43
  "features/handles_multiple_formats.feature",
42
44
  "features/steps/env.rb",
43
45
  "features/steps/httparty_response_steps.rb",
@@ -52,6 +54,7 @@ Gem::Specification.new do |s|
52
54
  "lib/httparty/core_extensions.rb",
53
55
  "lib/httparty/exceptions.rb",
54
56
  "lib/httparty/module_inheritable_attributes.rb",
57
+ "lib/httparty/net_digest_auth.rb",
55
58
  "lib/httparty/parser.rb",
56
59
  "lib/httparty/request.rb",
57
60
  "lib/httparty/response.rb",
@@ -77,7 +80,7 @@ Gem::Specification.new do |s|
77
80
  s.rdoc_options = ["--charset=UTF-8"]
78
81
  s.require_paths = ["lib"]
79
82
  s.rubyforge_project = %q{httparty}
80
- s.rubygems_version = %q{1.3.5}
83
+ s.rubygems_version = %q{1.3.6}
81
84
  s.summary = %q{Makes http fun! Also, makes consuming restful web services dead easy.}
82
85
  s.test_files = [
83
86
  "spec/httparty/cookie_hash_spec.rb",
@@ -102,27 +105,27 @@ Gem::Specification.new do |s|
102
105
  s.specification_version = 3
103
106
 
104
107
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
105
- s.add_runtime_dependency(%q<crack>, ["= 0.1.6"])
108
+ s.add_runtime_dependency(%q<crack>, ["= 0.1.7"])
106
109
  s.add_development_dependency(%q<activesupport>, ["~> 2.3"])
107
- s.add_development_dependency(%q<cucumber>, ["~> 0.4"])
110
+ s.add_development_dependency(%q<cucumber>, ["~> 0.7"])
108
111
  s.add_development_dependency(%q<fakeweb>, ["~> 1.2"])
109
112
  s.add_development_dependency(%q<mongrel>, ["~> 1.1"])
110
- s.add_development_dependency(%q<rspec>, ["= 1.2.9"])
113
+ s.add_development_dependency(%q<rspec>, ["~> 1.3"])
111
114
  else
112
- s.add_dependency(%q<crack>, ["= 0.1.6"])
115
+ s.add_dependency(%q<crack>, ["= 0.1.7"])
113
116
  s.add_dependency(%q<activesupport>, ["~> 2.3"])
114
- s.add_dependency(%q<cucumber>, ["~> 0.4"])
117
+ s.add_dependency(%q<cucumber>, ["~> 0.7"])
115
118
  s.add_dependency(%q<fakeweb>, ["~> 1.2"])
116
119
  s.add_dependency(%q<mongrel>, ["~> 1.1"])
117
- s.add_dependency(%q<rspec>, ["= 1.2.9"])
120
+ s.add_dependency(%q<rspec>, ["~> 1.3"])
118
121
  end
119
122
  else
120
- s.add_dependency(%q<crack>, ["= 0.1.6"])
123
+ s.add_dependency(%q<crack>, ["= 0.1.7"])
121
124
  s.add_dependency(%q<activesupport>, ["~> 2.3"])
122
- s.add_dependency(%q<cucumber>, ["~> 0.4"])
125
+ s.add_dependency(%q<cucumber>, ["~> 0.7"])
123
126
  s.add_dependency(%q<fakeweb>, ["~> 1.2"])
124
127
  s.add_dependency(%q<mongrel>, ["~> 1.1"])
125
- s.add_dependency(%q<rspec>, ["= 1.2.9"])
128
+ s.add_dependency(%q<rspec>, ["~> 1.3"])
126
129
  end
127
130
  end
128
131
 
@@ -1,19 +1,19 @@
1
1
  require 'pathname'
2
2
  require 'net/http'
3
3
  require 'net/https'
4
+ require 'uri'
5
+ require 'zlib'
4
6
  require 'crack'
5
7
 
6
- if Crack::VERSION != "0.1.6"
7
- warn "warning: HTTParty depends on version 0.1.6 of crack, not #{Crack::VERSION}."
8
- end
9
-
10
8
  dir = Pathname(__FILE__).dirname.expand_path
11
9
 
12
10
  require dir + 'httparty/module_inheritable_attributes'
13
11
  require dir + 'httparty/cookie_hash'
12
+ require dir + 'httparty/net_digest_auth'
14
13
 
15
14
  module HTTParty
16
- VERSION = "0.5.2".freeze
15
+ VERSION = "0.6.0".freeze
16
+ CRACK_DEPENDENCY = "0.1.7".freeze
17
17
 
18
18
  module AllowedFormatsDeprecation
19
19
  def const_missing(const)
@@ -73,6 +73,16 @@ module HTTParty
73
73
  default_options[:basic_auth] = {:username => u, :password => p}
74
74
  end
75
75
 
76
+ # Allows setting digest authentication username and password.
77
+ #
78
+ # class Foo
79
+ # include HTTParty
80
+ # digest_auth 'username', 'password'
81
+ # end
82
+ def digest_auth(u, p)
83
+ default_options[:digest_auth] = {:username => u, :password => p}
84
+ end
85
+
76
86
  # Allows setting default parameters to be appended to each request.
77
87
  # Great for api keys and such.
78
88
  #
@@ -86,6 +96,18 @@ module HTTParty
86
96
  default_options[:default_params].merge!(h)
87
97
  end
88
98
 
99
+ # Allows setting a default timeout for all HTTP calls
100
+ # Timeout is specified in seconds.
101
+ #
102
+ # class Foo
103
+ # include HTTParty
104
+ # default_timeout 10
105
+ # end
106
+ def default_timeout(t)
107
+ raise ArgumentError, 'Timeout must be an integer' unless t && t.is_a?(Integer)
108
+ default_options[:timeout] = t
109
+ end
110
+
89
111
  # Set an output stream for debugging, defaults to $stderr.
90
112
  # The output stream is passed on to Net::HTTP#set_debug_output.
91
113
  #
@@ -97,7 +119,7 @@ module HTTParty
97
119
  default_options[:debug_output] = stream
98
120
  end
99
121
 
100
- # Allows setting a base uri to be used for each request.
122
+ # Allows setting HTTP headers to be used for each request.
101
123
  #
102
124
  # class Foo
103
125
  # include HTTParty
@@ -154,6 +176,21 @@ module HTTParty
154
176
  default_options[:no_follow] = value
155
177
  end
156
178
 
179
+ # Declare that you wish to maintain the chosen HTTP method across redirects.
180
+ # The default behavior is to follow redirects via the GET method.
181
+ # If you wish to maintain the original method, you can set this option to true.
182
+ #
183
+ # @example
184
+ # class Foo
185
+ # include HTTParty
186
+ # base_uri 'http://google.com'
187
+ # maintain_method_across_redirects true
188
+ # end
189
+
190
+ def maintain_method_across_redirects(value = true)
191
+ default_options[:maintain_method_across_redirects] = value
192
+ end
193
+
157
194
  # Allows setting a PEM file to be used
158
195
  #
159
196
  # class Foo
@@ -302,3 +339,7 @@ require dir + 'httparty/exceptions'
302
339
  require dir + 'httparty/parser'
303
340
  require dir + 'httparty/request'
304
341
  require dir + 'httparty/response'
342
+
343
+ if Crack::VERSION != HTTParty::CRACK_DEPENDENCY
344
+ warn "warning: HTTParty depends on version #{HTTParty::CRACK_DEPENDENCY} of crack, not #{Crack::VERSION}."
345
+ end