Skip to content

Commit

Permalink
Merge pull request #2787 from ganmacs/http_helper-supports-https
Browse files Browse the repository at this point in the history
HTTP server helper supports https
  • Loading branch information
ganmacs committed Jan 28, 2020
2 parents b1d7a5c + 6d53664 commit ae8b374
Show file tree
Hide file tree
Showing 25 changed files with 738 additions and 26 deletions.
2 changes: 0 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ source 'https://rubygems.org/'

gemspec

gem 'async-http', '~> 0.42'

local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local")
if File.exist?(local_gemfile)
puts "Loading Gemfile.local ..." if $DEBUG # `ruby -d` or `bundle -v`
Expand Down
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
4 changes: 4 additions & 0 deletions lib/fluent/config/section.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ def to_h
@params
end

def dup
Section.new(@params.dup, @corresponding_config_element.dup)
end

def +(other)
Section.new(self.to_h.merge(other.to_h))
end
Expand Down
2 changes: 1 addition & 1 deletion lib/fluent/plugin/in_monitor_agent.rb
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ def start

log.debug "listening monitoring http server on http://#{@bind}:#{@port}/api/plugins for worker#{fluentd_worker_id}"
api_handler = APIHandler.new(self)
create_http_server(:in_monitor_http_server_helper, addr: @bind, port: @port, logger: log, default_app: NotFoundJson) do |serv|
http_server_create_http_server(:in_monitor_http_server_helper, addr: @bind, port: @port, logger: log, default_app: NotFoundJson) do |serv|
serv.get('/api/plugins') { |req| api_handler.plugins_ltsv(req) }
serv.get('/api/plugins.json') { |req| api_handler.plugins_json(req) }
serv.get('/api/config') { |req| api_handler.config_ltsv(req) }
Expand Down
2 changes: 1 addition & 1 deletion lib/fluent/plugin_helper/cert_option.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
require 'openssl'
require 'socket'

# this module is only for Socket/Server plugin helpers
# this module is only for Socket/Server/HttpServer plugin helpers
module Fluent
module PluginHelper
module CertOption
Expand Down
64 changes: 62 additions & 2 deletions lib/fluent/plugin_helper/http_server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,77 @@
end

require 'fluent/plugin_helper/thread'
require 'fluent/plugin_helper/server' # For Server::ServerTransportParams
require 'fluent/plugin_helper/http_server/ssl_context_builder'

module Fluent
module PluginHelper
module HttpServer
include Fluent::PluginHelper::Thread
include Fluent::Configurable

# stop : stop http server and mark callback thread as stopped
# shutdown : [-]
# close : correct stopped threads
# terminate: kill thread

def self.included(mod)
mod.include Fluent::PluginHelper::Server::ServerTransportParams
end

def initialize(*)
super
@_http_server = nil
end

def create_http_server(title, addr:, port:, logger:, default_app: nil, proto: nil, tls_opts: nil, &block)
logger.warn('this method is deprecated. Use #http_server_create_http_server instead')
http_server_create_http_server(title, addr: addr, port: port, logger: logger, default_app: default_app, proto: proto, tls_opts: tls_opts, &block)
end

# @param title [Symbol] the thread name. this value should be unique.
# @param addr [String] Listen address
# @param port [String] Listen port
# @param logger [Logger] logger used in this server
# @param default_app [Object] This method must have #call.
def create_http_server(title, addr:, port:, logger:, default_app: nil)
# @param proto [Symbol] :tls or :tcp
# @param tls_opts [Hash] options for TLS.
def http_server_create_http_server(title, addr:, port:, logger:, default_app: nil, proto: nil, tls_opts: nil, &block)
unless block_given?
raise ArgumentError, 'BUG: callback not specified'
end

@_http_server = HttpServer::Server.new(addr: addr, port: port, logger: logger, default_app: default_app) do |serv|
if proto == :tls || (@transport_config && @transport_config.protocol == :tls)
http_server_create_https_server(title, addr: addr, port: port, logger: logger, default_app: default_app, tls_opts: tls_opts, &block)
else
@_http_server = HttpServer::Server.new(addr: addr, port: port, logger: logger, default_app: default_app) do |serv|
yield(serv)
end

_block_until_http_server_start do |notify|
thread_create(title) do
@_http_server.start(notify)
end
end
end
end

# @param title [Symbol] the thread name. this value should be unique.
# @param addr [String] Listen address
# @param port [String] Listen port
# @param logger [Logger] logger used in this server
# @param default_app [Object] This method must have #call.
# @param tls_opts [Hash] options for TLS.
def http_server_create_https_server(title, addr:, port:, logger:, default_app: nil, tls_opts: nil)
topt =
if tls_opts
_http_server_overwrite_config(@transport_config, tls_opts)
else
@transport_config
end
ctx = Fluent::PluginHelper::HttpServer::SSLContextBuilder.new($log).build(topt)

@_http_server = HttpServer::Server.new(addr: addr, port: port, logger: logger, default_app: default_app, tls_context: ctx) do |serv|
yield(serv)
end

Expand All @@ -64,6 +114,16 @@ def stop

private

def _http_server_overwrite_config(config, opts)
conf = config.dup
Fluent::PluginHelper::Server::SERVER_TRANSPORT_PARAMS.map(&:to_s).each do |param|
if opts.key?(param)
conf[param] = opts[param]
end
end
conf
end

