httpclient 2.6.0.1 → 2.8.3

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.
@@ -1,5 +1,5 @@
1
1
  # HTTPClient - HTTP client library.
2
- # Copyright (C) 2000-2009 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
2
+ # Copyright (C) 2000-2015 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
3
3
  #
4
4
  # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
5
5
  # redistribute it and/or modify it under the same terms of Ruby's license;
@@ -20,70 +20,134 @@ class HTTPClient
20
20
  #
21
21
  # == Trust Anchor Control
22
22
  #
23
- # SSLConfig loads 'httpclient/cacert.p7s' as a trust anchor
23
+ # SSLConfig loads 'httpclient/cacert.pem' as a trust anchor
24
24
  # (trusted certificate(s)) with add_trust_ca in initialization time.
25
25
  # This means that HTTPClient instance trusts some CA certificates by default,
26
- # like Web browsers. 'httpclient/cacert.p7s' is created by the author and
27
- # included in released package.
26
+ # like Web browsers. 'httpclient/cacert.pem' is downloaded from curl web
27
+ # site by the author and included in released package.
28
28
  #
29
- # 'cacert.p7s' is automatically generated from JDK 1.6. Regardless its
30
- # filename extension (p7s), HTTPClient doesn't verify the signature in it.
29
+ # On JRuby, HTTPClient uses Java runtime's trusted CA certificates, not
30
+ # cacert.pem by default. You can load cacert.pem by calling
31
+ # SSLConfig#load_trust_ca manually like:
32
+ #
33
+ # HTTPClient.new { self.ssl_config.load_trust_ca }.get("https://...")
31
34
  #
32
35
  # You may want to change trust anchor by yourself. Call clear_cert_store
33
36
  # then add_trust_ca for that purpose.
34
37
  class SSLConfig
35
- include OpenSSL if SSLEnabled
38
+ include HTTPClient::Util
39
+ if SSLEnabled
40
+ include OpenSSL
41
+
42
+ module ::OpenSSL
43
+ module X509
44
+ class Store
45
+ attr_reader :_httpclient_cert_store_items
46
+
47
+ # TODO: use prepend instead when we drop JRuby + 1.9.x support
48
+ wrapped = {}
49
+
50
+ wrapped[:initialize] = instance_method(:initialize)
51
+ define_method(:initialize) do |*args|
52
+ wrapped[:initialize].bind(self).call(*args)
53
+ @_httpclient_cert_store_items = [ENV['SSL_CERT_FILE'] || :default]
54
+ end
55
+
56
+ [:add_cert, :add_file, :add_path].each do |m|
57
+ wrapped[m] = instance_method(m)
58
+ define_method(m) do |cert|
59
+ res = wrapped[m].bind(self).call(cert)
60
+ @_httpclient_cert_store_items << cert
61
+ res
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ class << self
70
+ private
71
+ def attr_config(symbol)
72
+ name = symbol.to_s
73
+ ivar_name = "@#{name}"
74
+ define_method(name) {
75
+ instance_variable_get(ivar_name)
76
+ }
77
+ define_method("#{name}=") { |rhs|
78
+ if instance_variable_get(ivar_name) != rhs
79
+ instance_variable_set(ivar_name, rhs)
80
+ change_notify
81
+ end
82
+ }
83
+ symbol
84
+ end
85
+ end
86
+
87
+
88
+ CIPHERS_DEFAULT = "ALL:!aNULL:!eNULL:!SSLv2" # OpenSSL >1.0.0 default
36
89
 
37
90
  # Which TLS protocol version (also called method) will be used. Defaults
38
- # to :auto which means that OpenSSL decides (In my tests this resulted
91
+ # to :auto which means that OpenSSL decides (In my tests this resulted
39
92
  # with always the highest available protocol being used).
40
93
  # String name of OpenSSL's SSL version method name: TLSv1_2, TLSv1_1, TLSv1,
41
94
  # SSLv2, SSLv23, SSLv3 or :auto (and nil) to allow version negotiation (default).
42
95
  # See {OpenSSL::SSL::SSLContext::METHODS} for a list of available versions
43
96
  # in your specific Ruby environment.
