Skip to content

Commit

Permalink
Add test
Browse files Browse the repository at this point in the history
Signed-off-by: Yuta Iwama <ganmacs@gmail.com>
  • Loading branch information
ganmacs committed Jan 22, 2020
1 parent 1f80633 commit b050f2e
Show file tree
Hide file tree
Showing 2 changed files with 197 additions and 1 deletion.
1 change: 1 addition & 0 deletions fluentd.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,5 @@ Gem::Specification.new do |gem|
gem.add_development_dependency("test-unit-rr", ["~> 1.0"])
gem.add_development_dependency("oj", [">= 2.14", "< 4"])
gem.add_development_dependency("ext_monitor", [">= 0.1.1", "< 0.2"])
gem.add_development_dependency("async-http")
end
197 changes: 196 additions & 1 deletion test/plugin_helper/test_http_server_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,25 @@
require 'fluent/event'
require 'net/http'
require 'uri'
require 'openssl'
require 'async'

class HtttpHelperTest < Test::Unit::TestCase
PORT = unused_port
NULL_LOGGER = Logger.new(nil)
TMP_DIR = File.expand_path(File.dirname(__FILE__) + '/../tmp/plugin_helper_http_server')
CERT_DIR = File.expand_path(File.dirname(__FILE__) + '/data/cert/without_ca')
CERT_CA_DIR = File.expand_path(File.dirname(__FILE__) + '/data/cert/with_ca')

class Dummy < Fluent::Plugin::TestBase
helpers :http_server
end

def on_driver
def on_driver(config = nil)
config ||= Fluent::Config.parse(config || '', '(name)', '')
Fluent::Test.setup
driver = Dummy.new
driver.configure(config)
driver.start
driver.after_start

Expand Down Expand Up @@ -47,6 +54,12 @@ def on_driver
end
end

def on_driver_transport(opts = {}, &block)
transport_conf = config_element('transport', 'tls', opts)
c = config_element('ROOT', '', {}, [transport_conf])
on_driver(c, &block)
end

%w[get head].each do |n|
define_method(n) do |uri, header = {}|
url = URI.parse(uri)
Expand All @@ -56,6 +69,14 @@ def on_driver
http.request(req)
end
end

define_method("secure_#{n}") do |uri, header = {}, verify: true, cert_path: nil, selfsigned: true, hostname: false|
url = URI.parse(uri)
headers = { 'Content-Type' => 'application/x-www-form-urlencoded/' }.merge(header)
start_https_request(url.host, url.port, verify: verify, cert_path: cert_path, selfsigned: selfsigned) do |https|
https.send(n, url.path, headers.to_a)
end
end
end

%w[post put patch delete options trace].each do |n|
Expand All @@ -70,6 +91,91 @@ def on_driver
end
end

# wrapper for net/http
Response = Struct.new(:code, :body, :headers)

# Use async-http as http client since net/http can't be set verify_hostname= now
# will be replaced when net/http supports verify_hostname=
def start_https_request(addr, port, verify: true, cert_path: nil, selfsigned: true, hostname: nil)
context = OpenSSL::SSL::SSLContext.new
context.set_params({})
if verify
cert_store = OpenSSL::X509::Store.new
cert_store.set_default_paths
if selfsigned && OpenSSL::X509.const_defined?('V_FLAG_CHECK_SS_SIGNATURE')
cert_store.flags = OpenSSL::X509::V_FLAG_CHECK_SS_SIGNATURE
end

if cert_path
cert_store.add_file(cert_path)
end

context.cert_store = cert_store
if !hostname && context.respond_to?(:verify_hostname=)
context.verify_hostname = false # In test code, using hostname to be connected is very difficult
end

context.verify_mode = OpenSSL::SSL::VERIFY_PEER
else
context.verify_mode = OpenSSL::SSL::VERIFY_NONE
end

client = Async::HTTP::Client.new(Async::HTTP::Endpoint.parse("https://#{addr}:#{port}", ssl_context: context))
reactor = Async::Reactor.new(nil, logger: NULL_LOGGER)

resp = nil
error = nil

reactor.run do
begin
response = yield(client)
rescue => e
error = e
end

resp = Response.new(response.status.to_s, response.body.read, response.headers)
end

if error
raise error
else
resp
end
end

# def start_https_request(addr, port, verify: true, cert_path: nil, selfsigned: true)
# https = Net::HTTP.new(addr, port)
# https.use_ssl = true

# if verify
# cert_store = OpenSSL::X509::Store.new
# cert_store.set_default_paths
# if selfsigned && OpenSSL::X509.const_defined?('V_FLAG_CHECK_SS_SIGNATURE')
# cert_store.flags = OpenSSL::X509::V_FLAG_CHECK_SS_SIGNATURE
# end

