httparty2 0.7.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/.gitignore +9 -0
  2. data/Gemfile +6 -0
  3. data/History +253 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.rdoc +54 -0
  6. data/Rakefile +13 -0
  7. data/bin/httparty +108 -0
  8. data/cucumber.yml +1 -0
  9. data/examples/aaws.rb +32 -0
  10. data/examples/basic.rb +32 -0
  11. data/examples/custom_parsers.rb +67 -0
  12. data/examples/delicious.rb +37 -0
  13. data/examples/google.rb +16 -0
  14. data/examples/rubyurl.rb +14 -0
  15. data/examples/tripit_sign_in.rb +33 -0
  16. data/examples/twitter.rb +31 -0
  17. data/examples/whoismyrep.rb +10 -0
  18. data/features/basic_authentication.feature +20 -0
  19. data/features/command_line.feature +7 -0
  20. data/features/deals_with_http_error_codes.feature +26 -0
  21. data/features/digest_authentication.feature +20 -0
  22. data/features/handles_compressed_responses.feature +19 -0
  23. data/features/handles_multiple_formats.feature +34 -0
  24. data/features/steps/env.rb +22 -0
  25. data/features/steps/httparty_response_steps.rb +26 -0
  26. data/features/steps/httparty_steps.rb +27 -0
  27. data/features/steps/mongrel_helper.rb +94 -0
  28. data/features/steps/remote_service_steps.rb +69 -0
  29. data/features/supports_redirection.feature +22 -0
  30. data/features/supports_timeout_option.feature +13 -0
  31. data/httparty.gemspec +31 -0
  32. data/lib/httparty.rb +455 -0
  33. data/lib/httparty/cookie_hash.rb +22 -0
  34. data/lib/httparty/core_extensions.rb +9 -0
  35. data/lib/httparty/exceptions.rb +26 -0
  36. data/lib/httparty/module_inheritable_attributes.rb +34 -0
  37. data/lib/httparty/net_digest_auth.rb +71 -0
  38. data/lib/httparty/parser.rb +140 -0
  39. data/lib/httparty/request.rb +252 -0
  40. data/lib/httparty/response.rb +85 -0
  41. data/lib/httparty/version.rb +3 -0
  42. data/spec/fixtures/delicious.xml +23 -0
  43. data/spec/fixtures/empty.xml +0 -0
  44. data/spec/fixtures/google.html +3 -0
  45. data/spec/fixtures/ssl/generate.sh +29 -0
  46. data/spec/fixtures/ssl/generated/1fe462c2.0 +16 -0
  47. data/spec/fixtures/ssl/generated/bogushost.crt +13 -0
  48. data/spec/fixtures/ssl/generated/ca.crt +16 -0
  49. data/spec/fixtures/ssl/generated/ca.key +15 -0
  50. data/spec/fixtures/ssl/generated/selfsigned.crt +14 -0
  51. data/spec/fixtures/ssl/generated/server.crt +13 -0
  52. data/spec/fixtures/ssl/generated/server.key +15 -0
  53. data/spec/fixtures/ssl/openssl-exts.cnf +9 -0
  54. data/spec/fixtures/twitter.json +1 -0
  55. data/spec/fixtures/twitter.xml +403 -0
  56. data/spec/fixtures/undefined_method_add_node_for_nil.xml +2 -0
  57. data/spec/httparty/cookie_hash_spec.rb +71 -0
  58. data/spec/httparty/net_digest_auth_spec.rb +93 -0
  59. data/spec/httparty/parser_spec.rb +155 -0
  60. data/spec/httparty/request_spec.rb +496 -0
  61. data/spec/httparty/response_spec.rb +193 -0
  62. data/spec/httparty/ssl_spec.rb +54 -0
  63. data/spec/httparty_spec.rb +621 -0
  64. data/spec/spec.opts +3 -0
  65. data/spec/spec_helper.rb +23 -0
  66. data/spec/support/ssl_test_helper.rb +25 -0
  67. data/spec/support/ssl_test_server.rb +69 -0
  68. data/spec/support/stub_response.rb +30 -0
  69. data/website/css/common.css +47 -0
  70. data/website/index.html +73 -0
  71. metadata +206 -0