44
- attr_reader :ssl_version
45
- # OpenSSL::X509::Certificate:: certificate for SSL client authenticateion.
46
- # nil by default. (no client authenticateion)
47
- attr_reader :client_cert
97
+ attr_config :ssl_version
98
+ # OpenSSL::X509::Certificate:: certificate for SSL client authentication.
99
+ # nil by default. (no client authentication)
100
+ attr_config :client_cert
48
101
  # OpenSSL::PKey::PKey:: private key for SSL client authentication.
49
- # nil by default. (no client authenticateion)
50
- attr_reader :client_key
102
+ # nil by default. (no client authentication)
103
+ attr_config :client_key
104
+ # OpenSSL::PKey::PKey:: private key pass phrase for client_key.
105
+ # nil by default. (no pass phrase)
106
+ attr_config :client_key_pass
51
107
 
52
108
  # A number which represents OpenSSL's verify mode. Default value is
53
109
  # OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT.
54
- attr_reader :verify_mode
110
+ attr_config :verify_mode
55
111
  # A number of verify depth. Certification path which length is longer than
56
112
  # this depth is not allowed.
57
- attr_reader :verify_depth
113
+ # CAUTION: this is OpenSSL specific option and ignored on JRuby.
114
+ attr_config :verify_depth
58
115
  # A callback handler for custom certificate verification. nil by default.
59
116
  # If the handler is set, handler.call is invoked just after general
60
117
  # OpenSSL's verification. handler.call is invoked with 2 arguments,
61
118
  # ok and ctx; ok is a result of general OpenSSL's verification. ctx is a
62
119
  # OpenSSL::X509::StoreContext.
63
- attr_reader :verify_callback
120
+ attr_config :verify_callback
64
121
  # SSL timeout in sec. nil by default.
65
- attr_reader :timeout
122
+ attr_config :timeout
66
123
  # A number of OpenSSL's SSL options. Default value is
67
124
  # OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_SSLv2
68
- attr_reader :options
125
+ # CAUTION: this is OpenSSL specific option and ignored on JRuby.
126
+ # Use ssl_version to specify the TLS version you want to use.
127
+ attr_config :options
69
128
  # A String of OpenSSL's cipher configuration. Default value is
70
129
  # ALL:!ADH:!LOW:!EXP:!MD5:+SSLv2:@STRENGTH
71
130
  # See ciphers(1) man in OpenSSL for more detail.
72
- attr_reader :ciphers
131
+ attr_config :ciphers
73
132
 
74
133
  # OpenSSL::X509::X509::Store used for verification. You can reset the
75
134
  # store with clear_cert_store and set the new store with cert_store=.
76
135
  attr_reader :cert_store # don't use if you don't know what it is.
77
136
 
78
137
  # For server side configuration. Ignore this.
79
- attr_reader :client_ca # :nodoc:
138
+ attr_config :client_ca # :nodoc:
139
+
140
+ # These array keeps original files/dirs that was added to @cert_store
141
+ def cert_store_items; @cert_store._httpclient_cert_store_items; end
142
+ attr_reader :cert_store_crl_items
80
143
 
81
144
  # Creates a SSLConfig.
82
145
  def initialize(client)
83
146
  return unless SSLEnabled
84
147
  @client = client
85
148
  @cert_store = X509::Store.new
86
- @client_cert = @client_key = @client_ca = nil
149
+ @cert_store_crl_items = []
150
+ @client_cert = @client_key = @client_key_pass = @client_ca = nil
87
151
  @verify_mode = SSL::VERIFY_PEER | SSL::VERIFY_FAIL_IF_NO_PEER_CERT
88
152
  @verify_depth = nil
89
153
  @verify_callback = nil
@@ -97,47 +161,22 @@ class HTTPClient
97
161
  @options |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2)
98
162
  @options |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3)
99
163
  # OpenSSL 0.9.8 default: "ALL:!ADH:!LOW:!EXP:!MD5:+SSLv2:@STRENGTH"
100
- @ciphers = "ALL:!aNULL:!eNULL:!SSLv2" # OpenSSL >1.0.0 default
164
+ @ciphers = CIPHERS_DEFAULT
101
165
  @cacerts_loaded = false
