httpclient 2.3.0.1 → 2.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +85 -0
  3. data/bin/httpclient +18 -6
  4. data/bin/jsonclient +85 -0
  5. data/lib/http-access2.rb +1 -1
  6. data/lib/httpclient.rb +262 -88
  7. data/lib/httpclient/auth.rb +269 -244
  8. data/lib/httpclient/cacert.pem +3952 -0
  9. data/lib/httpclient/cacert1024.pem +3866 -0
  10. data/lib/httpclient/connection.rb +1 -1
  11. data/lib/httpclient/cookie.rb +161 -514
  12. data/lib/httpclient/http.rb +57 -21
  13. data/lib/httpclient/include_client.rb +2 -0
  14. data/lib/httpclient/jruby_ssl_socket.rb +588 -0
  15. data/lib/httpclient/session.rb +259 -317
  16. data/lib/httpclient/ssl_config.rb +141 -188
  17. data/lib/httpclient/ssl_socket.rb +150 -0
  18. data/lib/httpclient/timeout.rb +1 -1
  19. data/lib/httpclient/util.rb +62 -1
  20. data/lib/httpclient/version.rb +1 -1
  21. data/lib/httpclient/webagent-cookie.rb +459 -0
  22. data/lib/jsonclient.rb +63 -0
  23. data/lib/oauthclient.rb +2 -1
  24. data/sample/jsonclient.rb +67 -0
  25. data/sample/oauth_twitter.rb +4 -4
  26. data/test/{ca-chain.cert → ca-chain.pem} +0 -0
  27. data/test/client-pass.key +18 -0
  28. data/test/helper.rb +10 -8
  29. data/test/jruby_ssl_socket/test_pemutils.rb +32 -0
  30. data/test/test_auth.rb +175 -4
  31. data/test/test_cookie.rb +147 -243
  32. data/test/test_http-access2.rb +17 -16
  33. data/test/test_httpclient.rb +458 -77
  34. data/test/test_jsonclient.rb +80 -0
  35. data/test/test_ssl.rb +341 -17
  36. data/test/test_webagent-cookie.rb +465 -0
  37. metadata +57 -55
  38. data/README.txt +0 -721
  39. data/lib/httpclient/cacert.p7s +0 -1858
  40. data/lib/httpclient/cacert_sha1.p7s +0 -1858
  41. data/sample/oauth_salesforce_10.rb +0 -63
@@ -0,0 +1,63 @@
1
+ require 'httpclient'
2
+ require 'json'
3
+
4
+ # JSONClient auto-converts Hash <-> JSON in request and response.
5
+ # * For POST or PUT request, convert Hash body to JSON String with 'application/json; charset=utf-8' header.
6
+ # * For response, convert JSON String to Hash when content-type is '(application|text)/(x-)?json'
7
+ class JSONClient < HTTPClient
8
+ CONTENT_TYPE_JSON_REGEX = /(application|text)\/(x-)?json/i
9
+ CONTENT_TYPE_JSON = 'application/json; charset=utf-8'
10
+
11
+ attr_reader :content_type_json_request
12
+ attr_reader :content_type_json_response_regex
13
+
14
+ def initialize(*args)
15
+ super
16
+ @content_type_json_request = CONTENT_TYPE_JSON
17
+ @content_type_json_response_regex = CONTENT_TYPE_JSON_REGEX
18
+ end
19
+
20
+ def post(uri, *args, &block)
21
+ request(:post, uri, argument_to_hash_for_json(args), &block)
22
+ end
23
+
24
+ def put(uri, *args, &block)
25
+ request(:put, uri, argument_to_hash_for_json(args), &block)
26
+ end
27
+
28
+ def request(method, uri, *args, &block)
29
+ res = super
30
+ if @content_type_json_response_regex =~ res.content_type
31
+ res = wrap_json_response(res)
32
+ end
33
+ res
34
+ end
35
+
36
+ private
37
+
38
+ def argument_to_hash_for_json(args)
39
+ hash = argument_to_hash(args, :body, :header, :follow_redirect)
40
+ if hash[:body].is_a?(Hash)
41
+ hash[:header] = json_header(hash[:header])
42
+ hash[:body] = JSON.generate(hash[:body])
43
+ end
44
+ hash
45
+ end
46
+
47
+ def json_header(header)
48
+ header ||= {}
49
+ if header.is_a?(Hash)
50
+ header['Content-Type'] = @content_type_json_request
51
+ else
52
+ header << ['Content-Type', @content_type_json_request]
53
+ end
54
+ header
55
+ end
56
+
57
+ def wrap_json_response(original)
58
+ res = ::HTTP::Message.new_response(JSON.parse(original.content))
59
+ res.http_header = original.http_header
60
+ res.previous = original
61
+ res
62
+ end
63
+ end
@@ -1,5 +1,5 @@
1
1
  # HTTPClient - HTTP client library.
