vault-kv 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +42 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +29 -0
  5. data/CHANGELOG.md +228 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +362 -0
  8. data/README.md +212 -0
  9. data/Rakefile +6 -0
  10. data/lib/vault.rb +49 -0
  11. data/lib/vault/api.rb +13 -0
  12. data/lib/vault/api/approle.rb +218 -0
  13. data/lib/vault/api/auth.rb +316 -0
  14. data/lib/vault/api/auth_tls.rb +92 -0
  15. data/lib/vault/api/auth_token.rb +242 -0
  16. data/lib/vault/api/help.rb +33 -0
  17. data/lib/vault/api/kv.rb +207 -0
  18. data/lib/vault/api/logical.rb +150 -0
  19. data/lib/vault/api/secret.rb +168 -0
  20. data/lib/vault/api/sys.rb +25 -0
  21. data/lib/vault/api/sys/audit.rb +91 -0
  22. data/lib/vault/api/sys/auth.rb +116 -0
  23. data/lib/vault/api/sys/health.rb +63 -0
  24. data/lib/vault/api/sys/init.rb +83 -0
  25. data/lib/vault/api/sys/leader.rb +48 -0
  26. data/lib/vault/api/sys/lease.rb +49 -0
  27. data/lib/vault/api/sys/mount.rb +103 -0
  28. data/lib/vault/api/sys/policy.rb +92 -0
  29. data/lib/vault/api/sys/seal.rb +81 -0
  30. data/lib/vault/client.rb +447 -0
  31. data/lib/vault/configurable.rb +48 -0
  32. data/lib/vault/defaults.rb +197 -0
  33. data/lib/vault/encode.rb +19 -0
  34. data/lib/vault/errors.rb +72 -0
  35. data/lib/vault/persistent.rb +1158 -0
  36. data/lib/vault/persistent/connection.rb +42 -0
  37. data/lib/vault/persistent/pool.rb +48 -0
  38. data/lib/vault/persistent/timed_stack_multi.rb +70 -0
  39. data/lib/vault/request.rb +43 -0
  40. data/lib/vault/response.rb +89 -0
  41. data/lib/vault/vendor/connection_pool.rb +150 -0
  42. data/lib/vault/vendor/connection_pool/timed_stack.rb +178 -0
  43. data/lib/vault/vendor/connection_pool/version.rb +5 -0
  44. data/lib/vault/version.rb +3 -0
  45. data/vault.gemspec +30 -0
  46. metadata +186 -0
