maedana-httpclient 2.1.5.2.1

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.
@@ -0,0 +1,84 @@
1
+ # HTTPClient - HTTP client library.
2
+ # Copyright (C) 2000-2009 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
+ class HTTPClient
10
+
11
+
12
+ # Represents a HTTP response to an asynchronous request. Async methods of
13
+ # HTTPClient such as get_async, post_async, etc. returns an instance of
14
+ # Connection.
15
+ #
16
+ # == How to use
17
+ #
18
+ # 1. Invoke HTTP method asynchronously and check if it's been finished
19
+ # periodically.
20
+ #
21
+ # connection = clnt.post_async(url, body)
22
+ # print 'posting.'
23
+ # while true
24
+ # break if connection.finished?
25
+ # print '.'
26
+ # sleep 1
27
+ # end
28
+ # puts '.'
29
+ # res = connection.pop
30
+ # p res.status
31
+ #
32
+ # 2. Read the response as an IO.
33
+ #
34
+ # connection = clnt.get_async('http://dev.ctor.org/')
35
+ # io = connection.pop.content
36
+ # while str = io.read(40)
37
+ # p str
38
+ # end
39
+ class Connection
40
+ attr_accessor :async_thread
41
+
42
+ def initialize(header_queue = [], body_queue = []) # :nodoc:
43
+ @headers = header_queue
44
+ @body = body_queue
45
+ @async_thread = nil
46
+ @queue = Queue.new
47
+ end
48
+
49
+ # Checks if the asynchronous invocation has been finished or not.
50
+ def finished?
51
+ if !@async_thread
52
+ # Not in async mode.
53
+ true
54
+ elsif @async_thread.alive?
55
+ # Working...
56
+ false
57
+ else
58
+ # Async thread have been finished.
59
+ join
60
+ true
61
+ end
62
+ end
63
+
64
+ # Retrieves a HTTP::Message instance of HTTP response. Do not invoke this
65
+ # method twice for now. The second invocation will be blocked.
66
+ def pop
67
+ @queue.pop
68
+ end
69
+
70
+ def push(result) # :nodoc:
71
+ @queue.push(result)
72
+ end
73
+
74
+ # Waits the completion of the asynchronous invocation.
75
+ def join
76
+ if @async_thread
77
+ @async_thread.join
78
+ end
79
+ nil
80
+ end
81
+ end
82
+
83
+
84
+ end
@@ -0,0 +1,562 @@
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 'uri'
15
+ require 'time'
16
+ require 'monitor'
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
+ domainname = domain.sub(/\.\z/, '').downcase
36
+ hostname = host.sub(/\.\z/, '').downcase
37
+ case domain
38
+ when /\d+\.\d+\.\d+\.\d+/
39
+ return (hostname == domainname)
40
+ when '.'
41
+ return true
42
+ when /^\./
43
+ # allows; host == rubyforge.org, domain == .rubyforge.org
44
+ return tail_match?(domainname, '.' + hostname)
45
+ else
46
+ return (hostname == domainname)
47
+ end
48
+ end
49
+
50
+ def total_dot_num(string)
51
+ string.scan(/\./).length()
52
+ end
53
+
54
+ end
55
+
56
+ class Cookie
57
+ include CookieUtils
58
+
59
+ attr_accessor :name, :value
60
+ attr_accessor :domain, :path
61
+ attr_accessor :expires ## for Netscape Cookie
62
+ attr_accessor :url
63
+ attr_writer :use, :secure, :discard, :domain_orig, :path_orig, :override
64
+
65
+ USE = 1
66
+ SECURE = 2
67
+ DOMAIN = 4
68
+ PATH = 8
69
+ DISCARD = 16
70
+ OVERRIDE = 32
71
+ OVERRIDE_OK = 32
72
+
73
+ def initialize()
74
+ @name = @value = @domain = @path = nil
75
+ @expires = nil
76
+ @url = nil
77
+ @use = @secure = @discard = @domain_orig = @path_orig = @override = nil
78
+ end
79
+
80
+ def discard?
81
+ @discard
82
+ end
83
+
84
+ def use?
85
+ @use
86
+ end
87
+
88
+ def secure?
89
+ @secure
90
+ end
91
+
92
+ def domain_orig?
93
+ @domain_orig
94
+ end
95
+
96
+ def path_orig?
97
+ @path_orig
98
+ end
99
+
100
+ def override?
101
+ @override
102
+ end
103
+
104
+ def flag
105
+ flg = 0
106
+ flg += USE if @use
107
+ flg += SECURE if @secure
108
+ flg += DOMAIN if @domain_orig
109
+ flg += PATH if @path_orig
110
+ flg += DISCARD if @discard
111
+ flg += OVERRIDE if @override
112
+ flg
113
+ end
114
+
115
+ def set_flag(flag)
116
+ flag = flag.to_i
117
+ @use = true if flag & USE > 0
118
+ @secure = true if flag & SECURE > 0
119
+ @domain_orig = true if flag & DOMAIN > 0
120
+ @path_orig = true if flag & PATH > 0
121
+ @discard = true if flag & DISCARD > 0
122
+ @override = true if flag & OVERRIDE > 0
123
+ end
124
+
125
+ def match?(url)
126
+ domainname = url.host
127
+ if (!domainname ||
128
+ !domain_match(domainname, @domain) ||
129
+ (@path && !head_match?(@path, url.path)) ||
130
+ (@secure && (url.scheme != 'https')) )
131
+ return false
132
+ else
133
+ return true
134
+ end
135
+ end
136
+
137
+ def join_quotedstr(array, sep)
138
+ ret = Array.new()
139
+ old_elem = nil
140
+ array.each{|elem|
141
+ if (elem.scan(/"/).length % 2) == 0
142
+ if old_elem
143
+ old_elem << sep << elem
144
+ else
145
+ ret << elem
146
+ old_elem = nil
147
+ end
148
+ else
149
+ if old_elem
150
+ old_elem << sep << elem
151
+ ret << old_elem
152
+ old_elem = nil
153
+ else
154
+ old_elem = elem.dup
155
+ end
156
+ end
157
+ }
158
+ ret
159
+ end
160
+
161
+ def parse(str, url)
162
+ @url = url
163
+ # TODO: should not depend on join_quotedstr. scan with escape like CSV.
164
+ cookie_elem = str.split(/;/)
165
+ cookie_elem = join_quotedstr(cookie_elem, ';')
166
+ cookie_elem -= [""] # del empty elements, a cookie might included ";;"
167
+ first_elem = cookie_elem.shift
168
+ if first_elem !~ /([^=]*)(\=(.*))?/
169
+ return
170
+ ## raise ArgumentError 'invalid cookie value'
171
+ end
172
+ @name = $1.strip
173
+ @value = normalize_cookie_value($3)
174
+ cookie_elem.each{|pair|
175
+ key, value = pair.split(/=/, 2) ## value may nil
176
+ key.strip!
177
+ value = normalize_cookie_value(value)
178
+ case key.downcase
179
+ when 'domain'
180
+ @domain = value
181
+ when 'expires'
182
+ @expires = nil
183
+ begin
184
+ @expires = Time.parse(value).gmtime() if value
185
+ rescue ArgumentError
186
+ end
187
+ when 'path'
188
+ @path = value
189
+ when 'secure'
190
+ @secure = true ## value may nil, but must 'true'.
191
+ else
192
+ ## ignore
193
+ end
194
+ }
195
+ end
196
+
197
+ def normalize_cookie_value(value)
198
+ if value
199
+ value = value.strip.sub(/\A"(.*)"\z/) { $1 }
200
+ value = nil if value.empty?
201
+ end
202
+ value
203
+ end
204
+ private :normalize_cookie_value
205
+ end
206
+
207
+ class CookieManager
208
+ include CookieUtils
209
+
210
+ ### errors
211
+ class Error < StandardError; end
212
+ class ErrorOverrideOK < Error; end
213
+ class SpecialError < Error; end
214
+
215
+ attr_reader :cookies
216
+ attr_accessor :cookies_file
217
+ attr_accessor :accept_domains, :reject_domains
218
+
219
+ # for conformance to http://wp.netscape.com/newsref/std/cookie_spec.html
220
+ attr_accessor :netscape_rule
221
+ SPECIAL_DOMAIN = [".com",".edu",".gov",".mil",".net",".org",".int"]
222
+
223
+ def initialize(file=nil)
224
+ @cookies = Array.new()
225
+ @cookies.extend(MonitorMixin)
226
+ @cookies_file = file
227
+ @is_saved = true
228
+ @reject_domains = Array.new()
229
+ @accept_domains = Array.new()
230
+ @netscape_rule = false
231
+ end
232
+
233
+ def cookies=(cookies)
234
+ @cookies = cookies
235
+ @cookies.extend(MonitorMixin)
236
+ end
237
+
238
+ def save_all_cookies(force = nil, save_unused = true, save_discarded = true)
239
+ @cookies.synchronize do
240
+ check_expired_cookies()
241
+ if @is_saved and !force
242
+ return
243
+ end
244
+ File.open(@cookies_file, 'w') do |f|
245
+ @cookies.each do |cookie|
246
+ if (cookie.use? or save_unused) and
247
+ (!cookie.discard? or save_discarded)
248
+ f.print(cookie.url.to_s,"\t",
249
+ cookie.name,"\t",
250
+ cookie.value,"\t",
251
+ cookie.expires.to_i,"\t",
252
+ cookie.domain,"\t",
253
+ cookie.path,"\t",
254
+ cookie.flag,"\n")
255
+ end
256
+ end
257
+ end
258
+ end
259
+ @is_saved = true
260
+ end
261
+
262
+ def save_cookies(force = nil)
263
+ save_all_cookies(force, false, false)
264
+ end
265
+
266
+ def check_expired_cookies()
267
+ @cookies.reject!{|cookie|
268
+ is_expired = (cookie.expires && (cookie.expires < Time.now.gmtime))
269
+ if is_expired && !cookie.discard?
270
+ @is_saved = false
271
+ end
272
+ is_expired
273
+ }
274
+ end
275
+
276
+ def parse(str, url)
277
+ cookie = WebAgent::Cookie.new()
278
+ cookie.parse(str, url)
279
+ add(cookie)
280
+ end
281
+
282
+ def make_cookie_str(cookie_list)
283
+ if cookie_list.empty?
284
+ return nil
285
+ end
286
+
287
+ ret = ''
288
+ c = cookie_list.shift
289
+ ret += "#{c.name}=#{c.value}"
290
+ cookie_list.each{|cookie|
291
+ ret += "; #{cookie.name}=#{cookie.value}"
292
+ }
293
+ return ret
294
+ end
295
+ private :make_cookie_str
296
+
297
+
298
+ def find(url)
299
+ return nil if @cookies.empty?
300
+
301
+ cookie_list = Array.new()
302
+ @cookies.each{|cookie|
303
+ is_expired = (cookie.expires && (cookie.expires < Time.now.gmtime))
304
+ if cookie.use? && !is_expired && cookie.match?(url)
305
+ if cookie_list.select{|c1| c1.name == cookie.name}.empty?
306
+ cookie_list << cookie
307
+ end
308
+ end
309
+ }
310
+ return make_cookie_str(cookie_list)
311
+ end
312
+
313
+ def find_cookie_info(domain, path, name)
314
+ @cookies.find{|c|
315
+ c.domain == domain && c.path == path && c.name == name
316
+ }
317
+ end
318
+ private :find_cookie_info
319
+
320
+ # not tested well; used only netscape_rule = true.
321
+ def cookie_error(err, override)
322
+ if !err.kind_of?(ErrorOverrideOK) || !override
323
+ raise err
324
+ end
325
+ end
326
+ private :cookie_error
327
+
328
+ def add(cookie)
329
+ url = cookie.url
330
+ name, value = cookie.name, cookie.value
331
+ expires, domain, path =
332
+ cookie.expires, cookie.domain, cookie.path
333
+ secure, domain_orig, path_orig =
334
+ cookie.secure?, cookie.domain_orig?, cookie.path_orig?
335
+ discard, override =
336
+ cookie.discard?, cookie.override?
337
+
338
+ domainname = url.host
339
+ domain_orig, path_orig = domain, path
340
+ use_security = override
341
+
342
+ if domain
343
+
344
+ # [DRAFT 12] s. 4.2.2 (does not apply in the case that
345
+ # host name is the same as domain attribute for version 0
346
+ # cookie)
347
+ # I think that this rule has almost the same effect as the
348
+ # tail match of [NETSCAPE].
349
+ if domain !~ /^\./ && domainname != domain
350
+ domain = '.'+domain
351
+ end
352
+
353
+ # [NETSCAPE] rule
354
+ if @netscape_rule
355
+ n = total_dot_num(domain)
356
+ if n < 2
357
+ cookie_error(SpecialError.new(), override)
358
+ elsif n == 2
359
+ ## [NETSCAPE] rule
360
+ ok = SPECIAL_DOMAIN.select{|sdomain|
361
+ sdomain == domain[-(sdomain.length)..-1]
362
+ }
363
+ if ok.empty?
364
+ cookie_error(SpecialError.new(), override)
365
+ end
366
+ end
367
+ end
368
+
369
+ # this implementation does not check RFC2109 4.3.2 case 2;
370
+ # the portion of host not in domain does not contain a dot.
371
+ # according to nsCookieService.cpp in Firefox 3.0.4, Firefox 3.0.4
372
+ # and IE does not check, too.
373
+ end
374
+
375
+ path ||= url.path.sub(%r|/[^/]*\z|, '')
376
+ domain ||= domainname
377
+ @cookies.synchronize do
378
+ cookie = find_cookie_info(domain, path, name)
379
+ if !cookie
380
+ cookie = WebAgent::Cookie.new()
381
+ cookie.use = true
382
+ @cookies << cookie
383
+ end
384
+ check_expired_cookies()
385
+ end
386
+
387
+ cookie.url = url
388
+ cookie.name = name
389
+ cookie.value = value
390
+ cookie.expires = expires
391
+ cookie.domain = domain
392
+ cookie.path = path
393
+
394
+ ## for flag
395
+ cookie.secure = secure
396
+ cookie.domain_orig = domain_orig
397
+ cookie.path_orig = path_orig
398
+ if discard || cookie.expires == nil
399
+ cookie.discard = true
400
+ else
401
+ cookie.discard = false
402
+ @is_saved = false
403
+ end
404
+ end
405
+
406
+ def load_cookies()
407
+ return if !File.readable?(@cookies_file)
408
+ @cookies.synchronize do
409
+ @cookies.clear
410
+ File.open(@cookies_file,'r'){|f|
411
+ while line = f.gets
412
+ cookie = WebAgent::Cookie.new()
413
+ @cookies << cookie
414
+ col = line.chomp.split(/\t/)
415
+ cookie.url = URI.parse(col[0])
416
+ cookie.name = col[1]
417
+ cookie.value = col[2]
418
+ if col[3].empty? or col[3] == '0'
419
+ cookie.expires = nil
420
+ else
421
+ cookie.expires = Time.at(col[3].to_i).gmtime
422
+ end
423
+ cookie.domain = col[4]
424
+ cookie.path = col[5]
425
+ cookie.set_flag(col[6])
426
+ end
427
+ }
428
+ end
429
+ end
430
+
431
+ def check_cookie_accept_domain(domain)
432
+ unless domain
433
+ return false
434
+ end
435
+ @accept_domains.each{|dom|
436
+ if domain_match(domain, dom)
437
+ return true
438
+ end
439
+ }
440
+ @reject_domains.each{|dom|
441
+ if domain_match(domain, dom)
442
+ return false
443
+ end
444
+ }
445
+ return true
446
+ end
447
+ end
448
+ end
449
+
450
+ __END__
451
+
452
+ =begin
453
+
454
+ == WebAgent::CookieManager Class
455
+
456
+ Load, save, parse and send cookies.
457
+
458
+ === Usage
459
+
460
+ ## initialize
461
+ cm = WebAgent::CookieManager.new("/home/foo/bar/cookie")
462
+
463
+ ## load cookie data
464
+ cm.load_cookies()
465
+
466
+ ## parse cookie from string (maybe "Set-Cookie:" header)
467
+ cm.parse(str)
468
+
469
+ ## send cookie data to url
470
+ f.write(cm.find(url))
471
+
472
+ ## save cookie to cookiefile
473
+ cm.save_cookies()
474
+
475
+
476
+ === Class Methods
477
+
478
+ -- CookieManager::new(file=nil)
479
+
480
+ create new CookieManager. If a file is provided,
481
+ use it as cookies' file.
482
+
483
+ === Methods
484
+
485
+ -- CookieManager#save_cookies(force = nil)
486
+
487
+ save cookies' data into file. if argument is true,
488
+ save data although data is not modified.
489
+
490
+ -- CookieManager#parse(str, url)
491
+
492
+ parse string and store cookie (to parse HTTP response header).
493
+
494
+ -- CookieManager#find(url)
495
+
496
+ get cookies and make into string (to send as HTTP request header).
497
+
498
+ -- CookieManager#add(cookie)
499
+
500
+ add new cookie.
501
+
502
+ -- CookieManager#load_cookies()
503
+
504
+ load cookies' data from file.
505
+
506
+
507
+ == WebAgent::CookieUtils Module
508
+
509
+ -- CookieUtils::head_match?(str1, str2)
510
+ -- CookieUtils::tail_match?(str1, str2)
511
+ -- CookieUtils::domain_match(host, domain)
512
+ -- CookieUtils::total_dot_num(str)
513
+
514
+
515
+ == WebAgent::Cookie Class
516
+
517
+ === Class Methods
518
+
519
+ -- Cookie::new()
520
+
521
+ create new cookie.
522
+
523
+ === Methods
524
+
525
+ -- Cookie#match?(url)
526
+
527
+ match cookie by url. if match, return true. otherwise,
528
+ return false.
529
+
530
+ -- Cookie#name
531
+ -- Cookie#name=(name)
532
+ -- Cookie#value
533
+ -- Cookie#value=(value)
534
+ -- Cookie#domain
535
+ -- Cookie#domain=(domain)
536
+ -- Cookie#path
537
+ -- Cookie#path=(path)
538
+ -- Cookie#expires
539
+ -- Cookie#expires=(expires)
540
+ -- Cookie#url
541
+ -- Cookie#url=(url)
542
+
543
+ accessor methods for cookie's items.
544
+
545
+ -- Cookie#discard?
546
+ -- Cookie#discard=(discard)
547
+ -- Cookie#use?
548
+ -- Cookie#use=(use)
549
+ -- Cookie#secure?
550
+ -- Cookie#secure=(secure)
551
+ -- Cookie#domain_orig?
552
+ -- Cookie#domain_orig=(domain_orig)
553
+ -- Cookie#path_orig?
554
+ -- Cookie#path_orig=(path_orig)
555
+ -- Cookie#override?
556
+ -- Cookie#override=(override)
557
+ -- Cookie#flag
558
+ -- Cookie#set_flag(flag_num)
559
+
560
+ accessor methods for flags.
561
+
562
+ =end