2
- # Copyright (C) 2000-2009 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
2
+ # Copyright (C) 2000-2015 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
3
3
  #
4
4
  # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
5
5
  # redistribute it and/or modify it under the same terms of Ruby's license;
@@ -33,6 +33,7 @@ class OAuthClient < HTTPClient
33
33
  @oauth_config = HTTPClient::OAuth::Config.new
34
34
  self.www_auth.oauth.set_config(nil, @oauth_config)
35
35
  self.www_auth.oauth.challenge(nil)
36
+ self.strict_response_size_check = true
36
37
  end
37
38
 
38
39
  # Get request token.
@@ -0,0 +1,67 @@
1
+ require 'httpclient'
2
+ require 'json'
3
+
4
+ module HTTP
5
+ class Message
6
+ # Returns JSON object of message body
7
+ alias original_content content
8
+ def content
9
+ if JSONClient::CONTENT_TYPE_JSON_REGEX =~ content_type
10
+ JSON.parse(original_content)
11
+ else
12
+ original_content
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+
19
+ # JSONClient provides JSON related methods in addition to HTTPClient.
20
+ class JSONClient < HTTPClient
21
+ CONTENT_TYPE_JSON_REGEX = /(application|text)\/(x-)?json/i
22
+
23
+ attr_accessor :content_type_json
24
+
25
+ class JSONRequestHeaderFilter
26
+ attr_accessor :replace
27
+
28
+ def initialize(client)
29
+ @client = client
30
+ @replace = false
31
+ end
32
+
33
+ def filter_request(req)
34
+ req.header['content-type'] = @client.content_type_json if @replace
35
+ end
36
+
37
+ def filter_response(req, res)
38
+ @replace = false
39
+ end
40
+ end
41
+
42
+ def initialize(*args)
43
+ super
44
+ @header_filter = JSONRequestHeaderFilter.new(self)
45
+ @request_filter << @header_filter
46
+ @content_type_json = 'application/json; charset=utf-8'
47
+ end
48
+
49
+ def post(uri, *args, &block)
50
+ @header_filter.replace = true
51
+ request(:post, uri, jsonify(argument_to_hash(args, :body, :header, :follow_redirect)), &block)
52
+ end
53
+
54
+ def put(uri, *args, &block)
55
+ @header_filter.replace = true
56
+ request(:put, uri, jsonify(argument_to_hash(args, :body, :header)), &block)
57
+ end
58
+
59
+ private
60
+
61
+ def jsonify(hash)
62
+ if hash[:body] && hash[:body].is_a?(Hash)
63
+ hash[:body] = JSON.generate(hash[:body])
64
+ end
65
+ hash
66
+ end
67
+ end
@@ -5,9 +5,9 @@ consumer_key = 'EDIT HERE'
5
5
  consumer_secret = 'EDIT HERE'
6
6
 
7
7
  callback = ARGV.shift # can be nil for OAuth 1.0. (not 1.0a)
8
- request_token_url = 'http://twitter.com/oauth/request_token'
9
- oob_authorize_url = 'http://twitter.com/oauth/authorize'
10
- access_token_url = 'http://twitter.com/oauth/access_token'
8
+ request_token_url = 'https://api.twitter.com/oauth/request_token'
9
+ oob_authorize_url = 'https://api.twitter.com/oauth/authorize'
10
+ access_token_url = 'https://api.twitter.com/oauth/access_token'
11
11
 
12
12
  STDOUT.sync = true
13
13
 
