tilia-http 4.1.0
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/.gitignore +19 -0
- data/.rspec +1 -0
- data/.rubocop.yml +35 -0
- data/.simplecov +4 -0
- data/.travis.yml +3 -0
- data/CHANGELOG.sabre.md +235 -0
- data/CONTRIBUTING.md +25 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +69 -0
- data/LICENSE +27 -0
- data/LICENSE.sabre +27 -0
- data/README.md +68 -0
- data/Rakefile +17 -0
- data/examples/asyncclient.rb +45 -0
- data/examples/basicauth.rb +39 -0
- data/examples/client.rb +20 -0
- data/examples/reverseproxy.rb +39 -0
- data/examples/stringify.rb +37 -0
- data/lib/tilia/http/auth/abstract_auth.rb +51 -0
- data/lib/tilia/http/auth/aws.rb +191 -0
- data/lib/tilia/http/auth/basic.rb +43 -0
- data/lib/tilia/http/auth/bearer.rb +37 -0
- data/lib/tilia/http/auth/digest.rb +187 -0
- data/lib/tilia/http/auth.rb +12 -0
- data/lib/tilia/http/client.rb +452 -0
- data/lib/tilia/http/client_exception.rb +15 -0
- data/lib/tilia/http/client_http_exception.rb +37 -0
- data/lib/tilia/http/http_exception.rb +21 -0
- data/lib/tilia/http/message.rb +241 -0
- data/lib/tilia/http/message_decorator_trait.rb +183 -0
- data/lib/tilia/http/message_interface.rb +154 -0
- data/lib/tilia/http/request.rb +235 -0
- data/lib/tilia/http/request_decorator.rb +160 -0
- data/lib/tilia/http/request_interface.rb +126 -0
- data/lib/tilia/http/response.rb +164 -0
- data/lib/tilia/http/response_decorator.rb +58 -0
- data/lib/tilia/http/response_interface.rb +36 -0
- data/lib/tilia/http/sapi.rb +165 -0
- data/lib/tilia/http/url_util.rb +70 -0
- data/lib/tilia/http/util.rb +51 -0
- data/lib/tilia/http/version.rb +9 -0
- data/lib/tilia/http.rb +416 -0
- data/test/http/auth/aws_test.rb +189 -0
- data/test/http/auth/basic_test.rb +60 -0
- data/test/http/auth/bearer_test.rb +47 -0
- data/test/http/auth/digest_test.rb +141 -0
- data/test/http/client_mock.rb +101 -0
- data/test/http/client_test.rb +331 -0
- data/test/http/message_decorator_test.rb +67 -0
- data/test/http/message_test.rb +163 -0
- data/test/http/request_decorator_test.rb +87 -0
- data/test/http/request_test.rb +132 -0
- data/test/http/response_decorator_test.rb +28 -0
- data/test/http/response_test.rb +38 -0
- data/test/http/sapi_mock.rb +12 -0
- data/test/http/sapi_test.rb +133 -0
- data/test/http/url_util_test.rb +155 -0
- data/test/http/util_test.rb +186 -0
- data/test/http_test.rb +102 -0
- data/test/test_helper.rb +6 -0
- data/tilia-http.gemspec +18 -0
- metadata +192 -0
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'digest'
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
module Tilia
|
5
|
+
module Http
|
6
|
+
class DigestTest < Minitest::Test
|
7
|
+
REALM = 'SabreDAV unittest'
|
8
|
+
|
9
|
+
def setup
|
10
|
+
@response = Response.new
|
11
|
+
@request = Request.new
|
12
|
+
@auth = Auth::Digest.new(REALM, @request, @response)
|
13
|
+
end
|
14
|
+
|
15
|
+
def uniqid
|
16
|
+
Digest::SHA1.hexdigest((Time.now.to_f + rand).to_s)[0..14]
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_server_tokens(qop = Auth::Digest::QOP_AUTH)
|
20
|
+
@auth.require_login
|
21
|
+
|
22
|
+
case qop
|
23
|
+
when Auth::Digest::QOP_AUTH
|
24
|
+
qopstr = 'auth'
|
25
|
+
when Auth::Digest::QOP_AUTHINT
|
26
|
+
qopstr = 'auth-int'
|
27
|
+
else
|
28
|
+
qopstr = 'auth,auth-int'
|
29
|
+
end
|
30
|
+
|
31
|
+
test_result = @response.header('WWW-Authenticate') =~ /Digest realm="#{REALM}",qop="#{qopstr}",nonce="([0-9a-f]*)",opaque="([0-9a-f]*)"/
|
32
|
+
|
33
|
+
assert(test_result, "The WWW-Authenticate response didn't match our pattern. We received: #{@response.header('WWW-Authenticate')}")
|
34
|
+
|
35
|
+
nonce = Regexp.last_match[1]
|
36
|
+
opaque = Regexp.last_match[2]
|
37
|
+
|
38
|
+
# Reset our environment
|
39
|
+
setup
|
40
|
+
@auth.qop = qop
|
41
|
+
|
42
|
+
[nonce, opaque]
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_digest
|
46
|
+
(nonce, opaque) = get_server_tokens
|
47
|
+
|
48
|
+
username = 'admin'
|
49
|
+
password = '12345'
|
50
|
+
nc = '00002'
|
51
|
+
cnonce = uniqid
|
52
|
+
|
53
|
+
digest_hash = Digest::MD5.hexdigest(
|
54
|
+
"#{Digest::MD5.hexdigest("#{username}:#{REALM}:#{password}")}:#{nonce}:#{nc}:#{cnonce}:auth:#{Digest::MD5.hexdigest('GET:/')}"
|
55
|
+
)
|
56
|
+
|
57
|
+
@request.method = 'GET'
|
58
|
+
@request.update_header('Authorization', "Digest username=\"#{username}\", realm=\"#{REALM}\", nonce=\"#{nonce}\", uri=\"/\", response=\"#{digest_hash}\", opaque=\"#{opaque}\", qop=auth,nc=#{nc},cnonce=\"#{cnonce}\"")
|
59
|
+
|
60
|
+
@auth.init
|
61
|
+
|
62
|
+
assert_equal(username, @auth.username)
|
63
|
+
assert_equal(REALM, @auth.realm)
|
64
|
+
assert(@auth.validate_a1(Digest::MD5.hexdigest("#{username}:#{REALM}:#{password}")), 'Authentication is deemed invalid through validateA1')
|
65
|
+
assert(@auth.validate_password(password), 'Authentication is deemed invalid through validatepassword')
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_invalid_digest
|
69
|
+
(nonce, opaque) = get_server_tokens
|
70
|
+
|
71
|
+
username = 'admin'
|
72
|
+
password = '12345'
|
73
|
+
nc = '00002'
|
74
|
+
cnonce = uniqid
|
75
|
+
|
76
|
+
digest_hash = Digest::MD5.hexdigest(
|
77
|
+
"#{Digest::MD5.hexdigest("#{username}:#{REALM}:#{password}")}:#{nonce}:#{nc}:#{cnonce}:auth:#{Digest::MD5.hexdigest('GET:/')}"
|
78
|
+
)
|
79
|
+
|
80
|
+
@request.method = 'GET'
|
81
|
+
@request.update_header('Authorization', "Digest username=\"#{username}\", realm=\"#{REALM}\", nonce=\"#{nonce}\", uri=\"/\", response=\"#{digest_hash}\", opaque=\"#{opaque}\", qop=auth,nc=#{nc},cnonce=\"#{cnonce}\"")
|
82
|
+
|
83
|
+
@auth.init
|
84
|
+
|
85
|
+
refute(@auth.validate_a1(Digest::MD5.hexdigest("#{username}:#{REALM}:#{password}randomness")), 'Authentication is deemed invalid through validateA1')
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_invalid_digest2
|
89
|
+
@request.method = 'GET'
|
90
|
+
@request.update_header('Authorization', 'basic blablabla')
|
91
|
+
|
92
|
+
@auth.init
|
93
|
+
refute(@auth.validate_a1(Digest::MD5.hexdigest('user:realm:password')))
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_digest_auth_int
|
97
|
+
@auth.qop = Auth::Digest::QOP_AUTHINT
|
98
|
+
(nonce, opaque) = get_server_tokens(Auth::Digest::QOP_AUTHINT)
|
99
|
+
|
100
|
+
username = 'admin'
|
101
|
+
password = '12345'
|
102
|
+
nc = '00003'
|
103
|
+
cnonce = uniqid
|
104
|
+
|
105
|
+
digest_hash = Digest::MD5.hexdigest(
|
106
|
+
"#{Digest::MD5.hexdigest("#{username}:#{REALM}:#{password}")}:#{nonce}:#{nc}:#{cnonce}:auth-int:#{Digest::MD5.hexdigest('POST:/:' + Digest::MD5.hexdigest('body'))}"
|
107
|
+
)
|
108
|
+
|
109
|
+
@request.method = 'POST'
|
110
|
+
@request.update_header('Authorization', "Digest username=\"#{username}\", realm=\"#{REALM}\", nonce=\"#{nonce}\", uri=\"/\", response=\"#{digest_hash}\", opaque=\"#{opaque}\", qop=auth-int,nc=#{nc},cnonce=\"#{cnonce}\"")
|
111
|
+
@request.body = 'body'
|
112
|
+
|
113
|
+
@auth.init
|
114
|
+
|
115
|
+
assert(@auth.validate_a1(Digest::MD5.hexdigest("#{username}:#{REALM}:#{password}")), 'Authentication is deemed invalid through validateA1')
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_digest_auth_both
|
119
|
+
@auth.qop = Auth::Digest::QOP_AUTHINT | Auth::Digest::QOP_AUTH
|
120
|
+
(nonce, opaque) = get_server_tokens(Auth::Digest::QOP_AUTHINT | Auth::Digest::QOP_AUTH)
|
121
|
+
|
122
|
+
username = 'admin'
|
123
|
+
password = '12345'
|
124
|
+
nc = '00003'
|
125
|
+
cnonce = uniqid
|
126
|
+
|
127
|
+
digest_hash = Digest::MD5.hexdigest(
|
128
|
+
"#{Digest::MD5.hexdigest("#{username}:#{REALM}:#{password}")}:#{nonce}:#{nc}:#{cnonce}:auth-int:#{Digest::MD5.hexdigest('POST:/:' + Digest::MD5.hexdigest('body'))}"
|
129
|
+
)
|
130
|
+
|
131
|
+
@request.method = 'POST'
|
132
|
+
@request.update_header('Authorization', "Digest username=\"#{username}\", realm=\"#{REALM}\", nonce=\"#{nonce}\", uri=\"/\", response=\"#{digest_hash}\", opaque=\"#{opaque}\", qop=auth-int,nc=#{nc},cnonce=\"#{cnonce}\"")
|
133
|
+
@request.body = 'body'
|
134
|
+
|
135
|
+
@auth.init
|
136
|
+
|
137
|
+
assert(@auth.validate_a1(Digest::MD5.hexdigest("#{username}:#{REALM}:#{password}")), 'Authentication is deemed invalid through validateA1')
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# TODO: document
|
2
|
+
module Tilia
|
3
|
+
module Http
|
4
|
+
class ClientMock < Client
|
5
|
+
# RUBY: attr_accessor :persisted_settings
|
6
|
+
|
7
|
+
# Making this method public.
|
8
|
+
#
|
9
|
+
# We are also going to persist all settings this method generates. While
|
10
|
+
# the underlying object doesn't behave exactly the same, it helps us
|
11
|
+
# simulate what curl does internally, and helps us identify problems with
|
12
|
+
# settings that are set by _some_ methods and not correctly reset by other
|
13
|
+
# methods after subsequent use.
|
14
|
+
# forces
|
15
|
+
def create_curl_settings_array(request)
|
16
|
+
settings = super(request)
|
17
|
+
settings = @persisted_settings.merge(settings)
|
18
|
+
@persisted_settings = settings
|
19
|
+
settings
|
20
|
+
end
|
21
|
+
|
22
|
+
# Making this method public.
|
23
|
+
def parse_curl_result(curl_handle)
|
24
|
+
super(curl_handle)
|
25
|
+
end
|
26
|
+
|
27
|
+
# This method is responsible for performing a single request.
|
28
|
+
#
|
29
|
+
# @param RequestInterface request
|
30
|
+
# @return [ResponseInterface]
|
31
|
+
def do_request(request)
|
32
|
+
response = nil
|
33
|
+
|
34
|
+
do_request_arguments = DoRequestArguments.new(request, response)
|
35
|
+
emit('doRequest', [do_request_arguments])
|
36
|
+
(request, response) = do_request_arguments.to_a
|
37
|
+
|
38
|
+
# If nothing modified response, we're using the default behavior.
|
39
|
+
if response.nil?
|
40
|
+
super(request)
|
41
|
+
else
|
42
|
+
response
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
# Returns a bunch of information about a curl request.
|
49
|
+
#
|
50
|
+
# This method exists so it can easily be overridden and mocked.
|
51
|
+
#
|
52
|
+
# @param resource curl_handle
|
53
|
+
# @return array
|
54
|
+
def curl_stuff(curl_handle)
|
55
|
+
to_return = nil
|
56
|
+
curl_stuff_arguments = CurlStuffArguments.new(to_return)
|
57
|
+
emit('curlStuff', [curl_stuff_arguments])
|
58
|
+
(to_return,) = curl_stuff_arguments.to_a
|
59
|
+
|
60
|
+
# If nothing modified return, we're using the default behavior.
|
61
|
+
if to_return.nil?
|
62
|
+
super(curl_handle)
|
63
|
+
else
|
64
|
+
to_return
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Calls curl_exec
|
69
|
+
#
|
70
|
+
# This method exists so it can easily be overridden and mocked.
|
71
|
+
#
|
72
|
+
# @param resource curl_handle
|
73
|
+
# @return [String]
|
74
|
+
def curl_exec(curl_handle, request)
|
75
|
+
to_return = nil
|
76
|
+
curl_exec_arguments = CurlExecArguments.new(to_return)
|
77
|
+
emit('curlExec', [curl_exec_arguments])
|
78
|
+
(to_return,) = curl_exec_arguments.to_a
|
79
|
+
|
80
|
+
# If nothing modified return, we're using the default behavior.
|
81
|
+
if to_return.nil?
|
82
|
+
super(curl_handle, request)
|
83
|
+
else
|
84
|
+
to_return
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
public
|
89
|
+
|
90
|
+
# TODO: document
|
91
|
+
def initialize
|
92
|
+
@persisted_settings = {}
|
93
|
+
super
|
94
|
+
end
|
95
|
+
|
96
|
+
DoRequestArguments = Struct.new(:request, :response)
|
97
|
+
CurlStuffArguments = Struct.new(:return)
|
98
|
+
CurlExecArguments = Struct.new(:curl_handle)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,331 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'stringio'
|
3
|
+
require 'http/client_mock'
|
4
|
+
|
5
|
+
module Tilia
|
6
|
+
module Http
|
7
|
+
class ClientTest < Minitest::Test
|
8
|
+
# it 'testCreateCurlSettingsArrayGET' do
|
9
|
+
# client = ClientMock.new
|
10
|
+
# client.add_curl_setting(Curl::CURLOPT_POSTREDIR, 0)
|
11
|
+
#
|
12
|
+
# request = Request.new('GET', 'http://example.org/', { 'X-Foo' => 'bar' })
|
13
|
+
#
|
14
|
+
# settings = {
|
15
|
+
# Curl::CURLOPT_HEADER => true,
|
16
|
+
# Curl::CURLOPT_POSTREDIR => 0,
|
17
|
+
# :CURLOPT_HTTPHEADER => ['X-Foo: bar'],
|
18
|
+
# Curl::CURLOPT_NOBODY => false,
|
19
|
+
# Curl::CURLOPT_URL => 'http://example.org/',
|
20
|
+
# Curl::CURLOPT_CUSTOMREQUEST => 'GET',
|
21
|
+
# Curl::CURLOPT_POSTFIELDS => '',
|
22
|
+
# :CURLOPT_PUT => false
|
23
|
+
# }
|
24
|
+
#
|
25
|
+
# assert_equal(settings, client.create_curl_settings_array(request))
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# it 'testCreateCurlSettingsArrayHEAD' do
|
29
|
+
# client = ClientMock.new
|
30
|
+
# request = Request.new('HEAD', 'http://example.org/', {'X-Foo' => 'bar'})
|
31
|
+
#
|
32
|
+
# settings = {
|
33
|
+
# Curl::CURLOPT_HEADER => true,
|
34
|
+
# Curl::CURLOPT_NOBODY => true,
|
35
|
+
# Curl::CURLOPT_CUSTOMREQUEST => 'HEAD',
|
36
|
+
# :CURLOPT_HTTPHEADER => ['X-Foo: bar'],
|
37
|
+
# Curl::CURLOPT_URL => 'http://example.org/',
|
38
|
+
# Curl::CURLOPT_POSTFIELDS => '',
|
39
|
+
# :CURLOPT_PUT => false
|
40
|
+
# }
|
41
|
+
#
|
42
|
+
# assert_equal(settings, client.create_curl_settings_array(request))
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# it 'testCreateCurlSettingsArrayGETAfterHEAD' do
|
46
|
+
# client = ClientMock.new
|
47
|
+
# request = Request.new('HEAD', 'http://example.org/', {'X-Foo' => 'bar'})
|
48
|
+
#
|
49
|
+
# # Parsing the settings for this method, and discarding the result.
|
50
|
+
# # This will cause the client to automatically persist previous
|
51
|
+
# # settings and will help us detect problems.
|
52
|
+
# client.create_curl_settings_array(request)
|
53
|
+
#
|
54
|
+
# # This is the real request.
|
55
|
+
# request = Request.new('GET', 'http://example.org/', {'X-Foo' => 'bar'})
|
56
|
+
#
|
57
|
+
# settings = {
|
58
|
+
# Curl::CURLOPT_CUSTOMREQUEST => 'GET',
|
59
|
+
# Curl::CURLOPT_HEADER => true,
|
60
|
+
# :CURLOPT_HTTPHEADER => ['X-Foo: bar'],
|
61
|
+
# Curl::CURLOPT_NOBODY => false,
|
62
|
+
# Curl::CURLOPT_URL => 'http://example.org/',
|
63
|
+
# Curl::CURLOPT_POSTFIELDS => '',
|
64
|
+
# :CURLOPT_PUT => false
|
65
|
+
# }
|
66
|
+
#
|
67
|
+
# assert_equal(settings, client.create_curl_settings_array(request))
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# it 'testCreateCurlSettingsArrayPUTStream' do
|
71
|
+
# pending
|
72
|
+
# client = ClientMock.new
|
73
|
+
#
|
74
|
+
# h = StringIO.new
|
75
|
+
# h.write('booh')
|
76
|
+
# request = Request.new('PUT', 'http://example.org/', {'X-Foo' => 'bar'}, h)
|
77
|
+
#
|
78
|
+
# settings = {
|
79
|
+
# Curl::CURLOPT_HEADER => true,
|
80
|
+
# :CURLOPT_PUT => true,
|
81
|
+
# :CURLOPT_INFILE => h,
|
82
|
+
# Curl::CURLOPT_NOBODY => false,
|
83
|
+
# Curl::CURLOPT_CUSTOMREQUEST => 'PUT',
|
84
|
+
# :CURLOPT_HTTPHEADER => ['X-Foo: bar'],
|
85
|
+
# Curl::CURLOPT_URL => 'http://example.org/'
|
86
|
+
# }
|
87
|
+
#
|
88
|
+
# assert_equal(settings, client.create_curl_settings_array(request))
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# it 'testCreateCurlSettingsArrayPUTString' do
|
92
|
+
# client = ClientMock.new
|
93
|
+
# request = Request.new('PUT', 'http://example.org/', {'X-Foo' => 'bar'}, 'boo')
|
94
|
+
#
|
95
|
+
# settings = {
|
96
|
+
# Curl::CURLOPT_HEADER => true,
|
97
|
+
# Curl::CURLOPT_NOBODY => false,
|
98
|
+
# Curl::CURLOPT_POSTFIELDS => 'boo',
|
99
|
+
# Curl::CURLOPT_CUSTOMREQUEST => 'PUT',
|
100
|
+
# :CURLOPT_HTTPHEADER => ['X-Foo: bar'],
|
101
|
+
# Curl::CURLOPT_URL => 'http://example.org/'
|
102
|
+
# }
|
103
|
+
#
|
104
|
+
# assert_equal(settings, client.create_curl_settings_array(request))
|
105
|
+
# end
|
106
|
+
|
107
|
+
def test_send
|
108
|
+
client = ClientMock.new
|
109
|
+
request = Request.new('GET', 'http://example.org/')
|
110
|
+
|
111
|
+
response = nil
|
112
|
+
client.on(
|
113
|
+
'doRequest',
|
114
|
+
lambda do |_|
|
115
|
+
response = Response.new(200)
|
116
|
+
end
|
117
|
+
)
|
118
|
+
|
119
|
+
response = client.send(request)
|
120
|
+
|
121
|
+
assert_equal(200, response.status)
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_send_client_error
|
125
|
+
client = ClientMock.new
|
126
|
+
request = Request.new('GET', 'http://example.org/')
|
127
|
+
|
128
|
+
client.on(
|
129
|
+
'doRequest',
|
130
|
+
lambda do |_|
|
131
|
+
fail ClientException, 'aaah'
|
132
|
+
end
|
133
|
+
)
|
134
|
+
|
135
|
+
called = false
|
136
|
+
client.on(
|
137
|
+
'exception',
|
138
|
+
lambda do |_a, _b, _c, _d|
|
139
|
+
called = true
|
140
|
+
end
|
141
|
+
)
|
142
|
+
|
143
|
+
assert_raises(ClientException) { client.send(request) }
|
144
|
+
|
145
|
+
assert(called)
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_send_http_error
|
149
|
+
client = ClientMock.new
|
150
|
+
request = Request.new('GET', 'http://example.org/')
|
151
|
+
|
152
|
+
client.on(
|
153
|
+
'doRequest',
|
154
|
+
lambda do |args|
|
155
|
+
args.response = Response.new(404)
|
156
|
+
end
|
157
|
+
)
|
158
|
+
|
159
|
+
called = 0
|
160
|
+
client.on(
|
161
|
+
'error',
|
162
|
+
lambda do |_a, _b, _c, _d|
|
163
|
+
called += 1
|
164
|
+
end
|
165
|
+
)
|
166
|
+
client.on(
|
167
|
+
'error:404',
|
168
|
+
lambda do |_a, _b, _c, _d|
|
169
|
+
called += 1
|
170
|
+
end
|
171
|
+
)
|
172
|
+
|
173
|
+
client.send(request)
|
174
|
+
assert_equal(2, called)
|
175
|
+
end
|
176
|
+
|
177
|
+
def test_send_retry
|
178
|
+
client = ClientMock.new
|
179
|
+
request = Request.new('GET', 'http://example.org/')
|
180
|
+
|
181
|
+
called = 0
|
182
|
+
client.on(
|
183
|
+
'doRequest',
|
184
|
+
lambda do |args|
|
185
|
+
called += 1
|
186
|
+
if called < 3
|
187
|
+
args.response = Response.new(404)
|
188
|
+
else
|
189
|
+
args.response = Response.new(200)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
)
|
193
|
+
|
194
|
+
error_called = 0
|
195
|
+
client.on(
|
196
|
+
'error',
|
197
|
+
lambda do |_a, _b, do_retry, _d|
|
198
|
+
error_called += 1
|
199
|
+
do_retry.value = true
|
200
|
+
end
|
201
|
+
)
|
202
|
+
|
203
|
+
response = client.send(request)
|
204
|
+
assert_equal(3, called)
|
205
|
+
assert_equal(2, error_called)
|
206
|
+
assert_equal(200, response.status)
|
207
|
+
end
|
208
|
+
|
209
|
+
def test_http_error_exception
|
210
|
+
client = ClientMock.new
|
211
|
+
client.throw_exceptions = true
|
212
|
+
request = Request.new('GET', 'http://example.org/')
|
213
|
+
|
214
|
+
client.on(
|
215
|
+
'doRequest',
|
216
|
+
lambda do |args|
|
217
|
+
args.response = Response.new(404)
|
218
|
+
end
|
219
|
+
)
|
220
|
+
|
221
|
+
error = assert_raises(Exception) { client.send(request) }
|
222
|
+
assert_kind_of(ClientHttpException, error)
|
223
|
+
assert_equal(404, error.http_status)
|
224
|
+
assert_kind_of(Response, error.response)
|
225
|
+
end
|
226
|
+
|
227
|
+
# it 'testParseCurlResult' do
|
228
|
+
# client = ClientMock.new
|
229
|
+
# client.on(
|
230
|
+
# 'curlStuff',
|
231
|
+
# lambda do |args|
|
232
|
+
# args.return = [
|
233
|
+
# {
|
234
|
+
# 'header_size' => 33,
|
235
|
+
# 'http_code' => 200
|
236
|
+
# },
|
237
|
+
# 0,
|
238
|
+
# ''
|
239
|
+
# ]
|
240
|
+
# end
|
241
|
+
# )
|
242
|
+
#
|
243
|
+
# body = "HTTP/1.1 200 OK\r\nHeader1:Val1\r\n\r\nFoo"
|
244
|
+
# result = client.parse_curl_result(body, 'foobar')
|
245
|
+
#
|
246
|
+
# assert_equal(Client::STATUS_SUCCESS, result['status'])
|
247
|
+
# assert_equal(200, result['http_code'])
|
248
|
+
# assert_equal(200, result['response'].status)
|
249
|
+
# assert_equal({ 'Header1' => ['Val1'] }, result['response'].headers)
|
250
|
+
# assert_equal('Foo', result['response'].body_as_string)
|
251
|
+
# end
|
252
|
+
#
|
253
|
+
# it 'testParseCurlError' do
|
254
|
+
# client = ClientMock.new
|
255
|
+
# client.on(
|
256
|
+
# 'curlStuff',
|
257
|
+
# lambda do |args|
|
258
|
+
# args.return = [
|
259
|
+
# { },
|
260
|
+
# 1,
|
261
|
+
# 'Curl error'
|
262
|
+
# ]
|
263
|
+
# end
|
264
|
+
# )
|
265
|
+
#
|
266
|
+
# body = "HTTP/1.1 200 OK\r\nHeader1:Val1\r\n\r\nFoo"
|
267
|
+
# result = client.parse_curl_result(body, 'foobar')
|
268
|
+
#
|
269
|
+
# assert_equal(Client::STATUS_CURLERROR, result['status'])
|
270
|
+
# assert_equal(1, result['curl_errno'])
|
271
|
+
# assert_equal('Curl error', result['curl_errmsg'])
|
272
|
+
# end
|
273
|
+
#
|
274
|
+
# it 'testDoRequest' do
|
275
|
+
# client = ClientMock.new
|
276
|
+
# request = Request.new('GET', 'http://example.org/')
|
277
|
+
# client.on(
|
278
|
+
# 'curlExec',
|
279
|
+
# lambda do |args|
|
280
|
+
# args.curl_handle = "HTTP/1.1 200 OK\r\nHeader1:Val1\r\n\r\nFoo"
|
281
|
+
# end
|
282
|
+
# )
|
283
|
+
#
|
284
|
+
# client.on(
|
285
|
+
# 'curlStuff',
|
286
|
+
# lambda do |args|
|
287
|
+
# args.return = [
|
288
|
+
# {
|
289
|
+
# 'header_size' => 33,
|
290
|
+
# 'http_code' => 200
|
291
|
+
# },
|
292
|
+
# 0,
|
293
|
+
# ''
|
294
|
+
# ]
|
295
|
+
# end
|
296
|
+
# )
|
297
|
+
# response = client.do_request(request)
|
298
|
+
# assert_equal(200, response.status)
|
299
|
+
# assert_equal({ 'Header1' => ['Val1'] }, response.headers)
|
300
|
+
# assert_equal('Foo', response.body_as_string)
|
301
|
+
# end
|
302
|
+
#
|
303
|
+
# it 'testDoRequestCurlError' do
|
304
|
+
# client = ClientMock.new
|
305
|
+
# request = Request.new('GET', 'http://example.org/')
|
306
|
+
# client.on(
|
307
|
+
# 'curlExec',
|
308
|
+
# lambda do |args|
|
309
|
+
# args.curl_handle = ''
|
310
|
+
# end
|
311
|
+
# )
|
312
|
+
#
|
313
|
+
# client.on(
|
314
|
+
# 'curlStuff',
|
315
|
+
# lambda do |args|
|
316
|
+
# args.return = [
|
317
|
+
# { },
|
318
|
+
# 1,
|
319
|
+
# 'Curl error'
|
320
|
+
# ]
|
321
|
+
# end
|
322
|
+
# )
|
323
|
+
#
|
324
|
+
# expect { response = client.do_request(request) }.to raise_error ClientException do |error|
|
325
|
+
# assert_equal(1, error.code)
|
326
|
+
# assert_equal('Curl error', error.message)
|
327
|
+
# end
|
328
|
+
# end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Tilia
|
4
|
+
module Http
|
5
|
+
class MessageDecoratorTest < Minitest::Test
|
6
|
+
def setup
|
7
|
+
@inner = Tilia::Http::Request.new
|
8
|
+
@outer = Tilia::Http::RequestDecorator.new(@inner)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_body
|
12
|
+
@outer.body = 'foo'
|
13
|
+
assert_equal('foo', @inner.body_as_stream.readlines.join(''))
|
14
|
+
assert_equal('foo', @outer.body_as_stream.readlines.join(''))
|
15
|
+
assert_equal('foo', @inner.body_as_string)
|
16
|
+
assert_equal('foo', @outer.body_as_string)
|
17
|
+
assert_equal('foo', @inner.body)
|
18
|
+
assert_equal('foo', @outer.body)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_headers
|
22
|
+
@outer.update_headers('a' => 'b')
|
23
|
+
|
24
|
+
assert_equal({ 'a' => ['b'] }, @inner.headers)
|
25
|
+
assert_equal({ 'a' => ['b'] }, @outer.headers)
|
26
|
+
|
27
|
+
@outer.update_headers('c' => 'd')
|
28
|
+
|
29
|
+
assert_equal({ 'a' => ['b'], 'c' => ['d'] }, @inner.headers)
|
30
|
+
assert_equal({ 'a' => ['b'], 'c' => ['d'] }, @outer.headers)
|
31
|
+
|
32
|
+
@outer.add_headers('e' => 'f')
|
33
|
+
|
34
|
+
assert_equal({ 'a' => ['b'], 'c' => ['d'], 'e' => ['f'] }, @inner.headers)
|
35
|
+
assert_equal({ 'a' => ['b'], 'c' => ['d'], 'e' => ['f'] }, @outer.headers)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_header
|
39
|
+
refute(@outer.header?('a'))
|
40
|
+
refute(@inner.header?('a'))
|
41
|
+
@outer.update_header('a', 'c')
|
42
|
+
assert(@outer.header?('a'))
|
43
|
+
assert(@inner.header?('a'))
|
44
|
+
|
45
|
+
assert_equal('c', @inner.header('A'))
|
46
|
+
assert_equal('c', @outer.header('A'))
|
47
|
+
|
48
|
+
@outer.add_header('A', 'd')
|
49
|
+
|
50
|
+
assert_equal(['c', 'd'], @inner.header_as_array('A'))
|
51
|
+
assert_equal(['c', 'd'], @outer.header_as_array('A'))
|
52
|
+
|
53
|
+
@outer.remove_header('a')
|
54
|
+
|
55
|
+
assert_nil(@inner.header('A'))
|
56
|
+
assert_nil(@outer.header('A'))
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_http_version
|
60
|
+
@outer.http_version = '1.0'
|
61
|
+
|
62
|
+
assert_equal('1.0', @inner.http_version)
|
63
|
+
assert_equal('1.0', @outer.http_version)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|