Skip to content

Commit 3ab898f

Browse files
committed
Add test
Signed-off-by: Yuta Iwama <[email protected]>
1 parent 3df24ff commit 3ab898f

File tree

3 files changed

+196
-3
lines changed

3 files changed

+196
-3
lines changed

Gemfile

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ source 'https://rubygems.org/'
22

33
gemspec
44

5-
gem 'async-http', '~> 0.42'
6-
75
local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local")
86
if File.exist?(local_gemfile)
97
puts "Loading Gemfile.local ..." if $DEBUG # `ruby -d` or `bundle -v`

fluentd.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,5 @@ Gem::Specification.new do |gem|
4949
gem.add_development_dependency("test-unit-rr", ["~> 1.0"])
5050
gem.add_development_dependency("oj", [">= 2.14", "< 4"])
5151
gem.add_development_dependency("ext_monitor", [">= 0.1.1", "< 0.2"])
52+
gem.add_development_dependency("async-http")
5253
end

test/plugin_helper/test_http_server_helper.rb

Lines changed: 195 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,24 @@
55
require 'fluent/event'
66
require 'net/http'
77
require 'uri'
8+
require 'openssl'
9+
require 'async'
810

911
class HtttpHelperTest < Test::Unit::TestCase
1012
PORT = unused_port
1113
NULL_LOGGER = Logger.new(nil)
14+
CERT_DIR = File.expand_path(File.dirname(__FILE__) + '/data/cert/without_ca')
15+
CERT_CA_DIR = File.expand_path(File.dirname(__FILE__) + '/data/cert/with_ca')
1216

1317
class Dummy < Fluent::Plugin::TestBase
1418
helpers :http_server
1519
end
1620

17-
def on_driver
21+
def on_driver(config = nil)
22+
config ||= Fluent::Config.parse(config || '', '(name)', '')
1823
Fluent::Test.setup
1924
driver = Dummy.new
25+
driver.configure(config)
2026
driver.start
2127
driver.after_start
2228

@@ -47,6 +53,12 @@ def on_driver
4753
end
4854
end
4955

56+
def on_driver_transport(opts = {}, &block)
57+
transport_conf = config_element('transport', 'tls', opts)
58+
c = config_element('ROOT', '', {}, [transport_conf])
59+
on_driver(c, &block)
60+
end
61+
5062
%w[get head].each do |n|
5163
define_method(n) do |uri, header = {}|
5264
url = URI.parse(uri)
@@ -56,6 +68,14 @@ def on_driver
5668
http.request(req)
5769
end
5870
end
71+
72+
define_method("secure_#{n}") do |uri, header = {}, verify: true, cert_path: nil, selfsigned: true, hostname: false|
73+
url = URI.parse(uri)
74+
headers = { 'Content-Type' => 'application/x-www-form-urlencoded/' }.merge(header)
75+
start_https_request(url.host, url.port, verify: verify, cert_path: cert_path, selfsigned: selfsigned) do |https|
76+
https.send(n, url.path, headers.to_a)
77+
end
78+
end
5979
end
6080

6181
%w[post put patch delete options trace].each do |n|
@@ -70,6 +90,91 @@ def on_driver
7090
end
7191
end
7292

93+
# wrapper for net/http
94+
Response = Struct.new(:code, :body, :headers)
95+
96+
# Use async-http as http client since net/http can't be set verify_hostname= now
97+
# will be replaced when net/http supports verify_hostname=
98+
def start_https_request(addr, port, verify: true, cert_path: nil, selfsigned: true, hostname: nil)
99+
context = OpenSSL::SSL::SSLContext.new
100+
context.set_params({})
101+
if verify
102+
cert_store = OpenSSL::X509::Store.new
103+
cert_store.set_default_paths
104+
if selfsigned && OpenSSL::X509.const_defined?('V_FLAG_CHECK_SS_SIGNATURE')
105+
cert_store.flags = OpenSSL::X509::V_FLAG_CHECK_SS_SIGNATURE
106+
end
107+
108+
if cert_path
109+
cert_store.add_file(cert_path)
110+
end
111+
112+
context.cert_store = cert_store
113+
if !hostname && context.respond_to?(:verify_hostname=)
114+
context.verify_hostname = false # In test code, using hostname to be connected is very difficult
115+
end
116+
117+
context.verify_mode = OpenSSL::SSL::VERIFY_PEER
118+
else
119+
context.verify_mode = OpenSSL::SSL::VERIFY_NONE
120+
end
121+
122+
client = Async::HTTP::Client.new(Async::HTTP::Endpoint.parse("https://#{addr}:#{port}", ssl_context: context))
123+
reactor = Async::Reactor.new(nil, logger: NULL_LOGGER)
124+
125+
resp = nil
126+
error = nil
127+
128+
reactor.run do
129+
begin
130+
response = yield(client)
131+
rescue => e # Async::Reactor rescue all error. handle it by myself
132+
error = e
133+
end
134+
135+
resp = Response.new(response.status.to_s, response.body.read, response.headers)
136+
end
137+
138+
if error
139+
raise error
140+
else
141+
resp
142+
end
143+
end
144+
145+
# def start_https_request(addr, port, verify: true, cert_path: nil, selfsigned: true)
146+
# https = Net::HTTP.new(addr, port)
147+
# https.use_ssl = true
148+
149+
# if verify
150+
# cert_store = OpenSSL::X509::Store.new
151+
# cert_store.set_default_paths
152+
# if selfsigned && OpenSSL::X509.const_defined?('V_FLAG_CHECK_SS_SIGNATURE')
153+
# cert_store.flags = OpenSSL::X509::V_FLAG_CHECK_SS_SIGNATURE
154+
# end
155+
156+
# if cert_path
157+
# cert_store.add_file(cert_path)
158+
# end
159+
160+
# https.cert_store = cert_store
161+
162+
# # https.verify_hostname = false
163+
164+
# https.verify_mode = OpenSSL::SSL::VERIFY_PEER
165+
# else
166+
# https.verify_mode = OpenSSL::SSL::VERIFY_NONE
167+
# end
168+
169+
# # if !hostname && context.respond_to?(:verify_hostname=)
170+
# # context.verify_hostname = false # In test code, using hostname to be connected is very difficult
171+
# # end
172+
173+
# https.start do
174+
# yield(https)
175+
# end
176+
# end
177+
73178
sub_test_case 'Create a HTTP server' do
74179
test 'monunt given path' do
75180
on_driver do |driver|
@@ -185,6 +290,95 @@ def on_driver
185290
end
186291
end
187292

