maedana-httpclient 2.1.5.2.1

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