@@ -58,4 +58,4 @@ puts "Hit [enter] to go"
58
58
  gets
59
59
 
60
60
  # Access to a protected resource. (DM)
61
- puts client.get("http://twitter.com/direct_messages.json")
61
+ puts client.get("https://api.twitter.com/1.1/direct_messages.json")
File without changes
@@ -0,0 +1,18 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ Proc-Type: 4,ENCRYPTED
3
+ DEK-Info: DES-EDE3-CBC,DE0F454B166A4941
4
+
5
+ Kub+uiaDkZAmUP2P1VKKB1tPcoJ/ZSs5sLckVv156XDfH+6OilEh+E4vXuKkJnW7
6
+ KFVM/nKrKPxLtNmKha0yx2bqZeUfUdpwq1GqTve84v/oJDTOhBXPlKlkMvzzVhdC
7
+ IeJ61BgSt4ZVWSAcorae8yvDtUCtVoc0YonuiEno5bjEOWMuOu9iwviDIO+IePdY
8
+ mgIPkEyPQOY6/Ir3ImLdqmpPfVPnNxx5fIw9VXDfTqWfY3qHnGECx17ko4PCxhkN
9
+ IwnXU8E6r6XRpHV58t7JkM88eD0crpQpZ8Ki1zVPtHq8DfQLwQI+FGt6PBmeneVl
10
+ Dne6UPIaEDpd9f5X+Q7+2jZCBOsGntNh4+E7AwnG+G4IpleUG308DWsXZZpYhfLy
11
+ 12WMzDlsaQ68qgO1a7rD+nOpIgUfIl7bdB242g7gWvXyVzZOGJIg/P3Fl6ydR7Al
12
+ afAQFH2L1YH7u9zJLIonMmVRz7VNUHwlVaPE18VGBbzwFOmZHj2THUUB3cOGfsC8
13
+ FgQz0JVZT5t7fAS53KRXhH/mWEimcrKSvZJxOBwoknQDtHS517wMhyUco63UYEQq
14
+ 2nkW6BD08Qc92xu14hWuWrActTtsJ3wyGSPMYbqo5QRvlnpaEzaQlMRXdBHYbSFJ
15
+ D5Eo2nXXqNPX7YbyIHh+cda80r9OwmH/gvXThQd79pMvNHPZ2TWnrlZF7YAdVxHH
16
+ etLrAVas2AxXs2LdhwFTI6dmxMv92gYz/WwMeZaNV7SJ4JIKHxGCmajv12cnGVh9
17
+ qCxMIFcpISr3EMwEAnF0npfQ6Xp6rKFUXuEml036vE8=
18
+ -----END RSA PRIVATE KEY-----
@@ -1,5 +1,4 @@
1
1
  # -*- encoding: utf-8 -*-
2
- require 'test/unit'
3
2
  begin
4
3
  require 'simplecov'
5
4
  require 'simplecov-rcov'
@@ -7,6 +6,7 @@ begin
7
6
  SimpleCov.start
8
7
  rescue LoadError
9
8
  end
9
+ require 'test/unit'
10
10
 
11
11
  require 'httpclient'
12
12
  require 'webrick'
@@ -59,11 +59,13 @@ module Helper
59
59
  @client = HTTPClient.new
60
60
  end
61
61
 
62
- #def setup_server
63
- # override it
64
- # @server = WEBrick::HTTPServer.new(...)
65
- # @server_thread = start_server_thread(@server)
66
- #end
62
+ def escape_noproxy
63
+ backup = HTTPClient::NO_PROXY_HOSTS.dup
64
+ HTTPClient::NO_PROXY_HOSTS.clear
65
+ yield
66
+ ensure
67
+ HTTPClient::NO_PROXY_HOSTS.replace(backup)
68
+ end
67
69
 
68
70
  def setup_proxyserver