102
166
  end
103
167
 
104
- # Sets SSL version method String. Possible values: "SSLv2" for SSL2,
105
- # "SSLv3" for SSL3 and TLS1.x, "SSLv23" for SSL3 with fallback to SSL2.
106
- def ssl_version=(ssl_version)
107
- @ssl_version = ssl_version
108
- change_notify
109
- end
110
-
111
- # Sets certificate (OpenSSL::X509::Certificate) for SSL client
112
- # authentication.
113
- # client_key and client_cert must be a pair.
114
- #
115
- # Calling this method resets all existing sessions.
116
- def client_cert=(client_cert)
117
- @client_cert = client_cert
118
- change_notify
119
- end
120
-
121
- # Sets private key (OpenSSL::PKey::PKey) for SSL client authentication.
122
- # client_key and client_cert must be a pair.
123
- #
124
- # Calling this method resets all existing sessions.
125
- def client_key=(client_key)
126
- @client_key = client_key
127
- change_notify
128
- end
129
-
130
168
  # Sets certificate and private key for SSL client authentication.
131
169
  # cert_file:: must be a filename of PEM/DER formatted file.
132
170
  # key_file:: must be a filename of PEM/DER formatted file. Key must be an
133
171
  # RSA key. If you want to use other PKey algorithm,
134
172
  # use client_key=.
135
173
  #
136
- # Calling this method resets all existing sessions.
174
+ # Calling this method resets all existing sessions if value is changed.
137
175
  def set_client_cert_file(cert_file, key_file, pass = nil)
138
- @client_cert = X509::Certificate.new(File.open(cert_file) { |f| f.read })
139
- @client_key = PKey::RSA.new(File.open(key_file) { |f| f.read }, pass)
140
- change_notify
176
+ if (@client_cert != cert_file) || (@client_key != key_file) || (@client_key_pass != pass)
177
+ @client_cert, @client_key, @client_key_pass = cert_file, key_file, pass
178
+ change_notify
179
+ end
141
180
  end
142
181
 
143
182
  # Sets OpenSSL's default trusted CA certificates. Generally, OpenSSL is
@@ -165,6 +204,7 @@ class HTTPClient
165
204
  def clear_cert_store
166
205
  @cacerts_loaded = true # avoid lazy override
167
206
  @cert_store = X509::Store.new
207
+ @cert_store._httpclient_cert_store_items.clear
168
208
  change_notify
169
209
  end
170
210
 
@@ -173,9 +213,12 @@ class HTTPClient
173
213
  #
174
214
  # Calling this method resets all existing sessions.
175
215
  def cert_store=(cert_store)
176
- @cacerts_loaded = true # avoid lazy override
177
- @cert_store = cert_store
178
- change_notify
216
+ # This is object equality check, since OpenSSL::X509::Store doesn't overload ==
217
+ if !@cacerts_loaded || (@cert_store != cert_store)
218
+ @cacerts_loaded = true # avoid lazy override
219
+ @cert_store = cert_store
220
+ change_notify
221
+ end
179
222
  end
180
223
 
181
224
  # Sets trust anchor certificate(s) for verification.
@@ -186,6 +229,9 @@ class HTTPClient
186
229
  #
187
230
  # Calling this method resets all existing sessions.
188
231
  def add_trust_ca(trust_ca_file_or_hashed_dir)
232
+ unless File.exist?(trust_ca_file_or_hashed_dir)
233
+ trust_ca_file_or_hashed_dir = File.join(File.dirname(__FILE__), trust_ca_file_or_hashed_dir)
234
+ end
189
235
  @cacerts_loaded = true # avoid lazy override
190
236
  add_trust_ca_to_store(@cert_store, trust_ca_file_or_hashed_dir)
191
237
  change_notify
@@ -211,74 +257,30 @@ class HTTPClient
211
257
  # crl:: a OpenSSL::X509::CRL or a filename of a PEM/DER formatted
212
258
  # OpenSSL::X509::CRL.
213
259
  #