@@ -0,0 +1,48 @@
1
+ require_relative "defaults"
2
+
3
+ module Vault
4
+ module Configurable
5
+ def self.keys
6
+ @keys ||= [
7
+ :address,
8
+ :token,
9
+ :hostname,
10
+ :open_timeout,
11
+ :proxy_address,
12
+ :proxy_password,
13
+ :proxy_port,
14
+ :proxy_username,
15
+ :pool_size,
16
+ :read_timeout,
17
+ :ssl_ciphers,
18
+ :ssl_pem_contents,
19
+ :ssl_pem_file,
20
+ :ssl_pem_passphrase,
21
+ :ssl_ca_cert,
22
+ :ssl_ca_path,
23
+ :ssl_cert_store,
24
+ :ssl_verify,
25
+ :ssl_timeout,
26
+ :timeout,
27
+ ]
28
+ end
29
+
30
+ Vault::Configurable.keys.each(&method(:attr_accessor))
31
+
32
+ # Configure yields self for block-style configuration.
33
+ #
34
+ # @yield [self]
35
+ def configure
36
+ yield self
37
+ end
38
+
39
+ # The list of options for this configurable.
40
+ #
41
+ # @return [Hash<Symbol, Object>]
42
+ def options
43
+ Hash[*Vault::Configurable.keys.map do |key|
44
+ [key, instance_variable_get(:"@#{key}")]
45
+ end.flatten]
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,197 @@
1
+ require "pathname"
2
+ require "base64"
3
+
4
+ module Vault
5
+ module Defaults
6
+ # The default vault address.
7
+ # @return [String]
8
+ VAULT_ADDRESS = "https://127.0.0.1:8200".freeze
9
+
10
+ # The path to the vault token on disk.
11
+ # @return [String]
12
+ VAULT_DISK_TOKEN = Pathname.new("#{ENV["HOME"]}/.vault-token").expand_path.freeze
13
+
14
+ # The list of SSL ciphers to allow. You should not change this value unless
15
+ # you absolutely know what you are doing!
16
+ # @return [String]
17
+ SSL_CIPHERS = "TLSv1.2:!aNULL:!eNULL".freeze
18
+
19
+ # The default number of attempts.
20
+ # @return [Fixnum]
21
+ RETRY_ATTEMPTS = 2
22
+
23
+ # The default backoff interval.
24
+ # @return [Fixnum]
25
+ RETRY_BASE = 0.05
26
+
27
+ # The maximum amount of time for a single exponential backoff to sleep.
28
+ RETRY_MAX_WAIT = 2.0
29
+
30
+ # The default size of the connection pool
31
+ DEFAULT_POOL_SIZE = 16
32
+
33
+ # The set of exceptions that are detect and retried by default
34
+ # with `with_retries`
35
+ RETRIED_EXCEPTIONS = [HTTPServerError]
36
+
37
+ class << self
38
+ # The list of calculated options for this configurable.
39
+ # @return [Hash]
40
+ def options
41
+ Hash[*Configurable.keys.map { |key| [key, public_send(key)] }.flatten]
42
+ end
43
+
44
+ # The address to communicate with Vault.
45
+ # @return [String]
46
+ def address
47
+ ENV["VAULT_ADDR"] || VAULT_ADDRESS
48
+ end
49
+
50
+ # The vault token to use for authentiation.
51
+ # @return [String, nil]
52
+ def token
53
+ if !ENV["VAULT_TOKEN"].nil?
54
+ return ENV["VAULT_TOKEN"]
55
+ end
56
+
57
+ if VAULT_DISK_TOKEN.exist? && VAULT_DISK_TOKEN.readable?
58
+ return VAULT_DISK_TOKEN.read.chomp
59
+ end
60
+
61
+ nil
62
+ end
63
+
64
+ # The SNI host to use when connecting to Vault via TLS.
65
+ # @return [String, nil]
66
+ def hostname
67
+ ENV["VAULT_TLS_SERVER_NAME"]
68
+ end
69
+
70
+ # The number of seconds to wait when trying to open a connection before
71
+ # timing out
72
+ # @return [String, nil]
73
+ def open_timeout
74
+ ENV["VAULT_OPEN_TIMEOUT"]
75
+ end
76
+
77
+ # The size of the connection pool to communicate with Vault
78
+ # @return Integer
79
+ def pool_size
80
+ if var = ENV["VAULT_POOL_SIZE"]
81
+ return var.to_i
82
+ else
83
+ DEFAULT_POOL_SIZE
84
+ end
85
+ end
86
+
87
+ # The HTTP Proxy server address as a string
88
+ # @return [String, nil]
89
+ def proxy_address
90
+ ENV["VAULT_PROXY_ADDRESS"]
91
+ end
92
+
93
+ # The HTTP Proxy server username as a string
94
+ # @return [String, nil]
95
+ def proxy_username
96
+ ENV["VAULT_PROXY_USERNAME"]
97
+ end
98
+
99
+ # The HTTP Proxy user password as a string
100
+ # @return [String, nil]
101
+ def proxy_password
102
+ ENV["VAULT_PROXY_PASSWORD"]
103
+ end
104
+
105
+ # The HTTP Proxy server port as a string
106
+ # @return [String, nil]
107
+ def proxy_port
108
+ ENV["VAULT_PROXY_PORT"]
109
+ end
110
+
111
+ # The number of seconds to wait when reading a response before timing out
112
+ # @return [String, nil]
113
+ def read_timeout
114
+ ENV["VAULT_READ_TIMEOUT"]
115
+ end
116
+
117
+ # The ciphers that will be used when communicating with vault over ssl
118
+ # You should only change the defaults if the ciphers are not available on
119
+ # your platform and you know what you are doing
120
+ # @return [String]
121
+ def ssl_ciphers
122
+ ENV["VAULT_SSL_CIPHERS"] || SSL_CIPHERS
123
+ end
124
+
125
+ # The raw contents (as a string) for the pem file. To specify the path to
126
+ # the pem file, use {#ssl_pem_file} instead. This value is preferred over
127
+ # the value for {#ssl_pem_file}, if set.
128
+ # @return [String, nil]
129
+ def ssl_pem_contents
130
+ if ENV["VAULT_SSL_PEM_CONTENTS_BASE64"]
131
+ Base64.decode64(ENV["VAULT_SSL_PEM_CONTENTS_BASE64"])
132
+ else
133
+ ENV["VAULT_SSL_PEM_CONTENTS"]
134
+ end
135
+ end
136
+
137
+ # The path to a pem on disk to use with custom SSL verification
138
+ # @return [String, nil]
139
+ def ssl_pem_file
140
+ ENV["VAULT_SSL_CERT"] || ENV["VAULT_SSL_PEM_FILE"]
141
+ end
142
+
143
+ # Passphrase to the pem file on disk to use with custom SSL verification
144
+ # @return [String, nil]
145
+ def ssl_pem_passphrase
146
+ ENV["VAULT_SSL_CERT_PASSPHRASE"]
147
+ end
148
+
149
+ # The path to the CA cert on disk to use for certificate verification
150
+ # @return [String, nil]
151
+ def ssl_ca_cert
152
+ ENV["VAULT_CACERT"]
153
+ end
154
+
155
+ # The CA cert store to use for certificate verification
156
+ # @return [OpenSSL::X509::Store, nil]
157
+ def ssl_cert_store
158
+ nil
159
+ end
160
+
161
+ # The path to the directory on disk holding CA certs to use
162
+ # for certificate verification
163
+ # @return [String, nil]
164
+ def ssl_ca_path
165
+ ENV["VAULT_CAPATH"]
166
+ end
167
+
168
+ # Verify SSL requests (default: true)
169
+ # @return [true, false]
170
+ def ssl_verify
171
+ # Vault CLI uses this envvar, so accept it by precedence
172
+ if !ENV["VAULT_SKIP_VERIFY"].nil?
173
+ return false
174
+ end
175
+
176
+ if ENV["VAULT_SSL_VERIFY"].nil?
177
+ true
178
+ else
179
+ %w[t y].include?(ENV["VAULT_SSL_VERIFY"].downcase[0])
180
+ end
181
+ end
182
+
183
+ # The number of seconds to wait for connecting and verifying SSL
184
+ # @return [String, nil]
185
+ def ssl_timeout
186
+ ENV["VAULT_SSL_TIMEOUT"]
187
+ end
188
+
189
+ # A default meta-attribute to set all timeout values - individually set
190
+ # timeout values will take precedence
191
+ # @return [String, nil]
192
+ def timeout
193
+ ENV["VAULT_TIMEOUT"]
194
+ end
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,19 @@
1
+ module Vault
2
+ module EncodePath
3
+
4
+ # Encodes a string according to the rules for URL paths. This is
5
+ # used as opposed to CGI.escape because in a URL path, space
6
+ # needs to be escaped as %20 and CGI.escapes a space as +.
7
+ #
8
+ # @param [String]
9
+ #
10
+ # @return [String]
11
+ def encode_path(path)
12
+ path.b.gsub(%r!([^a-zA-Z0-9_.-/]+)!) { |m|
13
+ '%' + m.unpack('H2' * m.bytesize).join('%').upcase
14
+ }
15
+ end
16
+
17
+ module_function :encode_path
18
+ end
19
+ end
@@ -0,0 +1,72 @@
1
+ module Vault
2
+ class VaultError < RuntimeError; end
3
+
4
+ class MissingTokenError < VaultError
5
+ def initialize
6
+ super <<-EOH
7
+ Missing Vault token! I cannot make requests to Vault without a token. Please
8
+ set a Vault token in the client:
9
+
10
+ Vault.token = "42d1dee5-eb6e-102c-8d23-cc3ba875da51"
11
+
12
+ or authenticate with Vault using the Vault CLI:
13
+
14
+ $ vault auth ...
15
+
16
+ or set the environment variable $VAULT_TOKEN to the token value:
17
+
18
+ $ export VAULT_TOKEN="..."
19
+
20
+ Please refer to the documentation for more examples.
21
+ EOH
22
+ end
23
+ end
24
+
25
+ class HTTPConnectionError < VaultError
26
+ attr_reader :address
27
+
28
+ def initialize(address, exception)
29
+ @address = address
30
+ @exception = exception
31
+
32
+ super <<-EOH
33
+ The Vault server at `#{address}' is not currently
34
+ accepting connections. Please ensure that the server is running and that your
35
+ authentication information is correct.
36
+
37
+ The original error was `#{exception.class}'. Additional information (if any) is
38
+ shown below:
39
+
40
+ #{exception.message}
41
+
42
+ Please refer to the documentation for more help.
43
+ EOH
44
+ end
45
+
46
+ def original_exception
47
+ @exception
48
+ end
49
+ end
50
+
51
+ class HTTPError < VaultError
52
+ attr_reader :address, :response, :code, :errors
53
+
54
+ def initialize(address, response, errors = [])
55
+ @address, @response, @errors = address, response, errors
56
+ @code = response.code.to_i
57
+ errors = errors.map { |error| " * #{error}" }
58
+
59
+ super <<-EOH
60
+ The Vault server at `#{address}' responded with a #{code}.
61
+ Any additional information the server supplied is shown below:
62
+
63
+ #{errors.join("\n").rstrip}
64
+
65
+ Please refer to the documentation for help.
66
+ EOH
67
+ end
68
+ end
69
+
70
+ class HTTPClientError < HTTPError; end
71
+ class HTTPServerError < HTTPError; end
72
+ end
@@ -0,0 +1,1158 @@
1
+ # Vendored and modified from github.com/drbrain/net-http-persistent
2
+ #
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'cgi' # for escaping
6
+ require 'vault/vendor/connection_pool'
7
+
8
+ begin
9
+ require 'net/http/pipeline'
10
+ rescue LoadError
11
+ end
12
+
13
+ autoload :OpenSSL, 'openssl'
14
+
15
+ ##
16
+ # Persistent connections for Net::HTTP
17
+ #
18
+ # PersistentHTTP maintains persistent connections across all the
19
+ # servers you wish to talk to. For each host:port you communicate with a
20
+ # single persistent connection is created.
21
+ #
22
+ # Multiple PersistentHTTP objects will share the same set of
23
+ # connections.
24
+ #
25
+ # For each thread you start a new connection will be created. A
26
+ # PersistentHTTP connection will not be shared across threads.
27
+ #
28
+ # You can shut down the HTTP connections when done by calling #shutdown. You
29
+ # should name your PersistentHTTP object if you intend to call this
30
+ # method.
31
+ #
32
+ # Example:
33
+ #
34
+ # require 'net/http/persistent'
35
+ #
36
+ # uri = URI 'http://example.com/awesome/web/service'
37
+ #
38
+ # http = PersistentHTTP.new 'my_app_name'
39
+ #
40
+ # # perform a GET
41
+ # response = http.request uri
42
+ #
43
+ # # or
44
+ #
45
+ # get = Net::HTTP::Get.new uri.request_uri
46
+ # response = http.request get
47
+ #
48
+ # # create a POST
49
+ # post_uri = uri + 'create'
50
+ # post = Net::HTTP::Post.new post_uri.path
51
+ # post.set_form_data 'some' => 'cool data'
52
+ #
53
+ # # perform the POST, the URI is always required
54
+ # response http.request post_uri, post
55
+ #
56
+ # Note that for GET, HEAD and other requests that do not have a body you want
57
+ # to use URI#request_uri not URI#path. The request_uri contains the query
58
+ # params which are sent in the body for other requests.
59
+ #
60
+ # == SSL
61
+ #
62
+ # SSL connections are automatically created depending upon the scheme of the
63
+ # URI. SSL connections are automatically verified against the default
64
+ # certificate store for your computer. You can override this by changing
65
+ # verify_mode or by specifying an alternate cert_store.
66
+ #
67
+ # Here are the SSL settings, see the individual methods for documentation:
68
+ #
69
+ # #certificate :: This client's certificate
70
+ # #ca_file :: The certificate-authorities
71
+ # #ca_path :: Directory with certificate-authorities
72
+ # #cert_store :: An SSL certificate store
73
+ # #ciphers :: List of SSl ciphers allowed
74
+ # #private_key :: The client's SSL private key
75
+ # #reuse_ssl_sessions :: Reuse a previously opened SSL session for a new
76
+ # connection
77
+ # #ssl_timeout :: SSL session lifetime
78
+ # #ssl_version :: Which specific SSL version to use
79
+ # #verify_callback :: For server certificate verification
80
+ # #verify_depth :: Depth of certificate verification
81
+ # #verify_mode :: How connections should be verified
82
+ #
83
+ # == Proxies
84
+ #
85
+ # A proxy can be set through #proxy= or at initialization time by providing a
86
+ # second argument to ::new. The proxy may be the URI of the proxy server or
87
+ # <code>:ENV</code> which will consult environment variables.
88
+ #
89
+ # See #proxy= and #proxy_from_env for details.
90
+ #
91
+ # == Headers
92
+ #
93
+ # Headers may be specified for use in every request. #headers are appended to
94
+ # any headers on the request. #override_headers replace existing headers on
95
+ # the request.
96
+ #
97
+ # The difference between the two can be seen in setting the User-Agent. Using
98
+ # <code>http.headers['User-Agent'] = 'MyUserAgent'</code> will send "Ruby,
99
+ # MyUserAgent" while <code>http.override_headers['User-Agent'] =
100
+ # 'MyUserAgent'</code> will send "MyUserAgent".
101
+ #
102
+ # == Tuning
103
+ #
104
+ # === Segregation
105
+ #
106
+ # By providing an application name to ::new you can separate your connections
107
+ # from the connections of other applications.
108
+ #
109
+ # === Idle Timeout
110
+ #
111
+ # If a connection hasn't been used for this number of seconds it will automatically be
112
+ # reset upon the next use to avoid attempting to send to a closed connection.
113
+ # The default value is 5 seconds. nil means no timeout. Set through #idle_timeout.
114
+ #
115
+ # Reducing this value may help avoid the "too many connection resets" error
116
+ # when sending non-idempotent requests while increasing this value will cause
117
+ # fewer round-trips.
118
+ #
119
+ # === Read Timeout
120
+ #
121
+ # The amount of time allowed between reading two chunks from the socket. Set
122
+ # through #read_timeout
123
+ #
124
+ # === Max Requests
125
+ #
126
+ # The number of requests that should be made before opening a new connection.
127
+ # Typically many keep-alive capable servers tune this to 100 or less, so the
128
+ # 101st request will fail with ECONNRESET. If unset (default), this value has no
129
+ # effect, if set, connections will be reset on the request after max_requests.
130
+ #
131
+ # === Open Timeout
132
+ #
133
+ # The amount of time to wait for a connection to be opened. Set through
134
+ # #open_timeout.
135
+ #
136
+ # === Socket Options
137
+ #
138
+ # Socket options may be set on newly-created connections. See #socket_options
139
+ # for details.
140
+ #
141
+ # === Non-Idempotent Requests
142
+ #
143
+ # By default non-idempotent requests will not be retried per RFC 2616. By
144
+ # setting retry_change_requests to true requests will automatically be retried
145
+ # once.
146
+ #
147
+ # Only do this when you know that retrying a POST or other non-idempotent
148
+ # request is safe for your application and will not create duplicate
149
+ # resources.
150
+ #
151
+ # The recommended way to handle non-idempotent requests is the following:
152
+ #
153
+ # require 'net/http/persistent'
154
+ #
155
+ # uri = URI 'http://example.com/awesome/web/service'
156
+ # post_uri = uri + 'create'
157
+ #
158
+ # http = PersistentHTTP.new 'my_app_name'
159
+ #
160
+ # post = Net::HTTP::Post.new post_uri.path
161
+ # # ... fill in POST request
162
+ #
163
+ # begin
164
+ # response = http.request post_uri, post
165
+ # rescue PersistentHTTP::Error
166
+ #
167
+ # # POST failed, make a new request to verify the server did not process
168
+ # # the request
169
+ # exists_uri = uri + '...'
170
+ # response = http.get exists_uri
171
+ #
172
+ # # Retry if it failed
173
+ # retry if response.code == '404'
174
+ # end
175
+ #
176
+ # The method of determining if the resource was created or not is unique to
177
+ # the particular service you are using. Of course, you will want to add
178
+ # protection from infinite looping.
179
+ #
180
+ # === Connection Termination
181
+ #
182
+ # If you are done using the PersistentHTTP instance you may shut down
183
+ # all the connections in the current thread with #shutdown. This is not
184
+ # recommended for normal use, it should only be used when it will be several
185
+ # minutes before you make another HTTP request.
186
+ #
187
+ # If you are using multiple threads, call #shutdown in each thread when the
188
+ # thread is done making requests. If you don't call shutdown, that's OK.
189
+ # Ruby will automatically garbage collect and shutdown your HTTP connections
190
+ # when the thread terminates.
191
+
192
+ module Vault
193
+ class PersistentHTTP
194
+
195
+ ##
196
+ # The beginning of Time
197
+
198
+ EPOCH = Time.at 0 # :nodoc:
199
+
200
+ ##
201
+ # Is OpenSSL available? This test works with autoload
202
+
203
+ HAVE_OPENSSL = defined? OpenSSL::SSL # :nodoc:
204
+
205
+ ##
206
+ # The default connection pool size is 1/4 the allowed open files.
207
+
208
+ DEFAULT_POOL_SIZE = 16
209
+
210
+ ##
211
+ # The version of PersistentHTTP you are using
212
+
213
+ VERSION = '3.0.0'
214
+
215
+ ##
216
+ # Exceptions rescued for automatic retry on ruby 2.0.0. This overlaps with
217
+ # the exception list for ruby 1.x.
218
+
219
+ RETRIED_EXCEPTIONS = [ # :nodoc:
220
+ (Net::ReadTimeout if Net.const_defined? :ReadTimeout),
221
+ IOError,
222
+ EOFError,
223
+ Errno::ECONNRESET,
224
+ Errno::ECONNABORTED,
225
+ Errno::EPIPE,
226
+ (OpenSSL::SSL::SSLError if HAVE_OPENSSL),
227
+ Timeout::Error,
228
+ ].compact
229
+
230
+ ##
231
+ # Error class for errors raised by PersistentHTTP. Various
232
+ # SystemCallErrors are re-raised with a human-readable message under this
233
+ # class.
234
+
235
+ class Error < StandardError; end
236
+
237
+ ##
238
+ # Use this method to detect the idle timeout of the host at +uri+. The
239
+ # value returned can be used to configure #idle_timeout. +max+ controls the
240
+ # maximum idle timeout to detect.
241
+ #
242
+ # After
243
+ #
244
+ # Idle timeout detection is performed by creating a connection then
245
+ # performing a HEAD request in a loop until the connection terminates
246
+ # waiting one additional second per loop.
247
+ #
248
+ # NOTE: This may not work on ruby > 1.9.
249
+
250
+ def self.detect_idle_timeout uri, max = 10
251
+ uri = URI uri unless URI::Generic === uri
252
+ uri += '/'
253
+
254
+ req = Net::HTTP::Head.new uri.request_uri
255
+
256
+ http = new 'net-http-persistent detect_idle_timeout'
257
+
258
+ http.connection_for uri do |connection|
259
+ sleep_time = 0
260
+
261
+ http = connection.http
262
+
263
+ loop do
264
+ response = http.request req
265
+
266
+ $stderr.puts "HEAD #{uri} => #{response.code}" if $DEBUG
267
+
268
+ unless Net::HTTPOK === response then
269
+ raise Error, "bad response code #{response.code} detecting idle timeout"
270
+ end
271
+
272
+ break if sleep_time >= max
273
+
274
+ sleep_time += 1
275
+
276
+ $stderr.puts "sleeping #{sleep_time}" if $DEBUG
277
+ sleep sleep_time
278
+ end
279
+ end
280
+ rescue
281
+ # ignore StandardErrors, we've probably found the idle timeout.
282
+ ensure
283
+ return sleep_time unless $!
284
+ end
285
+
286
+ ##
287
+ # This client's OpenSSL::X509::Certificate
288
+
289
+ attr_reader :certificate
290
+
291
+ ##
292
+ # For Net::HTTP parity
293
+
294
+ alias cert certificate
295
+
296
+ ##
297
+ # An SSL certificate authority. Setting this will set verify_mode to
298
+ # VERIFY_PEER.
299
+
300
+ attr_reader :ca_file
301
+
302
+ ##
303
+ # A directory of SSL certificates to be used as certificate authorities.
304
+ # Setting this will set verify_mode to VERIFY_PEER.
305
+
306
+ attr_reader :ca_path
307
+
308
+ ##
309
+ # An SSL certificate store. Setting this will override the default
310
+ # certificate store. See verify_mode for more information.
311
+
312
+ attr_reader :cert_store
313
+
314
+ ##
315
+ # The ciphers allowed for SSL connections
316
+
317
+ attr_reader :ciphers
318
+
319
+ ##
320
+ # Sends debug_output to this IO via Net::HTTP#set_debug_output.
321
+ #
322
+ # Never use this method in production code, it causes a serious security
323
+ # hole.
324
+
325
+ attr_accessor :debug_output
326
+
327
+ ##
328
+ # Current connection generation
329
+
330
+ attr_reader :generation # :nodoc:
331
+
332
+ ##
333
+ # Headers that are added to every request using Net::HTTP#add_field
334
+
335
+ attr_reader :headers
336
+
337
+ ##
338
+ # Maps host:port to an HTTP version. This allows us to enable version
339
+ # specific features.
340
+
341
+ attr_reader :http_versions
342
+
343
+ ##
344
+ # Maximum time an unused connection can remain idle before being
345
+ # automatically closed.
346
+
347
+ attr_accessor :idle_timeout
348
+
349
+ ##
350
+ # Maximum number of requests on a connection before it is considered expired
351
+ # and automatically closed.
352
+
353
+ attr_accessor :max_requests
354
+
355
+ ##
356
+ # The value sent in the Keep-Alive header. Defaults to 30. Not needed for
357
+ # HTTP/1.1 servers.
358
+ #
359
+ # This may not work correctly for HTTP/1.0 servers
360
+ #
361
+ # This method may be removed in a future version as RFC 2616 does not
362
+ # require this header.
363
+
364
+ attr_accessor :keep_alive
365
+
366
+ ##
367
+ # A name for this connection. Allows you to keep your connections apart
368
+ # from everybody else's.
369
+
370
+ attr_reader :name
371
+
372
+ ##
373
+ # Seconds to wait until a connection is opened. See Net::HTTP#open_timeout
374
+
375
+ attr_accessor :open_timeout
376
+
377
+ ##
378
+ # Headers that are added to every request using Net::HTTP#[]=
379
+
380
+ attr_reader :override_headers
381
+
382
+ ##
383
+ # This client's SSL private key
384
+
385
+ attr_reader :private_key
386
+
387
+ ##
388
+ # For Net::HTTP parity
389
+
390
+ alias key private_key
391
+
392
+ ##
393
+ # The URL through which requests will be proxied
394
+
395
+ attr_reader :proxy_uri
396
+
397
+ ##
398
+ # List of host suffixes which will not be proxied
399
+
400
+ attr_reader :no_proxy
401
+
402
+ ##
403
+ # Test-only accessor for the connection pool
404
+
405
+ attr_reader :pool # :nodoc:
406
+
407
+ ##
408
+ # Seconds to wait until reading one block. See Net::HTTP#read_timeout
409
+
410
+ attr_accessor :read_timeout
411
+
412
+ ##
413
+ # By default SSL sessions are reused to avoid extra SSL handshakes. Set
414
+ # this to false if you have problems communicating with an HTTPS server
415
+ # like:
416
+ #
417
+ # SSL_connect [...] read finished A: unexpected message (OpenSSL::SSL::SSLError)
418
+
419
+ attr_accessor :reuse_ssl_sessions
420
+
421
+ ##
422
+ # An array of options for Socket#setsockopt.
423
+ #
424
+ # By default the TCP_NODELAY option is set on sockets.
425
+ #
426
+ # To set additional options append them to this array:
427
+ #
428
+ # http.socket_options << [Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1]
429
+
430
+ attr_reader :socket_options
431
+
432
+ ##
433
+ # Current SSL connection generation
434
+
435
+ attr_reader :ssl_generation # :nodoc:
436
+
437
+ ##
438
+ # SSL session lifetime
439
+
440
+ attr_reader :ssl_timeout
441
+
442
+ ##
443
+ # SSL version to use.
444
+ #
445
+ # By default, the version will be negotiated automatically between client
446
+ # and server. Ruby 1.9 and newer only.
447
+
448
+ attr_reader :ssl_version
449
+
450
+ ##
451
+ # Where this instance's last-use times live in the thread local variables
452
+
453
+ attr_reader :timeout_key # :nodoc:
454
+
455
+ ##
456
+ # SSL verification callback. Used when ca_file or ca_path is set.
457
+
458
+ attr_reader :verify_callback
459
+
460
+ ##
461
+ # Sets the depth of SSL certificate verification
462
+
463
+ attr_reader :verify_depth
464
+
465
+ ##
466
+ # HTTPS verify mode. Defaults to OpenSSL::SSL::VERIFY_PEER which verifies
467
+ # the server certificate.
468
+ #
469
+ # If no ca_file, ca_path or cert_store is set the default system certificate
470
+ # store is used.
471
+ #
472
+ # You can use +verify_mode+ to override any default values.
473
+
474
+ attr_reader :verify_mode
475
+
476
+ ##
477
+ # Enable retries of non-idempotent requests that change data (e.g. POST
478
+ # requests) when the server has disconnected.
479
+ #
480
+ # This will in the worst case lead to multiple requests with the same data,
481
+ # but it may be useful for some applications. Take care when enabling
482
+ # this option to ensure it is safe to POST or perform other non-idempotent
483
+ # requests to the server.
484
+
485
+ attr_accessor :retry_change_requests
486
+
487
+ ##
488
+ # Creates a new PersistentHTTP.
489
+ #
490
+ # Set +name+ to keep your connections apart from everybody else's. Not
491
+ # required currently, but highly recommended. Your library name should be
492
+ # good enough. This parameter will be required in a future version.
493
+ #
494
+ # +proxy+ may be set to a URI::HTTP or :ENV to pick up proxy options from
495
+ # the environment. See proxy_from_env for details.
496
+ #
497
+ # In order to use a URI for the proxy you may need to do some extra work
498
+ # beyond URI parsing if the proxy requires a password:
499
+ #
500
+ # proxy = URI 'http://proxy.example'
501
+ # proxy.user = 'AzureDiamond'
502
+ # proxy.password = 'hunter2'
503
+ #
504
+ # Set +pool_size+ to limit the maximum number of connections allowed.
505
+ # Defaults to 1/4 the number of allowed file handles. You can have no more
506
+ # than this many threads with active HTTP transactions.
507
+
508
+ def initialize name=nil, proxy=nil, pool_size=DEFAULT_POOL_SIZE
509
+ @name = name
510
+
511
+ @debug_output = nil
512
+ @proxy_uri = nil
513
+ @no_proxy = []
514
+ @headers = {}
515
+ @override_headers = {}
516
+ @http_versions = {}
517
+ @keep_alive = 30
518
+ @open_timeout = nil
519
+ @read_timeout = nil
520
+ @idle_timeout = 5
521
+ @max_requests = nil
522
+ @socket_options = []
523
+ @ssl_generation = 0 # incremented when SSL session variables change
524
+
525
+ @socket_options << [Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1] if
526
+ Socket.const_defined? :TCP_NODELAY
527
+
528
+ @pool = PersistentHTTP::Pool.new size: pool_size do |http_args|
529
+ PersistentHTTP::Connection.new Net::HTTP, http_args, @ssl_generation
530
+ end
531
+
532
+ @certificate = nil
533
+ @ca_file = nil
534
+ @ca_path = nil
535
+ @ciphers = nil
536
+ @private_key = nil
537
+ @ssl_timeout = nil
538
+ @ssl_version = nil
539
+ @verify_callback = nil
540
+ @verify_depth = nil
541
+ @verify_mode = nil
542
+ @cert_store = nil
543
+
544
+ @generation = 0 # incremented when proxy URI changes
545
+
546
+ if HAVE_OPENSSL then
547
+ @verify_mode = OpenSSL::SSL::VERIFY_PEER
548
+ @reuse_ssl_sessions = OpenSSL::SSL.const_defined? :Session
549
+ end
550
+
551
+ @retry_change_requests = false
552
+
553
+ self.proxy = proxy if proxy
554
+ end
555
+
556
+ ##
557
+ # Sets this client's OpenSSL::X509::Certificate
558
+
559
+ def certificate= certificate
560
+ @certificate = certificate
561
+
562
+ reconnect_ssl
563
+ end
564
+
565
+ # For Net::HTTP parity
566
+ alias cert= certificate=
567
+
568
+ ##
569
+ # Sets the SSL certificate authority file.
570
+
571
+ def ca_file= file
572
+ @ca_file = file
573
+
574
+ reconnect_ssl
575
+ end
576
+
577
+ ##
578
+ # Sets the SSL certificate authority path.
579
+
580
+ def ca_path= path
581
+ @ca_path = path
582
+
583
+ reconnect_ssl
584
+ end
585
+
586
+ ##
587
+ # Overrides the default SSL certificate store used for verifying
588
+ # connections.
589
+
590
+ def cert_store= store
591
+ @cert_store = store
592
+
593
+ reconnect_ssl
594
+ end
595
+
596
+ ##
597
+ # The ciphers allowed for SSL connections
598
+
599
+ def ciphers= ciphers
600
+ @ciphers = ciphers
601
+
602
+ reconnect_ssl
603
+ end
604
+
605
+ ##
606
+ # Creates a new connection for +uri+
607
+
608
+ def connection_for uri
609
+ use_ssl = uri.scheme.downcase == 'https'
610
+
611
+ net_http_args = [uri.host, uri.port]
612
+
613
+ net_http_args.concat @proxy_args if
614
+ @proxy_uri and not proxy_bypass? uri.host, uri.port
615
+
616
+ connection = @pool.checkout net_http_args
617
+
618
+ http = connection.http
619
+
620
+ connection.ressl @ssl_generation if
621
+ connection.ssl_generation != @ssl_generation
622
+
623
+ if not http.started? then
624
+ ssl http if use_ssl
625
+ start http
626
+ elsif expired? connection then
627
+ reset connection
628
+ end
629
+
630
+ http.read_timeout = @read_timeout if @read_timeout
631
+ http.keep_alive_timeout = @idle_timeout if @idle_timeout
632
+
633
+ return yield connection
634
+ rescue Errno::ECONNREFUSED
635
+ address = http.proxy_address || http.address
636
+ port = http.proxy_port || http.port
637
+
638
+ raise Error, "connection refused: #{address}:#{port}"
639
+ rescue Errno::EHOSTDOWN
640
+ address = http.proxy_address || http.address
641
+ port = http.proxy_port || http.port
642
+
643
+ raise Error, "host down: #{address}:#{port}"
644
+ ensure
645
+ # Only perform checkin if we successfully checked a connection out
646
+ if connection
647
+ @pool.checkin net_http_args
648
+ end
649
+ end
650
+
651
+ ##
652
+ # Returns an error message containing the number of requests performed on
653
+ # this connection
654
+
655
+ def error_message connection
656
+ connection.requests -= 1 # fixup
657
+
658
+ age = Time.now - connection.last_use
659
+
660
+ "after #{connection.requests} requests on #{connection.http.object_id}, " \
661
+ "last used #{age} seconds ago"
662
+ end
663
+
664
+ ##
665
+ # URI::escape wrapper
666
+
667
+ def escape str
668
+ CGI.escape str if str
669
+ end
670
+
671
+ ##
672
+ # URI::unescape wrapper
673
+
674
+ def unescape str
675
+ CGI.unescape str if str
676
+ end
677
+
678
+
679
+ ##
680
+ # Returns true if the connection should be reset due to an idle timeout, or
681
+ # maximum request count, false otherwise.
682
+
683
+ def expired? connection
684
+ return true if @max_requests && connection.requests >= @max_requests
685
+ return false unless @idle_timeout
686
+ return true if @idle_timeout.zero?
687
+
688
+ Time.now - connection.last_use > @idle_timeout
689
+ end
690
+
691
+ ##
692
+ # Starts the Net::HTTP +connection+
693
+
694
+ def start http
695
+ http.set_debug_output @debug_output if @debug_output
696
+ http.open_timeout = @open_timeout if @open_timeout
697
+
698
+ http.start
699
+
700
+ socket = http.instance_variable_get :@socket
701
+
702
+ if socket then # for fakeweb
703
+ @socket_options.each do |option|
704
+ socket.io.setsockopt(*option)
705
+ end
706
+ end
707
+ end
708
+
709
+ ##
710
+ # Finishes the Net::HTTP +connection+
711
+
712
+ def finish connection
713
+ connection.finish
714
+
715
+ connection.http.instance_variable_set :@ssl_session, nil unless
716
+ @reuse_ssl_sessions
717
+ end
718
+
719
+ ##
720
+ # Returns the HTTP protocol version for +uri+
721
+
722
+ def http_version uri
723
+ @http_versions["#{uri.host}:#{uri.port}"]
724
+ end
725
+
726
+ ##
727
+ # Is +req+ idempotent according to RFC 2616?
728
+
729
+ def idempotent? req
730
+ case req
731
+ when Net::HTTP::Delete, Net::HTTP::Get, Net::HTTP::Head,
732
+ Net::HTTP::Options, Net::HTTP::Put, Net::HTTP::Trace then
733
+ true
734
+ end
735
+ end
736
+
737
+ ##
738
+ # Is the request +req+ idempotent or is retry_change_requests allowed.
739
+
740
+ def can_retry? req
741
+ @retry_change_requests && !idempotent?(req)
742
+ end
743
+
744
+ ##
745
+ # Adds "http://" to the String +uri+ if it is missing.
746
+
747
+ def normalize_uri uri
748
+ (uri =~ /^https?:/) ? uri : "http://#{uri}"
749
+ end
750
+
751
+ ##
752
+ # Pipelines +requests+ to the HTTP server at +uri+ yielding responses if a
753
+ # block is given. Returns all responses recieved.
754
+ #
755
+ # See
756
+ # Net::HTTP::Pipeline[http://docs.seattlerb.org/net-http-pipeline/Net/HTTP/Pipeline.html]
757
+ # for further details.
758
+ #
759
+ # Only if <tt>net-http-pipeline</tt> was required before
760
+ # <tt>net-http-persistent</tt> #pipeline will be present.
761
+
762
+ def pipeline uri, requests, &block # :yields: responses
763
+ connection_for uri do |connection|
764
+ connection.http.pipeline requests, &block
765
+ end
766
+ end
767
+
768
+ ##
769
+ # Sets this client's SSL private key
770
+
771
+ def private_key= key
772
+ @private_key = key
773
+
774
+ reconnect_ssl
775
+ end
776
+
777
+ # For Net::HTTP parity
778
+ alias key= private_key=
779
+
780
+ ##
781
+ # Sets the proxy server. The +proxy+ may be the URI of the proxy server,
782
+ # the symbol +:ENV+ which will read the proxy from the environment or nil to
783
+ # disable use of a proxy. See #proxy_from_env for details on setting the
784
+ # proxy from the environment.
785
+ #
786
+ # If the proxy URI is set after requests have been made, the next request
787
+ # will shut-down and re-open all connections.
788
+ #
789
+ # The +no_proxy+ query parameter can be used to specify hosts which shouldn't
790
+ # be reached via proxy; if set it should be a comma separated list of
791
+ # hostname suffixes, optionally with +:port+ appended, for example
792
+ # <tt>example.com,some.host:8080</tt>.
793
+
794
+ def proxy= proxy
795
+ @proxy_uri = case proxy
796
+ when :ENV then proxy_from_env
797
+ when URI::HTTP then proxy
798
+ when nil then # ignore
799
+ else raise ArgumentError, 'proxy must be :ENV or a URI::HTTP'
800
+ end
801
+
802
+ @no_proxy.clear
803
+
804
+ if @proxy_uri then
805
+ @proxy_args = [
806
+ @proxy_uri.host,
807
+ @proxy_uri.port,
808
+ unescape(@proxy_uri.user),
809
+ unescape(@proxy_uri.password),
810
+ ]
811
+
812
+ @proxy_connection_id = [nil, *@proxy_args].join ':'
813
+
814
+ if @proxy_uri.query then
815
+ @no_proxy = CGI.parse(@proxy_uri.query)['no_proxy'].join(',').downcase.split(',').map { |x| x.strip }.reject { |x| x.empty? }
816
+ end
817
+ end
818
+
819
+ reconnect
820
+ reconnect_ssl
821
+ end
822
+
823
+ ##
824
+ # Creates a URI for an HTTP proxy server from ENV variables.
825
+ #
826
+ # If +HTTP_PROXY+ is set a proxy will be returned.
827
+ #
828
+ # If +HTTP_PROXY_USER+ or +HTTP_PROXY_PASS+ are set the URI is given the
829
+ # indicated user and password unless HTTP_PROXY contains either of these in
830
+ # the URI.
831
+ #
832
+ # The +NO_PROXY+ ENV variable can be used to specify hosts which shouldn't
833
+ # be reached via proxy; if set it should be a comma separated list of
834
+ # hostname suffixes, optionally with +:port+ appended, for example
835
+ # <tt>example.com,some.host:8080</tt>. When set to <tt>*</tt> no proxy will
836
+ # be returned.
837
+ #
838
+ # For Windows users, lowercase ENV variables are preferred over uppercase ENV
839
+ # variables.
840
+
841
+ def proxy_from_env
842
+ env_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
843
+
844
+ return nil if env_proxy.nil? or env_proxy.empty?
845
+
846
+ uri = URI normalize_uri env_proxy
847
+
848
+ env_no_proxy = ENV['no_proxy'] || ENV['NO_PROXY']
849
+
850
+ # '*' is special case for always bypass
851
+ return nil if env_no_proxy == '*'
852
+
853
+ if env_no_proxy then
854
+ uri.query = "no_proxy=#{escape(env_no_proxy)}"
855
+ end
856
+
857
+ unless uri.user or uri.password then
858
+ uri.user = escape ENV['http_proxy_user'] || ENV['HTTP_PROXY_USER']
859
+ uri.password = escape ENV['http_proxy_pass'] || ENV['HTTP_PROXY_PASS']
860
+ end
861
+
862
+ uri
863
+ end
864
+
865
+ ##
866
+ # Returns true when proxy should by bypassed for host.
867
+
868
+ def proxy_bypass? host, port
869
+ host = host.downcase
870
+ host_port = [host, port].join ':'
871
+
872
+ @no_proxy.each do |name|
873
+ return true if host[-name.length, name.length] == name or
874
+ host_port[-name.length, name.length] == name
875
+ end
876
+
877
+ false
878
+ end
879
+
880
+ ##
881
+ # Forces reconnection of HTTP connections.
882
+
883
+ def reconnect
884
+ @generation += 1
885
+ end
886
+
887
+ ##
888
+ # Forces reconnection of SSL connections.
889
+
890
+ def reconnect_ssl
891
+ @ssl_generation += 1
892
+ end
893
+
894
+ ##
895
+ # Finishes then restarts the Net::HTTP +connection+
896
+
897
+ def reset connection
898
+ http = connection.http
899
+
900
+ finish connection
901
+
902
+ start http
903
+ rescue Errno::ECONNREFUSED
904
+ e = Error.new "connection refused: #{http.address}:#{http.port}"
905
+ e.set_backtrace $@
906
+ raise e
907
+ rescue Errno::EHOSTDOWN
908
+ e = Error.new "host down: #{http.address}:#{http.port}"
909
+ e.set_backtrace $@
910
+ raise e
911
+ end
912
+
913
+ ##
914
+ # Makes a request on +uri+. If +req+ is nil a Net::HTTP::Get is performed
915
+ # against +uri+.
916
+ #
917
+ # If a block is passed #request behaves like Net::HTTP#request (the body of
918
+ # the response will not have been read).
919
+ #
920
+ # +req+ must be a Net::HTTPRequest subclass (see Net::HTTP for a list).
921
+ #
922
+ # If there is an error and the request is idempotent according to RFC 2616
923
+ # it will be retried automatically.
924
+
925
+ def request uri, req = nil, &block
926
+ retried = false
927
+ bad_response = false
928
+
929
+ uri = URI uri
930
+ req = request_setup req || uri
931
+ response = nil
932
+
933
+ connection_for uri do |connection|
934
+ http = connection.http
935
+
936
+ begin
937
+ connection.requests += 1
938
+
939
+ response = http.request req, &block
940
+
941
+ if req.connection_close? or
942
+ (response.http_version <= '1.0' and
943
+ not response.connection_keep_alive?) or
944
+ response.connection_close? then
945
+ finish connection
946
+ end
947
+ rescue Net::HTTPBadResponse => e
948
+ message = error_message connection
949
+
950
+ finish connection
951
+
952
+ raise Error, "too many bad responses #{message}" if
953
+ bad_response or not can_retry? req
954
+
955
+ bad_response = true
956
+ retry
957
+ rescue *RETRIED_EXCEPTIONS => e
958
+ request_failed e, req, connection if
959
+ retried or not can_retry? req
960
+
961
+ reset connection
962
+
963
+ retried = true
964
+ retry
965
+ rescue Errno::EINVAL, Errno::ETIMEDOUT => e # not retried on ruby 2
966
+ request_failed e, req, connection if retried or not can_retry? req
967
+
968
+ reset connection
969
+
970
+ retried = true
971
+ retry
972
+ rescue Exception => e
973
+ finish connection
974
+
975
+ raise
976
+ ensure
977
+ connection.last_use = Time.now
978
+ end
979
+ end
980
+
981
+ @http_versions["#{uri.host}:#{uri.port}"] ||= response.http_version
982
+
983
+ response
984
+ end
985
+
986
+ ##
987
+ # Raises an Error for +exception+ which resulted from attempting the request
988
+ # +req+ on the +connection+.
989
+ #
990
+ # Finishes the +connection+.
991
+
992
+ def request_failed exception, req, connection # :nodoc:
993
+ due_to = "(due to #{exception.message} - #{exception.class})"
994
+ message = "too many connection resets #{due_to} #{error_message connection}"
995
+
996
+ finish connection
997
+
998
+ raise Error, message, exception.backtrace
999
+ end
1000
+
1001
+ ##
1002
+ # Creates a GET request if +req_or_uri+ is a URI and adds headers to the
1003
+ # request.
1004
+ #
1005
+ # Returns the request.
1006
+
1007
+ def request_setup req_or_uri # :nodoc:
1008
+ req = if URI === req_or_uri then
1009
+ Net::HTTP::Get.new req_or_uri.request_uri
1010
+ else
1011
+ req_or_uri
1012
+ end
1013
+
1014
+ @headers.each do |pair|
1015
+ req.add_field(*pair)
1016
+ end
1017
+
1018
+ @override_headers.each do |name, value|
1019
+ req[name] = value
1020
+ end
1021
+
1022
+ unless req['Connection'] then
1023
+ req.add_field 'Connection', 'keep-alive'
1024
+ req.add_field 'Keep-Alive', @keep_alive
1025
+ end
1026
+
1027
+ req
1028
+ end
1029
+
1030
+ ##
1031
+ # Shuts down all connections
1032
+ #
1033
+ # *NOTE*: Calling shutdown for can be dangerous!
1034
+ #
1035
+ # If any thread is still using a connection it may cause an error! Call
1036
+ # #shutdown when you are completely done making requests!
1037
+
1038
+ def shutdown
1039
+ @pool.available.shutdown do |http|
1040
+ http.finish
1041
+ end
1042
+ end
1043
+
1044
+ ##
1045
+ # Enables SSL on +connection+
1046
+
1047
+ def ssl connection
1048
+ connection.use_ssl = true
1049
+
1050
+ connection.ciphers = @ciphers if @ciphers
1051
+ connection.ssl_timeout = @ssl_timeout if @ssl_timeout
1052
+ connection.ssl_version = @ssl_version if @ssl_version
1053
+
1054
+ connection.verify_depth = @verify_depth
1055
+ connection.verify_mode = @verify_mode
1056
+
1057
+ if OpenSSL::SSL::VERIFY_PEER == OpenSSL::SSL::VERIFY_NONE and
1058
+ not Object.const_defined?(:I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG) then
1059
+ warn <<-WARNING
1060
+ !!!SECURITY WARNING!!!
1061
+
1062
+ The SSL HTTP connection to:
1063
+
1064
+ #{connection.address}:#{connection.port}
1065
+
1066
+ !!!MAY NOT BE VERIFIED!!!
1067
+
1068
+ On your platform your OpenSSL implementation is broken.
1069
+
1070
+ There is no difference between the values of VERIFY_NONE and VERIFY_PEER.
1071
+
1072
+ This means that attempting to verify the security of SSL connections may not
1073
+ work. This exposes you to man-in-the-middle exploits, snooping on the
1074
+ contents of your connection and other dangers to the security of your data.
1075
+
1076
+ To disable this warning define the following constant at top-level in your
1077
+ application:
1078
+
1079
+ I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG = nil
1080
+
1081
+ WARNING
1082
+ end
1083
+
1084
+ connection.ca_file = @ca_file if @ca_file
1085
+ connection.ca_path = @ca_path if @ca_path
1086
+
1087
+ if @ca_file or @ca_path then
1088
+ connection.verify_callback = @verify_callback if @verify_callback
1089
+ end
1090
+
1091
+ if @certificate and @private_key then
1092
+ connection.cert = @certificate
1093
+ connection.key = @private_key
1094
+ end
1095
+
1096
+ connection.cert_store = if @cert_store then
1097
+ @cert_store
1098
+ else
1099
+ store = OpenSSL::X509::Store.new
1100
+ store.set_default_paths
1101
+ store
1102
+ end
1103
+ end
1104
+
1105
+ ##
1106
+ # SSL session lifetime
1107
+
1108
+ def ssl_timeout= ssl_timeout
1109
+ @ssl_timeout = ssl_timeout
1110
+
1111
+ reconnect_ssl
1112
+ end
1113
+
1114
+ ##
1115
+ # SSL version to use
1116
+
1117
+ def ssl_version= ssl_version
1118
+ @ssl_version = ssl_version
1119
+
1120
+ reconnect_ssl
1121
+ end
1122
+
1123
+ ##
1124
+ # Sets the depth of SSL certificate verification
1125
+
1126
+ def verify_depth= verify_depth
1127
+ @verify_depth = verify_depth
1128
+
1129
+ reconnect_ssl
1130
+ end
1131
+
1132
+ ##
1133
+ # Sets the HTTPS verify mode. Defaults to OpenSSL::SSL::VERIFY_PEER.
1134
+ #
1135
+ # Setting this to VERIFY_NONE is a VERY BAD IDEA and should NEVER be used.
1136
+ # Securely transfer the correct certificate and update the default
1137
+ # certificate store or set the ca file instead.
1138
+
1139
+ def verify_mode= verify_mode
1140
+ @verify_mode = verify_mode
1141
+
1142
+ reconnect_ssl
1143
+ end
1144
+
1145
+ ##
1146
+ # SSL verification callback.
1147
+
1148
+ def verify_callback= callback
1149
+ @verify_callback = callback
1150
+
1151
+ reconnect_ssl
1152
+ end
1153
+
1154
+ end
1155
+ end
1156
+
1157
+ require_relative 'persistent/connection'
1158
+ require_relative 'persistent/pool'