fluent-plugin-out-http-ext-ignore-ssl-errors 0.0.1
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/.coveralls.yml +2 -0
- data/.gitignore +9 -0
- data/.travis.yml +18 -0
- data/CHANGELOG.md +24 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +11 -0
- data/README.md +43 -0
- data/Rakefile +11 -0
- data/fluent-plugin-out-http-ext.gemspec +23 -0
- data/lib/fluent/plugin/out_http_ext.rb +254 -0
- data/lib/fluent/test/http_output_test.rb +54 -0
- data/test/plugin/test_helper.rb +3 -0
- data/test/plugin/test_out_http_ext.rb +511 -0
- metadata +152 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 44998e4900ae688e02a1c43658ff89405b0c4c67
|
4
|
+
data.tar.gz: 9f27da2e6130e79d1323563fe5ac1ab3e23e8361
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6c4bd94668ce17d69136af53c59c2ec54283f3ceb44d849d1bc0093e72189352bcd531f0e77194b5aee34ad255e9c7cdfa16274735052d5afa1ff41f52ce8aff
|
7
|
+
data.tar.gz: fcc40c34c6115f4cbd9548c2cc1ab0235a09797ef097eedb999dd1a825df0fa1380866541df5686611d3fda1adcb78b9e9b001ee03d14266207aad493e58e6e5
|
data/.coveralls.yml
ADDED
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## 0.1.5
|
4
|
+
* Add headers directive
|
5
|
+
* Add use_ssl directive
|
6
|
+
|
7
|
+
## 0.1.4
|
8
|
+
* #11 Updated Fluentd dependency to: [">= 0.10.0", "< 2"]
|
9
|
+
* #10 `password` is now marked as a [secret option](https://github.com/fluent/fluentd/pull/604)
|
10
|
+
|
11
|
+
## 0.1.3
|
12
|
+
* Added a new configuration option: `raise_on_error` (default: true)
|
13
|
+
* In order to let the plugin raise exceptions like it did in 0.1.1: keep using your configuration as-is
|
14
|
+
* In order to suppress all exceptions: add `raise_on_error false` to your configuration
|
15
|
+
|
16
|
+
## 0.1.2
|
17
|
+
* #6 Catch all `StandardError`s during HTTP request to prevent td-agent from freezing
|
18
|
+
|
19
|
+
## 0.1.1
|
20
|
+
* #2 Use yajl instead of json as json serializer
|
21
|
+
* #1 Fix a bug where a nil HTTP response caused the plugin to stop working
|
22
|
+
|
23
|
+
## 0.1.0
|
24
|
+
* Initial release
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
2
|
+
you may not use this file except in compliance with the License.
|
3
|
+
You may obtain a copy of the License at
|
4
|
+
|
5
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
|
7
|
+
Unless required by applicable law or agreed to in writing, software
|
8
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
9
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
10
|
+
See the License for the specific language governing permissions and
|
11
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# fluent-plugin-out-http-ext, a plugin for [Fluentd](http://fluentd.org)
|
2
|
+
|
3
|
+
[](https://travis-ci.org/kawasakitoshiya/fluent-plugin-out-http-ext)
|
4
|
+
[](https://coveralls.io/github/kawasakitoshiya/fluent-plugin-out-http-ext?branch=master)
|
5
|
+
|
6
|
+
**This is a fork of [ento / fluent-plugin-out-http](https://github.com/ento/fluent-plugin-out-http)**
|
7
|
+
|
8
|
+
A generic [fluentd][1] output plugin for sending logs to an HTTP endpoint
|
9
|
+
|
10
|
+
## Configuration options
|
11
|
+
|
12
|
+
<match *>
|
13
|
+
type http_ext
|
14
|
+
endpoint_url http://localhost.local/api/<data.id> # <data.id> refres to data.id in the record like {"data"=> {"id"=> 1, "name"=> "foo"}}
|
15
|
+
http_method put # default: post
|
16
|
+
serializer json # default: form
|
17
|
+
rate_limit_msec 100 # default: 0 = no rate limiting
|
18
|
+
open_timeout 5 # default: nil = no timeout
|
19
|
+
read_timeout 10 # default: 60
|
20
|
+
raise_on_error false # default: true
|
21
|
+
raise_on_http_failure true # default: false
|
22
|
+
ignore_http_status_code 300,400..499 # default: nil # do not raise on these http_hstatus codes
|
23
|
+
authentication basic # default: none
|
24
|
+
username alice # default: ''
|
25
|
+
password bobpop # default: '', secret: true
|
26
|
+
use_ssl true # default: false
|
27
|
+
<headers>
|
28
|
+
HeaderExample1 header1
|
29
|
+
HeaderExample2 header2
|
30
|
+
</headers>
|
31
|
+
</match>
|
32
|
+
|
33
|
+
## Usage notes
|
34
|
+
|
35
|
+
If you'd like to retry failed requests, consider using [fluent-plugin-bufferize][3].
|
36
|
+
|
37
|
+
----
|
38
|
+
|
39
|
+
Heavily based on [fluent-plugin-growthforecast][2]
|
40
|
+
|
41
|
+
[1]: http://fluentd.org/
|
42
|
+
[2]: https://github.com/tagomoris/fluent-plugin-growthforecast
|
43
|
+
[3]: https://github.com/sabottenda/fluent-plugin-bufferize
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = "fluent-plugin-out-http-ext-ignore-ssl-errors"
|
5
|
+
gem.version = "0.0.1"
|
6
|
+
gem.authors = ["Joyce Chan"]
|
7
|
+
gem.email = ["none@none.com"]
|
8
|
+
gem.summary = %q{A generic Fluentd output plugin to send logs to an HTTP endpoint with SSL and Header option, extended from kawasakitoshiya@gmail.com's similarily named gem'}
|
9
|
+
gem.description = gem.summary
|
10
|
+
gem.licenses = ["Apache-2.0"]
|
11
|
+
|
12
|
+
gem.files = `git ls-files`.split($\)
|
13
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
14
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
|
17
|
+
gem.add_runtime_dependency "yajl-ruby", "~> 1.0"
|
18
|
+
gem.add_runtime_dependency "fluentd", [">= 0.10.0", "< 2"]
|
19
|
+
gem.add_development_dependency "bundler"
|
20
|
+
gem.add_development_dependency "rake"
|
21
|
+
gem.add_development_dependency "test-unit", ">= 3.1.0"
|
22
|
+
gem.add_development_dependency "coveralls"
|
23
|
+
end
|
@@ -0,0 +1,254 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
class Array
|
4
|
+
def to_set
|
5
|
+
Set.new(self)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
class Hash
|
11
|
+
"""
|
12
|
+
each traverse in hash
|
13
|
+
"""
|
14
|
+
def each_deep(&proc)
|
15
|
+
self.each_deep_detail([], &proc)
|
16
|
+
end
|
17
|
+
|
18
|
+
def each_deep_detail(directory, &proc)
|
19
|
+
self.each do |k, v|
|
20
|
+
current = directory + [k]
|
21
|
+
if v.kind_of?(Hash)
|
22
|
+
v.each_deep_detail(current, &proc)
|
23
|
+
else
|
24
|
+
yield(current, v)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
class StatusCodeParser
|
32
|
+
"""
|
33
|
+
parse status code string to array of codes
|
34
|
+
"""
|
35
|
+
def self.range?(str)
|
36
|
+
# i.e. 200..399 => return true
|
37
|
+
return /^\d{3}..\d{3}$/ =~ str ? true : false
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.number?(str)
|
41
|
+
return /^\d{3}$/ =~ str ? true : false
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.get_array(str)
|
45
|
+
if self.range?(str)
|
46
|
+
ends = str.split('..').map{|d| Integer(d)}
|
47
|
+
return (ends[0]..ends[1]).to_a
|
48
|
+
elsif self.number?(str)
|
49
|
+
return [str.to_i]
|
50
|
+
else
|
51
|
+
raise "invalid status code range format"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.convert(range_str)
|
56
|
+
elems = range_str.split(',')
|
57
|
+
status_codes = elems.flat_map do |elem|
|
58
|
+
self.get_array(elem)
|
59
|
+
end
|
60
|
+
return status_codes.to_set
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class Fluent::HTTPOutput < Fluent::Output
|
65
|
+
Fluent::Plugin.register_output('http_ext', self)
|
66
|
+
|
67
|
+
def initialize
|
68
|
+
super
|
69
|
+
require 'net/http'
|
70
|
+
require 'uri'
|
71
|
+
require 'yajl'
|
72
|
+
require 'set'
|
73
|
+
end
|
74
|
+
|
75
|
+
# Endpoint URL ex. localhost.local/api/
|
76
|
+
config_param :endpoint_url, :string
|
77
|
+
|
78
|
+
# HTTP method
|
79
|
+
config_param :http_method, :string, :default => :post
|
80
|
+
|
81
|
+
# form | json
|
82
|
+
config_param :serializer, :string, :default => :form
|
83
|
+
|
84
|
+
# true | false
|
85
|
+
config_param :use_ssl, :bool, :default => false
|
86
|
+
|
87
|
+
config_param :open_timeout, :integer, :default => nil
|
88
|
+
config_param :read_timeout, :integer, :default => 60
|
89
|
+
|
90
|
+
# Simple rate limiting: ignore any records within `rate_limit_msec`
|
91
|
+
# since the last one.
|
92
|
+
config_param :rate_limit_msec, :integer, :default => 0
|
93
|
+
|
94
|
+
# Raise errors that were rescued during HTTP requests?
|
95
|
+
config_param :raise_on_error, :bool, :default => true
|
96
|
+
|
97
|
+
# Raise errors when HTTP response code was not successful.
|
98
|
+
config_param :raise_on_http_failure, :bool, :default => false
|
99
|
+
config_param :ignore_http_status_code, :string, :default => nil
|
100
|
+
# nil | 'none' | 'basic'
|
101
|
+
config_param :authentication, :string, :default => nil
|
102
|
+
config_param :username, :string, :default => ''
|
103
|
+
config_param :password, :string, :default => '', :secret => true
|
104
|
+
|
105
|
+
def configure(conf)
|
106
|
+
super
|
107
|
+
|
108
|
+
serializers = [:json, :form]
|
109
|
+
@serializer = if serializers.include? @serializer.intern
|
110
|
+
@serializer.intern
|
111
|
+
else
|
112
|
+
:form
|
113
|
+
end
|
114
|
+
|
115
|
+
http_methods = [:get, :put, :post, :delete]
|
116
|
+
@http_method = if http_methods.include? @http_method.intern
|
117
|
+
@http_method.intern
|
118
|
+
else
|
119
|
+
:post
|
120
|
+
end
|
121
|
+
|
122
|
+
@ignore_http_status_code = if @ignore_http_status_code.nil?
|
123
|
+
[].to_set
|
124
|
+
else
|
125
|
+
StatusCodeParser.convert(@ignore_http_status_code)
|
126
|
+
end
|
127
|
+
|
128
|
+
@auth = case @authentication
|
129
|
+
when 'basic' then :basic
|
130
|
+
else
|
131
|
+
:none
|
132
|
+
end
|
133
|
+
@headers = {}
|
134
|
+
conf.elements.each do |element|
|
135
|
+
if element.name == 'headers'
|
136
|
+
@headers = element.to_hash
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def start
|
142
|
+
super
|
143
|
+
end
|
144
|
+
|
145
|
+
def shutdown
|
146
|
+
super
|
147
|
+
end
|
148
|
+
|
149
|
+
def format_url(tag, time, record)
|
150
|
+
'''
|
151
|
+
replace format string to value
|
152
|
+
example
|
153
|
+
/test/<data> =(use {data: 1})> /test/1
|
154
|
+
/test/<hash.data> =(use {hash:{data:2}})> /test/2
|
155
|
+
'''
|
156
|
+
result_url = @endpoint_url
|
157
|
+
record.each_deep do |key_dir, value|
|
158
|
+
result_url = result_url.gsub(/<#{key_dir.join(".")}>/, value.to_s)
|
159
|
+
end
|
160
|
+
return result_url
|
161
|
+
end
|
162
|
+
|
163
|
+
def set_body(req, tag, time, record)
|
164
|
+
if @serializer == :json
|
165
|
+
set_json_body(req, record)
|
166
|
+
else
|
167
|
+
req.set_form_data(record)
|
168
|
+
end
|
169
|
+
req
|
170
|
+
end
|
171
|
+
|
172
|
+
def set_header(req, tag, time, record)
|
173
|
+
@headers.each do |key, value|
|
174
|
+
req[key] = value
|
175
|
+
end
|
176
|
+
req
|
177
|
+
end
|
178
|
+
|
179
|
+
def set_json_body(req, data)
|
180
|
+
req.body = Yajl.dump(data)
|
181
|
+
req['Content-Type'] = 'application/json'
|
182
|
+
end
|
183
|
+
|
184
|
+
def create_request(tag, time, record)
|
185
|
+
url = format_url(tag, time, record)
|
186
|
+
uri = URI.parse(url)
|
187
|
+
req = Net::HTTP.const_get(@http_method.to_s.capitalize).new(uri.path)
|
188
|
+
set_body(req, tag, time, record)
|
189
|
+
set_header(req, tag, time, record)
|
190
|
+
return req, uri
|
191
|
+
end
|
192
|
+
|
193
|
+
def send_request(req, uri)
|
194
|
+
is_rate_limited = (@rate_limit_msec != 0 and not @last_request_time.nil?)
|
195
|
+
if is_rate_limited and ((Time.now.to_f - @last_request_time) * 1000.0 < @rate_limit_msec)
|
196
|
+
$log.info('Dropped request due to rate limiting')
|
197
|
+
return
|
198
|
+
end
|
199
|
+
|
200
|
+
res = nil
|
201
|
+
|
202
|
+
begin
|
203
|
+
if @auth and @auth == :basic
|
204
|
+
req.basic_auth(@username, @password)
|
205
|
+
end
|
206
|
+
@last_request_time = Time.now.to_f
|
207
|
+
client = Net::HTTP.new(uri.host, uri.port)
|
208
|
+
if @use_ssl
|
209
|
+
client.use_ssl = true
|
210
|
+
client.ca_file = OpenSSL::X509::DEFAULT_CERT_FILE
|
211
|
+
client.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
212
|
+
end
|
213
|
+
res = client.start {|http|
|
214
|
+
http.open_timeout = @open_timeout
|
215
|
+
http.read_timeout = @read_timeout
|
216
|
+
http.request(req)
|
217
|
+
}
|
218
|
+
rescue => e # rescue all StandardErrors
|
219
|
+
# server didn't respond
|
220
|
+
$log.warn "Net::HTTP.#{req.method.capitalize} raises exception: #{e.class}, '#{e.message}'"
|
221
|
+
raise e if @raise_on_error
|
222
|
+
else
|
223
|
+
unless res and res.is_a?(Net::HTTPSuccess)
|
224
|
+
res_summary = if res
|
225
|
+
"#{res.code} #{res.message} #{res.body}"
|
226
|
+
else
|
227
|
+
"res=nil"
|
228
|
+
end
|
229
|
+
warning = "failed to #{req.method} #{uri} (#{res_summary})"
|
230
|
+
$log.warn warning
|
231
|
+
if @raise_on_http_failure
|
232
|
+
unless @ignore_http_status_code.include?(res.code.to_i)
|
233
|
+
raise warning
|
234
|
+
else
|
235
|
+
$log.debug "ignore http status code #{req.method}"
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
end #end unless
|
240
|
+
end # end begin
|
241
|
+
end # end send_request
|
242
|
+
|
243
|
+
def handle_record(tag, time, record)
|
244
|
+
req, uri = create_request(tag, time, record)
|
245
|
+
send_request(req, uri)
|
246
|
+
end
|
247
|
+
|
248
|
+
def emit(tag, es, chain)
|
249
|
+
es.each do |time, record|
|
250
|
+
handle_record(tag, time, record)
|
251
|
+
end
|
252
|
+
chain.next
|
253
|
+
end
|
254
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
|
4
|
+
begin
|
5
|
+
Bundler.setup(:default, :development)
|
6
|
+
rescue Bundler::BundlerError => e
|
7
|
+
$stderr.puts e.message
|
8
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
9
|
+
exit e.status_code
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'test/unit'
|
13
|
+
|
14
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
15
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
16
|
+
|
17
|
+
require 'fluent/test'
|
18
|
+
|
19
|
+
unless ENV.has_key?('VERBOSE')
|
20
|
+
nulllogger = Object.new
|
21
|
+
nulllogger.instance_eval {|obj|
|
22
|
+
def method_missing(method, *args)
|
23
|
+
# pass
|
24
|
+
end
|
25
|
+
}
|
26
|
+
$log = nulllogger
|
27
|
+
end
|
28
|
+
|
29
|
+
class Test::Unit::TestCase
|
30
|
+
end
|
31
|
+
|
32
|
+
require 'webrick'
|
33
|
+
|
34
|
+
# to handle POST/PUT/DELETE ...
|
35
|
+
module WEBrick::HTTPServlet
|
36
|
+
class ProcHandler < AbstractServlet
|
37
|
+
alias do_POST do_GET
|
38
|
+
alias do_PUT do_GET
|
39
|
+
alias do_DELETE do_GET
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_code(server, port, path, headers={})
|
44
|
+
require 'net/http'
|
45
|
+
Net::HTTP.start(server, port){|http|
|
46
|
+
http.get(path, headers).code
|
47
|
+
}
|
48
|
+
end
|
49
|
+
def get_content(server, port, path, headers={})
|
50
|
+
require 'net/http'
|
51
|
+
Net::HTTP.start(server, port){|http|
|
52
|
+
http.get(path, headers).body
|
53
|
+
}
|
54
|
+
end
|
@@ -0,0 +1,511 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'uri'
|
3
|
+
require 'yajl'
|
4
|
+
require 'fluent/test/http_output_test'
|
5
|
+
require 'fluent/plugin/out_http_ext'
|
6
|
+
|
7
|
+
|
8
|
+
TEST_LISTEN_PORT = 5126
|
9
|
+
|
10
|
+
|
11
|
+
class HTTPOutputTestBase < Test::Unit::TestCase
|
12
|
+
# setup / teardown for servers
|
13
|
+
def setup
|
14
|
+
Fluent::Test.setup
|
15
|
+
@posts = []
|
16
|
+
@puts = []
|
17
|
+
@prohibited = 0
|
18
|
+
@requests = 0
|
19
|
+
@auth = false
|
20
|
+
@status = 200
|
21
|
+
@dummy_server_thread = Thread.new do
|
22
|
+
srv = if ENV['VERBOSE']
|
23
|
+
WEBrick::HTTPServer.new({:BindAddress => '127.0.0.1', :Port => TEST_LISTEN_PORT})
|
24
|
+
else
|
25
|
+
logger = WEBrick::Log.new('/dev/null', WEBrick::BasicLog::DEBUG)
|
26
|
+
WEBrick::HTTPServer.new({:BindAddress => '127.0.0.1', :Port => TEST_LISTEN_PORT, :Logger => logger, :AccessLog => []})
|
27
|
+
end
|
28
|
+
begin
|
29
|
+
allowed_methods = %w(POST PUT)
|
30
|
+
srv.mount_proc('/api/') { |req,res|
|
31
|
+
@requests += 1
|
32
|
+
unless allowed_methods.include? req.request_method
|
33
|
+
res.status = 405
|
34
|
+
res.body = 'request method mismatch'
|
35
|
+
next
|
36
|
+
end
|
37
|
+
if @auth and req.header['authorization'][0] == 'Basic YWxpY2U6c2VjcmV0IQ==' # pattern of user='alice' passwd='secret!'
|
38
|
+
# ok, authorized
|
39
|
+
elsif @auth
|
40
|
+
res.status = 403
|
41
|
+
@prohibited += 1
|
42
|
+
next
|
43
|
+
else
|
44
|
+
# ok, authorization not required
|
45
|
+
end
|
46
|
+
|
47
|
+
record = {:auth => nil}
|
48
|
+
if req.content_type == 'application/json'
|
49
|
+
record[:json] = Yajl.load(req.body)
|
50
|
+
else
|
51
|
+
record[:form] = Hash[*(req.body.split('&').map{|kv|kv.split('=')}.flatten)]
|
52
|
+
end
|
53
|
+
|
54
|
+
instance_variable_get("@#{req.request_method.downcase}s").push(record)
|
55
|
+
|
56
|
+
res.status = @status
|
57
|
+
}
|
58
|
+
srv.mount_proc('/') { |req,res|
|
59
|
+
res.status = 200
|
60
|
+
res.body = 'running'
|
61
|
+
}
|
62
|
+
srv.mount_proc('/slow_5') { |req,res|
|
63
|
+
sleep 5
|
64
|
+
res.status = 200
|
65
|
+
res.body = 'slow_5'
|
66
|
+
}
|
67
|
+
srv.mount_proc('/slow_10') { |req,res|
|
68
|
+
sleep 10
|
69
|
+
res.status = 200
|
70
|
+
res.body = 'slow_10'
|
71
|
+
}
|
72
|
+
srv.mount_proc('/status_code') { |req,res|
|
73
|
+
r = Yajl.load(req.body)
|
74
|
+
code = r["code"]
|
75
|
+
res.status = code.to_s
|
76
|
+
res.body = ''
|
77
|
+
}
|
78
|
+
|
79
|
+
srv.start
|
80
|
+
ensure
|
81
|
+
srv.shutdown
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# to wait completion of dummy server.start()
|
86
|
+
require 'thread'
|
87
|
+
cv = ConditionVariable.new
|
88
|
+
watcher = Thread.new {
|
89
|
+
connected = false
|
90
|
+
while not connected
|
91
|
+
begin
|
92
|
+
get_content('localhost', TEST_LISTEN_PORT, '/')
|
93
|
+
connected = true
|
94
|
+
rescue Errno::ECONNREFUSED
|
95
|
+
sleep 0.1
|
96
|
+
rescue StandardError => e
|
97
|
+
p e
|
98
|
+
sleep 0.1
|
99
|
+
end
|
100
|
+
end
|
101
|
+
cv.signal
|
102
|
+
}
|
103
|
+
mutex = Mutex.new
|
104
|
+
mutex.synchronize {
|
105
|
+
cv.wait(mutex)
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_dummy_server
|
110
|
+
host = '127.0.0.1'
|
111
|
+
port = TEST_LISTEN_PORT
|
112
|
+
client = Net::HTTP.start(host, port)
|
113
|
+
|
114
|
+
assert_equal '200', client.request_get('/').code
|
115
|
+
assert_equal '200', client.request_post('/api/service/metrics/hoge', 'number=1&mode=gauge').code
|
116
|
+
|
117
|
+
assert_equal 1, @posts.size
|
118
|
+
|
119
|
+
assert_equal '1', @posts[0][:form]['number']
|
120
|
+
assert_equal 'gauge', @posts[0][:form]['mode']
|
121
|
+
assert_nil @posts[0][:auth]
|
122
|
+
|
123
|
+
@auth = true
|
124
|
+
|
125
|
+
assert_equal '403', client.request_post('/api/service/metrics/pos', 'number=30&mode=gauge').code
|
126
|
+
|
127
|
+
req_with_auth = lambda do |number, mode, user, pass|
|
128
|
+
url = URI.parse("http://#{host}:#{port}/api/service/metrics/pos")
|
129
|
+
req = Net::HTTP::Post.new(url.path)
|
130
|
+
req.basic_auth user, pass
|
131
|
+
req.set_form_data({'number'=>number, 'mode'=>mode})
|
132
|
+
req
|
133
|
+
end
|
134
|
+
|
135
|
+
assert_equal '403', client.request(req_with_auth.call(500, 'count', 'alice', 'wrong password!')).code
|
136
|
+
|
137
|
+
assert_equal '403', client.request(req_with_auth.call(500, 'count', 'alice', 'wrong password!')).code
|
138
|
+
|
139
|
+
assert_equal 1, @posts.size
|
140
|
+
|
141
|
+
assert_equal '200', client.request(req_with_auth.call(500, 'count', 'alice', 'secret!')).code
|
142
|
+
|
143
|
+
assert_equal 2, @posts.size
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
def teardown
|
148
|
+
@dummy_server_thread.kill
|
149
|
+
@dummy_server_thread.join
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
class HTTPOutputTest < HTTPOutputTestBase
|
154
|
+
CONFIG = %[
|
155
|
+
endpoint_url http://127.0.0.1:#{TEST_LISTEN_PORT}/api/
|
156
|
+
]
|
157
|
+
|
158
|
+
CONFIG_JSON = %[
|
159
|
+
endpoint_url http://127.0.0.1:#{TEST_LISTEN_PORT}/api/
|
160
|
+
serializer json
|
161
|
+
]
|
162
|
+
|
163
|
+
CONFIG_PUT = %[
|
164
|
+
endpoint_url http://127.0.0.1:#{TEST_LISTEN_PORT}/api/
|
165
|
+
http_method put
|
166
|
+
]
|
167
|
+
|
168
|
+
CONFIG_HTTP_ERROR = %[
|
169
|
+
endpoint_url https://127.0.0.1:#{TEST_LISTEN_PORT + 1}/api/
|
170
|
+
]
|
171
|
+
|
172
|
+
CONFIG_HTTP_ERROR_SUPPRESSED = %[
|
173
|
+
endpoint_url https://127.0.0.1:#{TEST_LISTEN_PORT + 1}/api/
|
174
|
+
raise_on_error false
|
175
|
+
]
|
176
|
+
|
177
|
+
CONFIG_RAISE_ON_HTTP_FAILURE = %[
|
178
|
+
endpoint_url http://127.0.0.1:#{TEST_LISTEN_PORT}/api/
|
179
|
+
raise_on_http_failure true
|
180
|
+
]
|
181
|
+
|
182
|
+
RATE_LIMIT_MSEC = 1200
|
183
|
+
|
184
|
+
CONFIG_RATE_LIMIT = %[
|
185
|
+
endpoint_url http://127.0.0.1:#{TEST_LISTEN_PORT}/api/
|
186
|
+
rate_limit_msec #{RATE_LIMIT_MSEC}
|
187
|
+
]
|
188
|
+
|
189
|
+
CONFIG_NOT_READ_TIMEOUT = %[
|
190
|
+
endpoint_url http://127.0.0.1:#{TEST_LISTEN_PORT}/slow_5/
|
191
|
+
read_timeout 7
|
192
|
+
]
|
193
|
+
CONFIG_READ_TIMEOUT = %[
|
194
|
+
endpoint_url http://127.0.0.1:#{TEST_LISTEN_PORT}/slow_10/
|
195
|
+
read_timeout 7
|
196
|
+
]
|
197
|
+
CONFIG_IGNORE_NONE = %[
|
198
|
+
endpoint_url http://127.0.0.1:#{TEST_LISTEN_PORT}/status_code/
|
199
|
+
serializer json
|
200
|
+
raise_on_http_failure true
|
201
|
+
]
|
202
|
+
CONFIG_IGNORE_409 = %[
|
203
|
+
endpoint_url http://127.0.0.1:#{TEST_LISTEN_PORT}/status_code/
|
204
|
+
serializer json
|
205
|
+
raise_on_http_failure true
|
206
|
+
ignore_http_status_code 409
|
207
|
+
]
|
208
|
+
CONFIG_IGNORE_4XX = %[
|
209
|
+
endpoint_url http://127.0.0.1:#{TEST_LISTEN_PORT}/status_code/
|
210
|
+
serializer json
|
211
|
+
raise_on_http_failure true
|
212
|
+
ignore_http_status_code 400..499
|
213
|
+
]
|
214
|
+
CONFIG_IGNORE_4XX_5XX = %[
|
215
|
+
endpoint_url http://127.0.0.1:#{TEST_LISTEN_PORT}/status_code/
|
216
|
+
serializer json
|
217
|
+
raise_on_http_failure true
|
218
|
+
ignore_http_status_code 400..599
|
219
|
+
]
|
220
|
+
|
221
|
+
def create_driver(conf=CONFIG, tag='test.metrics')
|
222
|
+
Fluent::Test::OutputTestDriver.new(Fluent::HTTPOutput, tag).configure(conf)
|
223
|
+
end
|
224
|
+
|
225
|
+
def test_configure
|
226
|
+
d = create_driver
|
227
|
+
assert_equal "http://127.0.0.1:#{TEST_LISTEN_PORT}/api/", d.instance.endpoint_url
|
228
|
+
assert_equal :form, d.instance.serializer
|
229
|
+
|
230
|
+
d = create_driver CONFIG_JSON
|
231
|
+
assert_equal "http://127.0.0.1:#{TEST_LISTEN_PORT}/api/", d.instance.endpoint_url
|
232
|
+
assert_equal :json, d.instance.serializer
|
233
|
+
end
|
234
|
+
|
235
|
+
def test_emit_form
|
236
|
+
d = create_driver
|
237
|
+
d.emit({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => "\xe3\x81\x82".force_encoding("ascii-8bit") })
|
238
|
+
d.run
|
239
|
+
|
240
|
+
assert_equal 1, @posts.size
|
241
|
+
record = @posts[0]
|
242
|
+
|
243
|
+
assert_equal '50', record[:form]['field1']
|
244
|
+
assert_equal '20', record[:form]['field2']
|
245
|
+
assert_equal '10', record[:form]['field3']
|
246
|
+
assert_equal '1', record[:form]['otherfield']
|
247
|
+
assert_nil record[:auth]
|
248
|
+
|
249
|
+
d.emit({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
|
250
|
+
d.run
|
251
|
+
|
252
|
+
assert_equal 2, @posts.size
|
253
|
+
end
|
254
|
+
|
255
|
+
def test_emit_form_put
|
256
|
+
d = create_driver CONFIG_PUT
|
257
|
+
d.emit({ 'field1' => 50 })
|
258
|
+
d.run
|
259
|
+
|
260
|
+
assert_equal 0, @posts.size
|
261
|
+
assert_equal 1, @puts.size
|
262
|
+
record = @puts[0]
|
263
|
+
|
264
|
+
assert_equal '50', record[:form]['field1']
|
265
|
+
assert_nil record[:auth]
|
266
|
+
|
267
|
+
d.emit({ 'field1' => 50 })
|
268
|
+
d.run
|
269
|
+
|
270
|
+
assert_equal 0, @posts.size
|
271
|
+
assert_equal 2, @puts.size
|
272
|
+
end
|
273
|
+
|
274
|
+
def test_emit_json
|
275
|
+
binary_string = "\xe3\x81\x82".force_encoding("ascii-8bit")
|
276
|
+
d = create_driver CONFIG_JSON
|
277
|
+
d.emit({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => binary_string })
|
278
|
+
d.run
|
279
|
+
|
280
|
+
assert_equal 1, @posts.size
|
281
|
+
record = @posts[0]
|
282
|
+
|
283
|
+
assert_equal 50, record[:json]['field1']
|
284
|
+
assert_equal 20, record[:json]['field2']
|
285
|
+
assert_equal 10, record[:json]['field3']
|
286
|
+
assert_equal 1, record[:json]['otherfield']
|
287
|
+
assert_equal binary_string, record[:json]['binary']
|
288
|
+
assert_nil record[:auth]
|
289
|
+
end
|
290
|
+
|
291
|
+
def test_http_error_is_raised
|
292
|
+
d = create_driver CONFIG_HTTP_ERROR
|
293
|
+
assert_raise Errno::ECONNREFUSED do
|
294
|
+
d.emit({ 'field1' => 50 })
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
def test_http_error_is_suppressed_with_raise_on_error_false
|
299
|
+
d = create_driver CONFIG_HTTP_ERROR_SUPPRESSED
|
300
|
+
d.emit({ 'field1' => 50 })
|
301
|
+
d.run
|
302
|
+
# drive asserts the next output chain is called;
|
303
|
+
# so no exception means our plugin handled the error
|
304
|
+
|
305
|
+
assert_equal 0, @requests
|
306
|
+
end
|
307
|
+
|
308
|
+
def test_http_failure_is_not_raised_on_http_failure_true_and_status_201
|
309
|
+
@status = 201
|
310
|
+
|
311
|
+
d = create_driver CONFIG_RAISE_ON_HTTP_FAILURE
|
312
|
+
assert_nothing_raised do
|
313
|
+
d.emit({ 'field1' => 50 })
|
314
|
+
end
|
315
|
+
|
316
|
+
@status = 200
|
317
|
+
end
|
318
|
+
|
319
|
+
def test_http_failure_is_raised_on_http_failure_true
|
320
|
+
@status = 500
|
321
|
+
|
322
|
+
d = create_driver CONFIG_RAISE_ON_HTTP_FAILURE
|
323
|
+
assert_raise RuntimeError do
|
324
|
+
d.emit({ 'field1' => 50 })
|
325
|
+
end
|
326
|
+
|
327
|
+
@status = 200
|
328
|
+
end
|
329
|
+
|
330
|
+
def test_rate_limiting
|
331
|
+
d = create_driver CONFIG_RATE_LIMIT
|
332
|
+
record = { :k => 1 }
|
333
|
+
|
334
|
+
last_emit = _current_msec
|
335
|
+
d.emit(record)
|
336
|
+
d.run
|
337
|
+
|
338
|
+
assert_equal 1, @posts.size
|
339
|
+
|
340
|
+
d.emit({})
|
341
|
+
d.run
|
342
|
+
assert last_emit + RATE_LIMIT_MSEC > _current_msec, "Still under rate limiting interval"
|
343
|
+
assert_equal 1, @posts.size
|
344
|
+
|
345
|
+
wait_msec = 500
|
346
|
+
sleep (last_emit + RATE_LIMIT_MSEC - _current_msec + wait_msec) * 0.001
|
347
|
+
|
348
|
+
assert last_emit + RATE_LIMIT_MSEC < _current_msec, "No longer under rate limiting interval"
|
349
|
+
d.emit(record)
|
350
|
+
d.run
|
351
|
+
assert_equal 2, @posts.size
|
352
|
+
end
|
353
|
+
|
354
|
+
def test_read_timeout
|
355
|
+
d = create_driver CONFIG_READ_TIMEOUT
|
356
|
+
assert_equal 7, d.instance.read_timeout
|
357
|
+
err = Net.const_defined?(:ReadTimeout) ? Net::ReadTimeout : Timeout::Error
|
358
|
+
assert_raise err do
|
359
|
+
d.emit({})
|
360
|
+
d.run
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
def test_not_read_timeout
|
365
|
+
d = create_driver CONFIG_NOT_READ_TIMEOUT
|
366
|
+
assert_equal 7, d.instance.read_timeout
|
367
|
+
assert_nothing_raised do
|
368
|
+
d.emit({})
|
369
|
+
d.run
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
def test_ignore_none
|
374
|
+
d = create_driver CONFIG_IGNORE_NONE
|
375
|
+
assert_equal [].to_set, d.instance.ignore_http_status_code
|
376
|
+
|
377
|
+
assert_raise do
|
378
|
+
d.emit({:code=> 409})
|
379
|
+
d.run
|
380
|
+
end
|
381
|
+
|
382
|
+
assert_raise do
|
383
|
+
d.emit({:code => 500})
|
384
|
+
d.run
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
def test_ignore_409
|
389
|
+
d = create_driver CONFIG_IGNORE_409
|
390
|
+
assert_equal [409].to_set, d.instance.ignore_http_status_code
|
391
|
+
|
392
|
+
assert_nothing_raised do
|
393
|
+
d.emit({:code => 409})
|
394
|
+
d.run
|
395
|
+
end
|
396
|
+
assert_raise do
|
397
|
+
d.emit({:code => 404})
|
398
|
+
d.run
|
399
|
+
end
|
400
|
+
assert_raise do
|
401
|
+
d.emit({:code => 500})
|
402
|
+
d.run
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
def test_ignore_4XX
|
407
|
+
d = create_driver CONFIG_IGNORE_4XX
|
408
|
+
assert_equal (400..499).to_a.to_set, d.instance.ignore_http_status_code
|
409
|
+
|
410
|
+
assert_nothing_raised do
|
411
|
+
d.emit({:code => 409})
|
412
|
+
d.run
|
413
|
+
end
|
414
|
+
assert_nothing_raised do
|
415
|
+
d.emit({:code => 404})
|
416
|
+
d.run
|
417
|
+
end
|
418
|
+
assert_raise do
|
419
|
+
d.emit({:code => 500})
|
420
|
+
d.run
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
def test_ignore_4XX_5XX
|
425
|
+
d = create_driver CONFIG_IGNORE_4XX_5XX
|
426
|
+
assert_equal (400..599).to_a.to_set, d.instance.ignore_http_status_code
|
427
|
+
assert_nothing_raised do
|
428
|
+
d.emit({:code => 409})
|
429
|
+
d.run
|
430
|
+
end
|
431
|
+
assert_nothing_raised do
|
432
|
+
d.emit({:code => 404})
|
433
|
+
d.run
|
434
|
+
end
|
435
|
+
assert_nothing_raised do
|
436
|
+
d.emit({:code => 500})
|
437
|
+
d.run
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
def _current_msec
|
442
|
+
Time.now.to_f * 1000
|
443
|
+
end
|
444
|
+
|
445
|
+
def test_auth
|
446
|
+
@auth = true # enable authentication of dummy server
|
447
|
+
|
448
|
+
d = create_driver(CONFIG, 'test.metrics')
|
449
|
+
d.emit({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
|
450
|
+
d.run # failed in background, and output warn log
|
451
|
+
|
452
|
+
assert_equal 0, @posts.size
|
453
|
+
assert_equal 1, @prohibited
|
454
|
+
|
455
|
+
d = create_driver(CONFIG + %[
|
456
|
+
authentication basic
|
457
|
+
username alice
|
458
|
+
password wrong_password
|
459
|
+
], 'test.metrics')
|
460
|
+
d.emit({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
|
461
|
+
d.run # failed in background, and output warn log
|
462
|
+
|
463
|
+
assert_equal 0, @posts.size
|
464
|
+
assert_equal 2, @prohibited
|
465
|
+
|
466
|
+
d = create_driver(CONFIG + %[
|
467
|
+
authentication basic
|
468
|
+
username alice
|
469
|
+
password secret!
|
470
|
+
], 'test.metrics')
|
471
|
+
d.emit({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
|
472
|
+
d.run # failed in background, and output warn log
|
473
|
+
|
474
|
+
assert_equal 1, @posts.size
|
475
|
+
assert_equal 2, @prohibited
|
476
|
+
end
|
477
|
+
|
478
|
+
def test_status_code_parser()
|
479
|
+
assert_equal (400..409).to_a.to_set, StatusCodeParser.convert("400..409")
|
480
|
+
assert_equal ((400..409).to_a + [300]).to_set, StatusCodeParser.convert("400..409,300")
|
481
|
+
assert_equal ((400..409).to_a + [300]).to_set, StatusCodeParser.convert("300,400..409")
|
482
|
+
assert_equal [404, 409].to_set, StatusCodeParser.convert("404,409")
|
483
|
+
assert_equal [404, 409, 300, 301, 302, 303].to_set, StatusCodeParser.convert("404,409,300..303")
|
484
|
+
assert_equal [409].to_set, StatusCodeParser.convert("409")
|
485
|
+
assert_equal [].to_set, StatusCodeParser.convert("")
|
486
|
+
assert_raise do
|
487
|
+
StatusCodeParser.convert("400...499")
|
488
|
+
end
|
489
|
+
assert_raise do
|
490
|
+
StatusCodeParser.convert("10..20")
|
491
|
+
end
|
492
|
+
assert_raise do
|
493
|
+
StatusCodeParser.convert("4XX")
|
494
|
+
end
|
495
|
+
assert_raise do
|
496
|
+
StatusCodeParser.convert("4XX..5XX")
|
497
|
+
end
|
498
|
+
assert_raise do
|
499
|
+
StatusCodeParser.convert("200.0..400")
|
500
|
+
end
|
501
|
+
assert_raise do
|
502
|
+
StatusCodeParser.convert("-200..400")
|
503
|
+
end
|
504
|
+
|
505
|
+
end
|
506
|
+
|
507
|
+
def test_array_extend
|
508
|
+
assert_equal [].to_set, Set.new([])
|
509
|
+
assert_equal [1, 2].to_set, Set.new([1, 2])
|
510
|
+
end
|
511
|
+
end
|
metadata
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fluent-plugin-out-http-ext-ignore-ssl-errors
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joyce Chan
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-10-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: yajl-ruby
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: fluentd
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.10.0
|
34
|
+
- - "<"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '2'
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.10.0
|
44
|
+
- - "<"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '2'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: bundler
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: rake
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: test-unit
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 3.1.0
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 3.1.0
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: coveralls
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
description: A generic Fluentd output plugin to send logs to an HTTP endpoint with
|
104
|
+
SSL and Header option, extended from kawasakitoshiya@gmail.com's similarily named
|
105
|
+
gem'
|
106
|
+
email:
|
107
|
+
- none@none.com
|
108
|
+
executables: []
|
109
|
+
extensions: []
|
110
|
+
extra_rdoc_files: []
|
111
|
+
files:
|
112
|
+
- ".coveralls.yml"
|
113
|
+
- ".gitignore"
|
114
|
+
- ".travis.yml"
|
115
|
+
- CHANGELOG.md
|
116
|
+
- Gemfile
|
117
|
+
- LICENSE.txt
|
118
|
+
- README.md
|
119
|
+
- Rakefile
|
120
|
+
- fluent-plugin-out-http-ext.gemspec
|
121
|
+
- lib/fluent/plugin/out_http_ext.rb
|
122
|
+
- lib/fluent/test/http_output_test.rb
|
123
|
+
- test/plugin/test_helper.rb
|
124
|
+
- test/plugin/test_out_http_ext.rb
|
125
|
+
homepage:
|
126
|
+
licenses:
|
127
|
+
- Apache-2.0
|
128
|
+
metadata: {}
|
129
|
+
post_install_message:
|
130
|
+
rdoc_options: []
|
131
|
+
require_paths:
|
132
|
+
- lib
|
133
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
requirements: []
|
144
|
+
rubyforge_project:
|
145
|
+
rubygems_version: 2.5.1
|
146
|
+
signing_key:
|
147
|
+
specification_version: 4
|
148
|
+
summary: A generic Fluentd output plugin to send logs to an HTTP endpoint with SSL
|
149
|
+
and Header option, extended from kawasakitoshiya@gmail.com's similarily named gem'
|
150
|
+
test_files:
|
151
|
+
- test/plugin/test_helper.rb
|
152
|
+
- test/plugin/test_out_http_ext.rb
|