httpotato 1.0.2

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.
Files changed (70) hide show
  1. data/.gitignore +7 -0
  2. data/History +216 -0
  3. data/MIT-LICENSE +20 -0
  4. data/Manifest +47 -0
  5. data/README.rdoc +52 -0
  6. data/Rakefile +90 -0
  7. data/VERSION +1 -0
  8. data/bin/httpotato +108 -0
  9. data/cucumber.yml +1 -0
  10. data/examples/aaws.rb +32 -0
  11. data/examples/basic.rb +11 -0
  12. data/examples/custom_parsers.rb +67 -0
  13. data/examples/delicious.rb +37 -0
  14. data/examples/google.rb +16 -0
  15. data/examples/rubyurl.rb +14 -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 +23 -0
  25. data/features/steps/httpotato_response_steps.rb +26 -0
  26. data/features/steps/httpotato_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/httpotato.gemspec +150 -0
  32. data/lib/httpotato.rb +371 -0
  33. data/lib/httpotato/cookie_hash.rb +22 -0
  34. data/lib/httpotato/core_extensions.rb +31 -0
  35. data/lib/httpotato/exceptions.rb +26 -0
  36. data/lib/httpotato/module_inheritable_attributes.rb +34 -0
  37. data/lib/httpotato/net_digest_auth.rb +35 -0
  38. data/lib/httpotato/parser.rb +146 -0
  39. data/lib/httpotato/request.rb +231 -0
  40. data/lib/httpotato/response.rb +79 -0
  41. data/spec/fixtures/delicious.xml +23 -0
  42. data/spec/fixtures/empty.xml +0 -0
  43. data/spec/fixtures/google.html +3 -0
  44. data/spec/fixtures/ssl/generate.sh +29 -0
  45. data/spec/fixtures/ssl/generated/1fe462c2.0 +15 -0
  46. data/spec/fixtures/ssl/generated/bogushost.crt +13 -0
  47. data/spec/fixtures/ssl/generated/ca.crt +15 -0
  48. data/spec/fixtures/ssl/generated/ca.key +15 -0
  49. data/spec/fixtures/ssl/generated/selfsigned.crt +14 -0
  50. data/spec/fixtures/ssl/generated/server.crt +13 -0
  51. data/spec/fixtures/ssl/generated/server.key +15 -0
  52. data/spec/fixtures/ssl/openssl-exts.cnf +9 -0
  53. data/spec/fixtures/twitter.json +1 -0
  54. data/spec/fixtures/twitter.xml +403 -0
  55. data/spec/fixtures/undefined_method_add_node_for_nil.xml +2 -0
  56. data/spec/httpotato/cookie_hash_spec.rb +71 -0
  57. data/spec/httpotato/parser_spec.rb +155 -0
  58. data/spec/httpotato/request_spec.rb +430 -0
  59. data/spec/httpotato/response_spec.rb +188 -0
  60. data/spec/httpotato/ssl_spec.rb +54 -0
  61. data/spec/httpotato_spec.rb +570 -0
  62. data/spec/spec.opts +3 -0
  63. data/spec/spec_helper.rb +20 -0
  64. data/spec/support/ssl_test_helper.rb +25 -0
  65. data/spec/support/ssl_test_server.rb +69 -0
  66. data/spec/support/stub_response.rb +30 -0
  67. data/test.rb +39 -0
  68. data/website/css/common.css +47 -0
  69. data/website/index.html +66 -0
  70. metadata +260 -0
