httpclient 2.3.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.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +85 -0
  3. data/bin/httpclient +18 -6
  4. data/bin/jsonclient +85 -0
  5. data/lib/http-access2.rb +1 -1
  6. data/lib/httpclient.rb +262 -88
  7. data/lib/httpclient/auth.rb +269 -244
  8. data/lib/httpclient/cacert.pem +3952 -0
  9. data/lib/httpclient/cacert1024.pem +3866 -0
  10. data/lib/httpclient/connection.rb +1 -1
  11. data/lib/httpclient/cookie.rb +161 -514
  12. data/lib/httpclient/http.rb +57 -21
  13. data/lib/httpclient/include_client.rb +2 -0
  14. data/lib/httpclient/jruby_ssl_socket.rb +588 -0
  15. data/lib/httpclient/session.rb +259 -317
  16. data/lib/httpclient/ssl_config.rb +141 -188
  17. data/lib/httpclient/ssl_socket.rb +150 -0
  18. data/lib/httpclient/timeout.rb +1 -1
  19. data/lib/httpclient/util.rb +62 -1
  20. data/lib/httpclient/version.rb +1 -1
  21. data/lib/httpclient/webagent-cookie.rb +459 -0
  22. data/lib/jsonclient.rb +63 -0
  23. data/lib/oauthclient.rb +2 -1
  24. data/sample/jsonclient.rb +67 -0
  25. data/sample/oauth_twitter.rb +4 -4
  26. data/test/{ca-chain.cert → ca-chain.pem} +0 -0
  27. data/test/client-pass.key +18 -0
  28. data/test/helper.rb +10 -8
  29. data/test/jruby_ssl_socket/test_pemutils.rb +32 -0
  30. data/test/test_auth.rb +175 -4
  31. data/test/test_cookie.rb +147 -243
  32. data/test/test_http-access2.rb +17 -16
  33. data/test/test_httpclient.rb +458 -77
  34. data/test/test_jsonclient.rb +80 -0
  35. data/test/test_ssl.rb +341 -17
  36. data/test/test_webagent-cookie.rb +465 -0
  37. metadata +57 -55
  38. data/README.txt +0 -721
  39. data/lib/httpclient/cacert.p7s +0 -1858
  40. data/lib/httpclient/cacert_sha1.p7s +0 -1858
  41. data/sample/oauth_salesforce_10.rb +0 -63
@@ -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;
@@ -12,6 +12,39 @@ unless ''.respond_to?(:bytesize)
12
12
  end
13
13
  end
14
14
 
15
+ if RUBY_VERSION < "1.9.3"
16
+ require 'uri'
17
+ module URI
18
+ class Generic
19
+ def hostname
20
+ v = self.host
21
+ /\A\[(.*)\]\z/ =~ v ? $1 : v
22
+ end
23
+ end
24
+ end
25
+ end
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
+
15
48
 
16
49
  class HTTPClient
17
50
 
@@ -47,6 +80,12 @@ class HTTPClient
47
80
  def port
48
81
  super || default_port
49
82
  end
83
+
84
+ # Captured from uri/generic.rb
85
+ def hostname
86
+ v = self.host
87
+ /\A\[(.*)\]\z/ =~ v ? $1 : v
88
+ end
50
89
  end
51
90
  AddressableEnabled = true
52
91
  rescue LoadError
@@ -147,6 +186,28 @@ class HTTPClient
147
186
  end
148
187
  module_function :hash_find_value
149
188
 
189
+ # Try to require a feature and returns true/false if loaded
190
+ #
191
+ # It returns 'true' for the second require in contrast of the standard
192
+ # require returns false if the feature is already loaded.
193
+ def try_require(feature)
194
+ require feature
195
+ true
196
+ rescue LoadError
197
+ false
198
+ end
199
+ module_function :try_require
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
+
150
211
  # Checks if the given URI is https.
151
212
  def https?(uri)
152
213
  uri.scheme && uri.scheme.downcase == 'https'
@@ -1,3 +1,3 @@
1
1
  class HTTPClient
2
- VERSION = '2.3.0.1'
2
+ VERSION = '2.8.3'
3
3
  end
