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,405 @@
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
+ begin
12
+ require 'openssl'
13
+ SSLEnabled = true
14
+ rescue LoadError
15
+ SSLEnabled = false
16
+ end
17
+
18
+ # Represents SSL configuration for HTTPClient instance.
19
+ # The implementation depends on OpenSSL.
20
+ #
21
+ # == Trust Anchor Control
22
+ #
23
+ # SSLConfig loads 'httpclient/cacert.p7s' as a trust anchor
24
+ # (trusted certificate(s)) with add_trust_ca in initialization time.
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.
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.
31
+ #
32
+ # You may want to change trust anchor by yourself. Call clear_cert_store
33
+ # then add_trust_ca for that purpose.
34
+ class SSLConfig
35
+ include OpenSSL if SSLEnabled
36
+
37
+ # String name of OpenSSL's SSL version method name: TLSv1_2, TLSv1_1, TLSv1,
38
+ # SSLv2, SSLv23, SSLv3 or nil to allow version negotiation (default).
39
+ # See {OpenSSL::SSL::SSLContext::METHODS}.
40
+ attr_reader :ssl_version
41
+ # OpenSSL::X509::Certificate:: certificate for SSL client authenticateion.
42
+ # nil by default. (no client authenticateion)
43
+ attr_reader :client_cert
44
+ # OpenSSL::PKey::PKey:: private key for SSL client authentication.
45
+ # nil by default. (no client authenticateion)
46
+ attr_reader :client_key
47
+
48
+ # A number which represents OpenSSL's verify mode. Default value is
49
+ # OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT.
50
+ attr_reader :verify_mode
51
+ # A number of verify depth. Certification path which length is longer than
52
+ # this depth is not allowed.
53
+ attr_reader :verify_depth
54
+ # A callback handler for custom certificate verification. nil by default.
55
+ # If the handler is set, handler.call is invoked just after general
56
+ # OpenSSL's verification. handler.call is invoked with 2 arguments,
57
+ # ok and ctx; ok is a result of general OpenSSL's verification. ctx is a
58
+ # OpenSSL::X509::StoreContext.
59
+ attr_reader :verify_callback
60
+ # SSL timeout in sec. nil by default.
61
+ attr_reader :timeout
62
+ # A number of OpenSSL's SSL options. Default value is
63
+ # OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_SSLv2
64
+ attr_reader :options
65
+ # A String of OpenSSL's cipher configuration. Default value is
66
+ # ALL:!ADH:!LOW:!EXP:!MD5:+SSLv2:@STRENGTH
67
+ # See ciphers(1) man in OpenSSL for more detail.
68
+ attr_reader :ciphers
69
+
70
+ # OpenSSL::X509::X509::Store used for verification. You can reset the
71
+ # store with clear_cert_store and set the new store with cert_store=.
72
+ attr_reader :cert_store # don't use if you don't know what it is.
73
+
74
+ # For server side configuration. Ignore this.
75
+ attr_reader :client_ca # :nodoc:
76
+
77
+ # Creates a SSLConfig.
78
+ def initialize(client)
79
+ return unless SSLEnabled
80
+ @client = client
81
+ @cert_store = X509::Store.new
82
+ @client_cert = @client_key = @client_ca = nil
83
+ @verify_mode = SSL::VERIFY_PEER | SSL::VERIFY_FAIL_IF_NO_PEER_CERT
84
+ @verify_depth = nil
85
+ @verify_callback = nil
86
+ @dest = nil
87
+ @timeout = nil
88
+ @ssl_version = nil
89
+ @options = defined?(SSL::OP_ALL) ? SSL::OP_ALL | SSL::OP_NO_SSLv2 : nil
90
+ # OpenSSL 0.9.8 default: "ALL:!ADH:!LOW:!EXP:!MD5:+SSLv2:@STRENGTH"
91
+ @ciphers = "ALL:!aNULL:!eNULL:!SSLv2" # OpenSSL >1.0.0 default
92
+ @cacerts_loaded = false
93
+ end
94
+
95
+ # Sets SSL version method String. Possible values: "SSLv2" for SSL2,
96
+ # "SSLv3" for SSL3 and TLS1.x, "SSLv23" for SSL3 with fallback to SSL2.
97
+ def ssl_version=(ssl_version)
98
+ @ssl_version = ssl_version
99
+ change_notify
100
+ end
101
+
102
+ # Sets certificate (OpenSSL::X509::Certificate) for SSL client
103
+ # authentication.
104
+ # client_key and client_cert must be a pair.
105
+ #
106
+ # Calling this method resets all existing sessions.
107
+ def client_cert=(client_cert)
108
+ @client_cert = client_cert
109
+ change_notify
110
+ end
111
+
112
+ # Sets private key (OpenSSL::PKey::PKey) for SSL client authentication.
113
+ # client_key and client_cert must be a pair.
114
+ #
115
+ # Calling this method resets all existing sessions.
116
+ def client_key=(client_key)
117
+ @client_key = client_key
118
+ change_notify
119
+ end
120
+
121
+ # Sets certificate and private key for SSL client authentication.
122
+ # cert_file:: must be a filename of PEM/DER formatted file.
123
+ # key_file:: must be a filename of PEM/DER formatted file. Key must be an
124
+ # RSA key. If you want to use other PKey algorithm,
125
+ # use client_key=.
126
+ #
127
+ # Calling this method resets all existing sessions.
128
+ def set_client_cert_file(cert_file, key_file)
129
+ @client_cert = X509::Certificate.new(File.open(cert_file) { |f| f.read })
130
+ @client_key = PKey::RSA.new(File.open(key_file) { |f| f.read })
131
+ change_notify
132
+ end
133
+
134
+ # Sets OpenSSL's default trusted CA certificates. Generally, OpenSSL is
135
+ # configured to use OS's trusted CA certificates located at
136
+ # /etc/pki/certs or /etc/ssl/certs. Unfortunately OpenSSL's Windows build
137
+ # does not work with Windows Certificate Storage.
138
+ #
139
+ # On Windows or when you build OpenSSL manually, you can set the
140
+ # CA certificates directory by SSL_CERT_DIR env variable at runtime.
141
+ #
142
+ # SSL_CERT_DIR=/etc/ssl/certs ruby -rhttpclient -e "..."
143
+ #
144
+ # Calling this method resets all existing sessions.
145
+ def set_default_paths
146
+ @cacerts_loaded = true # avoid lazy override
147
+ @cert_store = X509::Store.new
148
+ @cert_store.set_default_paths
149
+ change_notify
150
+ end
151
+
152
+ # Drops current certificate store (OpenSSL::X509::Store) for SSL and create
153
+ # new one for the next session.
154
+ #
155
+ # Calling this method resets all existing sessions.
156
+ def clear_cert_store
157
+ @cacerts_loaded = true # avoid lazy override
158
+ @cert_store = X509::Store.new
159
+ change_notify
160
+ end
161
+
162
+ # Sets new certificate store (OpenSSL::X509::Store).
163
+ # don't use if you don't know what it is.
164
+ #
165
+ # Calling this method resets all existing sessions.
166
+ def cert_store=(cert_store)
167
+ @cacerts_loaded = true # avoid lazy override
168
+ @cert_store = cert_store
169
+ change_notify
170
+ end
171
+
172
+ # Sets trust anchor certificate(s) for verification.
173
+ # trust_ca_file_or_hashed_dir:: a filename of a PEM/DER formatted
174
+ # OpenSSL::X509::Certificate or
175
+ # a 'c-rehash'eddirectory name which stores
176
+ # trusted certificate files.
177
+ #
178
+ # Calling this method resets all existing sessions.
179
+ def add_trust_ca(trust_ca_file_or_hashed_dir)
180
+ @cacerts_loaded = true # avoid lazy override
181
+ add_trust_ca_to_store(@cert_store, trust_ca_file_or_hashed_dir)
182
+ change_notify
183
+ end
184
+ alias set_trust_ca add_trust_ca
185
+
186
+ def add_trust_ca_to_store(cert_store, trust_ca_file_or_hashed_dir)
187
+ if FileTest.directory?(trust_ca_file_or_hashed_dir)
188
+ cert_store.add_path(trust_ca_file_or_hashed_dir)
189
+ else
190
+ cert_store.add_file(trust_ca_file_or_hashed_dir)
191
+ end
192
+ end
193
+
194
+ # Loads default trust anchors.
195
+ # Calling this method resets all existing sessions.
196
+ def load_trust_ca
197
+ load_cacerts(@cert_store)
198
+ change_notify
199
+ end
200
+
201
+ # Adds CRL for verification.
202
+ # crl:: a OpenSSL::X509::CRL or a filename of a PEM/DER formatted
203
+ # OpenSSL::X509::CRL.
204
+ #
205
+ # Calling this method resets all existing sessions.
206
+ def add_crl(crl)
207
+ unless crl.is_a?(X509::CRL)
208
+ crl = X509::CRL.new(File.open(crl) { |f| f.read })
209
+ end
210
+ @cert_store.add_crl(crl)
211
+ @cert_store.flags = X509::V_FLAG_CRL_CHECK | X509::V_FLAG_CRL_CHECK_ALL
212
+ change_notify
213
+ end
214
+ alias set_crl add_crl
215
+
216
+ # Sets verify mode of OpenSSL. New value must be a combination of
217
+ # constants OpenSSL::SSL::VERIFY_*
218
+ #
219
+ # Calling this method resets all existing sessions.
220
+ def verify_mode=(verify_mode)
221
+ @verify_mode = verify_mode
222
+ change_notify
223
+ end
224
+
225
+ # Sets verify depth. New value must be a number.
226
+ #
227
+ # Calling this method resets all existing sessions.
228
+ def verify_depth=(verify_depth)
229
+ @verify_depth = verify_depth
230
+ change_notify
231
+ end
232
+
233
+ # Sets callback handler for custom certificate verification.
234
+ # See verify_callback.
235
+ #
236
+ # Calling this method resets all existing sessions.
237
+ def verify_callback=(verify_callback)
238
+ @verify_callback = verify_callback
239
+ change_notify
240
+ end
241
+
242
+ # Sets SSL timeout in sec.
243
+ #
244
+ # Calling this method resets all existing sessions.
245
+ def timeout=(timeout)
246
+ @timeout = timeout
247
+ change_notify
248
+ end
249
+
250
+ # Sets SSL options. New value must be a combination of # constants
251
+ # OpenSSL::SSL::OP_*
252
+ #
253
+ # Calling this method resets all existing sessions.
254
+ def options=(options)
255
+ @options = options
256
+ change_notify
257
+ end
258
+
259
+ # Sets cipher configuration. New value must be a String.
260
+ #
261
+ # Calling this method resets all existing sessions.
262
+ def ciphers=(ciphers)
263
+ @ciphers = ciphers
264
+ change_notify
265
+ end
266
+
267
+ def client_ca=(client_ca) # :nodoc:
268
+ @client_ca = client_ca
269
+ change_notify
270
+ end
271
+
272
+ # interfaces for SSLSocketWrap.
273
+ def set_context(ctx) # :nodoc:
274
+ load_trust_ca unless @cacerts_loaded
275
+ @cacerts_loaded = true
276
+ # Verification: Use Store#verify_callback instead of SSLContext#verify*?
277
+ ctx.cert_store = @cert_store
278
+ ctx.verify_mode = @verify_mode
279
+ ctx.verify_depth = @verify_depth if @verify_depth
280
+ ctx.verify_callback = @verify_callback || method(:default_verify_callback)
281
+ # SSL config
282
+ ctx.cert = @client_cert
283
+ ctx.key = @client_key
284
+ ctx.client_ca = @client_ca
285
+ ctx.timeout = @timeout
286
+ ctx.options = @options
287
+ ctx.ciphers = @ciphers
288
+ ctx.ssl_version = @ssl_version if @ssl_version
289
+ end
290
+
291
+ # post connection check proc for ruby < 1.8.5.
292
+ # this definition must match with the one in ext/openssl/lib/openssl/ssl.rb
293
+ def post_connection_check(peer_cert, hostname) # :nodoc:
294
+ check_common_name = true
295
+ cert = peer_cert
296
+ cert.extensions.each{|ext|
297
+ next if ext.oid != "subjectAltName"
298
+ ext.value.split(/,\s+/).each{|general_name|
299
+ if /\ADNS:(.*)/ =~ general_name
300
+ check_common_name = false
301
+ reg = Regexp.escape($1).gsub(/\\\*/, "[^.]+")
302
+ return true if /\A#{reg}\z/i =~ hostname
303
+ elsif /\AIP Address:(.*)/ =~ general_name
304
+ check_common_name = false
305
+ return true if $1 == hostname
306
+ end
307
+ }
308
+ }
309
+ if check_common_name
310
+ cert.subject.to_a.each{|oid, value|
311
+ if oid == "CN"
312
+ reg = Regexp.escape(value).gsub(/\\\*/, "[^.]+")
313
+ return true if /\A#{reg}\z/i =~ hostname
314
+ end
315
+ }
316
+ end
317
+ raise SSL::SSLError, "hostname was not match with the server certificate"
318
+ end
319
+
320
+ # Default callback for verification: only dumps error.
321
+ def default_verify_callback(is_ok, ctx)
322
+ if $DEBUG
323
+ if is_ok
324
+ warn("ok: #{ctx.current_cert.subject.to_s.dump}")
325
+ else
326
+ warn("ng: #{ctx.current_cert.subject.to_s.dump} at depth #{ctx.error_depth} - #{ctx.error}: #{ctx.error_string} in #{ctx.chain.inspect}")
327
+ end
328
+ warn(ctx.current_cert.to_text)
329
+ warn(ctx.current_cert.to_pem)
330
+ end
331
+ if !is_ok
332
+ depth = ctx.error_depth
333
+ code = ctx.error
334
+ msg = ctx.error_string
335
+ warn("at depth #{depth} - #{code}: #{msg}")
336
+ end
337
+ is_ok
338
+ end
339
+
340
+ # Sample callback method: CAUTION: does not check CRL/ARL.
341
+ def sample_verify_callback(is_ok, ctx)
342
+ unless is_ok
343
+ depth = ctx.error_depth
344
+ code = ctx.error
345
+ msg = ctx.error_string
346
+ warn("at depth #{depth} - #{code}: #{msg}") if $DEBUG
347
+ return false
348
+ end
349
+
350
+ cert = ctx.current_cert
351
+ self_signed = false
352
+ ca = false
353
+ pathlen = nil
354
+ server_auth = true
355
+ self_signed = (cert.subject.cmp(cert.issuer) == 0)
356
+
357
+ # Check extensions whatever its criticality is. (sample)
358
+ cert.extensions.each do |ex|
359
+ case ex.oid
360
+ when 'basicConstraints'
361
+ /CA:(TRUE|FALSE), pathlen:(\d+)/ =~ ex.value
362
+ ca = ($1 == 'TRUE')
363
+ pathlen = $2.to_i
364
+ when 'keyUsage'
365
+ usage = ex.value.split(/\s*,\s*/)
366
+ ca = usage.include?('Certificate Sign')
367
+ server_auth = usage.include?('Key Encipherment')
368
+ when 'extendedKeyUsage'
369
+ usage = ex.value.split(/\s*,\s*/)
370
+ server_auth = usage.include?('Netscape Server Gated Crypto')
371
+ when 'nsCertType'
372
+ usage = ex.value.split(/\s*,\s*/)
373
+ ca = usage.include?('SSL CA')
374
+ server_auth = usage.include?('SSL Server')
375
+ end
376
+ end
377
+
378
+ if self_signed
379
+ warn('self signing CA') if $DEBUG
380
+ return true
381
+ elsif ca
382
+ warn('middle level CA') if $DEBUG
383
+ return true
384
+ elsif server_auth
385
+ warn('for server authentication') if $DEBUG
386
+ return true
387
+ end
388
+
389
+ return false
390
+ end
391
+
392
+ private
393
+
394
+ def change_notify
395
+ @client.reset_all
396
+ end
397
+
398
+ def load_cacerts(cert_store)
399
+ file = File.join(File.dirname(__FILE__), 'cacert.p7s')
400
+ add_trust_ca_to_store(cert_store, file)
401
+ end
402
+ end
403
+
404
+
405
+ end
@@ -0,0 +1,140 @@
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
+ require 'timeout'
10
+ require 'thread'
11
+
12
+
13
+ class HTTPClient
14
+
15
+
16
+ # Replaces timeout.rb to avoid Thread creation and scheduling overhead.
17
+ #
18
+ # You should check another timeout replace in WEBrick.
19
+ # See lib/webrick/utils.rb in ruby/1.9.
20
+ #
21
+ # About this implementation:
22
+ # * Do not create Thread for each timeout() call. Just create 1 Thread for
23
+ # timeout scheduler.
24
+ # * Do not wakeup the scheduler thread so often. Let scheduler thread sleep
25
+ # until the nearest period.
26
+ if !defined?(JRUBY_VERSION) and RUBY_VERSION < '1.9'
27
+ class TimeoutScheduler
28
+
29
+ # Represents timeout period.
30
+ class Period
31
+ attr_reader :thread, :time
32
+
33
+ # Creates new Period.
34
+ def initialize(thread, time, ex)
35
+ @thread, @time, @ex = thread, time, ex
36
+ @lock = Mutex.new
37
+ end
38
+
39
+ # Raises if thread exists and alive.
40
+ def raise(message)
41
+ @lock.synchronize do
42
+ if @thread and @thread.alive?
43
+ @thread.raise(@ex, message)
44
+ end
45
+ end
46
+ end
47
+
48
+ # Cancel this Period. Mutex is needed to avoid too-late exception.
49
+ def cancel
50
+ @lock.synchronize do
51
+ @thread = nil
52
+ end
53
+ end
54
+ end
55
+
56
+ # Creates new TimeoutScheduler.
57
+ def initialize
58
+ @pool = {}
59
+ @next = nil
60
+ @thread = start_timer_thread
61
+ end
62
+
63
+ # Registers new timeout period.
64
+ def register(thread, sec, ex)
65
+ period = Period.new(thread, Time.now + sec, ex || ::Timeout::Error)
66
+ @pool[period] = true
67
+ if @next.nil? or period.time < @next
68
+ begin
69
+ @thread.wakeup
70
+ rescue ThreadError
71
+ # Thread may be dead by fork.
72
+ @thread = start_timer_thread
73
+ end
74
+ end
75
+ period
76
+ end
77
+
78
+ # Cancels the given period.
79
+ def cancel(period)
80
+ @pool.delete(period)
81
+ period.cancel
82
+ end
83
+
84
+ private
85
+
86
+ def start_timer_thread
87
+ thread = Thread.new {
88
+ while true
89
+ if @pool.empty?
90
+ @next = nil
91
+ sleep
92
+ else
93
+ min, = @pool.min { |a, b| a[0].time <=> b[0].time }
94
+ @next = min.time
95
+ sec = @next - Time.now
96
+ if sec > 0
97
+ sleep(sec)
98
+ end
99
+ end
100
+ now = Time.now
101
+ @pool.keys.each do |period|
102
+ if period.time < now
103
+ period.raise('execution expired')
104
+ cancel(period)
105
+ end
106
+ end
107
+ end
108
+ }
109
+ Thread.pass while thread.status != 'sleep'
110
+ thread
111
+ end
112
+ end
113
+
114
+ class << self
115
+ # CAUTION: caller must aware of race condition.
116
+ def timeout_scheduler
117
+ @timeout_scheduler ||= TimeoutScheduler.new
118
+ end
119
+ end
120
+ timeout_scheduler # initialize at first time.
121
+ end
122
+
123
+ module Timeout
124
+ if !defined?(JRUBY_VERSION) and RUBY_VERSION < '1.9'
125
+ def timeout(sec, ex = nil, &block)
126
+ return yield if sec == nil or sec.zero?
127
+ scheduler = nil
128
+ begin
129
+ scheduler = HTTPClient.timeout_scheduler
130
+ period = scheduler.register(Thread.current, sec, ex)
131
+ yield(sec)
132
+ ensure
133
+ scheduler.cancel(period) if scheduler and period
134
+ end
135
+ end
136
+ end
137
+ end
138
+
139
+
140
+ end