httparty-responsibly 0.17.1

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 (65) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +18 -0
  3. data/.gitignore +13 -0
  4. data/.rubocop.yml +92 -0
  5. data/.rubocop_todo.yml +124 -0
  6. data/.simplecov +1 -0
  7. data/.travis.yml +11 -0
  8. data/CONTRIBUTING.md +23 -0
  9. data/Changelog.md +509 -0
  10. data/Gemfile +24 -0
  11. data/Guardfile +16 -0
  12. data/MIT-LICENSE +20 -0
  13. data/README.md +78 -0
  14. data/Rakefile +10 -0
  15. data/bin/httparty +123 -0
  16. data/cucumber.yml +1 -0
  17. data/docs/README.md +106 -0
  18. data/examples/README.md +86 -0
  19. data/examples/aaws.rb +32 -0
  20. data/examples/basic.rb +28 -0
  21. data/examples/body_stream.rb +14 -0
  22. data/examples/crack.rb +19 -0
  23. data/examples/custom_parsers.rb +68 -0
  24. data/examples/delicious.rb +37 -0
  25. data/examples/google.rb +16 -0
  26. data/examples/headers_and_user_agents.rb +10 -0
  27. data/examples/logging.rb +36 -0
  28. data/examples/microsoft_graph.rb +52 -0
  29. data/examples/multipart.rb +22 -0
  30. data/examples/nokogiri_html_parser.rb +19 -0
  31. data/examples/peer_cert.rb +9 -0
  32. data/examples/rescue_json.rb +17 -0
  33. data/examples/rubyurl.rb +14 -0
  34. data/examples/stackexchange.rb +24 -0
  35. data/examples/stream_download.rb +26 -0
  36. data/examples/tripit_sign_in.rb +44 -0
  37. data/examples/twitter.rb +31 -0
  38. data/examples/whoismyrep.rb +10 -0
  39. data/httparty-responsibly.gemspec +27 -0
  40. data/lib/httparty.rb +668 -0
  41. data/lib/httparty/connection_adapter.rb +254 -0
  42. data/lib/httparty/cookie_hash.rb +21 -0
  43. data/lib/httparty/exceptions.rb +33 -0
  44. data/lib/httparty/hash_conversions.rb +69 -0
  45. data/lib/httparty/headers_processor.rb +30 -0
  46. data/lib/httparty/logger/apache_formatter.rb +45 -0
  47. data/lib/httparty/logger/curl_formatter.rb +91 -0
  48. data/lib/httparty/logger/logger.rb +28 -0
  49. data/lib/httparty/logger/logstash_formatter.rb +59 -0
  50. data/lib/httparty/module_inheritable_attributes.rb +56 -0
  51. data/lib/httparty/net_digest_auth.rb +136 -0
  52. data/lib/httparty/parser.rb +150 -0
  53. data/lib/httparty/request.rb +386 -0
  54. data/lib/httparty/request/body.rb +84 -0
  55. data/lib/httparty/request/multipart_boundary.rb +11 -0
  56. data/lib/httparty/response.rb +140 -0
  57. data/lib/httparty/response/headers.rb +33 -0
  58. data/lib/httparty/response_fragment.rb +19 -0
  59. data/lib/httparty/text_encoder.rb +70 -0
  60. data/lib/httparty/utils.rb +11 -0
  61. data/lib/httparty/version.rb +3 -0
  62. data/script/release +42 -0
  63. data/website/css/common.css +47 -0
  64. data/website/index.html +73 -0
  65. metadata +138 -0
