httpclient-jgraichen 2.3.4.2

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 (58) hide show
  1. checksums.yaml +7 -0
  2. data/README.txt +759 -0
  3. data/bin/httpclient +65 -0
  4. data/lib/hexdump.rb +50 -0
  5. data/lib/http-access2.rb +55 -0
  6. data/lib/http-access2/cookie.rb +1 -0
  7. data/lib/http-access2/http.rb +1 -0
  8. data/lib/httpclient.rb +1156 -0
  9. data/lib/httpclient/auth.rb +899 -0
  10. data/lib/httpclient/cacert.p7s +1912 -0
  11. data/lib/httpclient/connection.rb +88 -0
  12. data/lib/httpclient/cookie.rb +438 -0
  13. data/lib/httpclient/http.rb +1046 -0
  14. data/lib/httpclient/include_client.rb +83 -0
  15. data/lib/httpclient/session.rb +1028 -0
  16. data/lib/httpclient/ssl_config.rb +405 -0
  17. data/lib/httpclient/timeout.rb +140 -0
  18. data/lib/httpclient/util.rb +178 -0
  19. data/lib/httpclient/version.rb +3 -0
  20. data/lib/oauthclient.rb +110 -0
  21. data/sample/async.rb +8 -0
  22. data/sample/auth.rb +11 -0
  23. data/sample/cookie.rb +18 -0
  24. data/sample/dav.rb +103 -0
  25. data/sample/howto.rb +49 -0
  26. data/sample/oauth_buzz.rb +57 -0
  27. data/sample/oauth_friendfeed.rb +59 -0
  28. data/sample/oauth_twitter.rb +61 -0
  29. data/sample/ssl/0cert.pem +22 -0
  30. data/sample/ssl/0key.pem +30 -0
  31. data/sample/ssl/1000cert.pem +19 -0
  32. data/sample/ssl/1000key.pem +18 -0
  33. data/sample/ssl/htdocs/index.html +10 -0
  34. data/sample/ssl/ssl_client.rb +22 -0
  35. data/sample/ssl/webrick_httpsd.rb +29 -0
  36. data/sample/stream.rb +21 -0
  37. data/sample/thread.rb +27 -0
  38. data/sample/wcat.rb +21 -0
  39. data/test/ca-chain.cert +44 -0
  40. data/test/ca.cert +23 -0
  41. data/test/client.cert +19 -0
  42. data/test/client.key +15 -0
  43. data/test/helper.rb +129 -0
  44. data/test/htdigest +1 -0
  45. data/test/htpasswd +2 -0
  46. data/test/runner.rb +2 -0
  47. data/test/server.cert +19 -0
  48. data/test/server.key +15 -0
  49. data/test/sslsvr.rb +65 -0
  50. data/test/subca.cert +21 -0
  51. data/test/test_auth.rb +348 -0
  52. data/test/test_cookie.rb +412 -0
  53. data/test/test_hexdump.rb +14 -0
  54. data/test/test_http-access2.rb +507 -0
  55. data/test/test_httpclient.rb +1783 -0
  56. data/test/test_include_client.rb +52 -0
  57. data/test/test_ssl.rb +235 -0
  58. metadata +100 -0
