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