httpclient 2.6.0.1 → 2.8.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)