260
+ # On JRuby, instead of setting CRL by yourself you can set following
261
+ # options to let HTTPClient to perform revocation check with CRL and OCSP:
262
+ # -J-Dcom.sun.security.enableCRLDP=true -J-Dcom.sun.net.ssl.checkRevocation=true
263
+ # ex. jruby -J-Dcom.sun.security.enableCRLDP=true -J-Dcom.sun.net.ssl.checkRevocation=true app.rb
264
+ #
265
+ # Revoked cert example: https://test-sspev.verisign.com:2443/test-SSPEV-revoked-verisign.html
266
+ #
214
267
  # Calling this method resets all existing sessions.
215
268
  def add_crl(crl)
216
269
  unless crl.is_a?(X509::CRL)
217
270
  crl = X509::CRL.new(File.open(crl) { |f| f.read })
218
271
  end
219
272
  @cert_store.add_crl(crl)
273
+ @cert_store_crl_items << crl
220
274
  @cert_store.flags = X509::V_FLAG_CRL_CHECK | X509::V_FLAG_CRL_CHECK_ALL
221
275
  change_notify
222
276
  end
223
277
  alias set_crl add_crl
224
278
 
225
- # Sets verify mode of OpenSSL. New value must be a combination of
226
- # constants OpenSSL::SSL::VERIFY_*
227
- #
228
- # Calling this method resets all existing sessions.
229
- def verify_mode=(verify_mode)
230
- @verify_mode = verify_mode
231
- change_notify
232
- end
233
-
234
- # Sets verify depth. New value must be a number.
235
- #
236
- # Calling this method resets all existing sessions.
237
- def verify_depth=(verify_depth)
238
- @verify_depth = verify_depth
239
- change_notify
240
- end
241
-
242
- # Sets callback handler for custom certificate verification.
243
- # See verify_callback.
244
- #
245
- # Calling this method resets all existing sessions.
246
- def verify_callback=(verify_callback)
247
- @verify_callback = verify_callback
248
- change_notify
249
- end
250
-
251
- # Sets SSL timeout in sec.
252
- #
253
- # Calling this method resets all existing sessions.
254
- def timeout=(timeout)
255
- @timeout = timeout
256
- change_notify
257
- end
258
-
259
- # Sets SSL options. New value must be a combination of # constants
260
- # OpenSSL::SSL::OP_*
261
- #
262
- # Calling this method resets all existing sessions.
263
- def options=(options)
264
- @options = options
265
- change_notify
279
+ def verify?
280
+ @verify_mode && (@verify_mode & OpenSSL::SSL::VERIFY_PEER != 0)
266
281
  end
267
282
 
268
- # Sets cipher configuration. New value must be a String.
269
- #
270
- # Calling this method resets all existing sessions.
271
- def ciphers=(ciphers)
272
- @ciphers = ciphers
273
- change_notify
274
- end
275
-
276
- def client_ca=(client_ca) # :nodoc:
277
- @client_ca = client_ca
278
- change_notify
279
- end
280
-
281
- # interfaces for SSLSocketWrap.
283
+ # interfaces for SSLSocket.
282
284
  def set_context(ctx) # :nodoc:
283
285
  load_trust_ca unless @cacerts_loaded
284
286
  @cacerts_loaded = true
@@ -288,8 +290,14 @@ class HTTPClient
288
290
  ctx.verify_depth = @verify_depth if @verify_depth
289
291
  ctx.verify_callback = @verify_callback || method(:default_verify_callback)
290
292
  # SSL config
291
- ctx.cert = @client_cert
292
- ctx.key = @client_key
293
+ if @client_cert
294
+ ctx.cert = @client_cert.is_a?(X509::Certificate) ? @client_cert :
295
+ X509::Certificate.new(File.open(@client_cert) { |f| f.read })
296
+ end
297
+ if @client_key
298
+ ctx.key = @client_key.is_a?(PKey::PKey) ? @client_key :
299
+ PKey::RSA.new(File.open(@client_key) { |f| f.read }, @client_key_pass)
300
+ end
293
301
  ctx.client_ca = @client_ca
294
302
  ctx.timeout = @timeout
295
303
  ctx.options = @options
@@ -405,8 +413,9 @@ class HTTPClient
405
413
  nil
406
414
  end
