httpclient-jgraichen 2.3.4.2

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