69
71
  @proxyserver = WEBrick::HTTPProxyServer.new(
@@ -98,8 +100,8 @@ module Helper
98
100
  while server.status != :Running
99
101
  Thread.pass
100
102
  unless t.alive?
101
- t.join
102
- raise
103
+ t.join
104
+ raise
103
105
  end
104
106
  end
105
107
  t
@@ -0,0 +1,32 @@
1
+ require File.expand_path('helper', File.join(File.dirname(__FILE__), ".."))
2
+
3
+
4
+ class PEMUtilsTest < Test::Unit::TestCase
5
+ include Helper
6
+
7
+ def setup
8
+ @raw_cert = "-----BEGIN CERTIFICATE-----\nMIIDOTCCAiGgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBCMRMwEQYKCZImiZPyLGQB\nGRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVieS1sYW5nMRAwDgYDVQQDDAdSdWJ5\nIENBMB4XDTE2MDgxMDE3MjEzNFoXDTE3MDgxMDE3MjEzNFowSzETMBEGCgmSJomT\n8ixkARkWA29yZzEZMBcGCgmSJomT8ixkARkWCXJ1YnktbGFuZzEZMBcGA1UEAwwQ\nUnVieSBjZXJ0aWZpY2F0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\nAJCfsSXpSMpmZCVa+ZCM+QDgomnhDlvnrGDq6pasTaIspGTXgws+7r8Dt/cNe6EH\nHJpRH2cGRiO4yPcfcT9eS4X7k8OC4f33wHfACOmLu6LeoNE8ujmSk6L6WzLUI+sE\nnLZbFrXxoAo4XHsm8vEG9C+jEoXZ1p+47wrAGaDwDQTnzlMy4dT9pRQEJP2G/Rry\nUkuZn8SUWmh3/YS78iaSzsNF1cgE1ealHOrPPFDjiCGDaH/LHyUPYlbFSLZ/B7Qx\nLxi5sePLcywWq/EJrmWpgeVTDjtNijsdKv/A3qkY+fm/oD0pzt7XsfJaP9YKNyJO\nQFdxWZeiPcDF+Hwf+IwSr+kCAwEAAaMxMC8wDgYDVR0PAQH/BAQDAgeAMB0GA1Ud\nDgQWBBQNvzYzJyXemGhxbA8NMXLolDnPyjANBgkqhkiG9w0BAQsFAAOCAQEARIJV\noKejGlOTn71QutnNnu07UtTu0IHs6YqjYzzND+m4JXLN+wvYm72AFUG0b1L7dRg0\niK8XjQrlNQNVqP1Mc6tffchy20neOPOHeiO6qTdRU8P2S8D3Uwe+1qhgxjfE+cWc\nwZmWxYK4HA8c58PxWMqrkr2QqXDplG9KWLvOgrtPGiLLZcQSKhvvB63QzItHBDU6\nRayiJY3oPkK/HrIvFlySqFqzWmuyknkciOFywEHQMz/tcSFJ2QFpPj/tBz9VXohH\nZ8KscmfhZrTPBjo+ky1lz/WraWoz4LMiLnkC2ABczWLRSawu+v3Irx1NFJngt05e\npqwtqIUeg7j+JLiTaA==\n-----END CERTIFICATE-----"
9
+ end
10
+
11
+ def test_read_certificate
12
+ assert_nothing_raised do
13
+ binary = HTTPClient::JRubySSLSocket::PEMUtils.read_certificate(@raw_cert)
14
+ end
15
+ end
16
+
17
+ def test_read_certificate_works_with_random_ascii_text_outside_begin_end
18
+ raw_cert_with_ascii = "some text before begin\n" + @raw_cert + "\nsome text after end"
19
+ assert_nothing_raised do
20
+ binary = HTTPClient::JRubySSLSocket::PEMUtils.read_certificate(raw_cert_with_ascii)
21
+ end
22
+ end
23
+
24
+ def test_read_certificate_uses_all_content_if_missing_begin_end
25
+ cert = @raw_cert.sub(/-----BEGIN CERTIFICATE-----/, '').sub(/-----END CERTIFICATE-----/, '')
26
+ assert_nothing_raised do
27
+ binary = HTTPClient::JRubySSLSocket::PEMUtils.read_certificate(cert)
28
+ end
29
+ end
30
+
31
+
32
+ end
@@ -1,5 +1,8 @@
1
1
  require File.expand_path('helper', File.dirname(__FILE__))
2
2
  require 'digest/md5'
3
+ require 'rack'
4
+ require 'rack/lint'
5
+ require 'rack-ntlm'
3
6
 
4
7
  class TestAuth < Test::Unit::TestCase
5
8
  include Helper
@@ -34,6 +37,22 @@ class TestAuth < Test::Unit::TestCase
34
37
  '/digest_sess_auth',
35
38
  WEBrick::HTTPServlet::ProcHandler.new(method(:do_digest_sess_auth).to_proc)
36
39
  )