407
415
 
416
+ # Use 2048 bit certs trust anchor
408
417
  def load_cacerts(cert_store)
409
- file = File.join(File.dirname(__FILE__), 'cacert.p7s')
418
+ file = File.join(File.dirname(__FILE__), 'cacert.pem')
410
419
  add_trust_ca_to_store(cert_store, file)
411
420
  end
412
421
  end
@@ -0,0 +1,150 @@
1
+ # HTTPClient - HTTP client library.
2
+ # Copyright (C) 2000-2015 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
3
+ #
4
+ # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
5
+ # redistribute it and/or modify it under the same terms of Ruby's license;
6
+ # either the dual license version in 2003, or any later version.
7
+
8
+
9
+ require 'httpclient/ssl_config'
10
+
11
+
12
+ class HTTPClient
13
+
14
+ # Wraps up OpenSSL::SSL::SSLSocket and offers debugging features.
15
+ class SSLSocket
16
+ def self.create_socket(session)
17
+ opts = {
18
+ :debug_dev => session.debug_dev
19
+ }
20
+ site = session.proxy || session.dest
21
+ socket = session.create_socket(site.host, site.port)
22
+ begin
23
+ if session.proxy
24
+ session.connect_ssl_proxy(socket, Util.urify(session.dest.to_s))
25
+ end
26
+ new(socket, session.dest, session.ssl_config, opts)
27
+ rescue
28
+ socket.close
29
+ raise
30
+ end
31
+ end
32
+
33
+ def initialize(socket, dest, config, opts = {})
34
+ unless SSLEnabled
35
+ raise ConfigurationError.new('Ruby/OpenSSL module is required')
36
+ end
37
+ @socket = socket
38
+ @config = config
39
+ @ssl_socket = create_openssl_socket(@socket)
40
+ @debug_dev = opts[:debug_dev]
41
+ ssl_connect(dest.host)
42
+ end
43
+
44
+ def peer_cert
45
+ @ssl_socket.peer_cert
46
+ end
47
+
48
+ def close
49
+ @ssl_socket.close
50
+ @socket.close
51
+ end
52
+
53
+ def closed?
54
+ @socket.closed?
55
+ end
56
+
57
+ def eof?
58
+ @ssl_socket.eof?
59
+ end
60
+
61
+ def gets(rs)
62
+ str = @ssl_socket.gets(rs)
63
+ debug(str)
64
+ str
65
+ end
66
+
67
+ def read(size, buf = nil)
68
+ str = @ssl_socket.read(size, buf)
69
+ debug(str)
70
+ str
71
+ end
72
+
73
+ def readpartial(size, buf = nil)
74
+ str = @ssl_socket.readpartial(size, buf)
75
+ debug(str)
76
+ str
77
+ end
78
+
79
+ def <<(str)
80
+ rv = @ssl_socket.write(str)
81
+ debug(str)
82
+ rv
83
+ end
84
+
85
+ def flush
86
+ @ssl_socket.flush
87
+ end
88
+
89
+ def sync
90
+ @ssl_socket.sync
91
+ end
92
+
93
+ def sync=(sync)
94
+ @ssl_socket.sync = sync
95
+ end
96
+
97
+ private
98
+
99
+ def ssl_connect(hostname = nil)
100
+ if hostname && @ssl_socket.respond_to?(:hostname=)
101
+ @ssl_socket.hostname = hostname
102
+ end
103
+ @ssl_socket.connect
104
+ if $DEBUG
105
+ if @ssl_socket.respond_to?(:ssl_version)
106
+ warn("Protocol version: #{@ssl_socket.ssl_version}")
107
+ end
108
+ warn("Cipher: #{@ssl_socket.cipher.inspect}")
109
+ end
110
+ post_connection_check(hostname)
111
+ end
112
+
113
+ def post_connection_check(hostname)
114
+ verify_mode = @config.verify_mode || OpenSSL::SSL::VERIFY_NONE
115
+ if verify_mode == OpenSSL::SSL::VERIFY_NONE
116
+ return
117
+ elsif @ssl_socket.peer_cert.nil? and
118
+ check_mask(verify_mode, OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT)
119
+ raise OpenSSL::SSL::SSLError.new('no peer cert')
120
+ end
121
+ if @ssl_socket.respond_to?(:post_connection_check) and RUBY_VERSION > "1.8.4"
122
+ @ssl_socket.post_connection_check(hostname)
123
+ else
124
+ @config.post_connection_check(@ssl_socket.peer_cert, hostname)
125
+ end
126
+ end
127
+
128
+ def check_mask(value, mask)
129
+ value & mask == mask
130
+ end
131
+
132
+ def create_openssl_socket(socket)
133
+ ssl_socket = nil
134
+ if OpenSSL::SSL.const_defined?("SSLContext")
135
+ ctx = OpenSSL::SSL::SSLContext.new
136
+ @config.set_context(ctx)
137
+ ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ctx)
138
+ else
139
+ ssl_socket = OpenSSL::SSL::SSLSocket.new(socket)
140
+ @config.set_context(ssl_socket)
141
+ end
142
+ ssl_socket
143
+ end
144
+
145
+ def debug(str)
146
+ @debug_dev << str if @debug_dev && str
147
+ end
148
+ end
149
+
150
+ end
@@ -1,5 +1,5 @@
1
1
  # HTTPClient - HTTP client library.