@@ -0,0 +1,254 @@
1
+ module HTTParty
2
+ # Default connection adapter that returns a new Net::HTTP each time
3
+ #
4
+ # == Custom Connection Factories
5
+ #
6
+ # If you like to implement your own connection adapter, subclassing
7
+ # HTTParty::ConnectionAdapter will make it easier. Just override
8
+ # the #connection method. The uri and options attributes will have
9
+ # all the info you need to construct your http connection. Whatever
10
+ # you return from your connection method needs to adhere to the
11
+ # Net::HTTP interface as this is what HTTParty expects.
12
+ #
13
+ # @example log the uri and options
14
+ # class LoggingConnectionAdapter < HTTParty::ConnectionAdapter
15
+ # def connection
16
+ # puts uri
17
+ # puts options
18
+ # Net::HTTP.new(uri)
19
+ # end
20
+ # end
21
+ #
22
+ # @example count number of http calls
23
+ # class CountingConnectionAdapter < HTTParty::ConnectionAdapter
24
+ # @@count = 0
25
+ #
26
+ # self.count
27
+ # @@count
28
+ # end
29
+ #
30
+ # def connection
31
+ # self.count += 1
32
+ # super
33
+ # end
34
+ # end
35
+ #
36
+ # === Configuration
37
+ # There is lots of configuration data available for your connection adapter
38
+ # in the #options attribute. It is up to you to interpret them within your
39
+ # connection adapter. Take a look at the implementation of
40
+ # HTTParty::ConnectionAdapter#connection for examples of how they are used.
41
+ # The keys used in options are
42
+ # * :+timeout+: timeout in seconds
43
+ # * :+open_timeout+: http connection open_timeout in seconds, overrides timeout if set
44
+ # * :+read_timeout+: http connection read_timeout in seconds, overrides timeout if set
45
+ # * :+write_timeout+: http connection write_timeout in seconds, overrides timeout if set (Ruby >= 2.6.0 required)
46
+ # * :+debug_output+: see HTTParty::ClassMethods.debug_output.
47
+ # * :+cert_store+: contains certificate data. see method 'attach_ssl_certificates'
48
+ # * :+pem+: contains pem client certificate data. see method 'attach_ssl_certificates'
49
+ # * :+p12+: contains PKCS12 client client certificate data. see method 'attach_ssl_certificates'
50
+ # * :+verify+: verify the server’s certificate against the ca certificate.
51
+ # * :+verify_peer+: set to false to turn off server verification but still send client certificate
52
+ # * :+ssl_ca_file+: see HTTParty::ClassMethods.ssl_ca_file.
53
+ # * :+ssl_ca_path+: see HTTParty::ClassMethods.ssl_ca_path.
54
+ # * :+ssl_version+: SSL versions to allow. see method 'attach_ssl_certificates'
55
+ # * :+ciphers+: The list of SSL ciphers to support
56
+ # * :+connection_adapter_options+: contains the hash you passed to HTTParty.connection_adapter when you configured your connection adapter
57
+ # * :+local_host+: The local address to bind to
58
+ # * :+local_port+: The local port to bind to
59
+ # * :+http_proxyaddr+: HTTP Proxy address
60
+ # * :+http_proxyport+: HTTP Proxy port
61
+ # * :+http_proxyuser+: HTTP Proxy user
62
+ # * :+http_proxypass+: HTTP Proxy password
63
+ #
64
+ # === Inherited methods
65
+ # * :+clean_host+: Method used to sanitize host names
66
+
67
+ class ConnectionAdapter
68
+ # Private: Regex used to strip brackets from IPv6 URIs.
69
+ StripIpv6BracketsRegex = /\A\[(.*)\]\z/
70
+
71
+ OPTION_DEFAULTS = {
72
+ verify: true,
73
+ verify_peer: true
74
+ }
75
+
76
+ # Public
77
+ def self.call(uri, options)
78
+ new(uri, options).connection
79
+ end
80
+
81
+ def self.default_cert_store
82
+ @default_cert_store ||= OpenSSL::X509::Store.new.tap do |cert_store|
83
+ cert_store.set_default_paths
84
+ end
85
+ end
86
+
87
+ attr_reader :uri, :options
88
+
89
+ def initialize(uri, options = {})
90
+ uri_adapter = options[:uri_adapter] || URI
91
+ raise ArgumentError, "uri must be a #{uri_adapter}, not a #{uri.class}" unless uri.is_a? uri_adapter
92
+
93
+ @uri = uri
94
+ @options = OPTION_DEFAULTS.merge(options)
95
+ end
96
+
97
+ def connection
98
+ host = clean_host(uri.host)
99
+ port = uri.port || (uri.scheme == 'https' ? 443 : 80)
100
+ if options.key?(:http_proxyaddr)
101
+ http = Net::HTTP.new(
102
+ host,
103
+ port,
104
+ options[:http_proxyaddr],
105
+ options[:http_proxyport],
106
+ options[:http_proxyuser],
107
+ options[:http_proxypass]
108
+ )
109
+ else
110
+ http = Net::HTTP.new(host, port)
111
+ end
112
+
113
+ http.use_ssl = ssl_implied?(uri)
114
+
115
+ attach_ssl_certificates(http, options)
116
+
117
+ if add_timeout?(options[:timeout])
118
+ http.open_timeout = options[:timeout]
119
+ http.read_timeout = options[:timeout]
120
+
121
+ from_ruby_version('2.6.0', option: :write_timeout, warn: false) do
122
+ http.write_timeout = options[:timeout]
123
+ end
124
+ end
125
+
126
+ if add_timeout?(options[:read_timeout])
127
+ http.read_timeout = options[:read_timeout]
128
+ end
129
+
130
+ if add_timeout?(options[:open_timeout])
131
+ http.open_timeout = options[:open_timeout]
132
+ end
133
+
134
+ if add_timeout?(options[:write_timeout])
135
+ from_ruby_version('2.6.0', option: :write_timeout) do
136
+ http.write_timeout = options[:write_timeout]
137
+ end
138
+ end
139
+
140
+ if add_max_retries?(options[:max_retries])
141
+ from_ruby_version('2.5.0', option: :max_retries) do
142
+ http.max_retries = options[:max_retries]
143
+ end
144
+ end
145
+
146
+ if options[:debug_output]
147
+ http.set_debug_output(options[:debug_output])
148
+ end
149
+
150
+ if options[:ciphers]
151
+ http.ciphers = options[:ciphers]
152
+ end
153
+
154
+ # Bind to a specific local address or port
155
+ #
156
+ # @see https://bugs.ruby-lang.org/issues/6617
157
+ if options[:local_host]
158
+ from_ruby_version('2.0.0', option: :local_host) do
159
+ http.local_host = options[:local_host]
160
+ end
161
+ end
162
+
163
+ if options[:local_port]
164
+ from_ruby_version('2.0.0', option: :local_port) do
165
+ http.local_port = options[:local_port]
166
+ end
167
+ end
168
+
169
+ http
170
+ end
171
+
172
+ private
173
+
174
+ def from_ruby_version(ruby_version, option: nil, warn: true)
175
+ if RUBY_VERSION >= ruby_version
176
+ yield
177
+ elsif warn
178
+ Kernel.warn("Warning: option #{ option } requires Ruby version #{ ruby_version } or later")
179
+ end
180
+ end
181
+
182
+ def add_timeout?(timeout)
183
+ timeout && (timeout.is_a?(Integer) || timeout.is_a?(Float))
184
+ end
185
+
186
+ def add_max_retries?(max_retries)
187
+ max_retries && max_retries.is_a?(Integer) && max_retries >= 0
188
+ end
189
+
190
+ def clean_host(host)
191
+ strip_ipv6_brackets(host)
192
+ end
193
+
194
+ def strip_ipv6_brackets(host)
195
+ StripIpv6BracketsRegex =~ host ? $1 : host
196
+ end
197
+
198
+ def ssl_implied?(uri)
199
+ uri.port == 443 || uri.scheme == 'https'
200
+ end
201
+
202
+ def verify_ssl_certificate?
203
+ !(options[:verify] == false || options[:verify_peer] == false)
204
+ end
205
+
206
+ def attach_ssl_certificates(http, options)
207
+ if http.use_ssl?
208
+ if options.fetch(:verify, true)
209
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
210
+ if options[:cert_store]
211
+ http.cert_store = options[:cert_store]
212
+ else
213
+ # Use the default cert store by default, i.e. system ca certs
214
+ http.cert_store = self.class.default_cert_store
215
+ end
216
+ else
217
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
218
+ end
219
+
220
+ # Client certificate authentication
221
+ # Note: options[:pem] must contain the content of a PEM file having the private key appended
222
+ if options[:pem]
223
+ http.cert = OpenSSL::X509::Certificate.new(options[:pem])
224
+ http.key = OpenSSL::PKey::RSA.new(options[:pem], options[:pem_password])
225
+ http.verify_mode = verify_ssl_certificate? ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
226
+ end
227
+
228
+ # PKCS12 client certificate authentication
229
+ if options[:p12]
230
+ p12 = OpenSSL::PKCS12.new(options[:p12], options[:p12_password])
231
+ http.cert = p12.certificate
232
+ http.key = p12.key
233
+ http.verify_mode = verify_ssl_certificate? ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
234
+ end
235
+
236
+ # SSL certificate authority file and/or directory
237
+ if options[:ssl_ca_file]
238
+ http.ca_file = options[:ssl_ca_file]
239
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
240
+ end
241
+
242
+ if options[:ssl_ca_path]
243
+ http.ca_path = options[:ssl_ca_path]
244
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
245
+ end
246
+
247
+ # This is only Ruby 1.9+
248
+ if options[:ssl_version] && http.respond_to?(:ssl_version=)
249
+ http.ssl_version = options[:ssl_version]
250
+ end
251
+ end
252
+ end
253
+ end
254
+ end
@@ -0,0 +1,21 @@
1
+ class HTTParty::CookieHash < Hash #:nodoc:
2
+ CLIENT_COOKIES = %w(path expires domain path secure httponly)
3
+
4
+ def add_cookies(value)
5
+ case value
6
+ when Hash
7
+ merge!(value)
8
+ when String
9
+ value.split('; ').each do |cookie|
10
+ array = cookie.split('=', 2)
11
+ self[array[0].to_sym] = array[1]
12
+ end
13
+ else
14
+ raise "add_cookies only takes a Hash or a String"
15
+ end
16
+ end
17
+
18
+ def to_cookie_string
19
+ select { |k, v| !CLIENT_COOKIES.include?(k.to_s.downcase) }.collect { |k, v| "#{k}=#{v}" }.join("; ")
20
+ end
21
+ end
@@ -0,0 +1,33 @@
1
+ module HTTParty
2
+ # @abstact Exceptions raised by HTTParty inherit from Error
3
+ class Error < StandardError; end
4
+
5
+ # Exception raised when you attempt to set a non-existent format
6
+ class UnsupportedFormat < Error; end
7
+
8
+ # Exception raised when using a URI scheme other than HTTP or HTTPS
9
+ class UnsupportedURIScheme < Error; end
10
+
11
+ # @abstract Exceptions which inherit from ResponseError contain the Net::HTTP
12
+ # response object accessible via the {#response} method.
13
+ class ResponseError < Error
14
+ # Returns the response of the last request
15
+ # @return [Net::HTTPResponse] A subclass of Net::HTTPResponse, e.g.
16
+ # Net::HTTPOK
17
+ attr_reader :response
18
+
19
+ # Instantiate an instance of ResponseError with a Net::HTTPResponse object
20
+ # @param [Net::HTTPResponse]
21
+ def initialize(response)
22
+ @response = response
23
+ super(response)
24
+ end
25
+ end
26
+
27
+ # Exception that is raised when request has redirected too many times.
28
+ # Calling {#response} returns the Net:HTTP response object.
29
+ class RedirectionTooDeep < ResponseError; end
30
+
31
+ # Exception that is raised when request redirects and location header is present more than once
32
+ class DuplicateLocationHeader < ResponseError; end
33
+ end
@@ -0,0 +1,69 @@
1
+ require 'erb'
2
+
3
+ module HTTParty
4
+ module HashConversions
5
+ # @return <String> This hash as a query string
6
+ #
7
+ # @example
8
+ # { name: "Bob",
9
+ # address: {
10
+ # street: '111 Ruby Ave.',
11
+ # city: 'Ruby Central',
12
+ # phones: ['111-111-1111', '222-222-2222']
13
+ # }
14
+ # }.to_params
15
+ # #=> "name=Bob&address[city]=Ruby Central&address[phones][]=111-111-1111&address[phones][]=222-222-2222&address[street]=111 Ruby Ave."
16
+ def self.to_params(hash)
17
+ hash.to_hash.map { |k, v| normalize_param(k, v) }.join.chop
18
+ end
19
+
20
+ # @param key<Object> The key for the param.
21
+ # @param value<Object> The value for the param.
22
+ #
23
+ # @return <String> This key value pair as a param
24
+ #
25
+ # @example normalize_param(:name, "Bob Jones") #=> "name=Bob%20Jones&"
26
+ def self.normalize_param(key, value)
27
+ normalized_keys = normalize_keys(key, value)
28
+
29
+ normalized_keys.flatten.each_slice(2).inject('') do |string, (k, v)|
30
+ string + "#{k}=#{ERB::Util.url_encode(v.to_s)}&"
31
+ end
32
+ end
33
+
34
+ def self.normalize_keys(key, value)
35
+ stack = []
36
+ normalized_keys = []
37
+
38
+ if value.respond_to?(:to_ary)
39
+ if value.empty?
40
+ normalized_keys << ["#{key}[]", '']
41
+ else
42
+ normalized_keys = value.to_ary.flat_map do |element|
43
+ normalize_keys("#{key}[]", element)
44
+ end
45
+ end
46
+ elsif value.respond_to?(:to_hash)
47
+ stack << [key, value.to_hash]
48
+ else
49
+ normalized_keys << [key.to_s, value]
50
+ end
51
+
52
+ stack.each do |parent, hash|
53
+ hash.each do |child_key, child_value|
54
+ if child_value.respond_to?(:to_hash)
55
+ stack << ["#{parent}[#{child_key}]", child_value.to_hash]
56
+ elsif child_value.respond_to?(:to_ary)
57
+ child_value.to_ary.each do |v|
58
+ normalized_keys << normalize_keys("#{parent}[#{child_key}][]", v).flatten
59
+ end
60
+ else
61
+ normalized_keys << normalize_keys("#{parent}[#{child_key}]", child_value).flatten
62
+ end
63
+ end
64
+ end
65
+
66
+ normalized_keys
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,30 @@
1
+ module HTTParty
2
+ class HeadersProcessor
3
+ attr_reader :headers, :options
4
+
5
+ def initialize(headers, options)
6
+ @headers = headers
7
+ @options = options
8
+ end
9
+
10
+ def call
11
+ return unless options[:headers]
12
+
13
+ options[:headers] = headers.merge(options[:headers]) if headers.any?
14
+ options[:headers] = Utils.stringify_keys(process_dynamic_headers)
15
+ end
16
+
17
+ private
18
+
19
+ def process_dynamic_headers
20
+ options[:headers].each_with_object({}) do |header, processed_headers|
21
+ key, value = header
22
+ processed_headers[key] = if value.respond_to?(:call)
23
+ value.arity == 0 ? value.call : value.call(options)
24
+ else
25
+ value
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,45 @@
1
+ module HTTParty
2
+ module Logger
3
+ class ApacheFormatter #:nodoc:
4
+ TAG_NAME = HTTParty.name
5
+
6
+ attr_accessor :level, :logger
7
+
8
+ def initialize(logger, level)
9
+ @logger = logger
10
+ @level = level.to_sym
11
+ end
12
+
13
+ def format(request, response)
14
+ @request = request
15
+ @response = response
16
+
17
+ logger.public_send level, message
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :request, :response
23
+
24
+ def message
25
+ "[#{TAG_NAME}] [#{current_time}] #{response.code} \"#{http_method} #{path}\" #{content_length || '-'} "
26
+ end
27
+
28
+ def current_time
29
+ Time.now.strftime("%Y-%m-%d %H:%M:%S %z")
30
+ end
31
+
32
+ def http_method
33
+ request.http_method.name.split("::").last.upcase
34
+ end
35
+
36
+ def path
37
+ request.path.to_s
38
+ end
39
+
40
+ def content_length
41
+ response.respond_to?(:headers) ? response.headers['Content-Length'] : response['Content-Length']
42
+ end
43
+ end
44
+ end
45
+ end