@@ -0,0 +1,459 @@
1
+ # cookie.rb is redistributed file which is originally included in Webagent
2
+ # version 0.6.2 by TAKAHASHI `Maki' Masayoshi. And it contains some bug fixes.
3
+ # You can download the entire package of Webagent from
4
+ # http://www.rubycolor.org/arc/.
5
+
6
+
7
+ # Cookie class
8
+ #
9
+ # I refered to w3m's source to make these classes. Some comments
10
+ # are quoted from it. I'm thanksful for author(s) of it.
11
+ #
12
+ # w3m homepage: http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/eng/
13
+
14
+ require 'time'
15
+ require 'monitor'
16
+ require 'httpclient/util'
17
+
18
+ class WebAgent
19
+
20
+ module CookieUtils
21
+
22
+ def head_match?(str1, str2)
23
+ str1 == str2[0, str1.length]
24
+ end
25
+
26
+ def tail_match?(str1, str2)
27
+ if str1.length > 0
28
+ str1 == str2[-str1.length..-1].to_s
29
+ else
30
+ true
31
+ end
32
+ end
33
+
34
+ def domain_match(host, domain)
35
+ return false if domain.nil?
36
+ domainname = domain.sub(/\.\z/, '').downcase
37
+ hostname = host.sub(/\.\z/, '').downcase
38
+ case domain
39
+ when /\d+\.\d+\.\d+\.\d+/
40
+ return (hostname == domainname)
41
+ when '.'
42
+ return true
43
+ when /^\./
44
+ # allows; host == rubyforge.org, domain == .rubyforge.org
45
+ return tail_match?(domainname, '.' + hostname)
46
+ else
47
+ return (hostname == domainname)
48
+ end
49
+ end
50
+ end
51
+
52
+ class Cookie
53
+ include CookieUtils
54
+
55
+ attr_accessor :name, :value
56
+ attr_accessor :domain, :path
57
+ attr_accessor :expires ## for Netscape Cookie
58
+ attr_accessor :url
59
+ attr_writer :use, :secure, :http_only, :discard, :domain_orig, :path_orig, :override
60
+
61
+ USE = 1
62
+ SECURE = 2
63
+ DOMAIN = 4
64
+ PATH = 8
65
+ DISCARD = 16
66
+ OVERRIDE = 32
67
+ OVERRIDE_OK = 32
68
+ HTTP_ONLY = 64
69
+
70
+ def self.parse(str, url)
71
+ cookie = new
72
+ cookie.parse(str, url)
73
+ cookie
74
+ end
75
+
76
+ def initialize
77
+ @name = @value = @domain = @path = nil
78
+ @expires = nil
79
+ @url = nil
80
+ @use = @secure = @http_only = @discard = @domain_orig = @path_orig = @override = nil
81
+ end
82
+
83
+ def discard?
84
+ @discard
85
+ end
86
+
87
+ def use?
88
+ @use
89
+ end
90
+
91
+ def secure?
92
+ @secure
93
+ end
94
+
95
+ def http_only?
96
+ @http_only
97
+ end
98
+
99
+ def domain_orig?
100
+ @domain_orig
101
+ end
102
+
103
+ def path_orig?
104
+ @path_orig
105
+ end
106
+
107
+ def override?
108
+ @override
109
+ end
110
+
111
+ def flag
112
+ flg = 0
113
+ flg += USE if @use
114
+ flg += SECURE if @secure
115
+ flg += HTTP_ONLY if @http_only
116
+ flg += DOMAIN if @domain_orig
117
+ flg += PATH if @path_orig
118
+ flg += DISCARD if @discard
119
+ flg += OVERRIDE if @override
120
+ flg
121
+ end
122
+
123
+ def set_flag(flag)
124
+ flag = flag.to_i
125
+ @use = true if flag & USE > 0
126
+ @secure = true if flag & SECURE > 0
127
+ @http_only = true if flag & HTTP_ONLY > 0
128
+ @domain_orig = true if flag & DOMAIN > 0
129
+ @path_orig = true if flag & PATH > 0
130
+ @discard = true if flag & DISCARD > 0
131
+ @override = true if flag & OVERRIDE > 0
132
+ end
133
+
134
+ def match?(url)
135
+ domainname = url.host
136
+ if (!domainname ||
137
+ !domain_match(domainname, @domain) ||
138
+ (@path && !head_match?(@path, url.path.empty? ? '/' : url.path)) ||
139
+ (@secure && (url.scheme != 'https')) )
140
+ return false
141
+ else
142
+ return true
143
+ end
144
+ end
145
+
146
+ def join_quotedstr(array, sep)
147
+ ret = Array.new
148
+ old_elem = nil
149
+ array.each{|elem|
150
+ if (elem.scan(/"/).length % 2) == 0
151
+ if old_elem
152
+ old_elem << sep << elem
153
+ else
154
+ ret << elem
155
+ old_elem = nil
156
+ end
157
+ else
158
+ if old_elem
159
+ old_elem << sep << elem
160
+ ret << old_elem
161
+ old_elem = nil
162
+ else
163
+ old_elem = elem.dup
164
+ end
165
+ end
166
+ }
167
+ ret
168
+ end
169
+
170
+ def parse(str, url)
171
+ @url = url
172
+ # TODO: should not depend on join_quotedstr. scan with escape like CSV.
173
+ cookie_elem = str.split(/;/)
174
+ cookie_elem = join_quotedstr(cookie_elem, ';')
175
+ cookie_elem -= [""] # del empty elements, a cookie might included ";;"
176
+ first_elem = cookie_elem.shift
177
+ if first_elem !~ /([^=]*)(\=(.*))?/
178
+ return
179
+ ## raise ArgumentError 'invalid cookie value'
180
+ end
181
+ @name = $1.strip
182
+ @value = normalize_cookie_value($3)
183
+ cookie_elem.each{|pair|
184
+ key, value = pair.split(/=/, 2) ## value may nil
185
+ key.strip!
186
+ value = normalize_cookie_value(value)
187
+ case key.downcase
188
+ when 'domain'
189
+ @domain = value
190
+ when 'expires'
191
+ @expires = nil
192
+ begin
193
+ @expires = Time.parse(value).gmtime if value
194
+ rescue ArgumentError
195
+ end
196
+ when 'path'
197
+ @path = value
198
+ when 'secure'
199
+ @secure = true ## value may nil, but must 'true'.
200
+ when 'httponly'
201
+ @http_only = true ## value may nil, but must 'true'.
202
+ else
203
+ warn("Unknown key: #{key} = #{value}")
204
+ end
205
+ }
206
+ end
207
+
208
+ private
209
+
210
+ def normalize_cookie_value(value)
211
+ if value
212
+ value = value.strip.sub(/\A"(.*)"\z/) { $1 }
213
+ value = nil if value.empty?
214
+ end
215
+ value
216
+ end
217
+ end
218
+
219
+ ##
220
+ # An Array class that already includes the MonitorMixin module.
221
+ #
222
+ class SynchronizedArray < Array
223
+ include MonitorMixin
224
+ end
225
+
226
+ class CookieManager
227
+ include CookieUtils
228
+
229
+ ### errors
230
+ class Error < StandardError; end
231
+ class ErrorOverrideOK < Error; end
232
+ class SpecialError < Error; end
233
+
234
+ attr_reader :cookies
235
+ attr_accessor :cookies_file
236
+ attr_accessor :accept_domains, :reject_domains
237
+
238
+ def initialize(file=nil)
239
+ @cookies = SynchronizedArray.new
240
+ @cookies_file = file
241
+ @is_saved = true
242
+ @reject_domains = Array.new
243
+ @accept_domains = Array.new
244
+ @netscape_rule = false
245
+ end
246
+
247
+ def cookies=(cookies)
248
+ if cookies.is_a?(SynchronizedArray)
249
+ @cookies = cookies
250
+ else
251
+ @cookies = SynchronizedArray.new(cookies)
252
+ end
253
+ end
254
+
255
+ def save_all_cookies(force = nil, save_unused = true, save_discarded = true)
256
+ @cookies.synchronize do
257
+ check_expired_cookies
258
+ if @is_saved and !force
259
+ return
260
+ end
261
+ File.open(@cookies_file, 'w') do |f|
262
+ @cookies.each do |cookie|
263
+ if (cookie.use? or save_unused) and
264
+ (!cookie.discard? or save_discarded)
265
+ f.print(cookie.url.to_s,"\t",
266
+ cookie.name,"\t",
267
+ cookie.value,"\t",
268
+ cookie.expires.to_i,"\t",
269
+ cookie.domain,"\t",
270
+ cookie.path,"\t",
271
+ cookie.flag,"\n")
272
+ end
273
+ end
274
+ end
275
+ end
276
+ @is_saved = true
277
+ end
278
+
279
+ def save_cookies(force = nil)
280
+ save_all_cookies(force, false, false)
281
+ end
282
+
283
+ def check_expired_cookies
284
+ @cookies.reject!{|cookie|
285
+ is_expired = (cookie.expires && (cookie.expires < Time.now.gmtime))
286
+ if is_expired && !cookie.discard?
287
+ @is_saved = false
288
+ end
289
+ is_expired
290
+ }
291
+ end
292
+
293
+ def parse(str, url)
294
+ cookie = WebAgent::Cookie.new
295
+ cookie.parse(str, url)
296
+ add(cookie)
297
+ end
298
+
299
+ def find(url)
300
+ return nil if @cookies.empty?
301
+
302
+ cookie_list = Array.new
303
+ @cookies.each{|cookie|
304
+ is_expired = (cookie.expires && (cookie.expires < Time.now.gmtime))
305
+ if cookie.use? && !is_expired && cookie.match?(url)
306
+ if cookie_list.select{|c1| c1.name == cookie.name}.empty?
307
+ cookie_list << cookie
308
+ end
309
+ end
310
+ }
311
+ return make_cookie_str(cookie_list)
312
+ end
313
+ alias cookie_value find
314
+
315
+ def add(given)
316
+ check_domain(given.domain, given.url.host, given.override?)
317
+
318
+ domain = given.domain || given.url.host
319
+ path = given.path || given.url.path.sub(%r|/[^/]*\z|, '')
320
+
321
+ cookie = nil
322
+ @cookies.synchronize do
323
+ check_expired_cookies
324
+ cookie = @cookies.find { |c|
325
+ c.domain == domain && c.path == path && c.name == given.name
326
+ }
327
+ if !cookie
328
+ cookie = WebAgent::Cookie.new
329
+ cookie.use = true
330
+ @cookies << cookie
331
+ end
332
+ end
333
+
334
+ cookie.domain = domain
335
+ cookie.path = path
336
+ cookie.url = given.url
337
+ cookie.name = given.name
338
+ cookie.value = given.value
339
+ cookie.expires = given.expires
340
+ cookie.secure = given.secure?
341
+ cookie.http_only = given.http_only?
342
+ cookie.domain_orig = given.domain
343
+ cookie.path_orig = given.path
344
+
345
+ if cookie.discard? || cookie.expires.nil?
346
+ cookie.discard = true
347
+ else
348
+ cookie.discard = false
349
+ @is_saved = false
350
+ end
351
+ end
352
+
353
+ def load_cookies
354
+ return if !File.readable?(@cookies_file)
355
+ @cookies.synchronize do
356
+ @cookies.clear
357
+ File.open(@cookies_file,'r'){|f|
358
+ while line = f.gets
359
+ cookie = WebAgent::Cookie.new
360
+ @cookies << cookie
361
+ col = line.chomp.split(/\t/)
362
+ cookie.url = HTTPClient::Util.urify(col[0])
363
+ cookie.name = col[1]
364
+ cookie.value = col[2]
365
+ if col[3].empty? or col[3] == '0'
366
+ cookie.expires = nil
367
+ else
368
+ cookie.expires = Time.at(col[3].to_i).gmtime
369
+ end
370
+ cookie.domain = col[4]
371
+ cookie.path = col[5]
372
+ cookie.set_flag(col[6])
373
+ end
374
+ }
375
+ end
376
+ end
377
+
378
+ # Who use it?
379
+ def check_cookie_accept_domain(domain)
380
+ unless domain
381
+ return false
382
+ end
383
+ @accept_domains.each{|dom|
384
+ if domain_match(domain, dom)
385
+ return true
386
+ end
387
+ }
388
+ @reject_domains.each{|dom|
389
+ if domain_match(domain, dom)
390
+ return false
391
+ end
392
+ }
393
+ return true
394
+ end
395
+
396
+ private
397
+
398
+ def make_cookie_str(cookie_list)
399
+ if cookie_list.empty?
400
+ return nil
401
+ end
402
+
403
+ ret = ''
404
+ c = cookie_list.shift
405
+ ret += "#{c.name}=#{c.value}"
406
+ cookie_list.each{|cookie|
407
+ ret += "; #{cookie.name}=#{cookie.value}"
408
+ }
409
+ return ret
410
+ end
411
+
412
+ # for conformance to http://wp.netscape.com/newsref/std/cookie_spec.html
413
+ attr_accessor :netscape_rule
414
+ SPECIAL_DOMAIN = [".com",".edu",".gov",".mil",".net",".org",".int"]
415
+
416
+ def check_domain(domain, hostname, override)
417
+ return unless domain
418
+
419
+ # [DRAFT 12] s. 4.2.2 (does not apply in the case that
420
+ # host name is the same as domain attribute for version 0
421
+ # cookie)
422
+ # I think that this rule has almost the same effect as the
423
+ # tail match of [NETSCAPE].
424
+ if domain !~ /^\./ && hostname != domain
425
+ domain = '.'+domain
426
+ end
427
+ # [NETSCAPE] rule
428
+ if @netscape_rule
429
+ n = domain.scan(/\./).length
430
+ if n < 2
431
+ cookie_error(SpecialError.new, override)
432
+ elsif n == 2
433
+ ## [NETSCAPE] rule
434
+ ok = SPECIAL_DOMAIN.select{|sdomain|
435
+ sdomain == domain[-(sdomain.length)..-1]
436
+ }
437
+ if ok.empty?
438
+ cookie_error(SpecialError.new, override)
439
+ end
440
+ end
441
+ end
442
+ # this implementation does not check RFC2109 4.3.2 case 2;
443
+ # the portion of host not in domain does not contain a dot.
444
+ # according to nsCookieService.cpp in Firefox 3.0.4, Firefox 3.0.4
445
+ # and IE does not check, too.
446
+ end
447
+
448
+ # not tested well; used only netscape_rule = true.
449
+ def cookie_error(err, override)
450
+ if !err.kind_of?(ErrorOverrideOK) || !override
451
+ raise err
452
+ end
453
+ end
454
+ end
455
+ end
456
+
457
+ class HTTPClient
458
+ CookieManager = WebAgent::CookieManager
459
+ end unless defined?(HTTPClient::CookieManager)