# if cert_path
# cert_store.add_file(cert_path)
# end

# https.cert_store = cert_store

# # https.verify_hostname = false

# https.verify_mode = OpenSSL::SSL::VERIFY_PEER
# else
# https.verify_mode = OpenSSL::SSL::VERIFY_NONE
# end

# # if !hostname && context.respond_to?(:verify_hostname=)
# # context.verify_hostname = false # In test code, using hostname to be connected is very difficult
# # end

# https.start do
# yield(https)
# end
# end

sub_test_case 'Create a HTTP server' do
test 'monunt given path' do
on_driver do |driver|
Expand Down Expand Up @@ -185,6 +291,95 @@ def on_driver
end
end

sub_test_case 'create a HTTPS server' do
test '#configure' do
driver = Dummy.new

transport_conf = config_element('transport', 'tls', { 'version' => 'TLSv1_1' })
driver.configure(config_element('ROOT', '', {}, [transport_conf]))
assert_equal :tls, driver.transport_config.protocol
assert_equal :TLSv1_1, driver.transport_config.version
end

sub_test_case '#http_server_create_https_server' do
test 'can overwrite settings by using tls_context' do
on_driver_transport({ 'insecure' => 'false' }) do |driver|
tls = { 'insecure' => 'true' } # overwrite
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|
s.get('/example/hello') { [200, { 'Content-Type' => 'text/plain' }, 'hello get'] }
end

resp = secure_get("https://127.0.0.1:#{PORT}/example/hello", verify: false)
assert_equal('200', resp.code)
assert_equal('hello get', resp.body)
end
end

test 'with insecure in transport section' do
on_driver_transport({ 'insecure' => 'true' }) do |driver|
driver.http_server_create_https_server(:http_server_helper_test_tls, addr: '127.0.0.1', port: PORT, logger: NULL_LOGGER) do |s|
s.get('/example/hello') { [200, { 'Content-Type' => 'text/plain' }, 'hello get'] }
end

resp = secure_get("https://127.0.0.1:#{PORT}/example/hello", verify: false)
assert_equal('200', resp.code)
assert_equal('hello get', resp.body)

assert_raise OpenSSL::SSL::SSLError do
secure_get("https://127.0.0.1:#{PORT}/example/hello")
end
end
end

data(
'with passphrase' => ['apple', 'cert-pass.pem', 'cert-key-pass.pem'],
'without passphrase' => [nil, 'cert.pem', 'cert-key.pem'])
test 'load self-signed cert/key pair, verified from clients using cert files' do |(passphrase, cert, private_key)|
cert_path = File.join(CERT_DIR, cert)
private_key_path = File.join(CERT_DIR, private_key)
opt = { 'insecure' => 'false', 'private_key_path' => private_key_path, 'cert_path' => cert_path }
if passphrase
opt['private_key_passphrase'] = passphrase
end

on_driver_transport(opt) do |driver|
driver.http_server_create_https_server(:http_server_helper_test_tls, addr: '127.0.0.1', port: PORT, logger: NULL_LOGGER) do |s|
s.get('/example/hello') { [200, { 'Content-Type' => 'text/plain' }, 'hello get'] }
end

resp = secure_get("https://127.0.0.1:#{PORT}/example/hello", cert_path: cert_path)
assert_equal('200', resp.code)
assert_equal('hello get', resp.body)
end
end

data(
'with passphrase' => ['apple', 'cert-pass.pem', 'cert-key-pass.pem', 'ca-cert-pass.pem'],
'without passphrase' => [nil, 'cert.pem', 'cert-key.pem', 'ca-cert.pem'])
test 'load cert by private CA cert file, verified from clients using CA cert file' do |(passphrase, cert, cert_key, ca_cert)|
cert_path = File.join(CERT_CA_DIR, cert)
private_key_path = File.join(CERT_CA_DIR, cert_key)

ca_cert_path = File.join(CERT_CA_DIR, ca_cert)

opt = { 'insecure' => 'false', 'cert_path' => cert_path, 'private_key_path' => private_key_path }
if passphrase
opt['private_key_passphrase'] = passphrase
end

on_driver_transport(opt) do |driver|
driver.http_server_create_https_server(:http_server_helper_test_tls, addr: '127.0.0.1', port: PORT, logger: NULL_LOGGER) do |s|
s.get('/example/hello') { [200, { 'Content-Type' => 'text/plain' }, 'hello get'] }
end

resp = secure_get("https://127.0.0.1:#{PORT}/example/hello", cert_path: ca_cert_path)
assert_equal('200', resp.code)
assert_equal('hello get', resp.body)
end
end
end
end

test 'must be called #start and #stop' do
on_driver do |driver|
server = flexmock('Server') do |watcher|
Expand Down

0 comments on commit b050f2e

Please sign in to comment.