# To block until server is ready to listen
def _block_until_http_server_start
que = Queue.new
Expand Down
17 changes: 14 additions & 3 deletions lib/fluent/plugin_helper/http_server/compat/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

require 'fluent/plugin_helper/http_server/methods'
require 'fluent/plugin_helper/http_server/compat/webrick_handler'
require 'fluent/plugin_helper/http_server/compat/ssl_context_extractor'

module Fluent
module PluginHelper
Expand All @@ -24,16 +25,26 @@ module Compat
class Server
# @param logger [Logger]
# @param default_app [Object] ignored option. only for compat
def initialize(addr:, port:, logger:, default_app: nil)
# @param tls_context [OpenSSL::SSL::SSLContext]
def initialize(addr:, port:, logger:, default_app: nil, tls_context: nil)
@addr = addr
@port = port
@logger = logger
@server = WEBrick::HTTPServer.new(

config = {
BindAddress: @addr,
Port: @port,
Logger: WEBrick::Log.new(STDERR, WEBrick::Log::FATAL),
AccessLog: [],
)
}
if tls_context
require 'webrick/https'
@logger.warn('Webrick ignores given TLS version')
tls_opt = Fluent::PluginHelper::HttpServer::Compat::SSLContextExtractor.extract(tls_context)
config = tls_opt.merge(**config)
end

@server = WEBrick::HTTPServer.new(config)

# @example ["/example.json", :get, handler object]
@methods = []
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#
# Fluentd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

module Fluent
module PluginHelper
module HttpServer
module Compat
# This class converts OpenSSL::SSL::SSLContext to Webrick SSL Config because webrick does not have interface to pass OpenSSL::SSL::SSLContext directory
# https://github.com/ruby/webrick/blob/v1.6.0/lib/webrick/ssl.rb#L67-L88
class SSLContextExtractor

#
# memo: https://github.com/ruby/webrick/blob/v1.6.0/lib/webrick/ssl.rb#L180-L205
# @param ctx [OpenSSL::SSL::SSLContext]
def self.extract(ctx)
{
SSLEnable: true,
SSLPrivateKey: ctx.key,
SSLCertificate: ctx.cert,
SSLClientCA: ctx.client_ca,
SSLExtraChainCert: ctx.extra_chain_cert,
SSLCACertificateFile: ctx.ca_file,
SSLCACertificatePath: ctx.ca_path,
SSLCertificateStore: ctx.cert_store,
SSLTmpDhCallback: ctx.tmp_dh_callback,
SSLVerifyClient: ctx.verify_mode,
SSLVerifyDepth: ctx.verify_depth,
SSLVerifyCallback: ctx.verify_callback,
SSLServerNameCallback: ctx.servername_cb,
SSLTimeout: ctx.timeout,
SSLOptions: ctx.options,
SSLCiphers: ctx.ciphers,
}
end
end
end
end
end
end
22 changes: 14 additions & 8 deletions lib/fluent/plugin_helper/http_server/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,26 @@ module Fluent
module PluginHelper
module HttpServer
class Server
# @param logger [Logger]
# @param default_app [Object] This method must have #call.
def initialize(addr:, port:, logger:, default_app: nil)
# @param tls_context [OpenSSL::SSL::SSLContext]
def initialize(addr:, port:, logger:, default_app: nil, tls_context: nil)
@addr = addr
@port = port
@logger = logger

# TODO: support https and http2
@uri = URI("http://#{@addr}:#{@port}").to_s
# TODO: support http2
scheme = tls_context ? 'https' : 'http'
@uri = URI("#{scheme}://#{@addr}:#{@port}").to_s
@router = Router.new(default_app)
@reactor = Async::Reactor.new
@server = Async::HTTP::Server.new(
App.new(@router, @logger),
Async::HTTP::Endpoint.parse(@uri)
)
@reactor = Async::Reactor.new(nil, logger: @logger)

opts = if tls_context
{ ssl_context: tls_context }
else
{}
end
@server = Async::HTTP::Server.new(App.new(@router, @logger), Async::HTTP::Endpoint.parse(@uri, **opts))

if block_given?
yield(self)
Expand Down
41 changes: 41 additions & 0 deletions lib/fluent/plugin_helper/http_server/ssl_context_builder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#
# Fluentd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require 'fluent/plugin_helper/cert_option'

module Fluent
module PluginHelper
module HttpServer
# In order not to expose CertOption's methods unnecessary
class SSLContextBuilder
include Fluent::PluginHelper::CertOption

def initialize(log)
@log = log
end

# @param config [Fluent::Config::Section] @transport_config
def build(config)
cert_option_create_context(config.version, config.insecure, config.ciphers, config)
end

private

attr_reader :log
end
end
end
end
2 changes: 0 additions & 2 deletions test/config/test_section.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ class TestSection < ::Test::Unit::TestCase
assert_equal("email", s1.send)
assert_equal("normal", s1.klass)
assert_equal(5, s1.keys)

assert_raise(NoMethodError) { s1.dup }
end

test 'creates object which contains specified hash object itself, including fields with at prefix' do
Expand Down

0 comments on commit ae8b374

Please sign in to comment.