40
+ # NTLM endpoint
41
+ ntlm_handler = Rack::Handler::WEBrick.new(@server,
42
+ Rack::Builder.app do
43
+ use Rack::ShowExceptions
44
+ use Rack::ContentLength
45
+ use Rack::Ntlm, {:uri_pattern => /.*/, :auth => {:username => "admin", :password => "admin"}}
46
+ run lambda { |env| [200, { 'Content-Type' => 'text/html' }, ['ntlm_auth OK']] }
47
+ end
48
+ )
49
+ @server.mount(
50
+ '/ntlm_auth',
51
+ WEBrick::HTTPServlet::ProcHandler.new(Proc.new do |req, res|
52
+ ntlm_handler.service(req, res)
53
+ end)
54
+ )
55
+ # Htpasswd
37
56
  htpasswd = File.join(File.dirname(__FILE__), 'htpasswd')
38
57
  htpasswd_userdb = WEBrick::HTTPAuth::Htpasswd.new(htpasswd)
39
58
  htdigest = File.join(File.dirname(__FILE__), 'htdigest')
@@ -95,6 +114,28 @@ class TestAuth < Test::Unit::TestCase
95
114
  res.body = 'digest_sess_auth OK' + req.query_string.to_s
96
115
  end
97
116
 
117
+ # TODO: monkey patching for rack-ntlm-test-services's incompat.
118
+ module ::Net
119
+ module NTLM
120
+ # ruby-ntlm 0.3.0 -> 0.4.0
121
+ def self.decode_utf16le(*arg)
122
+ EncodeUtil.decode_utf16le(*arg)
123
+ end
124
+ # Make it work if @value == nil
125
+ class SecurityBuffer < FieldSet
126
+ remove_method(:data_size) if method_defined?(:data_size)
127
+ def data_size
128
+ @active && @value ? @value.size : 0
129
+ end
130
+ end
131
+ end
132
+ end
133
+ def test_ntlm_auth
134
+ c = HTTPClient.new
135
+ c.set_auth("http://localhost:#{serverport}/ntlm_auth", 'admin', 'admin')
136
+ assert_equal('ntlm_auth OK', c.get_content("http://localhost:#{serverport}/ntlm_auth"))
137
+ end
138
+
98
139
  def test_basic_auth
99
140
  c = HTTPClient.new
100
141
  c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
@@ -110,15 +151,96 @@ class TestAuth < Test::Unit::TestCase
110
151
  def test_BASIC_auth
111
152
  c = HTTPClient.new
112
153
  webrick_backup = @basic_auth.instance_eval { @auth_scheme }
113
- #httpaccess2_backup = c.www_auth.basic_auth.instance_eval { @scheme }
114
154
  begin
155
+ # WEBrick in ruby 1.8.7 uses 'BASIC' instead of 'Basic'
115
156
  @basic_auth.instance_eval { @auth_scheme = "BASIC" }
116
157
  c.www_auth.basic_auth.instance_eval { @scheme = "BASIC" }
158
+ #
117
159
  c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
