fluentd 1.13.3 → 1.16.5
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 +4 -4
- data/.github/ISSUE_TEMPLATE/{bug_report.yaml → bug_report.yml} +2 -0
- data/.github/ISSUE_TEMPLATE/config.yml +2 -2
- data/.github/ISSUE_TEMPLATE/{feature_request.yaml → feature_request.yml} +1 -0
- data/.github/workflows/stale-actions.yml +11 -9
- data/.github/workflows/test.yml +32 -0
- data/CHANGELOG.md +490 -10
- data/CONTRIBUTING.md +2 -2
- data/MAINTAINERS.md +7 -5
- data/README.md +3 -23
- data/Rakefile +1 -1
- data/SECURITY.md +14 -0
- data/fluentd.gemspec +7 -8
- data/lib/fluent/command/cat.rb +13 -3
- data/lib/fluent/command/ctl.rb +6 -3
- data/lib/fluent/command/fluentd.rb +73 -65
- data/lib/fluent/command/plugin_config_formatter.rb +1 -1
- data/lib/fluent/compat/output.rb +9 -6
- data/lib/fluent/config/dsl.rb +1 -1
- data/lib/fluent/config/error.rb +12 -0
- data/lib/fluent/config/literal_parser.rb +2 -2
- data/lib/fluent/config/parser.rb +1 -1
- data/lib/fluent/config/v1_parser.rb +3 -3
- data/lib/fluent/config/yaml_parser/fluent_value.rb +47 -0
- data/lib/fluent/config/yaml_parser/loader.rb +108 -0
- data/lib/fluent/config/yaml_parser/parser.rb +166 -0
- data/lib/fluent/config/yaml_parser/section_builder.rb +107 -0
- data/lib/fluent/config/yaml_parser.rb +56 -0
- data/lib/fluent/config.rb +14 -1
- data/lib/fluent/counter/server.rb +1 -1
- data/lib/fluent/counter/validator.rb +3 -3
- data/lib/fluent/daemon.rb +2 -4
- data/lib/fluent/engine.rb +1 -1
- data/lib/fluent/env.rb +4 -0
- data/lib/fluent/error.rb +3 -0
- data/lib/fluent/event.rb +8 -4
- data/lib/fluent/event_router.rb +47 -2
- data/lib/fluent/file_wrapper.rb +137 -0
- data/lib/fluent/log/console_adapter.rb +66 -0
- data/lib/fluent/log.rb +44 -5
- data/lib/fluent/match.rb +1 -1
- data/lib/fluent/msgpack_factory.rb +6 -1
- data/lib/fluent/oj_options.rb +1 -2
- data/lib/fluent/plugin/bare_output.rb +49 -8
- data/lib/fluent/plugin/base.rb +26 -9
- data/lib/fluent/plugin/buf_file.rb +34 -5
- data/lib/fluent/plugin/buf_file_single.rb +32 -3
- data/lib/fluent/plugin/buffer/file_chunk.rb +1 -1
- data/lib/fluent/plugin/buffer.rb +216 -70
- data/lib/fluent/plugin/filter.rb +35 -1
- data/lib/fluent/plugin/filter_record_transformer.rb +1 -1
- data/lib/fluent/plugin/in_forward.rb +2 -2
- data/lib/fluent/plugin/in_http.rb +39 -10
- data/lib/fluent/plugin/in_monitor_agent.rb +4 -2
- data/lib/fluent/plugin/in_sample.rb +1 -1
- data/lib/fluent/plugin/in_syslog.rb +13 -1
- data/lib/fluent/plugin/in_tail/group_watch.rb +204 -0
- data/lib/fluent/plugin/in_tail/position_file.rb +33 -33
- data/lib/fluent/plugin/in_tail.rb +216 -84
- data/lib/fluent/plugin/in_tcp.rb +47 -2
- data/lib/fluent/plugin/input.rb +39 -1
- data/lib/fluent/plugin/metrics.rb +119 -0
- data/lib/fluent/plugin/metrics_local.rb +96 -0
- data/lib/fluent/plugin/multi_output.rb +43 -6
- data/lib/fluent/plugin/out_copy.rb +1 -1
- data/lib/fluent/plugin/out_exec_filter.rb +2 -2
- data/lib/fluent/plugin/out_file.rb +20 -2
- data/lib/fluent/plugin/out_forward/ack_handler.rb +19 -4
- data/lib/fluent/plugin/out_forward/socket_cache.rb +2 -0
- data/lib/fluent/plugin/out_forward.rb +17 -9
- data/lib/fluent/plugin/out_secondary_file.rb +39 -22
- data/lib/fluent/plugin/output.rb +167 -78
- data/lib/fluent/plugin/parser.rb +3 -4
- data/lib/fluent/plugin/parser_apache2.rb +1 -1
- data/lib/fluent/plugin/parser_json.rb +1 -1
- data/lib/fluent/plugin/parser_syslog.rb +1 -1
- data/lib/fluent/plugin/storage_local.rb +3 -5
- data/lib/fluent/plugin.rb +10 -1
- data/lib/fluent/plugin_helper/child_process.rb +3 -0
- data/lib/fluent/plugin_helper/event_emitter.rb +8 -1
- data/lib/fluent/plugin_helper/event_loop.rb +2 -2
- data/lib/fluent/plugin_helper/http_server/server.rb +2 -1
- data/lib/fluent/plugin_helper/metrics.rb +129 -0
- data/lib/fluent/plugin_helper/record_accessor.rb +1 -1
- data/lib/fluent/plugin_helper/retry_state.rb +14 -4
- data/lib/fluent/plugin_helper/server.rb +35 -6
- data/lib/fluent/plugin_helper/service_discovery.rb +2 -2
- data/lib/fluent/plugin_helper/socket.rb +13 -2
- data/lib/fluent/plugin_helper/thread.rb +3 -3
- data/lib/fluent/plugin_helper.rb +1 -0
- data/lib/fluent/plugin_id.rb +3 -2
- data/lib/fluent/registry.rb +2 -1
- data/lib/fluent/root_agent.rb +6 -0
- data/lib/fluent/rpc.rb +4 -3
- data/lib/fluent/supervisor.rb +283 -259
- data/lib/fluent/system_config.rb +13 -3
- data/lib/fluent/test/driver/base.rb +11 -5
- data/lib/fluent/test/driver/filter.rb +4 -0
- data/lib/fluent/test/startup_shutdown.rb +6 -8
- data/lib/fluent/time.rb +21 -20
- data/lib/fluent/version.rb +1 -1
- data/lib/fluent/win32api.rb +38 -0
- data/lib/fluent/winsvc.rb +5 -8
- data/templates/new_gem/test/helper.rb.erb +0 -1
- data/test/command/test_cat.rb +31 -2
- data/test/command/test_ctl.rb +1 -2
- data/test/command/test_fluentd.rb +209 -24
- data/test/command/test_plugin_config_formatter.rb +0 -1
- data/test/compat/test_parser.rb +6 -6
- data/test/config/test_system_config.rb +13 -11
- data/test/config/test_types.rb +1 -1
- data/test/log/test_console_adapter.rb +110 -0
- data/test/plugin/in_tail/test_io_handler.rb +26 -8
- data/test/plugin/in_tail/test_position_file.rb +48 -59
- data/test/plugin/out_forward/test_ack_handler.rb +39 -0
- data/test/plugin/out_forward/test_socket_cache.rb +26 -1
- data/test/plugin/test_bare_output.rb +14 -1
- data/test/plugin/test_base.rb +133 -1
- data/test/plugin/test_buf_file.rb +62 -23
- data/test/plugin/test_buf_file_single.rb +65 -0
- data/test/plugin/test_buffer.rb +267 -3
- data/test/plugin/test_buffer_chunk.rb +11 -0
- data/test/plugin/test_filter.rb +12 -1
- data/test/plugin/test_filter_parser.rb +1 -1
- data/test/plugin/test_filter_stdout.rb +2 -2
- data/test/plugin/test_in_forward.rb +9 -11
- data/test/plugin/test_in_http.rb +65 -3
- data/test/plugin/test_in_monitor_agent.rb +216 -11
- data/test/plugin/test_in_object_space.rb +9 -3
- data/test/plugin/test_in_syslog.rb +35 -0
- data/test/plugin/test_in_tail.rb +1393 -385
- data/test/plugin/test_in_tcp.rb +87 -2
- data/test/plugin/test_in_udp.rb +28 -0
- data/test/plugin/test_in_unix.rb +2 -2
- data/test/plugin/test_input.rb +12 -1
- data/test/plugin/test_metrics.rb +294 -0
- data/test/plugin/test_metrics_local.rb +96 -0
- data/test/plugin/test_multi_output.rb +25 -1
- data/test/plugin/test_out_exec.rb +6 -4
- data/test/plugin/test_out_exec_filter.rb +6 -2
- data/test/plugin/test_out_file.rb +34 -17
- data/test/plugin/test_out_forward.rb +78 -77
- data/test/plugin/test_out_http.rb +1 -0
- data/test/plugin/test_out_stdout.rb +2 -2
- data/test/plugin/test_output.rb +297 -12
- data/test/plugin/test_output_as_buffered.rb +44 -44
- data/test/plugin/test_output_as_buffered_compress.rb +32 -18
- data/test/plugin/test_output_as_buffered_retries.rb +54 -7
- data/test/plugin/test_output_as_buffered_secondary.rb +4 -4
- data/test/plugin/test_parser_regexp.rb +1 -6
- data/test/plugin/test_parser_syslog.rb +1 -1
- data/test/plugin_helper/test_cert_option.rb +1 -1
- data/test/plugin_helper/test_child_process.rb +38 -16
- data/test/plugin_helper/test_event_emitter.rb +29 -0
- data/test/plugin_helper/test_http_server_helper.rb +1 -1
- data/test/plugin_helper/test_metrics.rb +137 -0
- data/test/plugin_helper/test_retry_state.rb +602 -38
- data/test/plugin_helper/test_server.rb +78 -6
- data/test/plugin_helper/test_timer.rb +2 -2
- data/test/test_config.rb +191 -24
- data/test/test_event_router.rb +17 -0
- data/test/test_file_wrapper.rb +53 -0
- data/test/test_formatter.rb +24 -21
- data/test/test_log.rb +122 -40
- data/test/test_msgpack_factory.rb +32 -0
- data/test/test_plugin_classes.rb +102 -0
- data/test/test_root_agent.rb +30 -1
- data/test/test_supervisor.rb +477 -257
- data/test/test_time_parser.rb +22 -0
- metadata +55 -34
- data/.drone.yml +0 -35
- data/.github/workflows/issue-auto-closer.yml +0 -12
- data/.github/workflows/linux-test.yaml +0 -36
- data/.github/workflows/macos-test.yaml +0 -30
- data/.github/workflows/windows-test.yaml +0 -46
- data/.gitlab-ci.yml +0 -103
- data/lib/fluent/plugin/file_wrapper.rb +0 -187
- data/test/plugin/test_file_wrapper.rb +0 -126
- data/test/test_logger_initializer.rb +0 -46
|
@@ -830,4 +830,69 @@ class FileSingleBufferTest < Test::Unit::TestCase
|
|
|
830
830
|
assert_equal :queued, queue[0].state
|
|
831
831
|
end
|
|
832
832
|
end
|
|
833
|
+
|
|
834
|
+
sub_test_case 'there are existing broken file chunks' do
|
|
835
|
+
setup do
|
|
836
|
+
FileUtils.rm_rf(@bufdir) rescue nil
|
|
837
|
+
FileUtils.mkdir_p(@bufdir)
|
|
838
|
+
end
|
|
839
|
+
|
|
840
|
+
teardown do
|
|
841
|
+
return unless @p
|
|
842
|
+
|
|
843
|
+
@p.stop unless @p.stopped?
|
|
844
|
+
@p.before_shutdown unless @p.before_shutdown?
|
|
845
|
+
@p.shutdown unless @p.shutdown?
|
|
846
|
+
@p.after_shutdown unless @p.after_shutdown?
|
|
847
|
+
@p.close unless @p.closed?
|
|
848
|
+
@p.terminate unless @p.terminated?
|
|
849
|
+
end
|
|
850
|
+
|
|
851
|
+
test '#resume backups empty chunk' do
|
|
852
|
+
id_output = 'backup_test'
|
|
853
|
+
@d = create_driver(%[
|
|
854
|
+
@id #{id_output}
|
|
855
|
+
<buffer tag>
|
|
856
|
+
@type file_single
|
|
857
|
+
path #{PATH}
|
|
858
|
+
</buffer>
|
|
859
|
+
])
|
|
860
|
+
@p = @d.instance.buffer
|
|
861
|
+
|
|
862
|
+
c1id = Fluent::UniqueId.generate
|
|
863
|
+
p1 = File.join(@bufdir, "fsb.foo.b#{Fluent::UniqueId.hex(c1id)}.buf")
|
|
864
|
+
File.open(p1, 'wb') { |f| } # create empty chunk file
|
|
865
|
+
|
|
866
|
+
Fluent::SystemConfig.overwrite_system_config('root_dir' => @bufdir) do
|
|
867
|
+
@p.start
|
|
868
|
+
end
|
|
869
|
+
|
|
870
|
+
assert { not File.exist?(p1) }
|
|
871
|
+
assert { File.exist?("#{@bufdir}/backup/worker0/#{id_output}/#{@d.instance.dump_unique_id_hex(c1id)}.log") }
|
|
872
|
+
end
|
|
873
|
+
|
|
874
|
+
test '#resume throws away broken chunk with disable_chunk_backup' do
|
|
875
|
+
id_output = 'backup_test'
|
|
876
|
+
@d = create_driver(%[
|
|
877
|
+
@id #{id_output}
|
|
878
|
+
<buffer tag>
|
|
879
|
+
@type file_single
|
|
880
|
+
path #{PATH}
|
|
881
|
+
disable_chunk_backup true
|
|
882
|
+
</buffer>
|
|
883
|
+
])
|
|
884
|
+
@p = @d.instance.buffer
|
|
885
|
+
|
|
886
|
+
c1id = Fluent::UniqueId.generate
|
|
887
|
+
p1 = File.join(@bufdir, "fsb.foo.b#{Fluent::UniqueId.hex(c1id)}.buf")
|
|
888
|
+
File.open(p1, 'wb') { |f| } # create empty chunk file
|
|
889
|
+
|
|
890
|
+
Fluent::SystemConfig.overwrite_system_config('root_dir' => @bufdir) do
|
|
891
|
+
@p.start
|
|
892
|
+
end
|
|
893
|
+
|
|
894
|
+
assert { not File.exist?(p1) }
|
|
895
|
+
assert { not File.exist?("#{@bufdir}/backup/worker0/#{id_output}/#{@d.instance.dump_unique_id_hex(c1id)}.log") }
|
|
896
|
+
end
|
|
897
|
+
end
|
|
833
898
|
end
|
data/test/plugin/test_buffer.rb
CHANGED
|
@@ -18,7 +18,7 @@ module FluentPluginBufferTest
|
|
|
18
18
|
end
|
|
19
19
|
class DummyMemoryChunkError < StandardError; end
|
|
20
20
|
class DummyMemoryChunk < Fluent::Plugin::Buffer::MemoryChunk
|
|
21
|
-
attr_reader :append_count, :rollbacked, :closed, :purged
|
|
21
|
+
attr_reader :append_count, :rollbacked, :closed, :purged, :chunk
|
|
22
22
|
attr_accessor :failing
|
|
23
23
|
def initialize(metadata, compress: :text)
|
|
24
24
|
super
|
|
@@ -238,8 +238,14 @@ class BufferTest < Test::Unit::TestCase
|
|
|
238
238
|
assert_nil @p.queue
|
|
239
239
|
assert_nil @p.dequeued
|
|
240
240
|
assert_nil @p.queued_num
|
|
241
|
-
|
|
242
|
-
|
|
241
|
+
assert_nil @p.stage_length_metrics
|
|
242
|
+
assert_nil @p.stage_size_metrics
|
|
243
|
+
assert_nil @p.queue_length_metrics
|
|
244
|
+
assert_nil @p.queue_size_metrics
|
|
245
|
+
assert_nil @p.available_buffer_space_ratios_metrics
|
|
246
|
+
assert_nil @p.total_queued_size_metrics
|
|
247
|
+
assert_nil @p.newest_timekey_metrics
|
|
248
|
+
assert_nil @p.oldest_timekey_metrics
|
|
243
249
|
assert_equal [], @p.timekeys
|
|
244
250
|
end
|
|
245
251
|
|
|
@@ -844,6 +850,116 @@ class BufferTest < Test::Unit::TestCase
|
|
|
844
850
|
test '#compress returns :text' do
|
|
845
851
|
assert_equal :text, @p.compress
|
|
846
852
|
end
|
|
853
|
+
|
|
854
|
+
# https://github.com/fluent/fluentd/issues/3089
|
|
855
|
+
test "closed chunk should not be committed" do
|
|
856
|
+
assert_equal 8 * 1024 * 1024, @p.chunk_limit_size
|
|
857
|
+
assert_equal 0.95, @p.chunk_full_threshold
|
|
858
|
+
|
|
859
|
+
purge_count = 0
|
|
860
|
+
|
|
861
|
+
stub.proxy(@p).generate_chunk(anything) do |chunk|
|
|
862
|
+
stub.proxy(chunk).purge do |result|
|
|
863
|
+
purge_count += 1
|
|
864
|
+
result
|
|
865
|
+
end
|
|
866
|
+
stub.proxy(chunk).commit do |result|
|
|
867
|
+
assert_false(chunk.closed?)
|
|
868
|
+
result
|
|
869
|
+
end
|
|
870
|
+
stub.proxy(chunk).rollback do |result|
|
|
871
|
+
assert_false(chunk.closed?)
|
|
872
|
+
result
|
|
873
|
+
end
|
|
874
|
+
chunk
|
|
875
|
+
end
|
|
876
|
+
|
|
877
|
+
m = @p.metadata(timekey: Time.parse('2016-04-11 16:40:00 +0000').to_i)
|
|
878
|
+
small_row = "x" * 1024 * 400
|
|
879
|
+
big_row = "x" * 1024 * 1024 * 8 # just `chunk_size_limit`, it does't cause BufferOverFlowError.
|
|
880
|
+
|
|
881
|
+
# Write 42 events in 1 event stream, last one is for triggering `ShouldRetry`
|
|
882
|
+
@p.write({m => [small_row] * 40 + [big_row] + ["x"]})
|
|
883
|
+
|
|
884
|
+
# Above event strem will be splitted twice by `Buffer#write_step_by_step`
|
|
885
|
+
#
|
|
886
|
+
# 1. `write_once`: 42 [events] * 1 [stream]
|
|
887
|
+
# 2. `write_step_by_step`: 4 [events]* 10 [streams] + 2 [events] * 1 [stream]
|
|
888
|
+
# 3. `write_step_by_step` (by `ShouldRetry`): 1 [event] * 42 [streams]
|
|
889
|
+
#
|
|
890
|
+
# The problematic data is built in the 2nd stage.
|
|
891
|
+
# In the 2nd stage, 5 streams are packed in a chunk.
|
|
892
|
+
# ((1024 * 400) [bytes] * 4 [events] * 5 [streams] = 8192000 [bytes] < `chunk_limit_size` (8MB)).
|
|
893
|
+
# So 3 chunks are used to store all data.
|
|
894
|
+
# The 1st chunk is already staged by `write_once`.
|
|
895
|
+
# The 2nd & 3rd chunks are newly created as unstaged.
|
|
896
|
+
# The 3rd chunk is purged before `ShouldRetry`, it's no problem:
|
|
897
|
+
# https://github.com/fluent/fluentd/blob/7e9eba736ff40ad985341be800ddc46558be75f2/lib/fluent/plugin/buffer.rb#L850
|
|
898
|
+
# The 2nd chunk is purged in `rescue ShouldRetry`:
|
|
899
|
+
# https://github.com/fluent/fluentd/blob/7e9eba736ff40ad985341be800ddc46558be75f2/lib/fluent/plugin/buffer.rb#L862
|
|
900
|
+
# It causes the issue described in https://github.com/fluent/fluentd/issues/3089#issuecomment-1811839198
|
|
901
|
+
|
|
902
|
+
assert_equal 2, purge_count
|
|
903
|
+
end
|
|
904
|
+
|
|
905
|
+
# https://github.com/fluent/fluentd/issues/4446
|
|
906
|
+
test "#write_step_by_step keeps chunks kept in locked in entire #write process" do
|
|
907
|
+
assert_equal 8 * 1024 * 1024, @p.chunk_limit_size
|
|
908
|
+
assert_equal 0.95, @p.chunk_full_threshold
|
|
909
|
+
|
|
910
|
+
mon_enter_counts_by_chunk = {}
|
|
911
|
+
mon_exit_counts_by_chunk = {}
|
|
912
|
+
|
|
913
|
+
stub.proxy(@p).generate_chunk(anything) do |chunk|
|
|
914
|
+
stub(chunk).mon_enter do
|
|
915
|
+
enter_count = 1 + mon_enter_counts_by_chunk.fetch(chunk, 0)
|
|
916
|
+
exit_count = mon_exit_counts_by_chunk.fetch(chunk, 0)
|
|
917
|
+
mon_enter_counts_by_chunk[chunk] = enter_count
|
|
918
|
+
|
|
919
|
+
# Assert that chunk is passed to &block of write_step_by_step before exiting the lock.
|
|
920
|
+
# (i.e. The lock count must be 2 greater than the exit count).
|
|
921
|
+
# Since ShouldRetry occurs once, the staged chunk takes the lock 3 times when calling the block.
|
|
922
|
+
if chunk.staged?
|
|
923
|
+
lock_in_block = enter_count == 3
|
|
924
|
+
assert_equal(enter_count - 2, exit_count) if lock_in_block
|
|
925
|
+
else
|
|
926
|
+
lock_in_block = enter_count == 2
|
|
927
|
+
assert_equal(enter_count - 2, exit_count) if lock_in_block
|
|
928
|
+
end
|
|
929
|
+
end
|
|
930
|
+
stub(chunk).mon_exit do
|
|
931
|
+
exit_count = 1 + mon_exit_counts_by_chunk.fetch(chunk, 0)
|
|
932
|
+
mon_exit_counts_by_chunk[chunk] = exit_count
|
|
933
|
+
end
|
|
934
|
+
chunk
|
|
935
|
+
end
|
|
936
|
+
|
|
937
|
+
m = @p.metadata(timekey: Time.parse('2016-04-11 16:40:00 +0000').to_i)
|
|
938
|
+
small_row = "x" * 1024 * 400
|
|
939
|
+
big_row = "x" * 1024 * 1024 * 8 # just `chunk_size_limit`, it does't cause BufferOverFlowError.
|
|
940
|
+
|
|
941
|
+
# Write 42 events in 1 event stream, last one is for triggering `ShouldRetry`
|
|
942
|
+
@p.write({m => [small_row] * 40 + [big_row] + ["x"]})
|
|
943
|
+
|
|
944
|
+
# Above event strem will be splitted twice by `Buffer#write_step_by_step`
|
|
945
|
+
#
|
|
946
|
+
# 1. `write_once`: 42 [events] * 1 [stream]
|
|
947
|
+
# 2. `write_step_by_step`: 4 [events]* 10 [streams] + 2 [events] * 1 [stream]
|
|
948
|
+
# 3. `write_step_by_step` (by `ShouldRetry`): 1 [event] * 42 [streams]
|
|
949
|
+
#
|
|
950
|
+
# Example of staged chunk lock behavior:
|
|
951
|
+
#
|
|
952
|
+
# 1. mon_enter in write_step_by_step
|
|
953
|
+
# 2. ShouldRetry occurs
|
|
954
|
+
# 3. mon_exit in write_step_by_step
|
|
955
|
+
# 4. mon_enter again in write_step_by_step (retry)
|
|
956
|
+
# 5. passed to &block of write_step_by_step
|
|
957
|
+
# 6. mon_enter in the block (write)
|
|
958
|
+
# 7. mon_exit in write_step_by_step
|
|
959
|
+
# 8. mon_exit in write
|
|
960
|
+
|
|
961
|
+
assert_equal(mon_enter_counts_by_chunk.values, mon_exit_counts_by_chunk.values)
|
|
962
|
+
end
|
|
847
963
|
end
|
|
848
964
|
|
|
849
965
|
sub_test_case 'standard format with configuration for test with lower chunk limit size' do
|
|
@@ -938,6 +1054,97 @@ class BufferTest < Test::Unit::TestCase
|
|
|
938
1054
|
@p.write({@dm0 => es}, format: @format)
|
|
939
1055
|
end
|
|
940
1056
|
end
|
|
1057
|
+
|
|
1058
|
+
data(
|
|
1059
|
+
first_chunk: Fluent::ArrayEventStream.new([[event_time('2016-04-11 16:00:02 +0000'), {"message" => "x" * 1_280_000}],
|
|
1060
|
+
[event_time('2016-04-11 16:00:02 +0000'), {"message" => "a"}],
|
|
1061
|
+
[event_time('2016-04-11 16:00:02 +0000'), {"message" => "b"}]]),
|
|
1062
|
+
intermediate_chunk: Fluent::ArrayEventStream.new([[event_time('2016-04-11 16:00:02 +0000'), {"message" => "a"}],
|
|
1063
|
+
[event_time('2016-04-11 16:00:02 +0000'), {"message" => "x" * 1_280_000}],
|
|
1064
|
+
[event_time('2016-04-11 16:00:02 +0000'), {"message" => "b"}]]),
|
|
1065
|
+
last_chunk: Fluent::ArrayEventStream.new([[event_time('2016-04-11 16:00:02 +0000'), {"message" => "a"}],
|
|
1066
|
+
[event_time('2016-04-11 16:00:02 +0000'), {"message" => "b"}],
|
|
1067
|
+
[event_time('2016-04-11 16:00:02 +0000'), {"message" => "x" * 1_280_000}]]),
|
|
1068
|
+
multiple_chunks: Fluent::ArrayEventStream.new([[event_time('2016-04-11 16:00:02 +0000'), {"message" => "a"}],
|
|
1069
|
+
[event_time('2016-04-11 16:00:02 +0000'), {"message" => "x" * 1_280_000}],
|
|
1070
|
+
[event_time('2016-04-11 16:00:02 +0000'), {"message" => "b"}],
|
|
1071
|
+
[event_time('2016-04-11 16:00:02 +0000'), {"message" => "x" * 1_280_000}]])
|
|
1072
|
+
)
|
|
1073
|
+
test '#write exceeds chunk_limit_size, raise BufferChunkOverflowError, but not lost whole messages' do |(es)|
|
|
1074
|
+
assert_equal [@dm0], @p.stage.keys
|
|
1075
|
+
assert_equal [], @p.queue.map(&:metadata)
|
|
1076
|
+
|
|
1077
|
+
assert_equal 1_280_000, @p.chunk_limit_size
|
|
1078
|
+
|
|
1079
|
+
nth = []
|
|
1080
|
+
es.entries.each_with_index do |entry, index|
|
|
1081
|
+
if entry.last["message"].size == @p.chunk_limit_size
|
|
1082
|
+
nth << index
|
|
1083
|
+
end
|
|
1084
|
+
end
|
|
1085
|
+
messages = []
|
|
1086
|
+
nth.each do |n|
|
|
1087
|
+
messages << "a 1280025 bytes record (nth: #{n}) is larger than buffer chunk limit size (1280000)"
|
|
1088
|
+
end
|
|
1089
|
+
|
|
1090
|
+
assert_raise Fluent::Plugin::Buffer::BufferChunkOverflowError.new(messages.join(", ")) do
|
|
1091
|
+
@p.write({@dm0 => es}, format: @format)
|
|
1092
|
+
end
|
|
1093
|
+
# message a and b are concatenated and staged
|
|
1094
|
+
staged_messages = Fluent::MessagePackFactory.msgpack_unpacker.feed_each(@p.stage[@dm0].chunk).collect do |record|
|
|
1095
|
+
record.last
|
|
1096
|
+
end
|
|
1097
|
+
assert_equal([2, [{"message" => "a"}, {"message" => "b"}]],
|
|
1098
|
+
[@p.stage[@dm0].size, staged_messages])
|
|
1099
|
+
# only es0 message is queued
|
|
1100
|
+
assert_equal [@dm0], @p.queue.map(&:metadata)
|
|
1101
|
+
assert_equal [5000], @p.queue.map(&:size)
|
|
1102
|
+
end
|
|
1103
|
+
|
|
1104
|
+
test "confirm that every message which is smaller than chunk threshold does not raise BufferChunkOverflowError" do
|
|
1105
|
+
assert_equal [@dm0], @p.stage.keys
|
|
1106
|
+
assert_equal [], @p.queue.map(&:metadata)
|
|
1107
|
+
timestamp = event_time('2016-04-11 16:00:02 +0000')
|
|
1108
|
+
es = Fluent::ArrayEventStream.new([[timestamp, {"message" => "a" * 1_000_000}],
|
|
1109
|
+
[timestamp, {"message" => "b" * 1_000_000}],
|
|
1110
|
+
[timestamp, {"message" => "c" * 1_000_000}]])
|
|
1111
|
+
|
|
1112
|
+
# https://github.com/fluent/fluentd/issues/1849
|
|
1113
|
+
# Even though 1_000_000 < 1_280_000 (chunk_limit_size), it raised BufferChunkOverflowError before.
|
|
1114
|
+
# It should not be raised and message a,b,c should be stored into 3 chunks.
|
|
1115
|
+
assert_nothing_raised do
|
|
1116
|
+
@p.write({@dm0 => es}, format: @format)
|
|
1117
|
+
end
|
|
1118
|
+
messages = []
|
|
1119
|
+
# pick up first letter to check whether chunk is queued in expected order
|
|
1120
|
+
3.times do |index|
|
|
1121
|
+
chunk = @p.queue[index]
|
|
1122
|
+
es = Fluent::MessagePackEventStream.new(chunk.chunk)
|
|
1123
|
+
es.ensure_unpacked!
|
|
1124
|
+
records = es.instance_eval{ @unpacked_records }
|
|
1125
|
+
records.each do |record|
|
|
1126
|
+
messages << record["message"][0]
|
|
1127
|
+
end
|
|
1128
|
+
end
|
|
1129
|
+
es = Fluent::MessagePackEventStream.new(@p.stage[@dm0].chunk)
|
|
1130
|
+
es.ensure_unpacked!
|
|
1131
|
+
staged_message = es.instance_eval{ @unpacked_records }.first["message"]
|
|
1132
|
+
# message a and b are queued, message c is staged
|
|
1133
|
+
assert_equal([
|
|
1134
|
+
[@dm0],
|
|
1135
|
+
"c" * 1_000_000,
|
|
1136
|
+
[@dm0, @dm0, @dm0],
|
|
1137
|
+
[5000, 1, 1],
|
|
1138
|
+
[["x"] * 5000, "a", "b"].flatten
|
|
1139
|
+
],
|
|
1140
|
+
[
|
|
1141
|
+
@p.stage.keys,
|
|
1142
|
+
staged_message,
|
|
1143
|
+
@p.queue.map(&:metadata),
|
|
1144
|
+
@p.queue.map(&:size),
|
|
1145
|
+
messages
|
|
1146
|
+
])
|
|
1147
|
+
end
|
|
941
1148
|
end
|
|
942
1149
|
|
|
943
1150
|
sub_test_case 'custom format with configuration for test with lower chunk limit size' do
|
|
@@ -1026,6 +1233,38 @@ class BufferTest < Test::Unit::TestCase
|
|
|
1026
1233
|
@p.write({@dm0 => es})
|
|
1027
1234
|
end
|
|
1028
1235
|
end
|
|
1236
|
+
|
|
1237
|
+
test 'confirm that every array message which is smaller than chunk threshold does not raise BufferChunkOverflowError' do
|
|
1238
|
+
assert_equal [@dm0], @p.stage.keys
|
|
1239
|
+
assert_equal [], @p.queue.map(&:metadata)
|
|
1240
|
+
|
|
1241
|
+
assert_equal 1_280_000, @p.chunk_limit_size
|
|
1242
|
+
|
|
1243
|
+
es = ["a" * 1_000_000, "b" * 1_000_000, "c" * 1_000_000]
|
|
1244
|
+
assert_nothing_raised do
|
|
1245
|
+
@p.write({@dm0 => es})
|
|
1246
|
+
end
|
|
1247
|
+
queue_messages = @p.queue.collect do |chunk|
|
|
1248
|
+
# collect first character of each message
|
|
1249
|
+
chunk.chunk[0]
|
|
1250
|
+
end
|
|
1251
|
+
assert_equal([
|
|
1252
|
+
[@dm0],
|
|
1253
|
+
1,
|
|
1254
|
+
"c",
|
|
1255
|
+
[@dm0, @dm0, @dm0],
|
|
1256
|
+
[5000, 1, 1],
|
|
1257
|
+
["x", "a", "b"]
|
|
1258
|
+
],
|
|
1259
|
+
[
|
|
1260
|
+
@p.stage.keys,
|
|
1261
|
+
@p.stage[@dm0].size,
|
|
1262
|
+
@p.stage[@dm0].chunk[0],
|
|
1263
|
+
@p.queue.map(&:metadata),
|
|
1264
|
+
@p.queue.map(&:size),
|
|
1265
|
+
queue_messages
|
|
1266
|
+
])
|
|
1267
|
+
end
|
|
1029
1268
|
end
|
|
1030
1269
|
|
|
1031
1270
|
sub_test_case 'with configuration for test with lower limits' do
|
|
@@ -1195,6 +1434,7 @@ class BufferTest < Test::Unit::TestCase
|
|
|
1195
1434
|
sub_test_case 'when compress is gzip' do
|
|
1196
1435
|
setup do
|
|
1197
1436
|
@p = create_buffer({'compress' => 'gzip'})
|
|
1437
|
+
@dm0 = create_metadata(Time.parse('2016-04-11 16:00:00 +0000').to_i, nil, nil)
|
|
1198
1438
|
end
|
|
1199
1439
|
|
|
1200
1440
|
test '#compress returns :gzip' do
|
|
@@ -1205,6 +1445,30 @@ class BufferTest < Test::Unit::TestCase
|
|
|
1205
1445
|
chunk = @p.generate_chunk(create_metadata)
|
|
1206
1446
|
assert chunk.singleton_class.ancestors.include?(Fluent::Plugin::Buffer::Chunk::Decompressable)
|
|
1207
1447
|
end
|
|
1448
|
+
|
|
1449
|
+
test '#write compressed data which exceeds chunk_limit_size, it raises BufferChunkOverflowError' do
|
|
1450
|
+
@p = create_buffer({'compress' => 'gzip', 'chunk_limit_size' => 70})
|
|
1451
|
+
timestamp = event_time('2016-04-11 16:00:02 +0000')
|
|
1452
|
+
es = Fluent::ArrayEventStream.new([[timestamp, {"message" => "012345"}], # overflow
|
|
1453
|
+
[timestamp, {"message" => "aaa"}],
|
|
1454
|
+
[timestamp, {"message" => "bbb"}]])
|
|
1455
|
+
assert_equal [], @p.queue.map(&:metadata)
|
|
1456
|
+
assert_equal 70, @p.chunk_limit_size
|
|
1457
|
+
|
|
1458
|
+
# calculate the actual boundary value. it varies on machine
|
|
1459
|
+
c = @p.generate_chunk(create_metadata)
|
|
1460
|
+
c.append(Fluent::ArrayEventStream.new([[timestamp, {"message" => "012345"}]]), compress: :gzip)
|
|
1461
|
+
overflow_bytes = c.bytesize
|
|
1462
|
+
|
|
1463
|
+
messages = "concatenated/appended a #{overflow_bytes} bytes record (nth: 0) is larger than buffer chunk limit size (70)"
|
|
1464
|
+
assert_raise Fluent::Plugin::Buffer::BufferChunkOverflowError.new(messages) do
|
|
1465
|
+
# test format == nil && compress == :gzip
|
|
1466
|
+
@p.write({@dm0 => es})
|
|
1467
|
+
end
|
|
1468
|
+
# message a and b occupies each chunks in full, so both of messages are queued (no staged chunk)
|
|
1469
|
+
assert_equal([2, [@dm0, @dm0], [1, 1], nil],
|
|
1470
|
+
[@p.queue.size, @p.queue.map(&:metadata), @p.queue.map(&:size), @p.stage[@dm0]])
|
|
1471
|
+
end
|
|
1208
1472
|
end
|
|
1209
1473
|
|
|
1210
1474
|
sub_test_case '#statistics' do
|
|
@@ -57,6 +57,17 @@ class BufferChunkTest < Test::Unit::TestCase
|
|
|
57
57
|
assert chunk.respond_to?(:msgpack_each)
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
+
test 'unpacker arg is not implemented for ChunkMessagePackEventStreamer' do
|
|
61
|
+
meta = Object.new
|
|
62
|
+
chunk = Fluent::Plugin::Buffer::Chunk.new(meta)
|
|
63
|
+
chunk.extend Fluent::ChunkMessagePackEventStreamer
|
|
64
|
+
|
|
65
|
+
unpacker = Fluent::MessagePackFactory.thread_local_msgpack_unpacker
|
|
66
|
+
|
|
67
|
+
assert_raise(NotImplementedError){ chunk.each(unpacker: unpacker) }
|
|
68
|
+
assert_raise(NotImplementedError){ chunk.msgpack_each(unpacker: unpacker) }
|
|
69
|
+
end
|
|
70
|
+
|
|
60
71
|
test 'some methods raise ArgumentError with an option of `compressed: :gzip` and without extending Compressble`' do
|
|
61
72
|
meta = Object.new
|
|
62
73
|
chunk = Fluent::Plugin::Buffer::Chunk.new(meta)
|
data/test/plugin/test_filter.rb
CHANGED
|
@@ -153,7 +153,7 @@ class FilterPluginTest < Test::Unit::TestCase
|
|
|
153
153
|
|
|
154
154
|
@p.configure(config_element('ROOT', '', {'@log_level' => 'debug'}))
|
|
155
155
|
|
|
156
|
-
assert
|
|
156
|
+
assert(@p.log.object_id != original_logger.object_id)
|
|
157
157
|
assert_equal Fluent::Log::LEVEL_DEBUG, @p.log.level
|
|
158
158
|
end
|
|
159
159
|
|
|
@@ -165,6 +165,17 @@ class FilterPluginTest < Test::Unit::TestCase
|
|
|
165
165
|
end
|
|
166
166
|
end
|
|
167
167
|
|
|
168
|
+
test 'can use metrics plugins and fallback methods' do
|
|
169
|
+
@p.configure(config_element('ROOT', '', {'@log_level' => 'debug'}))
|
|
170
|
+
|
|
171
|
+
%w[emit_size_metrics emit_records_metrics].each do |metric_name|
|
|
172
|
+
assert_true @p.instance_variable_get(:"@#{metric_name}").is_a?(Fluent::Plugin::Metrics)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
assert_equal 0, @p.emit_size
|
|
176
|
+
assert_equal 0, @p.emit_records
|
|
177
|
+
end
|
|
178
|
+
|
|
168
179
|
test 'are available with multi worker configuration in default' do
|
|
169
180
|
assert @p.multi_workers_ready?
|
|
170
181
|
end
|
|
@@ -63,7 +63,7 @@ class StdoutFilterTest < Test::Unit::TestCase
|
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
def test_invalid_output_type
|
|
66
|
-
assert_raise(Fluent::
|
|
66
|
+
assert_raise(Fluent::NotFoundPluginError) do
|
|
67
67
|
d = create_driver(CONFIG + config_element("", "", { "output_type" => "foo" }))
|
|
68
68
|
d.run {}
|
|
69
69
|
end
|
|
@@ -139,7 +139,7 @@ class StdoutFilterTest < Test::Unit::TestCase
|
|
|
139
139
|
def test_invalid_output_type
|
|
140
140
|
conf = config_element
|
|
141
141
|
conf.elements << config_element("format", "", { "@type" => "stdout", "output_type" => "foo" })
|
|
142
|
-
assert_raise(Fluent::
|
|
142
|
+
assert_raise(Fluent::NotFoundPluginError) do
|
|
143
143
|
d = create_driver(conf)
|
|
144
144
|
d.run {}
|
|
145
145
|
end
|
|
@@ -68,7 +68,6 @@ class ForwardInputTest < Test::Unit::TestCase
|
|
|
68
68
|
@d = d = create_driver
|
|
69
69
|
assert_equal @port, d.instance.port
|
|
70
70
|
assert_equal '127.0.0.1', d.instance.bind
|
|
71
|
-
assert_equal 0, d.instance.linger_timeout
|
|
72
71
|
assert_equal 0.5, d.instance.blocking_timeout
|
|
73
72
|
assert !d.instance.backlog
|
|
74
73
|
end
|
|
@@ -77,7 +76,6 @@ class ForwardInputTest < Test::Unit::TestCase
|
|
|
77
76
|
@d = d = create_driver(config_auth)
|
|
78
77
|
assert_equal @port, d.instance.port
|
|
79
78
|
assert_equal '127.0.0.1', d.instance.bind
|
|
80
|
-
assert_equal 0, d.instance.linger_timeout
|
|
81
79
|
assert !d.instance.backlog
|
|
82
80
|
|
|
83
81
|
assert d.instance.security
|
|
@@ -369,7 +367,7 @@ class ForwardInputTest < Test::Unit::TestCase
|
|
|
369
367
|
end
|
|
370
368
|
|
|
371
369
|
logs = d.instance.log.out.logs
|
|
372
|
-
assert{ logs.
|
|
370
|
+
assert{ logs.count{|line| line =~ /skip invalid event/ } == 2 }
|
|
373
371
|
|
|
374
372
|
d.instance_shutdown
|
|
375
373
|
end
|
|
@@ -595,10 +593,10 @@ class ForwardInputTest < Test::Unit::TestCase
|
|
|
595
593
|
|
|
596
594
|
# check log
|
|
597
595
|
logs = d.instance.log.logs
|
|
598
|
-
assert_equal 1, logs.
|
|
596
|
+
assert_equal 1, logs.count{|line|
|
|
599
597
|
line =~ / \[warn\]: Input chunk size is larger than 'chunk_size_warn_limit':/ &&
|
|
600
598
|
line =~ / tag="test.tag" host="#{LOCALHOST_HOSTNAME}" limit=16777216 size=16777501/
|
|
601
|
-
}
|
|
599
|
+
}, "large chunk warning is not logged"
|
|
602
600
|
|
|
603
601
|
d.instance_shutdown
|
|
604
602
|
end
|
|
@@ -621,10 +619,10 @@ class ForwardInputTest < Test::Unit::TestCase
|
|
|
621
619
|
|
|
622
620
|
# check log
|
|
623
621
|
logs = d.instance.log.logs
|
|
624
|
-
assert_equal 1, logs.
|
|
622
|
+
assert_equal 1, logs.count{ |line|
|
|
625
623
|
line =~ / \[warn\]: Input chunk size is larger than 'chunk_size_warn_limit':/ &&
|
|
626
624
|
line =~ / tag="test.tag" host="#{LOCALHOST_HOSTNAME}" limit=16777216 size=16777501/
|
|
627
|
-
}
|
|
625
|
+
}, "large chunk warning is not logged"
|
|
628
626
|
|
|
629
627
|
d.instance_shutdown
|
|
630
628
|
end
|
|
@@ -655,10 +653,10 @@ class ForwardInputTest < Test::Unit::TestCase
|
|
|
655
653
|
|
|
656
654
|
# check log
|
|
657
655
|
logs = d.instance.log.logs
|
|
658
|
-
assert_equal 1, logs.
|
|
656
|
+
assert_equal 1, logs.count{|line|
|
|
659
657
|
line =~ / \[warn\]: Input chunk size is larger than 'chunk_size_limit', dropped:/ &&
|
|
660
658
|
line =~ / tag="test.tag" host="#{LOCALHOST_HOSTNAME}" limit=33554432 size=33554989/
|
|
661
|
-
}
|
|
659
|
+
}, "large chunk warning is not logged"
|
|
662
660
|
|
|
663
661
|
d.instance_shutdown
|
|
664
662
|
end
|
|
@@ -678,9 +676,9 @@ class ForwardInputTest < Test::Unit::TestCase
|
|
|
678
676
|
|
|
679
677
|
# check log
|
|
680
678
|
logs = d.instance.log.logs
|
|
681
|
-
assert_equal 1, logs.
|
|
679
|
+
assert_equal 1, logs.count{|line|
|
|
682
680
|
line =~ / \[warn\]: incoming chunk is broken: host="#{LOCALHOST_HOSTNAME}" msg=#{data.inspect}/
|
|
683
|
-
}
|
|
681
|
+
}, "should not accept broken chunk"
|
|
684
682
|
|
|
685
683
|
d.instance_shutdown
|
|
686
684
|
end
|
data/test/plugin/test_in_http.rb
CHANGED
|
@@ -7,9 +7,8 @@ require 'timecop'
|
|
|
7
7
|
class HttpInputTest < Test::Unit::TestCase
|
|
8
8
|
class << self
|
|
9
9
|
def startup
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
ENV['SERVERENGINE_SOCKETMANAGER_PATH'] = socket_manager_path.to_s
|
|
10
|
+
@server = ServerEngine::SocketManager::Server.open
|
|
11
|
+
ENV['SERVERENGINE_SOCKETMANAGER_PATH'] = @server.path.to_s
|
|
13
12
|
end
|
|
14
13
|
|
|
15
14
|
def shutdown
|
|
@@ -540,6 +539,29 @@ class HttpInputTest < Test::Unit::TestCase
|
|
|
540
539
|
assert_equal_event_time time, d.events[1][1]
|
|
541
540
|
end
|
|
542
541
|
|
|
542
|
+
def test_application_ndjson
|
|
543
|
+
d = create_driver
|
|
544
|
+
events = [
|
|
545
|
+
["tag1", 1643935663, "{\"a\":1}\n{\"b\":2}"],
|
|
546
|
+
["tag2", 1643935664, "{\"a\":3}\r\n{\"b\":4}"]
|
|
547
|
+
]
|
|
548
|
+
|
|
549
|
+
expected = [
|
|
550
|
+
["tag1", 1643935663, {"a"=>1}],
|
|
551
|
+
["tag1", 1643935663, {"b"=>2}],
|
|
552
|
+
["tag2", 1643935664, {"a"=>3}],
|
|
553
|
+
["tag2", 1643935664, {"b"=>4}]
|
|
554
|
+
]
|
|
555
|
+
|
|
556
|
+
d.run(expect_records: 1) do
|
|
557
|
+
events.each do |tag, time, record|
|
|
558
|
+
res = post("/#{tag}?time=#{time}", record, {"Content-Type"=>"application/x-ndjson"})
|
|
559
|
+
assert_equal("200", res.code)
|
|
560
|
+
end
|
|
561
|
+
end
|
|
562
|
+
assert_equal(expected, d.events)
|
|
563
|
+
end
|
|
564
|
+
|
|
543
565
|
def test_msgpack
|
|
544
566
|
d = create_driver
|
|
545
567
|
time = event_time("2011-01-02 13:14:15 UTC")
|
|
@@ -856,6 +878,46 @@ class HttpInputTest < Test::Unit::TestCase
|
|
|
856
878
|
end
|
|
857
879
|
end
|
|
858
880
|
|
|
881
|
+
def test_cors_allow_credentials
|
|
882
|
+
d = create_driver(config + %[
|
|
883
|
+
cors_allow_origins ["http://foo.com"]
|
|
884
|
+
cors_allow_credentials
|
|
885
|
+
])
|
|
886
|
+
assert_equal true, d.instance.cors_allow_credentials
|
|
887
|
+
|
|
888
|
+
time = event_time("2011-01-02 13:14:15 UTC")
|
|
889
|
+
event = ["tag1", time, {"a"=>1}]
|
|
890
|
+
res_code = nil
|
|
891
|
+
res_header = nil
|
|
892
|
+
|
|
893
|
+
d.run do
|
|
894
|
+
res = post("/#{event[0]}", {"json"=>event[2].to_json, "time"=>time.to_i.to_s}, {"Origin"=>"http://foo.com"})
|
|
895
|
+
res_code = res.code
|
|
896
|
+
res_header = res["Access-Control-Allow-Credentials"]
|
|
897
|
+
end
|
|
898
|
+
assert_equal(
|
|
899
|
+
{
|
|
900
|
+
response_code: "200",
|
|
901
|
+
allow_credentials_header: "true",
|
|
902
|
+
events: [event]
|
|
903
|
+
},
|
|
904
|
+
{
|
|
905
|
+
response_code: res_code,
|
|
906
|
+
allow_credentials_header: res_header,
|
|
907
|
+
events: d.events
|
|
908
|
+
}
|
|
909
|
+
)
|
|
910
|
+
end
|
|
911
|
+
|
|
912
|
+
def test_cors_allow_credentials_for_wildcard_origins
|
|
913
|
+
assert_raise(Fluent::ConfigError) do
|
|
914
|
+
create_driver(config + %[
|
|
915
|
+
cors_allow_origins ["*"]
|
|
916
|
+
cors_allow_credentials
|
|
917
|
+
])
|
|
918
|
+
end
|
|
919
|
+
end
|
|
920
|
+
|
|
859
921
|
def test_content_encoding_gzip
|
|
860
922
|
d = create_driver
|
|
861
923
|
|