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.
- checksums.yaml +7 -0
- data/README.md +85 -0
- data/bin/httpclient +18 -6
- data/bin/jsonclient +85 -0
- data/lib/http-access2.rb +1 -1
- data/lib/httpclient.rb +262 -88
- data/lib/httpclient/auth.rb +269 -244
- data/lib/httpclient/cacert.pem +3952 -0
- data/lib/httpclient/cacert1024.pem +3866 -0
- data/lib/httpclient/connection.rb +1 -1
- data/lib/httpclient/cookie.rb +161 -514
- data/lib/httpclient/http.rb +57 -21
- data/lib/httpclient/include_client.rb +2 -0
- data/lib/httpclient/jruby_ssl_socket.rb +588 -0
- data/lib/httpclient/session.rb +259 -317
- data/lib/httpclient/ssl_config.rb +141 -188
- data/lib/httpclient/ssl_socket.rb +150 -0
- data/lib/httpclient/timeout.rb +1 -1
- data/lib/httpclient/util.rb +62 -1
- data/lib/httpclient/version.rb +1 -1
- data/lib/httpclient/webagent-cookie.rb +459 -0
- data/lib/jsonclient.rb +63 -0
- data/lib/oauthclient.rb +2 -1
- data/sample/jsonclient.rb +67 -0
- data/sample/oauth_twitter.rb +4 -4
- data/test/{ca-chain.cert → ca-chain.pem} +0 -0
- data/test/client-pass.key +18 -0
- data/test/helper.rb +10 -8
- data/test/jruby_ssl_socket/test_pemutils.rb +32 -0
- data/test/test_auth.rb +175 -4
- data/test/test_cookie.rb +147 -243
- data/test/test_http-access2.rb +17 -16
- data/test/test_httpclient.rb +458 -77
- data/test/test_jsonclient.rb +80 -0
- data/test/test_ssl.rb +341 -17
- data/test/test_webagent-cookie.rb +465 -0
- metadata +57 -55
- data/README.txt +0 -721
- data/lib/httpclient/cacert.p7s +0 -1858
- data/lib/httpclient/cacert_sha1.p7s +0 -1858
- data/sample/oauth_salesforce_10.rb +0 -63
data/lib/jsonclient.rb
ADDED
@@ -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
|
data/lib/oauthclient.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# HTTPClient - HTTP client library.
|
2
|
-
# Copyright (C) 2000-
|
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
|
data/sample/oauth_twitter.rb
CHANGED
@@ -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 = '
|
9
|
-
oob_authorize_url = '
|
10
|
-
access_token_url
|
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("
|
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-----
|
data/test/helper.rb
CHANGED
@@ -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
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
-
|
102
|
-
|
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
|
data/test/test_auth.rb
CHANGED
@@ -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
|
-
|
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
|
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
|
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
|