httparty-responsibly 0.17.0.r1

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 (64) 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 +502 -0
  10. data/Gemfile +23 -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 +684 -0
  41. data/lib/httparty/connection_adapter.rb +244 -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/logger/apache_formatter.rb +45 -0
  46. data/lib/httparty/logger/curl_formatter.rb +91 -0
  47. data/lib/httparty/logger/logger.rb +28 -0
  48. data/lib/httparty/logger/logstash_formatter.rb +59 -0
  49. data/lib/httparty/module_inheritable_attributes.rb +56 -0
  50. data/lib/httparty/net_digest_auth.rb +136 -0
  51. data/lib/httparty/parser.rb +150 -0
  52. data/lib/httparty/request.rb +386 -0
  53. data/lib/httparty/request/body.rb +84 -0
  54. data/lib/httparty/request/multipart_boundary.rb +11 -0
  55. data/lib/httparty/response.rb +140 -0
  56. data/lib/httparty/response/headers.rb +33 -0
  57. data/lib/httparty/response_fragment.rb +19 -0
  58. data/lib/httparty/text_encoder.rb +70 -0
  59. data/lib/httparty/utils.rb +11 -0
  60. data/lib/httparty/version.rb +3 -0
  61. data/script/release +42 -0
  62. data/website/css/common.css +47 -0
  63. data/website/index.html +73 -0
  64. metadata +138 -0
@@ -0,0 +1,244 @@
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 options[:debug_output]
141
+ http.set_debug_output(options[:debug_output])
142
+ end
143
+
144
+ if options[:ciphers]
145
+ http.ciphers = options[:ciphers]
146
+ end
147
+
148
+ # Bind to a specific local address or port
149
+ #
150
+ # @see https://bugs.ruby-lang.org/issues/6617
151
+ if options[:local_host]
152
+ from_ruby_version('2.0.0', option: :local_host) do
153
+ http.local_host = options[:local_host]
154
+ end
155
+ end
156
+
157
+ if options[:local_port]
158
+ from_ruby_version('2.0.0', option: :local_port) do
159
+ http.local_port = options[:local_port]
160
+ end
161
+ end
162
+
163
+ http
164
+ end
165
+
166
+ private
167
+
168
+ def from_ruby_version(ruby_version, option: nil, warn: true)
169
+ if RUBY_VERSION >= ruby_version
170
+ yield
171
+ elsif warn
172
+ Kernel.warn("Warning: option #{ option } requires Ruby version #{ ruby_version } or later")
173
+ end
174
+ end
175
+
176
+ def add_timeout?(timeout)
177
+ timeout && (timeout.is_a?(Integer) || timeout.is_a?(Float))
178
+ end
179
+
180
+ def clean_host(host)
181
+ strip_ipv6_brackets(host)
182
+ end
183
+
184
+ def strip_ipv6_brackets(host)
185
+ StripIpv6BracketsRegex =~ host ? $1 : host
186
+ end
187
+
188
+ def ssl_implied?(uri)
189
+ uri.port == 443 || uri.scheme == 'https'
190
+ end
191
+
192
+ def verify_ssl_certificate?
193
+ !(options[:verify] == false || options[:verify_peer] == false)
194
+ end
195
+
196
+ def attach_ssl_certificates(http, options)
197
+ if http.use_ssl?
198
+ if options.fetch(:verify, true)
199
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
200
+ if options[:cert_store]
201
+ http.cert_store = options[:cert_store]
202
+ else
203
+ # Use the default cert store by default, i.e. system ca certs
204
+ http.cert_store = self.class.default_cert_store
205
+ end
206
+ else
207
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
208
+ end
209
+
210
+ # Client certificate authentication
211
+ # Note: options[:pem] must contain the content of a PEM file having the private key appended
212
+ if options[:pem]
213
+ http.cert = OpenSSL::X509::Certificate.new(options[:pem])
214
+ http.key = OpenSSL::PKey::RSA.new(options[:pem], options[:pem_password])
215
+ http.verify_mode = verify_ssl_certificate? ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
216
+ end
217
+
218
+ # PKCS12 client certificate authentication
219
+ if options[:p12]
220
+ p12 = OpenSSL::PKCS12.new(options[:p12], options[:p12_password])
221
+ http.cert = p12.certificate
222
+ http.key = p12.key
223
+ http.verify_mode = verify_ssl_certificate? ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
224
+ end
225
+
226
+ # SSL certificate authority file and/or directory
227
+ if options[:ssl_ca_file]
228
+ http.ca_file = options[:ssl_ca_file]
229
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
230
+ end
231
+
232
+ if options[:ssl_ca_path]
233
+ http.ca_path = options[:ssl_ca_path]
234
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
235
+ end
236
+
237
+ # This is only Ruby 1.9+
238
+ if options[:ssl_version] && http.respond_to?(:ssl_version=)
239
+ http.ssl_version = options[:ssl_version]
240
+ end
241
+ end
242
+ end
243
+ end
244
+ 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,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
@@ -0,0 +1,91 @@
1
+ module HTTParty
2
+ module Logger
3
+ class CurlFormatter #:nodoc:
4
+ TAG_NAME = HTTParty.name
5
+ OUT = '>'.freeze
6
+ IN = '<'.freeze
7
+
8
+ attr_accessor :level, :logger
9
+
10
+ def initialize(logger, level)
11
+ @logger = logger
12
+ @level = level.to_sym
13
+ @messages = []
14
+ end
15
+
16
+ def format(request, response)
17
+ @request = request
18
+ @response = response
19
+
20
+ log_request
21
+ log_response
22
+
23
+ logger.public_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
53
+
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]
66
+
67
+ log OUT, 'Query: '
68
+ log_hash request.options[:query]
69
+ end
70
+
71
+ def log_response_headers
72
+ headers = response.respond_to?(:headers) ? response.headers : response
73
+ response.each_header do |response_header|
74
+ log IN, "#{response_header.capitalize}: #{headers[response_header]}"
75
+ end
76
+ end
77
+
78
+ def log_hash(hash)
79
+ hash.each { |k, v| log(OUT, "#{k}: #{v}") }
80
+ end
81
+
82
+ def log(direction, line = '')
83
+ messages << "[#{TAG_NAME}] [#{current_time}] #{direction} #{line}"
84
+ end
85
+
86
+ def current_time
87
+ Time.now.strftime("%Y-%m-%d %H:%M:%S %z")
88
+ end
89
+ end
90
+ end
91
+ end