@@ -0,0 +1,26 @@
1
+ Then /it should return an? (\w+)$/ do |class_string|
2
+ @response_from_httparty.should be_an_instance_of(class_string.class)
3
+ end
4
+
5
+ Then /the return value should match '(.*)'/ do |expected_text|
6
+ @response_from_httparty.should eql(expected_text)
7
+ end
8
+
9
+ Then /it should return a Hash equaling:/ do |hash_table|
10
+ @response_from_httparty.should be_an_instance_of(Hash)
11
+ @response_from_httparty.keys.length.should eql(hash_table.rows.length)
12
+ hash_table.hashes.each do |pair|
13
+ key, value = pair["key"], pair["value"]
14
+ @response_from_httparty.keys.should include(key)
15
+ @response_from_httparty[key].should eql(value)
16
+ end
17
+ end
18
+
19
+ Then /it should return a response with a (\d+) response code/ do |code|
20
+ @response_from_httparty.code.should eql(code.to_i)
21
+ end
22
+
23
+ Then /it should raise (?:an|a) ([\w:]+) exception/ do |exception|
24
+ @exception_from_httparty.should_not be_nil
25
+ @exception_from_httparty.class.name.should eql(exception)
26
+ end
@@ -0,0 +1,27 @@
1
+ When /^I set my HTTParty timeout option to (\d+)$/ do |timeout|
2
+ @request_options[:timeout] = timeout.to_i
3
+ end
4
+
5
+ When /I call HTTParty#get with '(.*)'$/ do |url|
6
+ begin
7
+ @response_from_httparty = HTTParty.get("http://#{@host_and_port}#{url}", @request_options)
8
+ rescue HTTParty::RedirectionTooDeep, Timeout::Error => e
9
+ @exception_from_httparty = e
10
+ end
11
+ end
12
+
13
+ When /I call HTTParty#get with '(.*)' and a basic_auth hash:/ do |url, auth_table|
14
+ h = auth_table.hashes.first
15
+ @response_from_httparty = HTTParty.get(
16
+ "http://#{@host_and_port}#{url}",
17
+ :basic_auth => { :username => h["username"], :password => h["password"] }
18
+ )
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
@@ -0,0 +1,94 @@
1
+ require 'base64'
2
+ class BasicMongrelHandler < Mongrel::HttpHandler
3
+ attr_accessor :content_type, :custom_headers, :response_body, :response_code, :preprocessor, :username, :password
4
+
5
+ def initialize
6
+ @content_type = "text/html"
7
+ @response_body = ""
8
+ @response_code = 200
9
+ @custom_headers = {}
10
+ end
11
+
12
+ def process(request, response)
13
+ preprocessor.call if preprocessor.is_a? Proc
14
+ reply_with(response, response_code, response_body)
15
+ end
16
+
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
23
+ end
24
+ end
25
+
26
+ class DeflateHandler < BasicMongrelHandler
27
+ def process(request, response)
28
+ response.start do |head, body|
29
+ head['Content-Encoding'] = 'deflate'
30
+ body.write Zlib::Deflate.deflate(response_body)
31
+ end
32
+ end
33
+ end
34
+
35
+ class GzipHandler < BasicMongrelHandler
36
+ def process(request, response)
37
+ response.start do |head, body|
38
+ head['Content-Encoding'] = 'gzip'
39
+ body.write gzip(response_body)
40
+ end
41
+ end
42
+
43
+ protected
44
+
45
+ def gzip(string)
46
+ sio = StringIO.new('', 'r+')
47
+ gz = Zlib::GzipWriter.new sio
48
+ gz.write string
49
+ gz.finish
50
+ sio.rewind
51
+ sio.read
52
+ end
53
+ end
54
+
55
+ module BasicAuthentication
56
+ def self.extended(base)
57
+ base.custom_headers["WWW-Authenticate"] = 'Basic Realm="Super Secret Page"'
58
+ end
59
+
60
+ def process(request, response)
61
+ if authorized?(request)
62
+ super
63
+ else
64
+ reply_with(response, 401, "Incorrect. You have 20 seconds to comply.")
65
+ end
66
+ end
67
+
68
+ def authorized?(request)
69
+ request.params["HTTP_AUTHORIZATION"] == "Basic " + Base64.encode64("#{@username}:#{@password}").strip
70
+ end
71
+ end
72
+
73
+ module DigestAuthentication
74
+ def self.extended(base)
75
+ base.custom_headers["WWW-Authenticate"] = 'Digest realm="testrealm@host.com",qop="auth,auth-int",nonce="nonce",opaque="opaque"'
76
+ end
77
+
78
+ def process(request, response)
79
+ if authorized?(request)
80
+ super
81
+ else
82
+ reply_with(response, 401, "Incorrect. You have 20 seconds to comply.")
83
+ end
84
+ end
85
+
86
+ def authorized?(request)
87
+ request.params["HTTP_AUTHORIZATION"] =~ /Digest.*uri=/
88
+ end
89
+ end
90
+
91
+ def new_mongrel_redirector(target_url, relative_path = false)
92
+ target_url = "http://#{@host_and_port}#{target_url}" unless relative_path
93
+ Mongrel::RedirectHandler.new(target_url)
94
+ end
@@ -0,0 +1,69 @@
1
+ Given /a remote service that returns '(.*)'/ do |response_body|
2
+ @handler = BasicMongrelHandler.new
3
+ Given "the response from the service has a body of '#{response_body}'"
4
+ end
5
+
6
+ Given /a remote service that returns a (\d+) status code/ do |code|
7
+ @handler = BasicMongrelHandler.new
8
+ @handler.response_code = code
9
+ end
10
+
11
+ Given /that service is accessed at the path '(.*)'/ do |path|
12
+ @server.register(path, @handler)
13
+ end
14
+
15
+ Given /^that service takes (\d+) seconds to generate a response$/ do |time|
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
26
+ end
27
+
28
+ Given /the response from the service has a Content-Type of '(.*)'/ do |content_type|
29
+ @handler.content_type = content_type
30
+ end
31
+
32
+ Given /the response from the service has a body of '(.*)'/ do |response_body|
33
+ @handler.response_body = response_body
34
+ end
35
+
36
+ Given /the url '(.*)' redirects to '(.*)'/ do |redirection_url, target_url|
37
+ @server.register redirection_url, new_mongrel_redirector(target_url)
38
+ end
39
+
40
+ Given /that service is protected by Basic Authentication/ do
41
+ @handler.extend BasicAuthentication
42
+ end
43
+
44
+ Given /that service is protected by Digest Authentication/ do
45
+ @handler.extend DigestAuthentication
46
+ end
47
+
48
+ Given /that service requires the username '(.*)' with the password '(.*)'/ do |username, password|
49
+ @handler.username = username
50
+ @handler.password = password
51
+ end
52
+
53
+ Given /a restricted page at '(.*)'/ do |url|
54
+ Given "a remote service that returns 'A response I will never see'"
55
+ And "that service is accessed at the path '#{url}'"
56
+ And "that service is protected by Basic Authentication"
57
+ And "that service requires the username 'something' with the password 'secret'"
58
+ end
59
+
60
+ # This joins the server thread, and halts cucumber, so you can actually hit the
61
+ # server with a browser. Runs until you kill it with Ctrl-c
62
+ Given /I want to hit this in a browser/ do
63
+ @server.acceptor.join
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
@@ -0,0 +1,22 @@
1
+ Feature: Supports Redirection
2
+
3
+ As a developer
4
+ I want to work with services that may redirect me
5
+ And I want it to follow a reasonable number of redirects
6
+ Because sometimes web services do that
7
+
8
+ Scenario: A service that redirects once
9
+ Given a remote service that returns 'Service Response'
10
+ And that service is accessed at the path '/landing_service.html'
11
+ And the url '/redirector.html' redirects to '/landing_service.html'
12
+ When I call HTTParty#get with '/redirector.html'
13
+ Then the return value should match 'Service Response'
14
+
15
+ # TODO: Look in to why this actually fails...
16
+ Scenario: A service that redirects to a relative URL
17
+
18
+ Scenario: A service that redirects infinitely
19
+ Given the url '/first.html' redirects to '/second.html'
20
+ And the url '/second.html' redirects to '/first.html'
21
+ When I call HTTParty#get with '/first.html'
22
+ Then it should raise an HTTParty::RedirectionTooDeep exception
@@ -0,0 +1,13 @@
1
+ Feature: Supports the timeout option
2
+ In order to handle inappropriately slow response times
3
+ As a developer
4
+ I want my request to raise an exception after my specified timeout has elapsed
5
+
6
+ Scenario: A long running response
7
+ Given a remote service that returns '<h1>Some HTML</h1>'
8
+ And that service is accessed at the path '/long_running_service.html'
9
+ And that service takes 2 seconds to generate a response
10
+ When I set my HTTParty timeout option to 1
11
+ And I call HTTParty#get with '/long_running_service.html'
12
+ Then it should raise a Timeout::Error exception
13
+ And I wait for the server to recover
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "httparty/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "httparty2"
7
+ s.version = HTTParty::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Anderson De Andrade", "John Nunemaker", "Sandro Turriate"]
10
+ s.email = ["adeandradeds@gmail.com"]
11
+ s.homepage = "https://github.com/adeandrade/httparty"
12
+ s.summary = %q{Makes http fun! Also, makes consuming restful web services dead easy.}
13
+ s.description = %q{Makes http fun! Also, makes consuming restful web services dead easy.}
14
+
15
+ s.add_dependency "activesupport", "~> 3.0"
16
+ s.add_dependency "multi_json", "~> 1.0"
17
+ s.add_dependency "multi_xml", "~> 0.2"
18
+
19
+ s.add_development_dependency "activesupport", "~> 3.0"
20
+ s.add_development_dependency "cucumber", "~> 1.0"
21
+ s.add_development_dependency "fakeweb", "~> 1.2"
22
+ s.add_development_dependency "rspec", "~> 2.6"
23
+ s.add_development_dependency "mongrel", "= 1.2.0.pre2"
24
+
25
+ s.post_install_message = "When you HTTParty, you must party hard!"
26
+
27
+ s.files = `git ls-files`.split("\n")
28
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
29
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
30
+ s.require_paths = ["lib"]
31
+ end
@@ -0,0 +1,455 @@
1
+ require 'pathname'
2
+ require 'net/http'
3
+ require 'net/https'
4
+ require 'uri'
5
+ require 'zlib'
6
+ require 'active_support/core_ext/object/blank'
7
+ require 'active_support/core_ext/object/to_param'
8
+ require 'active_support/core_ext/object/to_query'
9
+ require 'multi_json'
10
+ require 'multi_xml'
11
+
12
+ require 'httparty/module_inheritable_attributes'
13
+ require 'httparty/cookie_hash'
14
+ require 'httparty/net_digest_auth'
15
+ require 'httparty/version'
16
+
17
+ # @see HTTParty::ClassMethods
18
+ module HTTParty
19
+ module AllowedFormatsDeprecation
20
+ def const_missing(const)
21
+ if const.to_s =~ /AllowedFormats$/
22
+ Kernel.warn("Deprecated: Use HTTParty::Parser::SupportedFormats")
23
+ HTTParty::Parser::SupportedFormats
24
+ else
25
+ super
26
+ end
27
+ end
28
+ end
29
+
30
+ extend AllowedFormatsDeprecation
31
+
32
+ def self.included(base)
33
+ base.extend ClassMethods
34
+ base.send :include, HTTParty::ModuleInheritableAttributes
35
+ base.send(:mattr_inheritable, :default_options)
36
+ base.send(:mattr_inheritable, :default_cookies)
37
+ base.instance_variable_set("@default_options", {})
38
+ base.instance_variable_set("@default_cookies", CookieHash.new)
39
+ end
40
+
41
+ # == Common Request Options
42
+ # Request methods (get, post, put, delete, head, options) all take a common set of options. These are:
43
+ #
44
+ # [:+body+:] Body of the request. If passed a Hash, will try to normalize it first, by default passing it to ActiveSupport::to_params. Any other kind of object will get used as-is.
45
+ # [:+http_proxyaddr+:] Address of proxy server to use.
46
+ # [:+http_proxyport+:] Port of proxy server to use.
47
+ # [:+limit+:] Maximum number of redirects to follow. Takes precedences over :+no_follow+.
48
+ # [:+query+:] Query string, or a Hash representing it. Normalized according to the same rules as :+body+. If you specify this on a POST, you must use a Hash. See also HTTParty::ClassMethods.default_params.
49
+ # [:+timeout+:] Timeout for opening connection and reading data.
50
+ #
51
+ # There are also another set of options with names corresponding to various class methods. The methods in question are those that let you set a class-wide default, and the options override the defaults on a request-by-request basis. Those options are:
52
+ # * :+base_uri+: see HTTParty::ClassMethods.base_uri.
53
+ # * :+basic_auth+: see HTTParty::ClassMethods.basic_auth. Only one of :+basic_auth+ and :+digest_auth+ can be used at a time; if you try using both, you'll get an ArgumentError.
54
+ # * :+debug_output+: see HTTParty::ClassMethods.debug_output.
55
+ # * :+digest_auth+: see HTTParty::ClassMethods.digest_auth. Only one of :+basic_auth+ and :+digest_auth+ can be used at a time; if you try using both, you'll get an ArgumentError.
56
+ # * :+format+: see HTTParty::ClassMethods.format.
57
+ # * :+headers+: see HTTParty::ClassMethods.headers. Must be a Hash.
58
+ # * :+maintain_method_across_redirects+: see HTTParty::ClassMethods.maintain_method_across_redirects.
59
+ # * :+no_follow+: see HTTParty::ClassMethods.no_follow.
60
+ # * :+parser+: see HTTParty::ClassMethods.parser.
61
+ # * :+pem+: see HTTParty::ClassMethods.pem.
62
+ # * :+query_string_normalizer+: see HTTParty::ClassMethods.query_string_normalizer
63
+ # * :+ssl_ca_file+: see HTTParty::ClassMethods.ssl_ca_file.
64
+
65
+ module ClassMethods
66
+
67
+ extend AllowedFormatsDeprecation
68
+
69
+ # Allows setting http proxy information to be used
70
+ #
71
+ # class Foo
72
+ # include HTTParty
73
+ # http_proxy 'http://foo.com', 80
74
+ # end
75
+ def http_proxy(addr=nil, port = nil)
76
+ default_options[:http_proxyaddr] = addr
77
+ default_options[:http_proxyport] = port
78
+ end
79
+
80
+ # Allows setting a base uri to be used for each request.
81
+ # Will normalize uri to include http, etc.
82
+ #
83
+ # class Foo
84
+ # include HTTParty
85
+ # base_uri 'twitter.com'
86
+ # end
87
+ def base_uri(uri=nil)
88
+ return default_options[:base_uri] unless uri
89
+ default_options[:base_uri] = HTTParty.normalize_base_uri(uri)
90
+ end
91
+
92
+ # Allows setting basic authentication username and password.
93
+ #
94
+ # class Foo
95
+ # include HTTParty
96
+ # basic_auth 'username', 'password'
97
+ # end
98
+ def basic_auth(u, p)
99
+ default_options[:basic_auth] = {:username => u, :password => p}
100
+ end
101
+
102
+ # Allows setting digest authentication username and password.
103
+ #
104
+ # class Foo
105
+ # include HTTParty
106
+ # digest_auth 'username', 'password'
107
+ # end
108
+ def digest_auth(u, p)
109
+ default_options[:digest_auth] = {:username => u, :password => p}
110
+ end
111
+
112
+ # Do not send rails style query strings.
113
+ # Specically, don't use bracket notation when sending an array
114
+ #
115
+ # For a query:
116
+ # get '/', :query => {:selected_ids => [1,2,3]}
117
+ #
118
+ # The default query string looks like this:
119
+ # /?selected_ids[]=1&selected_ids[]=2&selected_ids[]=3
120
+ #
121
+ # Call `disable_rails_query_string_format` to transform the query string
122
+ # into:
123
+ # /?selected_ids=1&selected_ids=2&selected_ids=3
124
+ #
125
+ # @example
126
+ # class Foo
127
+ # include HTTParty
128
+ # disable_rails_query_string_format
129
+ # end
130
+ def disable_rails_query_string_format
131
+ query_string_normalizer Request::NON_RAILS_QUERY_STRING_NORMALIZER
132
+ end
133
+
134
+ # Allows setting default parameters to be appended to each request.
135
+ # Great for api keys and such.
136
+ #
137
+ # class Foo
138
+ # include HTTParty
139
+ # default_params :api_key => 'secret', :another => 'foo'
140
+ # end
141
+ def default_params(h={})
142
+ raise ArgumentError, 'Default params must be a hash' unless h.is_a?(Hash)
143
+ default_options[:default_params] ||= {}
144
+ default_options[:default_params].merge!(h)
145
+ end
146
+
147
+ # Allows setting a default timeout for all HTTP calls
148
+ # Timeout is specified in seconds.
149
+ #
150
+ # class Foo
151
+ # include HTTParty
152
+ # default_timeout 10
153
+ # end
154
+ def default_timeout(t)
155
+ raise ArgumentError, 'Timeout must be an integer or float' unless t && (t.is_a?(Integer) || t.is_a?(Float))
156
+ default_options[:timeout] = t
157
+ end
158
+
159
+ # Set an output stream for debugging, defaults to $stderr.
160
+ # The output stream is passed on to Net::HTTP#set_debug_output.
161
+ #
162
+ # class Foo
163
+ # include HTTParty
164
+ # debug_output $stderr
165
+ # end
166
+ def debug_output(stream = $stderr)
167
+ default_options[:debug_output] = stream
168
+ end
169
+
170
+ # Allows setting HTTP headers to be used for each request.
171
+ #
172
+ # class Foo
173
+ # include HTTParty
174
+ # headers 'Accept' => 'text/html'
175
+ # end
176
+ def headers(h={})
177
+ raise ArgumentError, 'Headers must be a hash' unless h.is_a?(Hash)
178
+ default_options[:headers] ||= {}
179
+ default_options[:headers].merge!(h)
180
+ end
181
+
182
+ def cookies(h={})
183
+ raise ArgumentError, 'Cookies must be a hash' unless h.is_a?(Hash)
184
+ default_cookies.add_cookies(h)
185
+ end
186
+
187
+ # Proceed to the location header when an HTTP response dictates a redirect.
188
+ # Redirects are always followed by default.
189
+ #
190
+ # @example
191
+ # class Foo
192
+ # include HTTParty
193
+ # base_uri 'http://google.com'
194
+ # follow_redirects true
195
+ # end
196
+ def follow_redirects(value = true)
197
+ default_options[:follow_redirects] = value
198
+ end
199
+
200
+ # Allows setting the format with which to parse.
201
+ # Must be one of the allowed formats ie: json, xml
202
+ #
203
+ # class Foo
204
+ # include HTTParty
205
+ # format :json
206
+ # end
207
+ def format(f = nil)
208
+ if f.nil?
209
+ default_options[:format]
210
+ else
211
+ parser(Parser) if parser.nil?
212
+ default_options[:format] = f
213
+ validate_format
214
+ end
215
+ end
216
+
217
+ # Declare whether or not to follow redirects. When true, an
218
+ # {HTTParty::RedirectionTooDeep} error will raise upon encountering a
219
+ # redirect. You can then gain access to the response object via
220
+ # HTTParty::RedirectionTooDeep#response.
221
+ #
222
+ # @see HTTParty::ResponseError#response
223
+ #
224
+ # @example
225
+ # class Foo
226
+ # include HTTParty
227
+ # base_uri 'http://google.com'
228
+ # no_follow true
229
+ # end
230
+ #
231
+ # begin
232
+ # Foo.get('/')
233
+ # rescue HTTParty::RedirectionTooDeep => e
234
+ # puts e.response.body
235
+ # end
236
+ def no_follow(value = false)
237
+ default_options[:no_follow] = value
238
+ end
239
+
240
+ # Declare that you wish to maintain the chosen HTTP method across redirects.
241
+ # The default behavior is to follow redirects via the GET method.
242
+ # If you wish to maintain the original method, you can set this option to true.
243
+ #
244
+ # @example
245
+ # class Foo
246
+ # include HTTParty
247
+ # base_uri 'http://google.com'
248
+ # maintain_method_across_redirects true
249
+ # end
250
+
251
+ def maintain_method_across_redirects(value = true)
252
+ default_options[:maintain_method_across_redirects] = value
253
+ end
254
+
255
+ # Allows setting a PEM file to be used
256
+ #
257
+ # class Foo
258
+ # include HTTParty
259
+ # pem File.read('/home/user/my.pem'), "optional password"
260
+ # end
261
+ def pem(pem_contents, password=nil)
262
+ default_options[:pem] = pem_contents
263
+ default_options[:pem_password] = password
264
+ end
265
+
266
+ # Override the way query strings are normalized.
267
+ # Helpful for overriding the default rails normalization of Array queries.
268
+ #
269
+ # For a query:
270
+ # get '/', :query => {:selected_ids => [1,2,3]}
271
+ #
272
+ # The default query string normalizer returns:
273
+ # /?selected_ids[]=1&selected_ids[]=2&selected_ids[]=3
274
+ #
275
+ # Let's change it to this:
276
+ # /?selected_ids=1&selected_ids=2&selected_ids=3
277
+ #
278
+ # Pass a Proc to the query normalizer which accepts the yielded query.
279
+ #
280
+ # @example Modifying Array query strings
281
+ # class ServiceWrapper
282
+ # include HTTParty
283
+ #
284
+ # query_string_normalizer proc { |query|
285
+ # query.map do |key, value|
286
+ # value.map {|v| "#{key}=#{v}"}
287
+ # end.join('&')
288
+ # }
289
+ # end
290
+ #
291
+ # @param [Proc] normalizer custom query string normalizer.
292
+ # @yield [Hash, String] query string
293
+ # @yieldreturn [Array] an array that will later be joined with '&'
294
+ def query_string_normalizer(normalizer)
295
+ default_options[:query_string_normalizer] = normalizer
296
+ end
297
+
298
+ # Allows setting an OpenSSL certificate authority file
299
+ #
300
+ # class Foo
301
+ # include HTTParty
302
+ # ssl_ca_file '/etc/ssl/certs/ca-certificates.crt'
303
+ # end
304
+ def ssl_ca_file(path)
305
+ default_options[:ssl_ca_file] = path
306
+ end
307
+
308
+ # Allows setting an OpenSSL certificate authority path (directory)
309
+ #
310
+ # class Foo
311
+ # include HTTParty
312
+ # ssl_ca_path '/etc/ssl/certs/'
313
+ # end
314
+ def ssl_ca_path(path)
315
+ default_options[:ssl_ca_path] = path
316
+ end
317
+
318
+ # Allows setting a custom parser for the response.
319
+ #
320
+ # class Foo
321
+ # include HTTParty
322
+ # parser Proc.new {|data| ...}
323
+ # end
324
+ def parser(custom_parser = nil)
325
+ if custom_parser.nil?
326
+ default_options[:parser]
327
+ else
328
+ default_options[:parser] = custom_parser
329
+ validate_format
330
+ end
331
+ end
332
+
333
+ # Allows making a get request to a url.
334
+ #
335
+ # class Foo
336
+ # include HTTParty
337
+ # end
338
+ #
339
+ # # Simple get with full url
340
+ # Foo.get('http://foo.com/resource.json')
341
+ #
342
+ # # Simple get with full url and query parameters
343
+ # # ie: http://foo.com/resource.json?limit=10
344
+ # Foo.get('http://foo.com/resource.json', :query => {:limit => 10})
345
+ def get(path, options={})
346
+ perform_request Net::HTTP::Get, path, options
347
+ end
348
+
349
+ # Allows making a post request to a url.
350
+ #
351
+ # class Foo
352
+ # include HTTParty
353
+ # end
354
+ #
355
+ # # Simple post with full url and setting the body
356
+ # Foo.post('http://foo.com/resources', :body => {:bar => 'baz'})
357
+ #
358
+ # # Simple post with full url using :query option,
359
+ # # which gets set as form data on the request.
360
+ # Foo.post('http://foo.com/resources', :query => {:bar => 'baz'})
361
+ def post(path, options={})
362
+ perform_request Net::HTTP::Post, path, options
363
+ end
364
+
365
+ # Perform a PUT request to a path
366
+ def put(path, options={})
367
+ perform_request Net::HTTP::Put, path, options
368
+ end
369
+
370
+ # Perform a DELETE request to a path
371
+ def delete(path, options={})
372
+ perform_request Net::HTTP::Delete, path, options
373
+ end
374
+
375
+ # Perform a HEAD request to a path
376
+ def head(path, options={})
377
+ perform_request Net::HTTP::Head, path, options
378
+ end
379
+
380
+ # Perform an OPTIONS request to a path
381
+ def options(path, options={})
382
+ perform_request Net::HTTP::Options, path, options
383
+ end
384
+
385
+ def default_options #:nodoc:
386
+ @default_options
387
+ end
388
+
389
+ private
390
+
391
+ def perform_request(http_method, path, options) #:nodoc:
392
+ options = default_options.dup.merge(options)
393
+ process_cookies(options)
394
+ Request.new(http_method, path, options).perform
395
+ end
396
+
397
+ def process_cookies(options) #:nodoc:
398
+ return unless options[:cookies] || default_cookies.any?
399
+ options[:headers] ||= headers.dup
400
+ options[:headers]["cookie"] = cookies.merge(options.delete(:cookies) || {}).to_cookie_string
401
+ end
402
+
403
+ def validate_format
404
+ if format && parser.respond_to?(:supports_format?) && !parser.supports_format?(format)
405
+ raise UnsupportedFormat, "'#{format.inspect}' Must be one of: #{parser.supported_formats.map{|f| f.to_s}.sort.join(', ')}"
406
+ end
407
+ end
408
+ end
409
+
410
+ def self.normalize_base_uri(url) #:nodoc:
411
+ normalized_url = url.dup
412
+ use_ssl = (normalized_url =~ /^https/) || normalized_url.include?(':443')
413
+ ends_with_slash = normalized_url =~ /\/$/
414
+
415
+ normalized_url.chop! if ends_with_slash
416
+ normalized_url.gsub!(/^https?:\/\//i, '')
417
+
418
+ "http#{'s' if use_ssl}://#{normalized_url}"
419
+ end
420
+
421
+ class Basement #:nodoc:
422
+ include HTTParty
423
+ end
424
+
425
+ def self.get(*args)
426
+ Basement.get(*args)
427
+ end
428
+
429
+ def self.post(*args)
430
+ Basement.post(*args)
431
+ end
432
+
433
+ def self.put(*args)
434
+ Basement.put(*args)
435
+ end
436
+
437
+ def self.delete(*args)
438
+ Basement.delete(*args)
439
+ end
440
+
441
+ def self.head(*args)
442
+ Basement.head(*args)
443
+ end
444
+
445
+ def self.options(*args)
446
+ Basement.options(*args)
447
+ end
448
+
449
+ end
450
+
451
+ require 'httparty/core_extensions'
452
+ require 'httparty/exceptions'
453
+ require 'httparty/parser'
454
+ require 'httparty/request'
455
+ require 'httparty/response'