293+
sub_test_case 'create a HTTPS server' do
294+
test '#configure' do
295+
driver = Dummy.new
296+
297+
transport_conf = config_element('transport', 'tls', { 'version' => 'TLSv1_1' })
298+
driver.configure(config_element('ROOT', '', {}, [transport_conf]))
299+
assert_equal :tls, driver.transport_config.protocol
300+
assert_equal :TLSv1_1, driver.transport_config.version
301+
end
302+
303+
sub_test_case '#http_server_create_https_server' do
304+
test 'can overwrite settings by using tls_context' do
305+
on_driver_transport({ 'insecure' => 'false' }) do |driver|
306+
tls = { 'insecure' => 'true' } # overwrite
307+
driver.http_server_create_https_server(:http_server_helper_test_tls, addr: '127.0.0.1', port: PORT, logger: NULL_LOGGER, tls_opts: tls) do |s|
308+
s.get('/example/hello') { [200, { 'Content-Type' => 'text/plain' }, 'hello get'] }
309+
end
310+
311+
resp = secure_get("https://127.0.0.1:#{PORT}/example/hello", verify: false)
312+
assert_equal('200', resp.code)
313+
assert_equal('hello get', resp.body)
314+
end
315+
end
316+
317+
test 'with insecure in transport section' do
318+
on_driver_transport({ 'insecure' => 'true' }) do |driver|
319+
driver.http_server_create_https_server(:http_server_helper_test_tls, addr: '127.0.0.1', port: PORT, logger: NULL_LOGGER) do |s|
320+
s.get('/example/hello') { [200, { 'Content-Type' => 'text/plain' }, 'hello get'] }
321+
end
322+
323+
resp = secure_get("https://127.0.0.1:#{PORT}/example/hello", verify: false)
324+
assert_equal('200', resp.code)
325+
assert_equal('hello get', resp.body)
326+
327+
assert_raise OpenSSL::SSL::SSLError do
328+
secure_get("https://127.0.0.1:#{PORT}/example/hello")
329+
end
330+
end
331+
end
332+
333+
data(
334+
'with passphrase' => ['apple', 'cert-pass.pem', 'cert-key-pass.pem'],
335+
'without passphrase' => [nil, 'cert.pem', 'cert-key.pem'])
336+
test 'load self-signed cert/key pair, verified from clients using cert files' do |(passphrase, cert, private_key)|
337+
cert_path = File.join(CERT_DIR, cert)
338+
private_key_path = File.join(CERT_DIR, private_key)
339+
opt = { 'insecure' => 'false', 'private_key_path' => private_key_path, 'cert_path' => cert_path }
340+
if passphrase
341+
opt['private_key_passphrase'] = passphrase
342+
end
343+
344+
on_driver_transport(opt) do |driver|
345+
driver.http_server_create_https_server(:http_server_helper_test_tls, addr: '127.0.0.1', port: PORT, logger: NULL_LOGGER) do |s|
346+
s.get('/example/hello') { [200, { 'Content-Type' => 'text/plain' }, 'hello get'] }
347+
end
348+
349+
resp = secure_get("https://127.0.0.1:#{PORT}/example/hello", cert_path: cert_path)
350+
assert_equal('200', resp.code)
351+
assert_equal('hello get', resp.body)
352+
end
353+
end
354+
355+
data(
356+
'with passphrase' => ['apple', 'cert-pass.pem', 'cert-key-pass.pem', 'ca-cert-pass.pem'],
357+
'without passphrase' => [nil, 'cert.pem', 'cert-key.pem', 'ca-cert.pem'])
358+
test 'load cert by private CA cert file, verified from clients using CA cert file' do |(passphrase, cert, cert_key, ca_cert)|
359+
cert_path = File.join(CERT_CA_DIR, cert)
360+
private_key_path = File.join(CERT_CA_DIR, cert_key)
361+
362+
ca_cert_path = File.join(CERT_CA_DIR, ca_cert)
363+
364+
opt = { 'insecure' => 'false', 'cert_path' => cert_path, 'private_key_path' => private_key_path }
365+
if passphrase
366+
opt['private_key_passphrase'] = passphrase
367+
end
368+
369+
on_driver_transport(opt) do |driver|
370+
driver.http_server_create_https_server(:http_server_helper_test_tls, addr: '127.0.0.1', port: PORT, logger: NULL_LOGGER) do |s|
371+
s.get('/example/hello') { [200, { 'Content-Type' => 'text/plain' }, 'hello get'] }
372+
end
373+
374+
resp = secure_get("https://127.0.0.1:#{PORT}/example/hello", cert_path: ca_cert_path)
375+
assert_equal('200', resp.code)
376+
assert_equal('hello get', resp.body)
377+
end
378+
end
379+
end
380+
end
381+
188382
test 'must be called #start and #stop' do
189383
on_driver do |driver|
190384
server = flexmock('Server') do |watcher|

0 commit comments

Comments
 (0)