160
+ res = c.get("http://localhost:#{serverport}/basic_auth")
161
+ assert_equal('basic_auth OK', res.content)
162
+ assert_equal(200, res.status)
163
+ assert_equal(401, res.previous.status)
164
+ assert_equal(nil, res.previous.previous)
165
+ ensure
166
+ @basic_auth.instance_eval { @auth_scheme = webrick_backup }
167
+ end
168
+ end
169
+
170
+ def test_BASIC_auth_force
171
+ c = HTTPClient.new
172
+ webrick_backup = @basic_auth.instance_eval { @auth_scheme }
173
+ begin
174
+ # WEBrick in ruby 1.8.7 uses 'BASIC' instead of 'Basic'
175
+ @basic_auth.instance_eval { @auth_scheme = "BASIC" }
176
+ c.www_auth.basic_auth.instance_eval { @scheme = "BASIC" }
177
+ #
178
+ c.force_basic_auth = true
179
+ c.debug_dev = str = ''
180
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
181
+ assert_equal('basic_auth OK', c.get_content("http://localhost:#{serverport}/basic_auth"))
182
+ assert_equal('Authorization: Basic YWRtaW46YWRtaW4='.upcase, str.split(/\r?\n/)[5].upcase)
183
+ ensure
184
+ @basic_auth.instance_eval { @auth_scheme = webrick_backup }
185
+ end
186
+ end
187
+
188
+ def test_BASIC_auth_async
189
+ # async methods don't issure retry call so for successful authentication you need to set force_basic_auth flag
190
+ c = HTTPClient.new(:force_basic_auth => true)
191
+ webrick_backup = @basic_auth.instance_eval { @auth_scheme }
192
+ begin
193
+ # WEBrick in ruby 1.8.7 uses 'BASIC' instead of 'Basic'
194
+ @basic_auth.instance_eval { @auth_scheme = "BASIC" }
195
+ c.www_auth.basic_auth.instance_eval { @scheme = "BASIC" }
196
+ #
197
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
198
+ conn = c.get_async("http://localhost:#{serverport}/basic_auth")
199
+ assert_equal('basic_auth OK', conn.pop.body.read)
200
+ ensure
201
+ @basic_auth.instance_eval { @auth_scheme = webrick_backup }
202
+ end
203
+ end
204
+
205
+ def test_BASIC_auth_nil_uri
206
+ c = HTTPClient.new
207
+ webrick_backup = @basic_auth.instance_eval { @auth_scheme }
208
+ begin
209
+ @basic_auth.instance_eval { @auth_scheme = "BASIC" }
210
+ c.www_auth.basic_auth.instance_eval { @scheme = "BASIC" }
211
+ c.set_auth(nil, 'admin', 'admin')
118
212
  assert_equal('basic_auth OK', c.get_content("http://localhost:#{serverport}/basic_auth"))
119
213
  ensure
120
214
  @basic_auth.instance_eval { @auth_scheme = webrick_backup }
121
- #c.www_auth.basic_auth.instance_eval { @scheme = httpaccess2_backup }
215
+ end
216
+ end
217
+
218
+ # To work this test consistently on CRuby you can to add 'Thread.pass' in
219
+ # @challenge iteration at BasicAuth#get like;
220
+ #
221
+ # return nil unless @challenge.find { |uri, ok|
222
+ # Thread.pass
223
+ # Util.uri_part_of(target_uri, uri) and ok
224
+ # }
225
+ def test_BASIC_auth_multi_thread
226
+ c = HTTPClient.new
227
+ webrick_backup = @basic_auth.instance_eval { @auth_scheme }
228
+ begin
229
+ @basic_auth.instance_eval { @auth_scheme = "BASIC" }
230
+ c.www_auth.basic_auth.instance_eval { @scheme = "BASIC" }
231
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
232
+
233
+ 100.times.map { |idx|
234
+ Thread.new(idx) { |idx2|
235
+ Thread.abort_on_exception = true
236
+ Thread.pass
237
+ c.get("http://localhost:#{serverport}/basic_auth?#{idx2}")
238
+ }
239
+ }.map { |t|
240
+ t.join
241
+ }
242
+ ensure
243
+ @basic_auth.instance_eval { @auth_scheme = webrick_backup }
122
244
  end
123
245
  end
124
246
 
@@ -129,7 +251,7 @@ class TestAuth < Test::Unit::TestCase
129
251
  c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
130
252
  c.debug_dev = str = ''
131
253
  c.get_content("http://localhost:#{serverport}/basic_auth/sub/dir/")
132
- assert_match /Authorization: Basic YWRtaW46YWRtaW4=/, str
254
+ assert_match(/Authorization: Basic YWRtaW46YWRtaW4=/, str)
133
255
  end
134
256
 
135
257
  def test_digest_auth
@@ -145,7 +267,7 @@ class TestAuth < Test::Unit::TestCase
145
267
  c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