@@ -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
+ instance_eval &preprocessor if preprocessor
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 HTTPotato#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 HTTPotato#get with '/first.html'
22
+ Then it should raise an HTTPotato::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 as 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 HTTPotato timeout option to 1
11
+ And I call HTTPotato#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,150 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{httpotato}
8
+ s.version = "1.0.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Wes Morgan", "Adrian Cushman", "Chris Gill"]
12
+ s.date = %q{2010-11-01}
13
+ s.default_executable = %q{httpotato}
14
+ s.description = %q{Makes http fun & JSON parsing fast! Also, makes consuming restful web services dead easy. Forked from HTTParty.}
15
+ s.email = %q{innovationlab@dnc.org}
16
+ s.executables = ["httpotato"]
17
+ s.extra_rdoc_files = [
18
+ "README.rdoc"
19
+ ]
20
+ s.files = [
21
+ ".gitignore",
22
+ "History",
23
+ "MIT-LICENSE",
24
+ "Manifest",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "bin/httpotato",
29
+ "cucumber.yml",
30
+ "examples/aaws.rb",
31
+ "examples/basic.rb",
32
+ "examples/custom_parsers.rb",
33
+ "examples/delicious.rb",
34
+ "examples/google.rb",
35
+ "examples/rubyurl.rb",
36
+ "examples/twitter.rb",
37
+ "examples/whoismyrep.rb",
38
+ "features/basic_authentication.feature",
39
+ "features/command_line.feature",
40
+ "features/deals_with_http_error_codes.feature",
41
+ "features/digest_authentication.feature",
42
+ "features/handles_compressed_responses.feature",
43
+ "features/handles_multiple_formats.feature",
44
+ "features/steps/env.rb",
45
+ "features/steps/httpotato_response_steps.rb",
46
+ "features/steps/httpotato_steps.rb",
47
+ "features/steps/mongrel_helper.rb",
48
+ "features/steps/remote_service_steps.rb",
49
+ "features/supports_redirection.feature",
50
+ "features/supports_timeout_option.feature",
51
+ "httpotato.gemspec",
52
+ "lib/httpotato.rb",
53
+ "lib/httpotato/cookie_hash.rb",
54
+ "lib/httpotato/core_extensions.rb",
55
+ "lib/httpotato/exceptions.rb",
56
+ "lib/httpotato/module_inheritable_attributes.rb",
57
+ "lib/httpotato/net_digest_auth.rb",
58
+ "lib/httpotato/parser.rb",
59
+ "lib/httpotato/request.rb",
60
+ "lib/httpotato/response.rb",
61
+ "spec/fixtures/delicious.xml",
62
+ "spec/fixtures/empty.xml",
63
+ "spec/fixtures/google.html",
64
+ "spec/fixtures/ssl/generate.sh",
65
+ "spec/fixtures/ssl/generated/1fe462c2.0",
66
+ "spec/fixtures/ssl/generated/bogushost.crt",
67
+ "spec/fixtures/ssl/generated/ca.crt",
68
+ "spec/fixtures/ssl/generated/ca.key",
69
+ "spec/fixtures/ssl/generated/selfsigned.crt",
70
+ "spec/fixtures/ssl/generated/server.crt",
71
+ "spec/fixtures/ssl/generated/server.key",
72
+ "spec/fixtures/ssl/openssl-exts.cnf",
73
+ "spec/fixtures/twitter.json",
74
+ "spec/fixtures/twitter.xml",
75
+ "spec/fixtures/undefined_method_add_node_for_nil.xml",
76
+ "spec/httpotato/cookie_hash_spec.rb",
77
+ "spec/httpotato/parser_spec.rb",
78
+ "spec/httpotato/request_spec.rb",
79
+ "spec/httpotato/response_spec.rb",
80
+ "spec/httpotato/ssl_spec.rb",
81
+ "spec/httpotato_spec.rb",
82
+ "spec/spec.opts",
83
+ "spec/spec_helper.rb",
84
+ "spec/support/ssl_test_helper.rb",
85
+ "spec/support/ssl_test_server.rb",
86
+ "spec/support/stub_response.rb",
87
+ "test.rb",
88
+ "website/css/common.css",
89
+ "website/index.html"
90
+ ]
91
+ s.homepage = %q{http://github.com/dnclabs/httpotato/}
92
+ s.post_install_message = %q{Winners don't use crack.}
93
+ s.rdoc_options = ["--charset=UTF-8"]
94
+ s.require_paths = ["lib"]
95
+ s.rubyforge_project = %q{httpotato}
96
+ s.rubygems_version = %q{1.3.7}
97
+ s.summary = %q{Makes http fun & JSON parsing fast! Also, makes consuming restful web services dead easy.}
98
+ s.test_files = [
99
+ "spec/httpotato/cookie_hash_spec.rb",
100
+ "spec/httpotato/parser_spec.rb",
101
+ "spec/httpotato/request_spec.rb",
102
+ "spec/httpotato/response_spec.rb",
103
+ "spec/httpotato/ssl_spec.rb",
104
+ "spec/httpotato_spec.rb",
105
+ "spec/spec_helper.rb",
106
+ "spec/support/ssl_test_helper.rb",
107
+ "spec/support/ssl_test_server.rb",
108
+ "spec/support/stub_response.rb",
109
+ "examples/aaws.rb",
110
+ "examples/basic.rb",
111
+ "examples/custom_parsers.rb",
112
+ "examples/delicious.rb",
113
+ "examples/google.rb",
114
+ "examples/rubyurl.rb",
115
+ "examples/twitter.rb",
116
+ "examples/whoismyrep.rb"
117
+ ]
118
+
119
+ if s.respond_to? :specification_version then
120
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
121
+ s.specification_version = 3
122
+
123
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
124
+ s.add_runtime_dependency(%q<crack>, ["= 0.1.8"])
125
+ s.add_runtime_dependency(%q<json>, ["= 1.4.6"])
126
+ s.add_development_dependency(%q<activesupport>, ["~> 2.3"])
127
+ s.add_development_dependency(%q<cucumber>, ["~> 0.7"])
128
+ s.add_development_dependency(%q<fakeweb>, ["~> 1.2"])
129
+ s.add_development_dependency(%q<mongrel>, ["~> 1.1"])
130
+ s.add_development_dependency(%q<rspec>, ["~> 1.3"])
131
+ else
132
+ s.add_dependency(%q<crack>, ["= 0.1.8"])
133
+ s.add_dependency(%q<json>, ["= 1.4.6"])
134
+ s.add_dependency(%q<activesupport>, ["~> 2.3"])
135
+ s.add_dependency(%q<cucumber>, ["~> 0.7"])
136
+ s.add_dependency(%q<fakeweb>, ["~> 1.2"])
137
+ s.add_dependency(%q<mongrel>, ["~> 1.1"])
138
+ s.add_dependency(%q<rspec>, ["~> 1.3"])
139
+ end
140
+ else
141
+ s.add_dependency(%q<crack>, ["= 0.1.8"])
142
+ s.add_dependency(%q<json>, ["= 1.4.6"])
143
+ s.add_dependency(%q<activesupport>, ["~> 2.3"])
144
+ s.add_dependency(%q<cucumber>, ["~> 0.7"])
145
+ s.add_dependency(%q<fakeweb>, ["~> 1.2"])
146
+ s.add_dependency(%q<mongrel>, ["~> 1.1"])
147
+ s.add_dependency(%q<rspec>, ["~> 1.3"])
148
+ end
149
+ end
150
+
@@ -0,0 +1,371 @@
1
+ require 'pathname'
2
+ require 'net/http'
3
+ require 'net/https'
4
+ require 'uri'
5
+ require 'zlib'
6
+ require 'crack'
7
+ require 'json/ext'
8
+
9
+ dir = Pathname(__FILE__).dirname.expand_path
10
+
11
+ require dir + 'httpotato/module_inheritable_attributes'
12
+ require dir + 'httpotato/cookie_hash'
13
+ require dir + 'httpotato/net_digest_auth'
14
+
15
+ module HTTPotato
16
+ VERSION = "1.0.2".freeze
17
+ CRACK_DEPENDENCY = "0.1.8".freeze
18
+ JSON_DEPENDENCY = "1.4.6".freeze
19
+
20
+ module AllowedFormatsDeprecation
21
+ def const_missing(const)
22
+ if const.to_s =~ /AllowedFormats$/
23
+ Kernel.warn("Deprecated: Use HTTPotato::Parser::SupportedFormats")
24
+ HTTPotato::Parser::SupportedFormats
25
+ else
26
+ super
27
+ end
28
+ end
29
+ end
30
+
31
+ extend AllowedFormatsDeprecation
32
+
33
+ def self.included(base)
34
+ base.extend ClassMethods
35
+ base.send :include, HTTPotato::ModuleInheritableAttributes
36
+ base.send(:mattr_inheritable, :default_options)
37
+ base.send(:mattr_inheritable, :default_cookies)
38
+ base.instance_variable_set("@default_options", {})
39
+ base.instance_variable_set("@default_cookies", CookieHash.new)
40
+ end
41
+
42
+ module ClassMethods
43
+ extend AllowedFormatsDeprecation
44
+
45
+ # Allows setting http proxy information to be used
46
+ #
47
+ # class Foo
48
+ # include HTTPotato
49
+ # http_proxy 'http://foo.com', 80
50
+ # end
51
+ def http_proxy(addr=nil, port = nil)
52
+ default_options[:http_proxyaddr] = addr
53
+ default_options[:http_proxyport] = port
54
+ end
55
+
56
+ # Allows setting a base uri to be used for each request.
57
+ # Will normalize uri to include http, etc.
58
+ #
59
+ # class Foo
60
+ # include HTTPotato
61
+ # base_uri 'twitter.com'
62
+ # end
63
+ def base_uri(uri=nil)
64
+ return default_options[:base_uri] unless uri
65
+ default_options[:base_uri] = HTTPotato.normalize_base_uri(uri)
66
+ end
67
+
68
+ # Allows setting basic authentication username and password.
69
+ #
70
+ # class Foo
71
+ # include HTTPotato
72
+ # basic_auth 'username', 'password'
73
+ # end
74
+ def basic_auth(u, p)
75
+ default_options[:basic_auth] = {:username => u, :password => p}
76
+ end
77
+
78
+ # Allows setting digest authentication username and password.
79
+ #
80
+ # class Foo
81
+ # include HTTPotato
82
+ # digest_auth 'username', 'password'
83
+ # end
84
+ def digest_auth(u, p)
85
+ default_options[:digest_auth] = {:username => u, :password => p}
86
+ end
87
+
88
+ # Allows setting default parameters to be appended to each request.
89
+ # Great for api keys and such.
90
+ #
91
+ # class Foo
92
+ # include HTTPotato
93
+ # default_params :api_key => 'secret', :another => 'foo'
94
+ # end
95
+ def default_params(h={})
96
+ raise ArgumentError, 'Default params must be a hash' unless h.is_a?(Hash)
97
+ default_options[:default_params] ||= {}
98
+ default_options[:default_params].merge!(h)
99
+ end
100
+
101
+ # Allows setting a default timeout for all HTTP calls
102
+ # Timeout is specified in seconds.
103
+ #
104
+ # class Foo
105
+ # include HTTPotato
106
+ # default_timeout 10
107
+ # end
108
+ def default_timeout(t)
109
+ raise ArgumentError, 'Timeout must be an integer' unless t && t.is_a?(Integer)
110
+ default_options[:timeout] = t
111
+ end
112
+
113
+ # Set an output stream for debugging, defaults to $stderr.
114
+ # The output stream is passed on to Net::HTTP#set_debug_output.
115
+ #
116
+ # class Foo
117
+ # include HTTPotato
118
+ # debug_output $stderr
119
+ # end
120
+ def debug_output(stream = $stderr)
121
+ default_options[:debug_output] = stream
122
+ end
123
+
124
+ # Allows setting HTTP headers to be used for each request.
125
+ #
126
+ # class Foo
127
+ # include HTTPotato
128
+ # headers 'Accept' => 'text/html'
129
+ # end
130
+ def headers(h={})
131
+ raise ArgumentError, 'Headers must be a hash' unless h.is_a?(Hash)
132
+ default_options[:headers] ||= {}
133
+ default_options[:headers].merge!(h)
134
+ end
135
+
136
+ def cookies(h={})
137
+ raise ArgumentError, 'Cookies must be a hash' unless h.is_a?(Hash)
138
+ default_cookies.add_cookies(h)
139
+ end
140
+
141
+ # Allows setting the format with which to parse.
142
+ # Must be one of the allowed formats ie: json, xml
143
+ #
144
+ # class Foo
145
+ # include HTTPotato
146
+ # format :json
147
+ # end
148
+ def format(f = nil)
149
+ if f.nil?
150
+ default_options[:format]
151
+ else
152
+ parser(Parser) if parser.nil?
153
+ default_options[:format] = f
154
+ validate_format
155
+ end
156
+ end
157
+
158
+ # Declare whether or not to follow redirects. When true, an
159
+ # {HTTPotato::RedirectionTooDeep} error will raise upon encountering a
160
+ # redirect. You can then gain access to the response object via
161
+ # HTTPotato::RedirectionTooDeep#response.
162
+ #
163
+ # @see HTTPotato::ResponseError#response
164
+ #
165
+ # @example
166
+ # class Foo
167
+ # include HTTPotato
168
+ # base_uri 'http://google.com'
169
+ # no_follow true
170
+ # end
171
+ #
172
+ # begin
173
+ # Foo.get('/')
174
+ # rescue HTTPotato::RedirectionTooDeep => e
175
+ # puts e.response.body
176
+ # end
177
+ def no_follow(value = false)
178
+ default_options[:no_follow] = value
179
+ end
180
+
181
+ # Declare that you wish to maintain the chosen HTTP method across redirects.
182
+ # The default behavior is to follow redirects via the GET method.
183
+ # If you wish to maintain the original method, you can set this option to true.
184
+ #
185
+ # @example
186
+ # class Foo
187
+ # include HTTPotato
188
+ # base_uri 'http://google.com'
189
+ # maintain_method_across_redirects true
190
+ # end
191
+
192
+ def maintain_method_across_redirects(value = true)
193
+ default_options[:maintain_method_across_redirects] = value
194
+ end
195
+
196
+ # Allows setting a PEM file to be used
197
+ #
198
+ # class Foo
199
+ # include HTTPotato
200
+ # pem File.read('/home/user/my.pem')
201
+ # end
202
+ def pem(pem_contents)
203
+ default_options[:pem] = pem_contents
204
+ end
205
+
206
+ # Allows setting an OpenSSL certificate authority file
207
+ #
208
+ # class Foo
209
+ # include HTTPotato
210
+ # ssl_ca_file '/etc/ssl/certs/ca-certificates.crt'
211
+ # end
212
+ def ssl_ca_file(path)
213
+ default_options[:ssl_ca_file] = path
214
+ end
215
+
216
+ # Allows setting an OpenSSL certificate authority path (directory)
217
+ #
218
+ # class Foo
219
+ # include HTTPotato
220
+ # ssl_ca_path '/etc/ssl/certs/'
221
+ # end
222
+ def ssl_ca_path(path)
223
+ default_options[:ssl_ca_path] = path
224
+ end
225
+
226
+ # Allows setting a custom parser for the response.
227
+ #
228
+ # class Foo
229
+ # include HTTPotato
230
+ # parser Proc.new {|data| ...}
231
+ # end
232
+ def parser(custom_parser = nil)
233
+ if custom_parser.nil?
234
+ default_options[:parser]
235
+ else
236
+ default_options[:parser] = custom_parser
237
+ validate_format
238
+ end
239
+ end
240
+
241
+ # Allows making a get request to a url.
242
+ #
243
+ # class Foo
244
+ # include HTTPotato
245
+ # end
246
+ #
247
+ # # Simple get with full url
248
+ # Foo.get('http://foo.com/resource.json')
249
+ #
250
+ # # Simple get with full url and query parameters
251
+ # # ie: http://foo.com/resource.json?limit=10
252
+ # Foo.get('http://foo.com/resource.json', :query => {:limit => 10})
253
+ def get(path, options={})
254
+ perform_request Net::HTTP::Get, path, options
255
+ end
256
+
257
+ # Allows making a post request to a url.
258
+ #
259
+ # class Foo
260
+ # include HTTPotato
261
+ # end
262
+ #
263
+ # # Simple post with full url and setting the body
264
+ # Foo.post('http://foo.com/resources', :body => {:bar => 'baz'})
265
+ #
266
+ # # Simple post with full url using :query option,
267
+ # # which gets set as form data on the request.
268
+ # Foo.post('http://foo.com/resources', :query => {:bar => 'baz'})
269
+ def post(path, options={})
270
+ perform_request Net::HTTP::Post, path, options
271
+ end
272
+
273
+ # Perform a PUT request to a path
274
+ def put(path, options={})
275
+ perform_request Net::HTTP::Put, path, options
276
+ end
277
+
278
+ # Perform a DELETE request to a path
279
+ def delete(path, options={})
280
+ perform_request Net::HTTP::Delete, path, options
281
+ end
282
+
283
+ # Perform a HEAD request to a path
284
+ def head(path, options={})
285
+ perform_request Net::HTTP::Head, path, options
286
+ end
287
+
288
+ # Perform an OPTIONS request to a path
289
+ def options(path, options={})
290
+ perform_request Net::HTTP::Options, path, options
291
+ end
292
+
293
+ def default_options #:nodoc:
294
+ @default_options
295
+ end
296
+
297
+ private
298
+
299
+ def perform_request(http_method, path, options) #:nodoc:
300
+ options = default_options.dup.merge(options)
301
+ process_cookies(options)
302
+ Request.new(http_method, path, options).perform
303
+ end
304
+
305
+ def process_cookies(options) #:nodoc:
306
+ return unless options[:cookies] || default_cookies.any?
307
+ options[:headers] ||= headers.dup
308
+ options[:headers]["cookie"] = cookies.merge(options.delete(:cookies) || {}).to_cookie_string
309
+ end
310
+
311
+ def validate_format
312
+ if format && parser.respond_to?(:supports_format?) && !parser.supports_format?(format)
313
+ raise UnsupportedFormat, "'#{format.inspect}' Must be one of: #{parser.supported_formats.map{|f| f.to_s}.sort.join(', ')}"
314
+ end
315
+ end
316
+ end
317
+
318
+ def self.normalize_base_uri(url) #:nodoc:
319
+ normalized_url = url.dup
320
+ use_ssl = (normalized_url =~ /^https/) || normalized_url.include?(':443')
321
+ ends_with_slash = normalized_url =~ /\/$/
322
+
323
+ normalized_url.chop! if ends_with_slash
324
+ normalized_url.gsub!(/^https?:\/\//i, '')
325
+
326
+ "http#{'s' if use_ssl}://#{normalized_url}"
327
+ end
328
+
329
+ class Basement #:nodoc:
330
+ include HTTPotato
331
+ end
332
+
333
+ def self.get(*args)
334
+ Basement.get(*args)
335
+ end
336
+
337
+ def self.post(*args)
338
+ Basement.post(*args)
339
+ end
340
+
341
+ def self.put(*args)
342
+ Basement.put(*args)
343
+ end
344
+
345
+ def self.delete(*args)
346
+ Basement.delete(*args)
347
+ end
348
+
349
+ def self.head(*args)
350
+ Basement.head(*args)
351
+ end
352
+
353
+ def self.options(*args)
354
+ Basement.options(*args)
355
+ end
356
+
357
+ end
358
+
359
+ require dir + 'httpotato/core_extensions'
360
+ require dir + 'httpotato/exceptions'
361
+ require dir + 'httpotato/parser'
362
+ require dir + 'httpotato/request'
363
+ require dir + 'httpotato/response'
364
+
365
+ if JSON::VERSION != HTTPotato::JSON_DEPENDENCY
366
+ warn "warning: HTTPotato depends on version #{HTTPotato::JSON_DEPENDENCY} of json, not #{JSON::VERSION}."
367
+ end
368
+
369
+ if Crack::VERSION != HTTPotato::CRACK_DEPENDENCY
370
+ warn "warning: HTTPotato depends on version #{HTTPotato::CRACK_DEPENDENCY} of crack, not #{Crack::VERSION}."
371
+ end