2
- # Copyright (C) 2000-2009 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
2
+ # Copyright (C) 2000-2015 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
3
3
  #
4
4
  # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
5
5
  # redistribute it and/or modify it under the same terms of Ruby's license;
@@ -1,5 +1,5 @@
1
1
  # HTTPClient - HTTP client library.
2
- # Copyright (C) 2000-2012 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
2
+ # Copyright (C) 2000-2015 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
3
3
  #
4
4
  # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
5
5
  # redistribute it and/or modify it under the same terms of Ruby's license;
@@ -24,6 +24,28 @@ if RUBY_VERSION < "1.9.3"
24
24
  end
25
25
  end
26
26
 
27
+ # With recent JRuby 1.7 + jruby-openssl, X509CRL#extentions_to_text causes
28
+ # StringIndexOOBException when we try to dump SSL Server Certificate.
29
+ # when one of extensions has "" as value.
30
+ if defined?(JRUBY_VERSION)
31
+ require 'openssl'
32
+ require 'java'
33
+ module OpenSSL
34
+ module X509
35
+ class Certificate
36
+ java_import 'java.security.cert.Certificate'
37
+ java_import 'java.security.cert.CertificateFactory'
38
+ java_import 'java.io.ByteArrayInputStream'
39
+ def to_text
40
+ cf = CertificateFactory.getInstance('X.509')
41
+ cf.generateCertificate(ByteArrayInputStream.new(self.to_der.to_java_bytes)).toString
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+
27
49
  class HTTPClient
28
50
 
29
51
 
@@ -176,6 +198,16 @@ class HTTPClient
176
198
  end
177
199
  module_function :try_require
178
200
 
201
+ # show one warning message only once by caching message
202
+ #
203
+ # it cached all messages in memory so be careful not to show many kinds of warning message.
204
+ @@__warned = {}
205
+ def warning(message)
206
+ return if @@__warned.key?(message)
207
+ warn(message)
208
+ @@__warned[message] = true
209
+ end
210
+
179
211
  # Checks if the given URI is https.
180
212
  def https?(uri)
181
213
  uri.scheme && uri.scheme.downcase == 'https'
@@ -1,3 +1,3 @@
1
1
  class HTTPClient
2
- VERSION = '2.6.0.1'
2
+ VERSION = '2.8.3'
3
3
  end
@@ -342,7 +342,7 @@ class WebAgent
342
342
  cookie.domain_orig = given.domain
343
343
  cookie.path_orig = given.path
344
344
 
345
- if cookie.discard? || cookie.expires == nil
345
+ if cookie.discard? || cookie.expires.nil?
346
346
  cookie.discard = true
347
347
  else
348
348
  cookie.discard = false
@@ -456,4 +456,4 @@ end
456
456
 
457
457
  class HTTPClient
458
458
  CookieManager = WebAgent::CookieManager
459
- end
459
+ end unless defined?(HTTPClient::CookieManager)