146
268
  c.debug_dev = str = ''
147
269
  c.get_content("http://localhost:#{serverport}/digest_auth/sub/dir/")
148
- assert_match /Authorization: Digest/, str
270
+ assert_match(/Authorization: Digest/, str)
149
271
  end
150
272
 
151
273
  def test_digest_auth_with_block
@@ -210,6 +332,16 @@ class TestAuth < Test::Unit::TestCase
210
332
  assert_match(/Proxy-Authorization: Basic YWRtaW46YWRtaW4=/, str)
211
333
  end
212
334
 
335
+ def test_proxy_auth_force
336
+ c = HTTPClient.new
337
+ c.set_proxy_auth('admin', 'admin')
338
+ c.force_basic_auth = true
339
+ c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK"
340
+ c.debug_dev = str = ''
341
+ c.get_content('http://example.com/')
342
+ assert_match(/Proxy-Authorization: Basic YWRtaW46YWRtaW4=/, str)
343
+ end
344
+
213
345
  def test_proxy_auth_reuses_credentials
214
346
  c = HTTPClient.new
215
347
  c.set_proxy_auth('admin', 'admin')
@@ -318,4 +450,43 @@ class TestAuth < Test::Unit::TestCase
318
450
  assert(str.index(%q(POST /photos)))
319
451
  assert(str.index(%q(Authorization: OAuth realm="http://photos.example.net/", oauth_consumer_key="dpf43f3p2l4k3l03", oauth_nonce="kllo9940pd9333jh", oauth_signature="wPkvxykrw%2BBTdCcGqKr%2B3I%2BPsiM%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1191242096", oauth_token="nnch734d00sl2jdk", oauth_version="1.0")))
320
452
  end
453
+
454
+ def test_basic_auth_post_with_multipart
455
+ retry_times = 0
456
+ begin
457
+ c = HTTPClient.new
458
+ c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin')
459
+ File.open(__FILE__) do |f|
460
+ # read 'f' twice for authorization negotiation
461
+ assert_equal('basic_auth OK', c.post("http://localhost:#{serverport}/basic_auth", :file => f).content)
462
+ end
463
+ rescue Errno::ECONNRESET, HTTPClient::KeepAliveDisconnected
464
+ # TODO: WEBrick server returns ECONNRESET/EPIPE before sending Unauthorized response to client?
465
+ raise if retry_times > 5
466
+ retry_times += 1
467
+ sleep 1
468
+ retry
469
+ end
470
+ end
471
+
472
+ def test_negotiate_and_basic
473
+ c = HTTPClient.new
474
+ c.test_loopback_http_response << %Q(HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: NTLM\r\nWWW-Authenticate: Basic realm="foo"\r\nConnection: Keep-Alive\r\nContent-Length: 0\r\n\r\n)
475
+ c.test_loopback_http_response << %Q(HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: NTLM TlRMTVNTUAACAAAAAAAAACgAAAABAAAAAAAAAAAAAAA=\r\nConnection: Keep-Alive\r\nContent-Length: 0\r\n\r\n)
476
+ c.test_loopback_http_response << %Q(HTTP/1.0 200 OK\r\nConnection: Keep-Alive\r\nContent-Length: 1\r\n\r\na)
477
+ c.test_loopback_http_response << %Q(HTTP/1.0 200 OK\r\nConnection: Keep-Alive\r\nContent-Length: 1\r\n\r\nb)
478
+ c.debug_dev = str = ''
479
+ c.set_auth('http://www.example.org/', 'admin', 'admin')
480
+ # Do NTLM negotiation
481
+ c.get('http://www.example.org/foo')
482
+ # BasicAuth authenticator should not respond to it because NTLM
483
+ # negotiation has been finished.
484
+ assert_match(%r(Authorization: NTLM), str)
485
+ assert_not_match(%r(Authorization: Basic), str)
486
+ # ditto for other resource that is protected with NTLM
487
+ c.debug_dev = str = ''
488
+ c.get('http://www.example.org/foo/subdir')
489
+ assert_not_match(%r(Authorization: NTLM), str)
490
+ assert_not_match(%r(Authorization: Basic), str)
491
+ end
321
492
  end