fluent-plugin-out-http 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,42 +1,42 @@
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
- require 'webrick/https'
34
-
35
- # to handle POST/PUT/DELETE ...
36
- module WEBrick::HTTPServlet
37
- class ProcHandler < AbstractServlet
38
- alias do_POST do_GET
39
- alias do_PUT do_GET
40
- alias do_DELETE do_GET
41
- end
42
- end
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
+ require 'webrick/https'
34
+
35
+ # to handle POST/PUT/DELETE ...
36
+ module WEBrick::HTTPServlet
37
+ class ProcHandler < AbstractServlet
38
+ alias do_POST do_GET
39
+ alias do_PUT do_GET
40
+ alias do_DELETE do_GET
41
+ end
42
+ end
@@ -1,21 +1,21 @@
1
- require 'fluent/plugin/formatter'
2
-
3
- module Fluent
4
- module Plugin
5
- class TestFormatter < Formatter
6
- Fluent::Plugin.register_formatter('test', self)
7
-
8
- def configure(conf)
9
- super
10
- end
11
-
12
- def format(tag, time, record)
13
- output = {
14
- "wrapped" => true,
15
- "record" => record
16
- }
17
- output
18
- end
19
- end
20
- end
21
- end
1
+ require 'fluent/plugin/formatter'
2
+
3
+ module Fluent
4
+ module Plugin
5
+ class TestFormatter < Formatter
6
+ Fluent::Plugin.register_formatter('test', self)
7
+
8
+ def configure(conf)
9
+ super
10
+ end
11
+
12
+ def format(tag, time, record)
13
+ output = {
14
+ "wrapped" => true,
15
+ "record" => record
16
+ }
17
+ output
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,732 +1,771 @@
1
- # -*- coding: utf-8 -*-
2
- require 'net/http'
3
- require 'uri'
4
- require 'yajl'
5
- require 'fluent/test/http_output_test'
6
- require 'fluent/plugin/out_http'
7
- require 'fluent/test/driver/output'
8
- require 'fluent/test/helpers'
9
- require_relative "./script/plugin/formatter_test"
10
-
11
- module OS
12
- # ref. http://stackoverflow.com/questions/170956/how-can-i-find-which-operating-system-my-ruby-program-is-running-on
13
- def OS.windows?
14
- (/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil
15
- end
16
-
17
- def OS.mac?
18
- (/darwin/ =~ RUBY_PLATFORM) != nil
19
- end
20
-
21
- def OS.unix?
22
- !OS.windows?
23
- end
24
-
25
- def OS.linux?
26
- OS.unix? and not OS.mac?
27
- end
28
- end
29
-
30
- class HTTPOutputTestBase < Test::Unit::TestCase
31
- include Fluent::Test::Helpers
32
-
33
- def self.port
34
- 5126
35
- end
36
-
37
- def self.server_config
38
- config = {BindAddress: '127.0.0.1', Port: port}
39
- if ENV['VERBOSE']
40
- logger = WEBrick::Log.new(STDOUT, WEBrick::BasicLog::DEBUG)
41
- config[:Logger] = logger
42
- config[:AccessLog] = []
43
- end
44
- config
45
- end
46
-
47
- def self.test_http_client(**opts)
48
- opts = opts.merge(open_timeout: 1, read_timeout: 1)
49
- Net::HTTP.start('127.0.0.1', port, **opts)
50
- end
51
-
52
- # setup / teardown for servers
53
- def setup
54
- Fluent::Test.setup
55
- @posts = []
56
- @puts = []
57
- @prohibited = 0
58
- @requests = 0
59
- @auth = false
60
- @headers = {}
61
- @dummy_server_thread = Thread.new do
62
- srv = WEBrick::HTTPServer.new(self.class.server_config)
63
- begin
64
- allowed_methods = %w(POST PUT)
65
- srv.mount_proc('/api') { |req,res|
66
- @requests += 1
67
- unless allowed_methods.include? req.request_method
68
- res.status = 405
69
- res.body = 'request method mismatch'
70
- next
71
- end
72
- req.each do |key, value|
73
- @headers[key] = value
74
- end
75
- if @auth and req.header['authorization'][0] == 'Basic YWxpY2U6c2VjcmV0IQ==' # pattern of user='alice' passwd='secret!'
76
- # ok, authorized
77
- # pattern of bear #{Base64.encode64('secret token!')}
78
- elsif @auth and req.header['authorization'][0] == 'bearer c2VjcmV0IHRva2VuIQ=='
79
- # pattern of jwt
80
- # header: {
81
- # "alg": "HS256",
82
- # "typ": "JWT"
83
- # }
84
- # payload: {
85
- # "iss": "Hoge Publisher",
86
- # "sub": "Hoge User"
87
- # }
88
- # signature:
89
- # HS256(base64UrlEncode(header) + "." +
90
- # base64UrlEncode(payload) + "." +
91
- # secret)
92
- elsif @auth and req.header['authorization'][0] == 'jwt eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJIb2dlIFB1Ymxpc2hlciIsInN1YiI6IkhvZ2UgVXNlciJ9.V2NL7YgCWNt5d3vTXFrcRLpRImO2cU2JZ4mQglqw3rE'
93
- elsif @auth
94
- res.status = 403
95
- @prohibited += 1
96
- next
97
- else
98
- # ok, authorization not required
99
- end
100
-
101
- record = {:auth => nil}
102
- if req.content_type == 'application/json'
103
- record[:json] = Yajl.load(req.body)
104
- elsif req.content_type == 'text/plain'
105
- puts req
106
- record[:data] = req.body
107
- elsif req.content_type == 'application/octet-stream'
108
- record[:data] = req.body
109
- else
110
- record[:form] = Hash[*(req.body.split('&').map{|kv|kv.split('=')}.flatten)]
111
- end
112
-
113
- instance_variable_get("@#{req.request_method.downcase}s").push(record)
114
-
115
- res.status = 200
116
- }
117
- srv.mount_proc('/modified-api') { |req,res|
118
- res.status = 303
119
- res.body = 'See other'
120
- }
121
- srv.mount_proc('/') { |req,res|
122
- res.status = 200
123
- res.body = 'running'
124
- }
125
- srv.start
126
- ensure
127
- srv.shutdown
128
- end
129
- end
130
-
131
- # to wait completion of dummy server.start()
132
- require 'thread'
133
- cv = ConditionVariable.new
134
- watcher = Thread.new {
135
- connected = false
136
- while not connected
137
- begin
138
- client = self.class.test_http_client
139
- client.request_get('/')
140
- connected = true
141
- rescue Errno::ECONNREFUSED
142
- sleep 0.1
143
- rescue StandardError => e
144
- p e
145
- sleep 0.1
146
- end
147
- end
148
- cv.signal
149
- }
150
- mutex = Mutex.new
151
- mutex.synchronize {
152
- cv.wait(mutex)
153
- }
154
- end
155
-
156
- def test_dummy_server
157
- client = self.class.test_http_client
158
- post_header = { 'Content-Type' => 'application/x-www-form-urlencoded' }
159
-
160
- assert_equal '200', client.request_get('/').code
161
- assert_equal '200', client.request_post('/api/service/metrics/hoge', 'number=1&mode=gauge', post_header).code
162
-
163
- assert_equal 1, @posts.size
164
-
165
- assert_equal '1', @posts[0][:form]['number']
166
- assert_equal 'gauge', @posts[0][:form]['mode']
167
- assert_nil @posts[0][:auth]
168
-
169
- assert_equal '303', client.request_get('/modified-api').code
170
-
171
- @auth = true
172
-
173
- assert_equal '403', client.request_post('/api/service/metrics/pos', 'number=30&mode=gauge', post_header).code
174
-
175
- req_with_auth = lambda do |number, mode, user, pass|
176
- req = Net::HTTP::Post.new("/api/service/metrics/pos")
177
- req.content_type = 'application/x-www-form-urlencoded'
178
- req.basic_auth user, pass
179
- req.set_form_data({'number'=>number, 'mode'=>mode})
180
- req
181
- end
182
-
183
- assert_equal '403', client.request(req_with_auth.call(500, 'count', 'alice', 'wrong password!')).code
184
-
185
- assert_equal '403', client.request(req_with_auth.call(500, 'count', 'alice', 'wrong password!')).code
186
-
187
- assert_equal 1, @posts.size
188
-
189
- assert_equal '200', client.request(req_with_auth.call(500, 'count', 'alice', 'secret!')).code
190
-
191
- assert_equal 2, @posts.size
192
-
193
- end
194
-
195
- def teardown
196
- @dummy_server_thread.kill
197
- @dummy_server_thread.join
198
- end
199
-
200
- def create_driver(conf)
201
- Fluent::Test::Driver::Output.new(Fluent::Plugin::HTTPOutput).configure(conf)
202
- end
203
- end
204
-
205
- class HTTPOutputTest < HTTPOutputTestBase
206
- CONFIG = %[
207
- endpoint_url http://127.0.0.1:#{port}/api/
208
- ]
209
-
210
- CONFIG_QUERY_PARAM = %[
211
- endpoint_url http://127.0.0.1:#{port}/api?foo=bar&baz=qux
212
- ]
213
-
214
- CONFIG_JSON = %[
215
- endpoint_url http://127.0.0.1:#{port}/api/
216
- serializer json
217
- ]
218
-
219
- CONFIG_TEXT = %[
220
- endpoint_url http://127.0.0.1:#{port}/api/
221
- serializer text
222
- ]
223
-
224
- CONFIG_RAW = %[
225
- endpoint_url http://127.0.0.1:#{port}/api/
226
- serializer raw
227
- ]
228
-
229
- CONFIG_PUT = %[
230
- endpoint_url http://127.0.0.1:#{port}/api/
231
- http_method put
232
- ]
233
-
234
- CONFIG_HTTP_ERROR = %[
235
- endpoint_url https://127.0.0.1:#{port - 1}/api/
236
- ]
237
-
238
- CONFIG_HTTP_ERROR_SUPPRESSED = %[
239
- endpoint_url https://127.0.0.1:#{port - 1}/api/
240
- raise_on_error false
241
- ]
242
-
243
- RATE_LIMIT_MSEC = 1200
244
-
245
- CONFIG_RATE_LIMIT = %[
246
- endpoint_url http://127.0.0.1:#{port}/api/
247
- rate_limit_msec #{RATE_LIMIT_MSEC}
248
- ]
249
-
250
- def test_configure
251
- d = create_driver CONFIG
252
- assert_equal "http://127.0.0.1:#{self.class.port}/api/", d.instance.endpoint_url
253
- assert_equal :form, d.instance.serializer
254
-
255
- d = create_driver CONFIG_JSON
256
- assert_equal "http://127.0.0.1:#{self.class.port}/api/", d.instance.endpoint_url
257
- assert_equal :json, d.instance.serializer
258
- end
259
-
260
- test 'lack of tag in chunk_keys' do
261
- assert_raise_message(/'tag' in chunk_keys is required./) do
262
- create_driver(Fluent::Config::Element.new(
263
- 'ROOT', '', {
264
- '@type' => 'http',
265
- 'endpoint_url' => "http://127.0.0.1:#{self.class.port}/api/",
266
- 'buffered' => true,
267
- }, [
268
- Fluent::Config::Element.new('buffer', 'mykey', {
269
- 'chunk_keys' => 'mykey'
270
- }, [])
271
- ]
272
- ))
273
- end
274
- end
275
-
276
- def test_emit_form
277
- d = create_driver CONFIG
278
- d.run(default_tag: 'test.metrics') do
279
- d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => "\xe3\x81\x82".force_encoding("ascii-8bit") })
280
- end
281
-
282
- assert_equal 1, @posts.size
283
- record = @posts[0]
284
-
285
- assert_equal '50', record[:form]['field1']
286
- assert_equal '20', record[:form]['field2']
287
- assert_equal '10', record[:form]['field3']
288
- assert_equal '1', record[:form]['otherfield']
289
- assert_equal URI.encode_www_form_component("あ").upcase, record[:form]['binary'].upcase
290
- assert_nil record[:auth]
291
-
292
- d.run(default_tag: 'test.metrics') do
293
- d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
294
- end
295
-
296
- assert_equal 2, @posts.size
297
- end
298
-
299
- def test_emit_form_with_query_params
300
- d = create_driver CONFIG_QUERY_PARAM
301
- d.run(default_tag: 'test.metrics') do
302
- d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => "\xe3\x81\x82".force_encoding("ascii-8bit") })
303
- end
304
-
305
- assert_equal 1, @posts.size
306
- record = @posts[0]
307
-
308
- assert_equal '50', record[:form]['field1']
309
- assert_equal '20', record[:form]['field2']
310
- assert_equal '10', record[:form]['field3']
311
- assert_equal '1', record[:form]['otherfield']
312
- assert_equal URI.encode_www_form_component("あ").upcase, record[:form]['binary'].upcase
313
- assert_nil record[:auth]
314
-
315
- d.run(default_tag: 'test.metrics') do
316
- d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
317
- end
318
-
319
- assert_equal 2, @posts.size
320
- end
321
-
322
- def test_emit_form_with_custom_headers
323
- d = create_driver CONFIG + %[custom_headers {"key":"custom","token":"arbitrary"}]
324
- d.run(default_tag: 'test.metrics') do
325
- d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => "\xe3\x81\x82".force_encoding("ascii-8bit") })
326
- end
327
-
328
- assert_true @headers.has_key?("key")
329
- assert_equal "custom", @headers["key"]
330
- assert_true @headers.has_key?("token")
331
- assert_equal "arbitrary", @headers["token"]
332
-
333
- assert_equal 1, @posts.size
334
- record = @posts[0]
335
-
336
- assert_equal '50', record[:form]['field1']
337
- assert_equal '20', record[:form]['field2']
338
- assert_equal '10', record[:form]['field3']
339
- assert_equal '1', record[:form]['otherfield']
340
- assert_equal URI.encode_www_form_component("あ").upcase, record[:form]['binary'].upcase
341
- assert_nil record[:auth]
342
-
343
- d.run(default_tag: 'test.metrics') do
344
- d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
345
- end
346
-
347
- assert_equal 2, @posts.size
348
- end
349
-
350
- class BufferedEmitTest < self
351
- def test_emit_form
352
- d = create_driver CONFIG + %[buffered true]
353
- d.run(default_tag: 'test.metrics', shutdown: false) do
354
- d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => "\xe3\x81\x82".force_encoding("ascii-8bit") })
355
- end
356
-
357
- assert_equal 1, @posts.size
358
- record = @posts[0]
359
-
360
- assert_equal '50', record[:form]['field1']
361
- assert_equal '20', record[:form]['field2']
362
- assert_equal '10', record[:form]['field3']
363
- assert_equal '1', record[:form]['otherfield']
364
- assert_equal URI.encode_www_form_component("あ").upcase, record[:form]['binary'].upcase
365
- assert_nil record[:auth]
366
-
367
- d.run(default_tag: 'test.metrics', shutdown: false) do
368
- d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
369
- end
370
-
371
- assert_equal 2, @posts.size
372
- end
373
-
374
- def test_emit_form_with_placeholders
375
- d = create_driver(Fluent::Config::Element.new(
376
- 'ROOT', '' ,
377
- {"endpoint_url" => "${endpoint}",
378
- "buffered" => true},
379
- [Fluent::Config::Element.new('buffer', 'tag, endpoint', {"@type" => "memory"} ,[])]))
380
-
381
- d.run(default_tag: 'test.metrics', shutdown: false) do
382
- d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => "\xe3\x81\x82".force_encoding("ascii-8bit"), 'endpoint' => "http://127.0.0.1:#{self.class.port}/modified-api/" })
383
- end
384
-
385
- assert_equal 0, @posts.size # post into other URI
386
- assert_equal "http://127.0.0.1:#{self.class.port}/modified-api/", d.instance.endpoint_url
387
- end
388
-
389
- def test_emit_form_put
390
- d = create_driver CONFIG_PUT + %[buffered true]
391
- d.run(default_tag: 'test.metrics', shutdown: false) do
392
- d.feed({ 'field1' => 50 })
393
- end
394
-
395
- assert_equal 0, @posts.size
396
- assert_equal 1, @puts.size
397
- record = @puts[0]
398
-
399
- assert_equal '50', record[:form]['field1']
400
- assert_nil record[:auth]
401
-
402
- d.run(default_tag: 'test.metrics', shutdown: false) do
403
- d.feed({ 'field1' => 50 })
404
- end
405
-
406
- assert_equal 0, @posts.size
407
- assert_equal 2, @puts.size
408
- end
409
-
410
- def test_emit_json
411
- binary_string = "\xe3\x81\x82"
412
- d = create_driver CONFIG_JSON + %[buffered true]
413
- d.run(default_tag: 'test.metrics') do
414
- d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => binary_string })
415
- end
416
-
417
- assert_equal 1, @posts.size
418
- record = @posts[0]
419
-
420
- assert_equal 50, record[:json]['field1']
421
- assert_equal 20, record[:json]['field2']
422
- assert_equal 10, record[:json]['field3']
423
- assert_equal 1, record[:json]['otherfield']
424
- assert_equal binary_string, record[:json]['binary']
425
- assert_nil record[:auth]
426
- end
427
- end
428
-
429
- def test_emit_form_put
430
- d = create_driver CONFIG_PUT
431
- d.run(default_tag: 'test.metrics') do
432
- d.feed({ 'field1' => 50 })
433
- end
434
-
435
- assert_equal 0, @posts.size
436
- assert_equal 1, @puts.size
437
- record = @puts[0]
438
-
439
- assert_equal '50', record[:form]['field1']
440
- assert_nil record[:auth]
441
-
442
- d.run(default_tag: 'test.metrics') do
443
- d.feed({ 'field1' => 50 })
444
- end
445
-
446
- assert_equal 0, @posts.size
447
- assert_equal 2, @puts.size
448
- end
449
-
450
- def test_emit_json
451
- binary_string = "\xe3\x81\x82"
452
- d = create_driver CONFIG_JSON
453
- d.run(default_tag: 'test.metrics') do
454
- d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => binary_string })
455
- end
456
-
457
- assert_equal 1, @posts.size
458
- record = @posts[0]
459
-
460
- assert_equal 50, record[:json]['field1']
461
- assert_equal 20, record[:json]['field2']
462
- assert_equal 10, record[:json]['field3']
463
- assert_equal 1, record[:json]['otherfield']
464
- assert_equal binary_string, record[:json]['binary']
465
- assert_nil record[:auth]
466
- end
467
-
468
- def test_emit_text
469
- binary_string = "\xe3\x81\x82"
470
- d = create_driver CONFIG_TEXT
471
- d.run(default_tag: 'test.metrics') do
472
- d.feed({ "message" => "hello" })
473
- end
474
- assert_equal 1, @posts.size
475
- record = @posts[0]
476
- assert_equal 'hello', record[:data]
477
- assert_nil record[:auth]
478
- end
479
-
480
- def test_emit_raw
481
- binary_string = "\xe3\x81\x82"
482
- d = create_driver CONFIG_RAW + %[format msgpack]
483
- d.run(default_tag: 'test.metrics') do
484
- d.feed({ "message" => "hello" })
485
- end
486
- assert_equal 1, @posts.size
487
- record = @posts[0]
488
- assert_equal ({ "message" => "hello" }).to_msgpack, record[:data]
489
- assert_nil record[:auth]
490
- end
491
-
492
- def test_http_error_is_raised
493
- d = create_driver CONFIG_HTTP_ERROR
494
- assert_raise Errno::ECONNREFUSED do
495
- d.run(default_tag: 'test.metrics') do
496
- d.feed({ 'field1' => 50 })
497
- end
498
- end
499
- end
500
-
501
- def test_http_error_is_suppressed_with_raise_on_error_false
502
- d = create_driver CONFIG_HTTP_ERROR_SUPPRESSED
503
- d.run(default_tag: 'test.metrics') do
504
- d.feed({ 'field1' => 50 })
505
- end
506
- # drive asserts the next output chain is called;
507
- # so no exception means our plugin handled the error
508
-
509
- assert_equal 0, @requests
510
- end
511
-
512
- def test_rate_limiting
513
- d = create_driver CONFIG_RATE_LIMIT
514
- record = { :k => 1 }
515
-
516
- last_emit = _current_msec
517
- d.run(default_tag: 'test.metrics') do
518
- d.feed(record)
519
- end
520
-
521
- assert_equal 1, @posts.size
522
-
523
- d.run(default_tag: 'test.metrics') do
524
- d.feed({})
525
- end
526
- assert last_emit + RATE_LIMIT_MSEC > _current_msec, "Still under rate limiting interval"
527
- assert_equal 1, @posts.size
528
-
529
- wait_msec = 500
530
- sleep (last_emit + RATE_LIMIT_MSEC - _current_msec + wait_msec) * 0.001
531
-
532
- assert last_emit + RATE_LIMIT_MSEC < _current_msec, "No longer under rate limiting interval"
533
- d.run(default_tag: 'test.metrics') do
534
- d.feed(record)
535
- end
536
- assert_equal 2, @posts.size
537
- end
538
-
539
- def _current_msec
540
- Time.now.to_f * 1000
541
- end
542
-
543
- def test_auth
544
- @auth = true # enable authentication of dummy server
545
-
546
- d = create_driver(CONFIG)
547
- d.run(default_tag: 'test.metrics') do
548
- d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
549
- end # failed in background, and output warn log
550
-
551
- assert_equal 0, @posts.size
552
- assert_equal 1, @prohibited
553
-
554
- d = create_driver(CONFIG + %[
555
- authentication basic
556
- username alice
557
- password wrong_password
558
- ])
559
- d.run(default_tag: 'test.metrics') do
560
- d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
561
- end # failed in background, and output warn log
562
-
563
- assert_equal 0, @posts.size
564
- assert_equal 2, @prohibited
565
-
566
- d = create_driver(CONFIG + %[
567
- authentication basic
568
- username alice
569
- password secret!
570
- ])
571
- d.run(default_tag: 'test.metrics') do
572
- d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
573
- end # failed in background, and output warn log
574
-
575
- assert_equal 1, @posts.size
576
- assert_equal 2, @prohibited
577
-
578
- require 'base64'
579
- d = create_driver(CONFIG + %[
580
- authentication bearer
581
- token #{Base64.encode64('secret token!')}
582
- ])
583
- d.run(default_tag: 'test.metrics') do
584
- d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
585
- end # failed in background, and output warn log
586
-
587
- assert_equal 2, @posts.size
588
- assert_equal 2, @prohibited
589
-
590
- d = create_driver(CONFIG + %[
591
- authentication jwt
592
- token eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJIb2dlIFB1Ymxpc2hlciIsInN1YiI6IkhvZ2UgVXNlciJ9.V2NL7YgCWNt5d3vTXFrcRLpRImO2cU2JZ4mQglqw3rE
593
- ])
594
- d.run(default_tag: 'test.metrics') do
595
- d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
596
- end # failed in background, and output warn log
597
-
598
- assert_equal 3, @posts.size
599
- assert_equal 2, @prohibited
600
- end
601
-
602
- class CustomFormatterTest < self
603
- def test_new_config
604
- config = Fluent::Config::Element.new(
605
- 'ROOT', '',
606
- {"@type" => "http",
607
- "endpoint_url" => "http://127.0.0.1:#{self.class.port}/api/",
608
- "serializer" => "json"}, [
609
- Fluent::Config::Element.new('format', '', {
610
- "@type" => "test"
611
- }, [])
612
- ])
613
- d = create_driver config
614
- payload = {"field" => 1}
615
- d.run(default_tag: 'test.metrics') do
616
- d.feed(payload)
617
- end
618
-
619
- record = @posts[0]
620
- expected = {"wrapped" => true, "record" => payload}
621
- assert_equal expected, record[:json]
622
- end
623
-
624
- def test_legacy_config
625
- config = %[
626
- endpoint_url http://127.0.0.1:#{self.class.port}/api/
627
- serializer json
628
- format test
629
- ]
630
-
631
- d = create_driver config
632
- payload = {"field" => 1}
633
- d.run(default_tag: 'test.metrics') do
634
- d.feed(payload)
635
- end
636
-
637
- record = @posts[0]
638
- expected = {"wrapped" => true, "record" => payload}
639
- assert_equal expected, record[:json]
640
- end
641
- end
642
- end
643
-
644
- class HTTPSOutputTest < HTTPOutputTestBase
645
- def self.port
646
- 5127
647
- end
648
-
649
- def self.server_config
650
- config = super
651
- config[:SSLEnable] = true
652
- config[:SSLCertName] = [["CN", WEBrick::Utils::getservername]]
653
- config
654
- end
655
-
656
- def self.test_http_client
657
- super(
658
- use_ssl: true,
659
- verify_mode: OpenSSL::SSL::VERIFY_NONE,
660
- )
661
- end
662
-
663
- def test_configure
664
- test_uri = URI.parse("https://127.0.0.1/")
665
-
666
- ssl_config = %[
667
- endpoint_url https://127.0.0.1:#{self.class.port}/api/
668
- ]
669
- d = create_driver ssl_config
670
- expected_endpoint_url = "https://127.0.0.1:#{self.class.port}/api/"
671
- assert_equal expected_endpoint_url, d.instance.endpoint_url
672
- http_opts = d.instance.http_opts(test_uri)
673
- assert_equal true, http_opts[:use_ssl]
674
- assert_equal OpenSSL::SSL::VERIFY_PEER, http_opts[:verify_mode]
675
-
676
- no_verify_config = %[
677
- endpoint_url https://127.0.0.1:#{self.class.port}/api/
678
- ssl_no_verify true
679
- ]
680
- d = create_driver no_verify_config
681
- http_opts = d.instance.http_opts(test_uri)
682
- assert_equal true, http_opts[:use_ssl]
683
- assert_equal OpenSSL::SSL::VERIFY_NONE, http_opts[:verify_mode]
684
-
685
- cacert_file_config = %[
686
- endpoint_url https://127.0.0.1:#{self.class.port}/api/
687
- ssl_no_verify true
688
- cacert_file /tmp/ssl.cert
689
- ]
690
- d = create_driver cacert_file_config
691
- FileUtils::touch '/tmp/ssl.cert'
692
- http_opts = d.instance.http_opts(test_uri)
693
- assert_equal true, http_opts[:use_ssl]
694
- assert_equal OpenSSL::SSL::VERIFY_NONE, http_opts[:verify_mode]
695
- assert_equal true, File.file?('/tmp/ssl.cert')
696
- puts http_opts
697
- assert_equal File.join('/tmp/ssl.cert'), http_opts[:ca_file]
698
- end
699
-
700
- def test_emit_form_ssl
701
- config = %[
702
- endpoint_url https://127.0.0.1:#{self.class.port}/api/
703
- ssl_no_verify true
704
- ]
705
- d = create_driver config
706
- d.run(default_tag: 'test.metrics') do
707
- d.feed({ 'field1' => 50 })
708
- end
709
-
710
- assert_equal 1, @posts.size
711
- record = @posts[0]
712
-
713
- assert_equal '50', record[:form]['field1']
714
- end
715
-
716
- def test_emit_form_ssl_ca
717
- config = %[
718
- endpoint_url https://127.0.0.1:#{self.class.port}/api/
719
- ssl_no_verify true
720
- cacert_file /tmp/ssl.cert
721
- ]
722
- d = create_driver config
723
- d.run(default_tag: 'test.metrics') do
724
- d.feed({ 'field1' => 50 })
725
- end
726
-
727
- assert_equal 1, @posts.size
728
- record = @posts[0]
729
-
730
- assert_equal '50', record[:form]['field1']
731
- end
732
- end
1
+ # -*- coding: utf-8 -*-
2
+ require 'net/http'
3
+ require 'uri'
4
+ require 'yajl'
5
+ require 'fluent/test/http_output_test'
6
+ require 'fluent/plugin/out_http'
7
+ require 'fluent/test/driver/output'
8
+ require 'fluent/test/helpers'
9
+ require_relative "./script/plugin/formatter_test"
10
+
11
+ module OS
12
+ # ref. http://stackoverflow.com/questions/170956/how-can-i-find-which-operating-system-my-ruby-program-is-running-on
13
+ def OS.windows?
14
+ (/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil
15
+ end
16
+
17
+ def OS.mac?
18
+ (/darwin/ =~ RUBY_PLATFORM) != nil
19
+ end
20
+
21
+ def OS.unix?
22
+ !OS.windows?
23
+ end
24
+
25
+ def OS.linux?
26
+ OS.unix? and not OS.mac?
27
+ end
28
+ end
29
+
30
+ class HTTPOutputTestBase < Test::Unit::TestCase
31
+ include Fluent::Test::Helpers
32
+
33
+ def self.port
34
+ 5126
35
+ end
36
+
37
+ def self.server_config
38
+ config = {BindAddress: '127.0.0.1', Port: port}
39
+ if ENV['VERBOSE']
40
+ logger = WEBrick::Log.new(STDOUT, WEBrick::BasicLog::DEBUG)
41
+ config[:Logger] = logger
42
+ config[:AccessLog] = []
43
+ end
44
+ config
45
+ end
46
+
47
+ def self.test_http_client(**opts)
48
+ opts = opts.merge(open_timeout: 1, read_timeout: 1)
49
+ Net::HTTP.start('127.0.0.1', port, **opts)
50
+ end
51
+
52
+ # setup / teardown for servers
53
+ def setup
54
+ Fluent::Test.setup
55
+ @posts = []
56
+ @puts = []
57
+ @prohibited = 0
58
+ @requests = 0
59
+ @auth = false
60
+ @headers = {}
61
+ @dummy_server_thread = Thread.new do
62
+ srv = WEBrick::HTTPServer.new(self.class.server_config)
63
+ begin
64
+ allowed_methods = %w(POST PUT)
65
+ srv.mount_proc('/api') { |req,res|
66
+ @requests += 1
67
+ unless allowed_methods.include? req.request_method
68
+ res.status = 405
69
+ res.body = 'request method mismatch'
70
+ next
71
+ end
72
+ req.each do |key, value|
73
+ @headers[key] = value
74
+ end
75
+ if @auth and req.header['authorization'][0] == 'Basic YWxpY2U6c2VjcmV0IQ==' # pattern of user='alice' passwd='secret!'
76
+ # ok, authorized
77
+ # pattern of bear #{Base64.encode64('secret token!')}
78
+ elsif @auth and req.header['authorization'][0] == 'bearer c2VjcmV0IHRva2VuIQ=='
79
+ # pattern of jwt
80
+ # header: {
81
+ # "alg": "HS256",
82
+ # "typ": "JWT"
83
+ # }
84
+ # payload: {
85
+ # "iss": "Hoge Publisher",
86
+ # "sub": "Hoge User"
87
+ # }
88
+ # signature:
89
+ # HS256(base64UrlEncode(header) + "." +
90
+ # base64UrlEncode(payload) + "." +
91
+ # secret)
92
+ elsif @auth and req.header['authorization'][0] == 'jwt eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJIb2dlIFB1Ymxpc2hlciIsInN1YiI6IkhvZ2UgVXNlciJ9.V2NL7YgCWNt5d3vTXFrcRLpRImO2cU2JZ4mQglqw3rE'
93
+ elsif @auth
94
+ res.status = 403
95
+ @prohibited += 1
96
+ next
97
+ else
98
+ # ok, authorization not required
99
+ end
100
+
101
+ record = {:auth => nil}
102
+ if req.content_type == 'application/json'
103
+ record[:json] = Yajl.load(req.body)
104
+ elsif req.content_type == 'text/plain'
105
+ puts req
106
+ record[:data] = req.body
107
+ elsif req.content_type == 'application/octet-stream'
108
+ record[:data] = req.body
109
+ elsif req.content_type == 'application/x-ndjson'
110
+ data = []
111
+ req.body.each_line { |l|
112
+ data << Yajl.load(l)
113
+ }
114
+ record[:x_ndjson] = data
115
+ else
116
+ record[:form] = Hash[*(req.body.split('&').map{|kv|kv.split('=')}.flatten)]
117
+ end
118
+
119
+ instance_variable_get("@#{req.request_method.downcase}s").push(record)
120
+
121
+ res.status = 200
122
+ }
123
+ srv.mount_proc('/modified-api') { |req,res|
124
+ res.status = 303
125
+ res.body = 'See other'
126
+ }
127
+ srv.mount_proc('/') { |req,res|
128
+ res.status = 200
129
+ res.body = 'running'
130
+ }
131
+ srv.start
132
+ ensure
133
+ srv.shutdown
134
+ end
135
+ end
136
+
137
+ # to wait completion of dummy server.start()
138
+ require 'thread'
139
+ cv = ConditionVariable.new
140
+ watcher = Thread.new {
141
+ connected = false
142
+ while not connected
143
+ begin
144
+ client = self.class.test_http_client
145
+ client.request_get('/')
146
+ connected = true
147
+ rescue Errno::ECONNREFUSED
148
+ sleep 0.1
149
+ rescue StandardError => e
150
+ p e
151
+ sleep 0.1
152
+ end
153
+ end
154
+ cv.signal
155
+ }
156
+ mutex = Mutex.new
157
+ mutex.synchronize {
158
+ cv.wait(mutex)
159
+ }
160
+ end
161
+
162
+ def test_dummy_server
163
+ client = self.class.test_http_client
164
+ post_header = { 'Content-Type' => 'application/x-www-form-urlencoded' }
165
+
166
+ assert_equal '200', client.request_get('/').code
167
+ assert_equal '200', client.request_post('/api/service/metrics/hoge', 'number=1&mode=gauge', post_header).code
168
+
169
+ assert_equal 1, @posts.size
170
+
171
+ assert_equal '1', @posts[0][:form]['number']
172
+ assert_equal 'gauge', @posts[0][:form]['mode']
173
+ assert_nil @posts[0][:auth]
174
+
175
+ assert_equal '303', client.request_get('/modified-api').code
176
+
177
+ @auth = true
178
+
179
+ assert_equal '403', client.request_post('/api/service/metrics/pos', 'number=30&mode=gauge', post_header).code
180
+
181
+ req_with_auth = lambda do |number, mode, user, pass|
182
+ req = Net::HTTP::Post.new("/api/service/metrics/pos")
183
+ req.content_type = 'application/x-www-form-urlencoded'
184
+ req.basic_auth user, pass
185
+ req.set_form_data({'number'=>number, 'mode'=>mode})
186
+ req
187
+ end
188
+
189
+ assert_equal '403', client.request(req_with_auth.call(500, 'count', 'alice', 'wrong password!')).code
190
+
191
+ assert_equal '403', client.request(req_with_auth.call(500, 'count', 'alice', 'wrong password!')).code
192
+
193
+ assert_equal 1, @posts.size
194
+
195
+ assert_equal '200', client.request(req_with_auth.call(500, 'count', 'alice', 'secret!')).code
196
+
197
+ assert_equal 2, @posts.size
198
+
199
+ end
200
+
201
+ def teardown
202
+ @dummy_server_thread.kill
203
+ @dummy_server_thread.join
204
+ end
205
+
206
+ def create_driver(conf)
207
+ Fluent::Test::Driver::Output.new(Fluent::Plugin::HTTPOutput).configure(conf)
208
+ end
209
+ end
210
+
211
+ class HTTPOutputTest < HTTPOutputTestBase
212
+ CONFIG = %[
213
+ endpoint_url http://127.0.0.1:#{port}/api/
214
+ ]
215
+
216
+ CONFIG_QUERY_PARAM = %[
217
+ endpoint_url http://127.0.0.1:#{port}/api?foo=bar&baz=qux
218
+ ]
219
+
220
+ CONFIG_JSON = %[
221
+ endpoint_url http://127.0.0.1:#{port}/api/
222
+ serializer json
223
+ ]
224
+
225
+ CONFIG_TEXT = %[
226
+ endpoint_url http://127.0.0.1:#{port}/api/
227
+ serializer text
228
+ ]
229
+
230
+ CONFIG_RAW = %[
231
+ endpoint_url http://127.0.0.1:#{port}/api/
232
+ serializer raw
233
+ ]
234
+
235
+ CONFIG_PUT = %[
236
+ endpoint_url http://127.0.0.1:#{port}/api/
237
+ http_method put
238
+ ]
239
+
240
+ CONFIG_HTTP_ERROR = %[
241
+ endpoint_url https://127.0.0.1:#{port - 1}/api/
242
+ ]
243
+
244
+ CONFIG_HTTP_ERROR_SUPPRESSED = %[
245
+ endpoint_url https://127.0.0.1:#{port - 1}/api/
246
+ raise_on_error false
247
+ ]
248
+
249
+ RATE_LIMIT_MSEC = 1200
250
+
251
+ CONFIG_RATE_LIMIT = %[
252
+ endpoint_url http://127.0.0.1:#{port}/api/
253
+ rate_limit_msec #{RATE_LIMIT_MSEC}
254
+ ]
255
+
256
+ def test_configure
257
+ d = create_driver CONFIG
258
+ assert_equal "http://127.0.0.1:#{self.class.port}/api/", d.instance.endpoint_url
259
+ assert_equal :form, d.instance.serializer
260
+ assert_equal [503], d.instance.recoverable_status_codes
261
+
262
+ d = create_driver CONFIG_JSON
263
+ assert_equal "http://127.0.0.1:#{self.class.port}/api/", d.instance.endpoint_url
264
+ assert_equal :json, d.instance.serializer
265
+ end
266
+
267
+ test 'lack of tag in chunk_keys' do
268
+ assert_raise_message(/'tag' in chunk_keys is required./) do
269
+ create_driver(Fluent::Config::Element.new(
270
+ 'ROOT', '', {
271
+ '@type' => 'http',
272
+ 'endpoint_url' => "http://127.0.0.1:#{self.class.port}/api/",
273
+ 'buffered' => true,
274
+ }, [
275
+ Fluent::Config::Element.new('buffer', 'mykey', {
276
+ 'chunk_keys' => 'mykey'
277
+ }, [])
278
+ ]
279
+ ))
280
+ end
281
+ end
282
+
283
+ def test_emit_form
284
+ d = create_driver CONFIG
285
+ d.run(default_tag: 'test.metrics') do
286
+ d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => "\xe3\x81\x82".force_encoding("ascii-8bit") })
287
+ end
288
+
289
+ assert_equal 1, @posts.size
290
+ record = @posts[0]
291
+
292
+ assert_equal '50', record[:form]['field1']
293
+ assert_equal '20', record[:form]['field2']
294
+ assert_equal '10', record[:form]['field3']
295
+ assert_equal '1', record[:form]['otherfield']
296
+ assert_equal URI.encode_www_form_component("あ").upcase, record[:form]['binary'].upcase
297
+ assert_nil record[:auth]
298
+
299
+ d.run(default_tag: 'test.metrics') do
300
+ d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
301
+ end
302
+
303
+ assert_equal 2, @posts.size
304
+ end
305
+
306
+ def test_emit_form_with_query_params
307
+ d = create_driver CONFIG_QUERY_PARAM
308
+ d.run(default_tag: 'test.metrics') do
309
+ d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => "\xe3\x81\x82".force_encoding("ascii-8bit") })
310
+ end
311
+
312
+ assert_equal 1, @posts.size
313
+ record = @posts[0]
314
+
315
+ assert_equal '50', record[:form]['field1']
316
+ assert_equal '20', record[:form]['field2']
317
+ assert_equal '10', record[:form]['field3']
318
+ assert_equal '1', record[:form]['otherfield']
319
+ assert_equal URI.encode_www_form_component("あ").upcase, record[:form]['binary'].upcase
320
+ assert_nil record[:auth]
321
+
322
+ d.run(default_tag: 'test.metrics') do
323
+ d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
324
+ end
325
+
326
+ assert_equal 2, @posts.size
327
+ end
328
+
329
+ def test_emit_form_with_custom_headers
330
+ d = create_driver CONFIG + %[custom_headers {"key":"custom","token":"arbitrary"}]
331
+ d.run(default_tag: 'test.metrics') do
332
+ d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => "\xe3\x81\x82".force_encoding("ascii-8bit") })
333
+ end
334
+
335
+ assert_true @headers.has_key?("key")
336
+ assert_equal "custom", @headers["key"]
337
+ assert_true @headers.has_key?("token")
338
+ assert_equal "arbitrary", @headers["token"]
339
+
340
+ assert_equal 1, @posts.size
341
+ record = @posts[0]
342
+
343
+ assert_equal '50', record[:form]['field1']
344
+ assert_equal '20', record[:form]['field2']
345
+ assert_equal '10', record[:form]['field3']
346
+ assert_equal '1', record[:form]['otherfield']
347
+ assert_equal URI.encode_www_form_component("あ").upcase, record[:form]['binary'].upcase
348
+ assert_nil record[:auth]
349
+
350
+ d.run(default_tag: 'test.metrics') do
351
+ d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
352
+ end
353
+
354
+ assert_equal 2, @posts.size
355
+ end
356
+
357
+ class BufferedEmitTest < self
358
+ def test_emit_form
359
+ d = create_driver CONFIG + %[buffered true]
360
+ d.run(default_tag: 'test.metrics', shutdown: false) do
361
+ d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => "\xe3\x81\x82".force_encoding("ascii-8bit") })
362
+ end
363
+
364
+ assert_equal 1, @posts.size
365
+ record = @posts[0]
366
+
367
+ assert_equal '50', record[:form]['field1']
368
+ assert_equal '20', record[:form]['field2']
369
+ assert_equal '10', record[:form]['field3']
370
+ assert_equal '1', record[:form]['otherfield']
371
+ assert_equal URI.encode_www_form_component("あ").upcase, record[:form]['binary'].upcase
372
+ assert_nil record[:auth]
373
+
374
+ d.run(default_tag: 'test.metrics', shutdown: false) do
375
+ d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
376
+ end
377
+
378
+ assert_equal 2, @posts.size
379
+ end
380
+
381
+ def test_emit_form_with_placeholders
382
+ d = create_driver(Fluent::Config::Element.new(
383
+ 'ROOT', '' ,
384
+ {"endpoint_url" => "${endpoint}",
385
+ "buffered" => true},
386
+ [Fluent::Config::Element.new('buffer', 'tag, endpoint', {"@type" => "memory"} ,[])]))
387
+
388
+ d.run(default_tag: 'test.metrics', shutdown: false) do
389
+ d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => "\xe3\x81\x82".force_encoding("ascii-8bit"), 'endpoint' => "http://127.0.0.1:#{self.class.port}/modified-api/" })
390
+ end
391
+
392
+ assert_equal 0, @posts.size # post into other URI
393
+ assert_equal "http://127.0.0.1:#{self.class.port}/modified-api/", d.instance.endpoint_url
394
+ end
395
+
396
+ def test_emit_form_put
397
+ d = create_driver CONFIG_PUT + %[buffered true]
398
+ d.run(default_tag: 'test.metrics', shutdown: false) do
399
+ d.feed({ 'field1' => 50 })
400
+ end
401
+
402
+ assert_equal 0, @posts.size
403
+ assert_equal 1, @puts.size
404
+ record = @puts[0]
405
+
406
+ assert_equal '50', record[:form]['field1']
407
+ assert_nil record[:auth]
408
+
409
+ d.run(default_tag: 'test.metrics', shutdown: false) do
410
+ d.feed({ 'field1' => 50 })
411
+ end
412
+
413
+ assert_equal 0, @posts.size
414
+ assert_equal 2, @puts.size
415
+ end
416
+
417
+ def test_emit_json
418
+ binary_string = "\xe3\x81\x82"
419
+ d = create_driver CONFIG_JSON + %[buffered true]
420
+ d.run(default_tag: 'test.metrics') do
421
+ d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => binary_string })
422
+ end
423
+
424
+ assert_equal 1, @posts.size
425
+ record = @posts[0]
426
+
427
+ assert_equal 50, record[:json]['field1']
428
+ assert_equal 20, record[:json]['field2']
429
+ assert_equal 10, record[:json]['field3']
430
+ assert_equal 1, record[:json]['otherfield']
431
+ assert_equal binary_string, record[:json]['binary']
432
+ assert_nil record[:auth]
433
+ end
434
+
435
+ def test_emit_x_ndjson
436
+ binary_string = "\xe3\x81\x82"
437
+ d = create_driver CONFIG_JSON + %[buffered true\nbulk_request]
438
+ d.run(default_tag: 'test.metrics') do
439
+ d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => binary_string })
440
+ d.feed({ 'field1' => 70, 'field2' => 30, 'field3' => 20, 'otherfield' => 2, 'binary' => binary_string })
441
+ end
442
+
443
+ assert_equal 1, @posts.size
444
+ record = @posts[0]
445
+
446
+ expected =[
447
+ {
448
+ "binary" => "\u3042",
449
+ "field1" => 50,
450
+ "field2" => 20,
451
+ "field3" => 10,
452
+ "otherfield" => 1
453
+ },
454
+ {
455
+ "binary" => "\u3042",
456
+ "field1" => 70,
457
+ "field2" => 30,
458
+ "field3" => 20,
459
+ "otherfield" => 2
460
+ }
461
+ ]
462
+
463
+ assert_equal expected, record[:x_ndjson]
464
+ assert_nil record[:auth]
465
+ end
466
+ end
467
+
468
+ def test_emit_form_put
469
+ d = create_driver CONFIG_PUT
470
+ d.run(default_tag: 'test.metrics') do
471
+ d.feed({ 'field1' => 50 })
472
+ end
473
+
474
+ assert_equal 0, @posts.size
475
+ assert_equal 1, @puts.size
476
+ record = @puts[0]
477
+
478
+ assert_equal '50', record[:form]['field1']
479
+ assert_nil record[:auth]
480
+
481
+ d.run(default_tag: 'test.metrics') do
482
+ d.feed({ 'field1' => 50 })
483
+ end
484
+
485
+ assert_equal 0, @posts.size
486
+ assert_equal 2, @puts.size
487
+ end
488
+
489
+ def test_emit_json
490
+ binary_string = "\xe3\x81\x82"
491
+ d = create_driver CONFIG_JSON
492
+ d.run(default_tag: 'test.metrics') do
493
+ d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => binary_string })
494
+ end
495
+
496
+ assert_equal 1, @posts.size
497
+ record = @posts[0]
498
+
499
+ assert_equal 50, record[:json]['field1']
500
+ assert_equal 20, record[:json]['field2']
501
+ assert_equal 10, record[:json]['field3']
502
+ assert_equal 1, record[:json]['otherfield']
503
+ assert_equal binary_string, record[:json]['binary']
504
+ assert_nil record[:auth]
505
+ end
506
+
507
+ def test_emit_text
508
+ binary_string = "\xe3\x81\x82"
509
+ d = create_driver CONFIG_TEXT
510
+ d.run(default_tag: 'test.metrics') do
511
+ d.feed({ "message" => "hello" })
512
+ end
513
+ assert_equal 1, @posts.size
514
+ record = @posts[0]
515
+ assert_equal 'hello', record[:data]
516
+ assert_nil record[:auth]
517
+ end
518
+
519
+ def test_emit_raw
520
+ binary_string = "\xe3\x81\x82"
521
+ d = create_driver CONFIG_RAW + %[format msgpack]
522
+ d.run(default_tag: 'test.metrics') do
523
+ d.feed({ "message" => "hello" })
524
+ end
525
+ assert_equal 1, @posts.size
526
+ record = @posts[0]
527
+ assert_equal ({ "message" => "hello" }).to_msgpack, record[:data]
528
+ assert_nil record[:auth]
529
+ end
530
+
531
+ def test_http_error_is_raised
532
+ d = create_driver CONFIG_HTTP_ERROR
533
+ assert_raise Errno::ECONNREFUSED do
534
+ d.run(default_tag: 'test.metrics') do
535
+ d.feed({ 'field1' => 50 })
536
+ end
537
+ end
538
+ end
539
+
540
+ def test_http_error_is_suppressed_with_raise_on_error_false
541
+ d = create_driver CONFIG_HTTP_ERROR_SUPPRESSED
542
+ d.run(default_tag: 'test.metrics') do
543
+ d.feed({ 'field1' => 50 })
544
+ end
545
+ # drive asserts the next output chain is called;
546
+ # so no exception means our plugin handled the error
547
+
548
+ assert_equal 0, @requests
549
+ end
550
+
551
+ def test_rate_limiting
552
+ d = create_driver CONFIG_RATE_LIMIT
553
+ record = { :k => 1 }
554
+
555
+ last_emit = _current_msec
556
+ d.run(default_tag: 'test.metrics') do
557
+ d.feed(record)
558
+ end
559
+
560
+ assert_equal 1, @posts.size
561
+
562
+ d.run(default_tag: 'test.metrics') do
563
+ d.feed({})
564
+ end
565
+ assert last_emit + RATE_LIMIT_MSEC > _current_msec, "Still under rate limiting interval"
566
+ assert_equal 1, @posts.size
567
+
568
+ wait_msec = 500
569
+ sleep (last_emit + RATE_LIMIT_MSEC - _current_msec + wait_msec) * 0.001
570
+
571
+ assert last_emit + RATE_LIMIT_MSEC < _current_msec, "No longer under rate limiting interval"
572
+ d.run(default_tag: 'test.metrics') do
573
+ d.feed(record)
574
+ end
575
+ assert_equal 2, @posts.size
576
+ end
577
+
578
+ def _current_msec
579
+ Time.now.to_f * 1000
580
+ end
581
+
582
+ def test_auth
583
+ @auth = true # enable authentication of dummy server
584
+
585
+ d = create_driver(CONFIG)
586
+ d.run(default_tag: 'test.metrics') do
587
+ d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
588
+ end # failed in background, and output warn log
589
+
590
+ assert_equal 0, @posts.size
591
+ assert_equal 1, @prohibited
592
+
593
+ d = create_driver(CONFIG + %[
594
+ authentication basic
595
+ username alice
596
+ password wrong_password
597
+ ])
598
+ d.run(default_tag: 'test.metrics') do
599
+ d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
600
+ end # failed in background, and output warn log
601
+
602
+ assert_equal 0, @posts.size
603
+ assert_equal 2, @prohibited
604
+
605
+ d = create_driver(CONFIG + %[
606
+ authentication basic
607
+ username alice
608
+ password secret!
609
+ ])
610
+ d.run(default_tag: 'test.metrics') do
611
+ d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
612
+ end # failed in background, and output warn log
613
+
614
+ assert_equal 1, @posts.size
615
+ assert_equal 2, @prohibited
616
+
617
+ require 'base64'
618
+ d = create_driver(CONFIG + %[
619
+ authentication bearer
620
+ token #{Base64.encode64('secret token!')}
621
+ ])
622
+ d.run(default_tag: 'test.metrics') do
623
+ d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
624
+ end # failed in background, and output warn log
625
+
626
+ assert_equal 2, @posts.size
627
+ assert_equal 2, @prohibited
628
+
629
+ d = create_driver(CONFIG + %[
630
+ authentication jwt
631
+ token eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJIb2dlIFB1Ymxpc2hlciIsInN1YiI6IkhvZ2UgVXNlciJ9.V2NL7YgCWNt5d3vTXFrcRLpRImO2cU2JZ4mQglqw3rE
632
+ ])
633
+ d.run(default_tag: 'test.metrics') do
634
+ d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
635
+ end # failed in background, and output warn log
636
+
637
+ assert_equal 3, @posts.size
638
+ assert_equal 2, @prohibited
639
+ end
640
+
641
+ class CustomFormatterTest < self
642
+ def test_new_config
643
+ config = Fluent::Config::Element.new(
644
+ 'ROOT', '',
645
+ {"@type" => "http",
646
+ "endpoint_url" => "http://127.0.0.1:#{self.class.port}/api/",
647
+ "serializer" => "json"}, [
648
+ Fluent::Config::Element.new('format', '', {
649
+ "@type" => "test"
650
+ }, [])
651
+ ])
652
+ d = create_driver config
653
+ payload = {"field" => 1}
654
+ d.run(default_tag: 'test.metrics') do
655
+ d.feed(payload)
656
+ end
657
+
658
+ record = @posts[0]
659
+ expected = {"wrapped" => true, "record" => payload}
660
+ assert_equal expected, record[:json]
661
+ end
662
+
663
+ def test_legacy_config
664
+ config = %[
665
+ endpoint_url http://127.0.0.1:#{self.class.port}/api/
666
+ serializer json
667
+ format test
668
+ ]
669
+
670
+ d = create_driver config
671
+ payload = {"field" => 1}
672
+ d.run(default_tag: 'test.metrics') do
673
+ d.feed(payload)
674
+ end
675
+
676
+ record = @posts[0]
677
+ expected = {"wrapped" => true, "record" => payload}
678
+ assert_equal expected, record[:json]
679
+ end
680
+ end
681
+ end
682
+
683
+ class HTTPSOutputTest < HTTPOutputTestBase
684
+ def self.port
685
+ 5127
686
+ end
687
+
688
+ def self.server_config
689
+ config = super
690
+ config[:SSLEnable] = true
691
+ config[:SSLCertName] = [["CN", WEBrick::Utils::getservername]]
692
+ config
693
+ end
694
+
695
+ def self.test_http_client
696
+ super(
697
+ use_ssl: true,
698
+ verify_mode: OpenSSL::SSL::VERIFY_NONE,
699
+ )
700
+ end
701
+
702
+ def test_configure
703
+ test_uri = URI.parse("https://127.0.0.1/")
704
+
705
+ ssl_config = %[
706
+ endpoint_url https://127.0.0.1:#{self.class.port}/api/
707
+ ]
708
+ d = create_driver ssl_config
709
+ expected_endpoint_url = "https://127.0.0.1:#{self.class.port}/api/"
710
+ assert_equal expected_endpoint_url, d.instance.endpoint_url
711
+ http_opts = d.instance.http_opts(test_uri)
712
+ assert_equal true, http_opts[:use_ssl]
713
+ assert_equal OpenSSL::SSL::VERIFY_PEER, http_opts[:verify_mode]
714
+
715
+ no_verify_config = %[
716
+ endpoint_url https://127.0.0.1:#{self.class.port}/api/
717
+ ssl_no_verify true
718
+ ]
719
+ d = create_driver no_verify_config
720
+ http_opts = d.instance.http_opts(test_uri)
721
+ assert_equal true, http_opts[:use_ssl]
722
+ assert_equal OpenSSL::SSL::VERIFY_NONE, http_opts[:verify_mode]
723
+
724
+ cacert_file_config = %[
725
+ endpoint_url https://127.0.0.1:#{self.class.port}/api/
726
+ ssl_no_verify true
727
+ cacert_file /tmp/ssl.cert
728
+ ]
729
+ d = create_driver cacert_file_config
730
+ FileUtils::touch '/tmp/ssl.cert'
731
+ http_opts = d.instance.http_opts(test_uri)
732
+ assert_equal true, http_opts[:use_ssl]
733
+ assert_equal OpenSSL::SSL::VERIFY_NONE, http_opts[:verify_mode]
734
+ assert_equal true, File.file?('/tmp/ssl.cert')
735
+ puts http_opts
736
+ assert_equal File.join('/tmp/ssl.cert'), http_opts[:ca_file]
737
+ end
738
+
739
+ def test_emit_form_ssl
740
+ config = %[
741
+ endpoint_url https://127.0.0.1:#{self.class.port}/api/
742
+ ssl_no_verify true
743
+ ]
744
+ d = create_driver config
745
+ d.run(default_tag: 'test.metrics') do
746
+ d.feed({ 'field1' => 50 })
747
+ end
748
+
749
+ assert_equal 1, @posts.size
750
+ record = @posts[0]
751
+
752
+ assert_equal '50', record[:form]['field1']
753
+ end
754
+
755
+ def test_emit_form_ssl_ca
756
+ config = %[
757
+ endpoint_url https://127.0.0.1:#{self.class.port}/api/
758
+ ssl_no_verify true
759
+ cacert_file /tmp/ssl.cert
760
+ ]
761
+ d = create_driver config
762
+ d.run(default_tag: 'test.metrics') do
763
+ d.feed({ 'field1' => 50 })
764
+ end
765
+
766
+ assert_equal 1, @posts.size
767
+ record = @posts[0]
768
+
769
+ assert_equal '50', record[:form]['field1']
770
+ end
771
+ end