@@ -0,0 +1,88 @@
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
+ response_or_exception = @queue.pop
68
+ if response_or_exception.is_a? Exception
69
+ raise response_or_exception
70
+ end
71
+ response_or_exception
72
+ end
73
+
74
+ def push(result) # :nodoc:
75
+ @queue.push(result)
76
+ end
77
+
78
+ # Waits the completion of the asynchronous invocation.
79
+ def join
80
+ if @async_thread
81
+ @async_thread.join
82
+ end
83
+ nil
84
+ end
85
+ end
86
+
87
+
88
+ end
@@ -0,0 +1,438 @@
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
+ 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
+ end
50
+
51
+ class Cookie
52
+ include CookieUtils
53
+
54
+ attr_accessor :name, :value
55
+ attr_accessor :domain, :path
56
+ attr_accessor :expires ## for Netscape Cookie
57
+ attr_accessor :url
58
+ attr_writer :use, :secure, :http_only, :discard, :domain_orig, :path_orig, :override
59
+
60
+ USE = 1
61
+ SECURE = 2
62
+ DOMAIN = 4
63
+ PATH = 8
64
+ DISCARD = 16
65
+ OVERRIDE = 32
66
+ OVERRIDE_OK = 32
67
+ HTTP_ONLY = 64
68
+
69
+ def initialize
70
+ @name = @value = @domain = @path = nil
71
+ @expires = nil
72
+ @url = nil
73
+ @use = @secure = @http_only = @discard = @domain_orig = @path_orig = @override = nil
74
+ end
75
+
76
+ def discard?
77
+ @discard
78
+ end
79
+
80
+ def use?
81
+ @use
82
+ end
83
+
84
+ def secure?
85
+ @secure
86
+ end
87
+
88
+ def http_only?
89
+ @http_only
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 += HTTP_ONLY if @http_only
109
+ flg += DOMAIN if @domain_orig
110
+ flg += PATH if @path_orig
111
+ flg += DISCARD if @discard
112
+ flg += OVERRIDE if @override
113
+ flg
114
+ end
115
+
116
+ def set_flag(flag)
117
+ flag = flag.to_i
118
+ @use = true if flag & USE > 0
119
+ @secure = true if flag & SECURE > 0
120
+ @http_only = true if flag & HTTP_ONLY > 0
121
+ @domain_orig = true if flag & DOMAIN > 0
122
+ @path_orig = true if flag & PATH > 0
123
+ @discard = true if flag & DISCARD > 0
124
+ @override = true if flag & OVERRIDE > 0
125
+ end
126
+
127
+ def match?(url)
128
+ domainname = url.host
129
+ if (!domainname ||
130
+ !domain_match(domainname, @domain) ||
131
+ (@path && !head_match?(@path, url.path.empty? ? '/' : url.path)) ||
132
+ (@secure && (url.scheme != 'https')) )
133
+ return false
134
+ else
135
+ return true
136
+ end
137
+ end
138
+
139
+ def join_quotedstr(array, sep)
140
+ ret = Array.new
141
+ old_elem = nil
142
+ array.each{|elem|
143
+ if (elem.scan(/"/).length % 2) == 0
144
+ if old_elem
145
+ old_elem << sep << elem
146
+ else
147
+ ret << elem
148
+ old_elem = nil
149
+ end
150
+ else
151
+ if old_elem
152
+ old_elem << sep << elem
153
+ ret << old_elem
154
+ old_elem = nil
155
+ else
156
+ old_elem = elem.dup
157
+ end
158
+ end
159
+ }
160
+ ret
161
+ end
162
+
163
+ def parse(str, url)
164
+ @url = url
165
+ # TODO: should not depend on join_quotedstr. scan with escape like CSV.
166
+ cookie_elem = str.split(/;/)
167
+ cookie_elem = join_quotedstr(cookie_elem, ';')
168
+ cookie_elem -= [""] # del empty elements, a cookie might included ";;"
169
+ first_elem = cookie_elem.shift
170
+ if first_elem !~ /([^=]*)(\=(.*))?/
171
+ return
172
+ ## raise ArgumentError 'invalid cookie value'
173
+ end
174
+ @name = $1.strip
175
+ @value = normalize_cookie_value($3)
176
+ cookie_elem.each{|pair|
177
+ key, value = pair.split(/=/, 2) ## value may nil
178
+ key.strip!
179
+ value = normalize_cookie_value(value)
180
+ case key.downcase
181
+ when 'domain'
182
+ @domain = value
183
+ when 'expires'
184
+ @expires = nil
185
+ begin
186
+ @expires = Time.parse(value).gmtime if value
187
+ rescue ArgumentError
188
+ end
189
+ when 'path'
190
+ @path = value
191
+ when 'secure'
192
+ @secure = true ## value may nil, but must 'true'.
193
+ when 'httponly'
194
+ @http_only = true ## value may nil, but must 'true'.
195
+ else
196
+ ## ignore
197
+ end
198
+ }
199
+ end
200
+
201
+ private
202
+
203
+ def normalize_cookie_value(value)
204
+ if value
205
+ value = value.strip.sub(/\A"(.*)"\z/) { $1 }
206
+ value = nil if value.empty?
207
+ end
208
+ value
209
+ end
210
+ end
211
+
212
+ class CookieManager
213
+ include CookieUtils
214
+
215
+ ### errors
216
+ class Error < StandardError; end
217
+ class ErrorOverrideOK < Error; end
218
+ class SpecialError < Error; end
219
+
220
+ attr_reader :cookies
221
+ attr_accessor :cookies_file
222
+ attr_accessor :accept_domains, :reject_domains
223
+
224
+ def initialize(file=nil)
225
+ @cookies = Array.new
226
+ @cookies.extend(MonitorMixin)
227
+ @cookies_file = file
228
+ @is_saved = true
229
+ @reject_domains = Array.new
230
+ @accept_domains = Array.new
231
+ @netscape_rule = false
232
+ end
233
+
234
+ def cookies=(cookies)
235
+ @cookies = cookies
236
+ @cookies.extend(MonitorMixin)
237
+ end
238
+
239
+ def save_all_cookies(force = nil, save_unused = true, save_discarded = true)
240
+ @cookies.synchronize do
241
+ check_expired_cookies
242
+ if @is_saved and !force
243
+ return
244
+ end
245
+ File.open(@cookies_file, 'w') do |f|
246
+ @cookies.each do |cookie|
247
+ if (cookie.use? or save_unused) and
248
+ (!cookie.discard? or save_discarded)
249
+ f.print(cookie.url.to_s,"\t",
250
+ cookie.name,"\t",
251
+ cookie.value,"\t",
252
+ cookie.expires.to_i,"\t",
253
+ cookie.domain,"\t",
254
+ cookie.path,"\t",
255
+ cookie.flag,"\n")
256
+ end
257
+ end
258
+ end
259
+ end
260
+ @is_saved = true
261
+ end
262
+
263
+ def save_cookies(force = nil)
264
+ save_all_cookies(force, false, false)
265
+ end
266
+
267
+ def check_expired_cookies
268
+ @cookies.reject!{|cookie|
269
+ is_expired = (cookie.expires && (cookie.expires < Time.now.gmtime))
270
+ if is_expired && !cookie.discard?
271
+ @is_saved = false
272
+ end
273
+ is_expired
274
+ }
275
+ end
276
+
277
+ def parse(str, url)
278
+ cookie = WebAgent::Cookie.new
279
+ cookie.parse(str, url)
280
+ add(cookie)
281
+ end
282
+
283
+ def find(url)
284
+ return nil if @cookies.empty?
285
+
286
+ cookie_list = Array.new
287
+ @cookies.each{|cookie|
288
+ is_expired = (cookie.expires && (cookie.expires < Time.now.gmtime))
289
+ if cookie.use? && !is_expired && cookie.match?(url)
290
+ if cookie_list.select{|c1| c1.name == cookie.name}.empty?
291
+ cookie_list << cookie
292
+ end
293
+ end
294
+ }
295
+ return make_cookie_str(cookie_list)
296
+ end
297
+
298
+ def add(given)
299
+ check_domain(given.domain, given.url.host, given.override?)
300
+
301
+ domain = given.domain || given.url.host
302
+ path = given.path || given.url.path.sub(%r|/[^/]*\z|, '')
303
+
304
+ cookie = nil
305
+ @cookies.synchronize do
306
+ check_expired_cookies
307
+ cookie = @cookies.find { |c|
308
+ c.domain == domain && c.path == path && c.name == given.name
309
+ }
310
+ if !cookie
311
+ cookie = WebAgent::Cookie.new
312
+ cookie.use = true
313
+ @cookies << cookie
314
+ end
315
+ end
316
+
317
+ cookie.domain = domain
318
+ cookie.path = path
319
+ cookie.url = given.url
320
+ cookie.name = given.name
321
+ cookie.value = given.value
322
+ cookie.expires = given.expires
323
+ cookie.secure = given.secure?
324
+ cookie.http_only = given.http_only?
325
+ cookie.domain_orig = given.domain
326
+ cookie.path_orig = given.path
327
+
328
+ if cookie.discard? || cookie.expires == nil
329
+ cookie.discard = true
330
+ else
331
+ cookie.discard = false
332
+ @is_saved = false
333
+ end
334
+ end
335
+
336
+ def load_cookies
337
+ return if !File.readable?(@cookies_file)
338
+ @cookies.synchronize do
339
+ @cookies.clear
340
+ File.open(@cookies_file,'r'){|f|
341
+ while line = f.gets
342
+ cookie = WebAgent::Cookie.new
343
+ @cookies << cookie
344
+ col = line.chomp.split(/\t/)
345
+ cookie.url = HTTPClient::Util.urify(col[0])
346
+ cookie.name = col[1]
347
+ cookie.value = col[2]
348
+ if col[3].empty? or col[3] == '0'
349
+ cookie.expires = nil
350
+ else
351
+ cookie.expires = Time.at(col[3].to_i).gmtime
352
+ end
353
+ cookie.domain = col[4]
354
+ cookie.path = col[5]
355
+ cookie.set_flag(col[6])
356
+ end
357
+ }
358
+ end
359
+ end
360
+
361
+ # Who use it?
362
+ def check_cookie_accept_domain(domain)
363
+ unless domain
364
+ return false
365
+ end
366
+ @accept_domains.each{|dom|
367
+ if domain_match(domain, dom)
368
+ return true
369
+ end
370
+ }
371
+ @reject_domains.each{|dom|
372
+ if domain_match(domain, dom)
373
+ return false
374
+ end
375
+ }
376
+ return true
377
+ end
378
+
379
+ private
380
+
381
+ def make_cookie_str(cookie_list)
382
+ if cookie_list.empty?
383
+ return nil
384
+ end
385
+
386
+ ret = ''
387
+ c = cookie_list.shift
388
+ ret += "#{c.name}=#{c.value}"
389
+ cookie_list.each{|cookie|
390
+ ret += "; #{cookie.name}=#{cookie.value}"
391
+ }
392
+ return ret
393
+ end
394
+
395
+ # for conformance to http://wp.netscape.com/newsref/std/cookie_spec.html
396
+ attr_accessor :netscape_rule
397
+ SPECIAL_DOMAIN = [".com",".edu",".gov",".mil",".net",".org",".int"]
398
+
399
+ def check_domain(domain, hostname, override)
400
+ return unless domain
401
+
402
+ # [DRAFT 12] s. 4.2.2 (does not apply in the case that
403
+ # host name is the same as domain attribute for version 0
404
+ # cookie)
405
+ # I think that this rule has almost the same effect as the
406
+ # tail match of [NETSCAPE].
407
+ if domain !~ /^\./ && hostname != domain
408
+ domain = '.'+domain
409
+ end
410
+ # [NETSCAPE] rule
411
+ if @netscape_rule
412
+ n = domain.scan(/\./).length
413
+ if n < 2
414
+ cookie_error(SpecialError.new, override)
415
+ elsif n == 2
416
+ ## [NETSCAPE] rule
417
+ ok = SPECIAL_DOMAIN.select{|sdomain|
418
+ sdomain == domain[-(sdomain.length)..-1]
419
+ }
420
+ if ok.empty?
421
+ cookie_error(SpecialError.new, override)
422
+ end
423
+ end
424
+ end
425
+ # this implementation does not check RFC2109 4.3.2 case 2;
426
+ # the portion of host not in domain does not contain a dot.
427
+ # according to nsCookieService.cpp in Firefox 3.0.4, Firefox 3.0.4
428
+ # and IE does not check, too.
429
+ end
430
+
431
+ # not tested well; used only netscape_rule = true.
432
+ def cookie_error(err, override)
433
+ if !err.kind_of?(ErrorOverrideOK) || !override
434
+ raise err
435
+ end
436
+ end
437
+ end
438
+ end