httpclient 2.3.0.1 → 2.8.3

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 (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